void World6::rotate_planets() { m_dayPeriodSun = new CLerpNodePathInterval("dayPeriodSunInterval", 20, CLerpInterval::BT_no_blend, true, false, m_sun, NodePath()); m_dayPeriodSun->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodSun->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodMercury = new CLerpNodePathInterval("orbitPeriodMercuryInterval", 0.241 * m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootMercury, NodePath()); m_orbitPeriodMercury->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodMercury->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodMercury = new CLerpNodePathInterval("dayPeriodMercuryInterval", 59 * m_dayscale, CLerpInterval::BT_no_blend, true, false, m_mercury, NodePath()); m_dayPeriodMercury->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodMercury->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodVenus = new CLerpNodePathInterval("orbitPeriodVenusInterval", 0.615 * m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootVenus, NodePath()); m_orbitPeriodVenus->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodVenus->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodVenus = new CLerpNodePathInterval("dayPeriodVenusInterval", 243 * m_dayscale, CLerpInterval::BT_no_blend, true, false, m_venus, NodePath()); m_dayPeriodVenus->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodVenus->set_end_hpr (LVecBase3f(360, 0, 0)); // Here the earth interval has been changed to rotate like the rest of the // planets and send a message before it starts turning again. To send a // message, the call is simply messenger.send("message"). The "newYear" // message is picked up by the accept("newYear"...) statement earlier, and // calls the incYear function as a result m_orbitPeriodEarth = new CLerpNodePathInterval("orbitPeriodEarthInterval", m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootEarth, NodePath()); m_orbitPeriodEarth->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodEarth->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodEarth->set_done_event("newYear"); m_dayPeriodEarth = new CLerpNodePathInterval("dayPeriodEarthInterval", m_dayscale, CLerpInterval::BT_no_blend, true, false, m_earth, NodePath()); m_dayPeriodEarth->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodEarth->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodMoon = new CLerpNodePathInterval("orbitPeriodMoonInterval", 0.0749 * m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootMoon, NodePath()); m_orbitPeriodMoon->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodMoon->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodMoon = new CLerpNodePathInterval("dayPeriodMoonInterval", 0.0749 * m_dayscale, CLerpInterval::BT_no_blend, true, false, m_moon, NodePath()); m_dayPeriodMoon->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodMoon->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodMars = new CLerpNodePathInterval("orbitPeriodMarsInterval", 1.881 * m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootMars, NodePath()); m_orbitPeriodMars->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodMars->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodMars = new CLerpNodePathInterval("dayPeriodMarsInterval", 1.03 * m_dayscale, CLerpInterval::BT_no_blend, true, false, m_mars, NodePath()); m_dayPeriodMars->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodMars->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodSun->loop(); m_orbitPeriodMercury->loop(); m_dayPeriodMercury->loop(); m_orbitPeriodVenus->loop(); m_dayPeriodVenus->loop(); m_orbitPeriodEarth->loop(); m_dayPeriodEarth->loop(); m_orbitPeriodMoon->loop(); m_dayPeriodMoon->loop(); m_orbitPeriodMars->loop(); m_dayPeriodMars->loop(); // Note: setup a task to step the interval manager AsyncTaskManager::get_global_ptr()->add( new GenericAsyncTask("intervalManagerTask", step_interval_manager, this)); }
World::World(WindowFramework* windowFrameworkPtr) : m_windowFrameworkPtr(windowFrameworkPtr) { // preconditions if(m_windowFrameworkPtr == NULL) { nout << "ERROR: World::World(WindowFramework* windowFrameworkPtr) parameter windowFrameworkPtr cannot be NULL." << endl; return; } m_keyMap.resize(K_keys); m_keyMap[K_left ] = false; m_keyMap[K_right ] = false; m_keyMap[K_forward ] = false; m_keyMap[K_cam_left ] = false; m_keyMap[K_cam_right] = false; m_windowFrameworkPtr->set_background_type(WindowFramework::BT_black); // Post the instructions m_titleNp = add_title("Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)"); m_inst1Np = add_instructions(0.95, "[ESC]: Quit"); m_inst2Np = add_instructions(0.90, "[Left Arrow]: Rotate Ralph Left"); m_inst3Np = add_instructions(0.85, "[Right Arrow]: Rotate Ralph Right"); m_inst4Np = add_instructions(0.80, "[Up Arrow]: Run Ralph Forward"); m_inst6Np = add_instructions(0.70, "[A]: Rotate Camera Left"); m_inst7Np = add_instructions(0.65, "[S]: Rotate Camera Right"); // Set up the environment // // This environment model contains collision meshes. If you look // in the egg file, you will see the following: // // <Collide> { Polyset keep descend } // // This tag causes the following mesh to be converted to a collision // mesh -- a mesh which is optimized for collision, not rendering. // It also keeps the original mesh, so there are now two copies --- // one optimized for rendering, one for collisions. NodePath modelsNp = m_windowFrameworkPtr->get_panda_framework()->get_models(); m_environNp = m_windowFrameworkPtr->load_model(modelsNp, "../models/world"); NodePath renderNp = m_windowFrameworkPtr->get_render(); m_environNp.reparent_to(renderNp); m_environNp.set_pos(0,0,0); // Create the main character, Ralph LPoint3f ralphStartPos = m_environNp.find("**/start_point").get_pos(); CActor::AnimMap ralphAnims; ralphAnims["../models/ralph-run"].push_back("run"); ralphAnims["../models/ralph-walk"].push_back("walk"); m_ralph.load_actor(m_windowFrameworkPtr, "../models/ralph", &ralphAnims, PartGroup::HMF_ok_wrong_root_name| PartGroup::HMF_ok_anim_extra| PartGroup::HMF_ok_part_extra); m_ralph.reparent_to(renderNp); m_ralph.set_scale(0.2); m_ralph.set_pos(ralphStartPos); // Create a floater object. We use the "floater" as a temporary // variable in a variety of calculations. m_floaterNp = NodePath("floater"); m_floaterNp.reparent_to(renderNp); // Accept the control keys for movement and rotation m_windowFrameworkPtr->enable_keyboard(); m_windowFrameworkPtr->get_panda_framework()->define_key("escape" , "sysExit" , sys_exit , NULL); m_windowFrameworkPtr->get_panda_framework()->define_key("arrow_left" , "left" , call_set_key<K_left , true >, this); m_windowFrameworkPtr->get_panda_framework()->define_key("arrow_right" , "right" , call_set_key<K_right , true >, this); m_windowFrameworkPtr->get_panda_framework()->define_key("arrow_up" , "forward" , call_set_key<K_forward , true >, this); m_windowFrameworkPtr->get_panda_framework()->define_key("a" , "cam-left" , call_set_key<K_cam_left , true >, this); m_windowFrameworkPtr->get_panda_framework()->define_key("s" , "cam-right" , call_set_key<K_cam_right, true >, this); m_windowFrameworkPtr->get_panda_framework()->define_key("arrow_left-up" , "leftUp" , call_set_key<K_left , false>, this); m_windowFrameworkPtr->get_panda_framework()->define_key("arrow_right-up", "rightUp" , call_set_key<K_right , false>, this); m_windowFrameworkPtr->get_panda_framework()->define_key("arrow_up-up" , "forwardUp" , call_set_key<K_forward , false>, this); m_windowFrameworkPtr->get_panda_framework()->define_key("a-up" , "cam-leftUp" , call_set_key<K_cam_left , false>, this); m_windowFrameworkPtr->get_panda_framework()->define_key("s-up" , "cam-rightUp", call_set_key<K_cam_right, false>, this); PT(GenericAsyncTask) taskPtr = new GenericAsyncTask("moveTask", call_move, this); if(taskPtr != NULL) { AsyncTaskManager::get_global_ptr()->add(taskPtr); } // Game state variables m_isMoving = false; // Set up the camera // Note: no need to disable the mouse in C++ NodePath cameraNp = m_windowFrameworkPtr->get_camera_group(); cameraNp.set_pos(m_ralph.get_x(), m_ralph.get_y()+10, 2); // We will detect the height of the terrain by creating a collision // ray and casting it downward toward the terrain. One ray will // start above ralph's head, and the other will start above the camera. // A ray may hit the terrain, or it may hit a rock or a tree. If it // hits the terrain, we can detect the height. If it hits anything // else, we rule that the move is illegal. NodePath ralphGroundColNp; m_ralphGroundRayPtr = new CollisionRay(); if(m_ralphGroundRayPtr != NULL) { m_ralphGroundRayPtr->set_origin(0, 0, 1000); m_ralphGroundRayPtr->set_direction(0, 0, -1); m_ralphGroundColPtr = new CollisionNode("ralphRay"); if(m_ralphGroundColPtr != NULL) { m_ralphGroundColPtr->add_solid(m_ralphGroundRayPtr); m_ralphGroundColPtr->set_from_collide_mask(BitMask32::bit(0)); m_ralphGroundColPtr->set_into_collide_mask(BitMask32::all_off()); ralphGroundColNp = m_ralph.attach_new_node(m_ralphGroundColPtr); m_ralphGroundHandlerPtr = new CollisionHandlerQueue(); if(m_ralphGroundHandlerPtr != NULL) { m_collisionTraverser.add_collider(ralphGroundColNp, m_ralphGroundHandlerPtr); } } } NodePath camGroundColNp; m_camGroundRayPtr = new CollisionRay(); if(m_camGroundRayPtr != NULL) { m_camGroundRayPtr->set_origin(0, 0, 1000); m_camGroundRayPtr->set_direction(0, 0, -1); m_camGroundColPtr = new CollisionNode("camRay"); if(m_camGroundColPtr != NULL) { m_camGroundColPtr->add_solid(m_camGroundRayPtr); m_camGroundColPtr->set_from_collide_mask(BitMask32::bit(0)); m_camGroundColPtr->set_into_collide_mask(BitMask32::all_off()); camGroundColNp = cameraNp.attach_new_node(m_camGroundColPtr); m_camGroundHandlerPtr = new CollisionHandlerQueue(); if(m_camGroundHandlerPtr != NULL) { m_collisionTraverser.add_collider(camGroundColNp, m_camGroundHandlerPtr); } } } // Uncomment this line to see the collision rays //ralphGroundColNp.show(); //camGroundColNp.show(); // Uncomment this line to show a visual representation of the // collisions occuring //m_collisionTraverser.show_collisions(renderNp); // Create some lighting PT(AmbientLight) ambientLightPtr = new AmbientLight("ambientLight"); if(ambientLightPtr != NULL) { ambientLightPtr->set_color(Colorf(.3, .3, .3, 1)); renderNp.set_light(renderNp.attach_new_node(ambientLightPtr)); } PT(DirectionalLight) directionalLightPtr = new DirectionalLight("directionalLightPtr"); if(directionalLightPtr != NULL) { directionalLightPtr->set_direction(LVecBase3f(-5, -5, -5)); directionalLightPtr->set_color(Colorf(1, 1, 1, 1)); directionalLightPtr->set_specular_color(Colorf(1, 1, 1, 1)); renderNp.set_light(renderNp.attach_new_node(directionalLightPtr)); } }
World::World(WindowFramework* windowFramework) : m_windowFramework(windowFramework), m_title(), m_inst1(), m_inst2(), m_inst3(), m_inst4(), m_altCam(), m_teapot(), m_teapotInterval(), m_bufferViewer(NULL) // m_tvMen { // Note: set background color here m_windowFramework->get_graphics_output()->get_active_display_region(0)-> set_clear_color(Colorf(0, 0, 0, 1)); // Post the instructions. m_title = add_title("Panda3D: Tutorial - Using Render-to-Texture"); m_inst1 = add_instructions(0.95,"ESC: Quit"); m_inst2 = add_instructions(0.90,"Up/Down: Zoom in/out on the Teapot"); m_inst3 = add_instructions(0.85,"Left/Right: Move teapot left/right"); m_inst4 = add_instructions(0.80,"V: View the render-to-texture results"); //we get a handle to the default window PT(GraphicsOutput) mainWindow = m_windowFramework->get_graphics_output(); // we now get buffer thats going to hold the texture of our new scene PT(GraphicsOutput) altBuffer = mainWindow->make_texture_buffer( "hello", 256, 256); // now we have to setup a new scene graph to make this scene NodePath altRender("new render"); // this takes care of setting up the camera properly m_altCam = m_windowFramework->make_camera(); // Note: set the size and shape of the "film" within the lens equal to the // buffer of our new scene DCAST(Camera, m_altCam.node())->get_lens()->set_film_size( altBuffer->get_x_size(), altBuffer->get_y_size()); // Note: make a DisplayRegion for the camera PT(DisplayRegion) dr = altBuffer->make_display_region(0, 1, 0, 1); dr->set_sort(0); dr->set_camera(m_altCam); m_altCam.reparent_to(altRender); m_altCam.set_pos(0, -10, 0); // get the teapot and rotates it for a simple animation const NodePath& models = m_windowFramework->get_panda_framework()->get_models(); m_teapot = m_windowFramework->load_model(models, "../models/teapot"); m_teapot.reparent_to(altRender); m_teapot.set_pos(0, 0, -1); const bool bakeInStart = true; const bool fluid = false; m_teapotInterval = new CLerpNodePathInterval("teapotInterval", 1.5, CLerpInterval::BT_no_blend, bakeInStart, fluid, m_teapot, NodePath()); m_teapotInterval->set_start_hpr(m_teapot.get_hpr()); m_teapotInterval->set_end_hpr(LVecBase3f(m_teapot.get_h()+360, m_teapot.get_p()+360, m_teapot.get_r()+360)); m_teapotInterval->loop(); // put some lighting on the teapot PT(DirectionalLight) dlight = new DirectionalLight("dlight"); PT(AmbientLight) alight = new AmbientLight("alight"); NodePath dlnp = altRender.attach_new_node(dlight); NodePath alnp = altRender.attach_new_node(alight); dlight->set_color(Colorf(0.8, 0.8, 0.5, 1)); alight->set_color(Colorf(0.2, 0.2, 0.2, 1)); dlnp.set_hpr(0, -60, 0); altRender.set_light(dlnp); altRender.set_light(alnp); // Panda contains a built-in viewer that lets you view the results of // your render-to-texture operations. This code configures the viewer. WORLD_DEFINE_KEY("v", "toggleBufferViewer", toggle_buffer_viewer); m_bufferViewer = new CBufferViewer(m_windowFramework); m_bufferViewer->set_position(CBufferViewer::CP_llcorner); m_bufferViewer->set_card_size(1.0, 0.0); // Create the tv-men. Each TV-man will display the // offscreen-texture on his TV screen. make_tv_man(-5, 30, 1, altBuffer->get_texture(), 0.9); make_tv_man( 5, 30, 1, altBuffer->get_texture(), 1.4); make_tv_man( 0, 23, -3, altBuffer->get_texture(), 2.0); make_tv_man(-5, 20, -6, altBuffer->get_texture(), 1.1); make_tv_man( 5, 18, -5, altBuffer->get_texture(), 1.7); WORLD_DEFINE_KEY("escape", "exit", quit); WORLD_DEFINE_KEY("arrow_up", "zoomIn", zoom_in); WORLD_DEFINE_KEY("arrow_down", "zoomOut", zoom_out); WORLD_DEFINE_KEY("arrow_left", "moveLeft", move_left); WORLD_DEFINE_KEY("arrow_right", "moveRight", move_right); WORLD_ADD_TASK("worldAsyncTask", async_task); }
World::World(WindowFramework* windowFrameworkPtr) : m_windowFrameworkPtr(windowFrameworkPtr) { // preconditions if(m_windowFrameworkPtr == NULL) { nout << "ERROR: parameter windowFrameworkPtr cannot be NULL." << endl; return; } // This code puts the standard title and instruction text on screen COnscreenText title("title", COnscreenText::TS_plain); title.set_text("Panda3D: Tutorial - Joint Manipulation"); title.set_fg(Colorf(1,1,1,1)); title.set_pos(LVecBase2f(0.7, -0.95)); title.set_scale(0.07); title.reparent_to(m_windowFrameworkPtr->get_aspect_2d()); m_titleNp = title.generate(); m_esckeyTextNp = gen_label_text("ESC: Quit" , 0); m_onekeyTextNp = gen_label_text("[1]: Teapot" , 1); m_twokeyTextNp = gen_label_text("[2]: Candy cane", 2); m_threekeyTextNp = gen_label_text("[3]: Banana" , 3); m_fourkeyTextNp = gen_label_text("[4]: Sword" , 4); // setup key input m_windowFrameworkPtr->enable_keyboard(); m_windowFrameworkPtr->get_panda_framework()->define_key("escape", "Exit" , sys_exit , NULL); m_windowFrameworkPtr->get_panda_framework()->define_key("1" , "Teapot" , call_set_object<M_teapot >, this); m_windowFrameworkPtr->get_panda_framework()->define_key("2" , "CandyCane", call_set_object<M_candy_cane>, this); m_windowFrameworkPtr->get_panda_framework()->define_key("3" , "Banana" , call_set_object<M_banana >, this); m_windowFrameworkPtr->get_panda_framework()->define_key("4" , "Sword" , call_set_object<M_sword >, this); // Disable mouse-based camera-control // Note: disable by default in C++ // Position the camera m_windowFrameworkPtr->get_camera_group().set_pos(0,-15, 2); // Load our animated character // Note: File eve_walk.egg is broken! // The name of the animation is case sensitive and at line 13 of file // eve_walk.egg you should read `Eve' instead of `eve'. You need to // correct this in order to bind the animation automatically using // auto_bind() or WindowFramework::loop_animations(). Or you can use // PartGroup::HMF_ok_wrong_root_name to ask auto_bind() to be more // forgiving. CActor::AnimMap eveAnims; eveAnims["../models/eve_walk"].push_back("walk"); m_eve.load_actor(m_windowFrameworkPtr, "../models/eve", &eveAnims, PartGroup::HMF_ok_wrong_root_name); // Put it in the scene NodePath renderNp = m_windowFrameworkPtr->get_render(); m_eve.reparent_to(renderNp); // Now we use control_joint to get a NodePath that's in control of her neck // This must be done before any animations are played m_eveNeckNp = m_eve.control_joint("Neck"); // We now play an animation. An animation must be played, or at least posed // for the nodepath we just got from control_joint to actually effect the model m_eve.find_anim("walk")->set_play_rate(2); m_eve.loop("walk", true); // Now we add a task that will take care of turning the head PT(GenericAsyncTask) turnHeadTask = new GenericAsyncTask("turnHead", call_turn_head, this); if(turnHeadTask != NULL) { AsyncTaskManager::get_global_ptr()->add(turnHeadTask); } // Now we will expose the joint the hand joint. ExposeJoint allows us to // get the position of a joint while it is animating. This is different than // control_joint which stops that joint from animating but lets us move it. // This is particularly useful for putting an object (like a weapon) in an // actor's hand m_eveRightHandNp = m_eve.expose_joint("RightHand"); // This is a table with models, positions, rotations, and scales of objects to // be attached to our exposed joint. These are stock models and so they needed // to be repositioned to look right. vector<ModelData> positions(M_models); positions[M_teapot ] = ModelData("../models/teapot" , LVecBase3f(0.00,-0.66,-0.95), LVecBase3f(90, 0,90), 0.40); positions[M_candy_cane] = ModelData("../models/candycane", LVecBase3f(0.15,-0.99,-0.22), LVecBase3f(90, 0,90), 1.00); positions[M_banana ] = ModelData("../models/banana" , LVecBase3f(0.08,-0.10, 0.09), LVecBase3f( 0,-90, 0), 1.75); positions[M_sword ] = ModelData("../models/sword" , LVecBase3f(0.11, 0.19, 0.06), LVecBase3f( 0, 0,90), 1.00); // A list that will store our models objects m_modelsNp.reserve(M_models); NodePath modelsNp = m_windowFrameworkPtr->get_panda_framework()->get_models(); for(vector<ModelData>::iterator i = positions.begin(); i < positions.end(); ++i) { // Load the model NodePath np = m_windowFrameworkPtr->load_model(modelsNp, i->m_filename); // Position it np.set_pos(i->m_pos); // Rotate it np.set_hpr(i->m_hpr); // Scale it np.set_scale(i->m_scale); // Reparent the model to the exposed joint. That way when the joint moves, // the model we just loaded will move with it. np.reparent_to(m_eveRightHandNp); // Add it to our models list m_modelsNp.push_back(np); } // Make object 0 the first shown set_object(M_teapot); // Put in some default lighting setup_lights(); }
World::World(WindowFramework* windowFrameworkPtr) : UP(0,0,1), m_windowFrameworkPtr(windowFrameworkPtr) { // preconditions if(m_windowFrameworkPtr == NULL) { nout << "ERROR: parameter windowFrameworkPtr cannot be NULL." << endl; return; } // This code puts the standard title and instruction text on screen COnscreenText title("title", COnscreenText::TS_plain); title.set_text("Panda3D: Tutorial - Collision Detection"); title.set_fg(Colorf(1,1,1,1)); title.set_pos(LVecBase2f(0.7,-0.95)); title.set_scale(0.07); title.reparent_to(m_windowFrameworkPtr->get_aspect_2d()); m_titleNp = title.generate(); COnscreenText instructions("instructions"); instructions.set_text("Mouse pointer tilts the board"); instructions.set_pos(LVecBase2f(-1.3, 0.95)); instructions.set_fg(Colorf(1,1,1,1)); instructions.set_align(TextNode::A_left); instructions.set_scale(0.05); instructions.reparent_to(m_windowFrameworkPtr->get_aspect_2d()); m_instructionsNp = instructions.generate(); // Escape quits m_windowFrameworkPtr->enable_keyboard(); m_windowFrameworkPtr->get_panda_framework()->define_key("escape", "sysExit", sys_exit, NULL); // Disable mouse-based camera control // Note: irrelevant in C++ // Place the camera NodePath cameraNp = m_windowFrameworkPtr->get_camera_group(); cameraNp.set_pos_hpr(0, 0, 25, 0, -90, 0); // Load the maze and place it in the scene NodePath modelsNp = m_windowFrameworkPtr->get_panda_framework()->get_models(); m_mazeNp = m_windowFrameworkPtr->load_model(modelsNp, "../models/maze"); NodePath renderNp = m_windowFrameworkPtr->get_render(); m_mazeNp.reparent_to(renderNp); // Most times, you want collisions to be tested against invisible geometry // rather than every polygon. This is because testing against every polygon // in the scene is usually too slow. You can have simplified or approximate // geometry for the solids and still get good results. // // Sometimes you'll want to create and position your own collision solids in // code, but it's often easier to have them built automatically. This can be // done by adding special tags into an egg file. Check maze.egg and ball.egg // and look for lines starting with <Collide>. The part is brackets tells // Panda exactly what to do. Polyset means to use the polygons in that group // as solids, while Sphere tells panda to make a collision sphere around them // Keep means to keep the polygons in the group as visable geometry (good // for the ball, not for the triggers), and descend means to make sure that // the settings are applied to any subgroups. // // Once we have the collision tags in the models, we can get to them using // NodePath's find command // Find the collision node named wall_collide m_wallsNp = m_mazeNp.find("**/wall_collide"); // Collision objects are sorted using BitMasks. BitMasks are ordinary numbers // with extra methods for working with them as binary bits. Every collision // solid has both a from mask and an into mask. Before Panda tests two // objects, it checks to make sure that the from and into collision masks // have at least one bit in common. That way things that shouldn't interact // won't. Normal model nodes have collision masks as well. By default they // are set to bit 20. If you want to collide against actual visable polygons, // set a from collide mask to include bit 20 // // For this example, we will make everything we want the ball to collide with // include bit 0 m_wallsNp.node()->set_into_collide_mask(BitMask32::bit(0)); // CollisionNodes are usually invisible but can be shown. Uncomment the next // line to see the collision walls // m_wallsNp.show(); // We will now find the triggers for the holes and set their masks to 0 as // well. We also set their names to make them easier to identify during // collisions m_loseTriggers.reserve(NB_HOLES); for(int i = 0; i < NB_HOLES; ++i) { ostringstream filename; filename << "**/hole_collide" << i; NodePath triggerNp = m_mazeNp.find(filename.str()); triggerNp.node()->set_into_collide_mask(BitMask32::bit(0)); triggerNp.node()->set_name("loseTrigger"); m_loseTriggers.push_back(triggerNp); // Uncomment this line to see the triggers // triggerNp.show(); } // Ground_collide is a single polygon on the same plane as the ground in the // maze. We will use a ray to collide with it so that we will know exactly // what height to put the ball at every frame. Since this is not something // that we want the ball itself to collide with, it has a different // bitmask. m_mazeGroundNp = m_mazeNp.find("**/ground_collide"); m_mazeGroundNp.node()->set_into_collide_mask(BitMask32::bit(1)); // Load the ball and attach it to the scene // It is on a root dummy node so that we can rotate the ball itself without // rotating the ray that will be attached to it m_ballRootNp = renderNp.attach_new_node("ballRoot"); m_ballNp = m_windowFrameworkPtr->load_model(modelsNp, "../models/ball"); m_ballNp.reparent_to(m_ballRootNp); // Find the collision sphere for the ball which was created in the egg file // Notice that it has a from collision mask of bit 0, and an into collision // mask of no bits. This means that the ball can only cause collisions, not // be collided into m_ballSphereNp = m_ballNp.find("**/ball"); DCAST(CollisionNode, m_ballSphereNp.node())->set_from_collide_mask(BitMask32::bit(0)); m_ballSphereNp.node()->set_into_collide_mask(BitMask32::all_off()); // No we create a ray to start above the ball and cast down. This is to // Determine the height the ball should be at and the angle the floor is // tilting. We could have used the sphere around the ball itself, but it // would not be as reliable // Create the ray m_ballGroundRayPtr = new CollisionRay(); if(m_ballGroundRayPtr != NULL) { // Set its origin m_ballGroundRayPtr->set_origin(0,0,10); // And its direction m_ballGroundRayPtr->set_direction(0,0,-1); // Collision solids go in CollisionNode // Create and name the node m_ballGroundColPtr = new CollisionNode("groundRay"); if(m_ballGroundColPtr != NULL) { // Add the ray m_ballGroundColPtr->add_solid(m_ballGroundRayPtr); // Set its bitmasks m_ballGroundColPtr->set_from_collide_mask(BitMask32::bit(1)); m_ballGroundColPtr->set_into_collide_mask(BitMask32::all_off()); // Attach the node to the ballRoot so that the ray is relative to the ball // (it will always be 10 feet over the ball and point down) m_ballGroundColNp = m_ballRootNp.attach_new_node(m_ballGroundColPtr); // Uncomment this line to see the ray // m_ballGroundColNp.show(); } } // Finally, we create a CollisionTraverser. CollisionTraversers are what // do the job of calculating collisions // Note: no need to in this implementation // Collision traversers tell collision handlers about collisions, and then // the handler decides what to do with the information. We are using a // CollisionHandlerQueue, which simply creates a list of all of the // collisions in a given pass. There are more sophisticated handlers like // one that sends events and another that tries to keep collided objects // apart, but the results are often better with a simple queue m_cHandlerPtr = new CollisionHandlerQueue(); if(m_cHandlerPtr != NULL) { // Now we add the collision nodes that can create a collision to the // traverser. The traverser will compare these to all others nodes in the // scene. There is a limit of 32 CollisionNodes per traverser // We add the collider, and the handler to use as a pair m_cTrav.add_collider(m_ballSphereNp, m_cHandlerPtr); m_cTrav.add_collider(m_ballGroundColNp, m_cHandlerPtr); } // Collision traversers have a built in tool to help visualize collisions. // Uncomment the next line to see it. // m_cTrav.show_collisions(renderNp); // This section deals with lighting for the ball. Only the ball was lit // because the maze has static lighting pregenerated by the modeler PT(AmbientLight) ambientLightPtr = new AmbientLight("ambientLight"); if(ambientLightPtr != NULL) { ambientLightPtr->set_color(Colorf(0.55, 0.55, 0.55, 1)); m_ballRootNp.set_light(renderNp.attach_new_node(ambientLightPtr)); } PT(DirectionalLight) directionalLightPtr = new DirectionalLight("directionalLight"); if(directionalLightPtr != NULL) { directionalLightPtr->set_direction(LVecBase3f(0, 0, -1)); directionalLightPtr->set_color(Colorf(0.375, 0.375, 0.375, 1)); directionalLightPtr->set_specular_color(Colorf(1, 1, 1, 1)); m_ballRootNp.set_light(renderNp.attach_new_node(directionalLightPtr)); } // This section deals with adding a specular highlight to the ball to make // it look shiny PT(Material) materialPtr = new Material(); if(materialPtr != NULL) { materialPtr->set_specular(Colorf(1,1,1,1)); materialPtr->set_shininess(96); m_ballNp.set_material(materialPtr, 1); } // Finally, we call start for more initialization start(); }
void World5::rotate_planets() { // rotatePlanets creates intervals to actually use the hierarchy we created // to turn the sun, planets, and moon to give a rough representation of the // solar system. The next lesson will go into more depth on intervals. m_dayPeriodSun = new CLerpNodePathInterval("dayPeriodSunInterval", 20, CLerpInterval::BT_no_blend, true, false, m_sun, NodePath()); m_dayPeriodSun->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodSun->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodMercury = new CLerpNodePathInterval("orbitPeriodMercuryInterval", 0.241 * m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootMercury, NodePath()); m_orbitPeriodMercury->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodMercury->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodMercury = new CLerpNodePathInterval("dayPeriodMercuryInterval", 59 * m_dayscale, CLerpInterval::BT_no_blend, true, false, m_mercury, NodePath()); m_dayPeriodMercury->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodMercury->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodVenus = new CLerpNodePathInterval("orbitPeriodVenusInterval", 0.615 * m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootVenus, NodePath()); m_orbitPeriodVenus->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodVenus->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodVenus = new CLerpNodePathInterval("dayPeriodVenusInterval", 243 * m_dayscale, CLerpInterval::BT_no_blend, true, false, m_venus, NodePath()); m_dayPeriodVenus->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodVenus->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodEarth = new CLerpNodePathInterval("orbitPeriodEarthInterval", m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootEarth, NodePath()); m_orbitPeriodEarth->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodEarth->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodEarth = new CLerpNodePathInterval("dayPeriodEarthInterval", m_dayscale, CLerpInterval::BT_no_blend, true, false, m_earth, NodePath()); m_dayPeriodEarth->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodEarth->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodMoon = new CLerpNodePathInterval("orbitPeriodMoonInterval", 0.0749 * m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootMoon, NodePath()); m_orbitPeriodMoon->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodMoon->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodMoon = new CLerpNodePathInterval("dayPeriodMoonInterval", 0.0749 * m_dayscale, CLerpInterval::BT_no_blend, true, false, m_moon, NodePath()); m_dayPeriodMoon->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodMoon->set_end_hpr (LVecBase3f(360, 0, 0)); m_orbitPeriodMars = new CLerpNodePathInterval("orbitPeriodMarsInterval", 1.881 * m_yearscale, CLerpInterval::BT_no_blend, true, false, m_orbitRootMars, NodePath()); m_orbitPeriodMars->set_start_hpr(LVecBase3f( 0, 0, 0)); m_orbitPeriodMars->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodMars = new CLerpNodePathInterval("dayPeriodMarsInterval", 1.03 * m_dayscale, CLerpInterval::BT_no_blend, true, false, m_mars, NodePath()); m_dayPeriodMars->set_start_hpr(LVecBase3f( 0, 0, 0)); m_dayPeriodMars->set_end_hpr (LVecBase3f(360, 0, 0)); m_dayPeriodSun->loop(); m_orbitPeriodMercury->loop(); m_dayPeriodMercury->output(nout); m_dayPeriodMercury->loop(); m_orbitPeriodVenus->loop(); m_dayPeriodVenus->loop(); m_orbitPeriodEarth->loop(); m_dayPeriodEarth->loop(); m_orbitPeriodMoon->loop(); m_dayPeriodMoon->loop(); m_orbitPeriodMars->loop(); m_dayPeriodMars->loop(); // Note: setup a task to step the interval manager AsyncTaskManager::get_global_ptr()->add( new GenericAsyncTask("intervalManagerTask", step_interval_manager, this)); }