// Function to put title on the screen. NodePath World::add_title(const string& text) const { COnscreenText title("title", COnscreenText::TS_plain); title.set_text(text); title.set_fg(Colorf(1,1,1,1)); title.set_pos(LVecBase2f(1.3,-0.95)); title.set_align(TextNode::A_right); title.set_scale(0.07); title.reparent_to(m_windowFrameworkPtr->get_aspect_2d()); return title.generate(); }
// Macro-like function used to reduce the amount to code needed to create the // on screen instructions COnscreenText World6::gen_label_text(const string& text, int i) const { COnscreenText label("label"); label.set_text(text); label.set_pos(LVecBase2f(-1.3, 0.95-0.05*i)); label.set_fg(Colorf(1, 1, 1, 1)); label.set_align(TextNode::A_left); label.set_scale(0.05); label.reparent_to(m_windowFramework->get_aspect_2d()); return label; }
// Function to put instructions on the screen. NodePath World::add_instructions(float pos, const string& msg) const { COnscreenText instructions("instructions", COnscreenText::TS_plain); instructions.set_text(msg); instructions.set_fg(Colorf(1,1,1,1)); instructions.set_pos(LVecBase2f(-1.3, pos)); instructions.set_align(TextNode::A_left); instructions.set_scale(0.05); instructions.reparent_to(m_windowFrameworkPtr->get_aspect_2d()); return instructions.generate(); }
// Function to put title on the screen. NodePath World::add_title(const string& text) { COnscreenText title("title"); title.set_text(text); title.set_fg(Colorf(1, 1, 1, 1)); title.set_pos(LVecBase2f(1.3, -0.95)); title.set_align(TextNode::A_right); title.set_scale(0.07); title.reparent_to(m_windowFramework->get_aspect_2d()); // Note: set the draw order to make sure the the BufferViewer // renders in front of the title just like in the // Python original version. title.set_draw_order(0); return title.generate(); }
// The initialization method caused when a world object is created World6::World6(WindowFramework* windowFrameworkPtr) : m_windowFramework(windowFrameworkPtr), m_title("title", COnscreenText::TS_plain), m_sizescale(1), m_sky(), m_skyTex(NULL), m_sun(), m_sunTex(NULL), m_orbitscale(1), m_orbitRootMercury(), m_orbitRootVenus(), m_orbitRootMars(), m_orbitRootEarth(), m_orbitRootMoon(), m_mercury(), m_mercuryTex(NULL), m_venus(), m_venusTex(NULL), m_mars(), m_marsTex(NULL), m_earth(), m_earthTex(NULL), m_moon(), m_moonTex(NULL), m_yearscale(1), m_dayscale(1), m_dayPeriodSun(NULL), m_orbitPeriodMercury(NULL), m_dayPeriodMercury(NULL), m_orbitPeriodVenus(NULL), m_dayPeriodVenus(NULL), m_orbitPeriodEarth(NULL), m_dayPeriodEarth(NULL), m_orbitPeriodMoon(NULL), m_dayPeriodMoon(NULL), m_orbitPeriodMars(NULL), m_dayPeriodMars(NULL), m_mouse1EventText("label"), m_skeyEventText("label"), m_ykeyEventText("label"), m_vkeyEventText("label"), m_ekeyEventText("label"), m_mkeyEventText("label"), m_yearCounterText("label"), m_yearCounter(0), m_simRunning(true) { // The standard camera position and background initialization m_windowFramework->get_graphics_window()->get_active_display_region(0)-> set_clear_color(Colorf(0, 0, 0, 0)); // base.disableMouse() // Note: mouse ain't enable by default in C++ NodePath camera = m_windowFramework->get_camera_group(); camera.set_pos(0, 0, 45); camera.set_hpr(0, -90, 0); // The global variables we used to control the speed and size of objects m_yearscale = 60; m_dayscale = m_yearscale / 365.0 * 5; m_orbitscale = 10; m_sizescale = 0.6; load_planets(); // Load, texture, and position the planets rotate_planets(); // Set up the motion to start them moving // The standard title text that's in every tutorial // Things to note: // -fg represents the forground color of the text in (r,g,b,a) format // -pos represents the position of the text on the screen. // The coordinate system is a x-y based wih 0,0 as the center of the // screen // -align sets the alingment of the text relative to the pos argument. // Default is center align. // -scale set the scale of the text // -mayChange argument lets us change the text later in the program. // By default mayChange is set to 0. Trying to change text when // mayChange is set to 0 will cause the program to crash. m_title.set_text("Panda3D: Tutorial 1 - Solar System"); m_title.set_fg(Colorf(1, 1, 1, 1)); m_title.set_pos(LVecBase2f(0.8, -0.95)); m_title.set_scale(0.07); m_title.reparent_to(m_windowFramework->get_aspect_2d()); m_mouse1EventText = gen_label_text( "Mouse Button 1: Toggle entire Solar System [RUNNING]", 0); m_skeyEventText = gen_label_text("[S]: Toggle Sun [RUNNING]", 1); m_ykeyEventText = gen_label_text("[Y]: Toggle Mercury [RUNNING]", 2); m_vkeyEventText = gen_label_text("[V]: Toggle Venus [RUNNING]", 3); m_ekeyEventText = gen_label_text("[E]: Toggle Earth [RUNNING]", 4); m_mkeyEventText = gen_label_text("[M]: Toggle Mars [RUNNING]", 5); m_yearCounterText = gen_label_text("0 Earth years completed", 6); m_yearCounter = 0; // year counter for earth years m_simRunning = true; // boolean to keep track of the // state of the global simulation // Events // Each self.accept statement creates an event handler object that will call // the specified function when that event occurs. // Certain events like "mouse1", "a", "b", "c" ... "z", "1", "2", "3"..."0" // are references to keyboard keys and mouse buttons. You can also define // your own events to be used within your program. In this tutorial, the // event "newYear" is not tied to a physical input device, but rather // is sent by the function that rotates the Earth whenever a revolution // completes to tell the counter to update // Note: need to listen to input events m_windowFramework->enable_keyboard(); PandaFramework* pfw = m_windowFramework->get_panda_framework(); // Exit the program when escape is pressed pfw->define_key("escape", "sysExit", sys_exit, NULL); pfw->define_key("mouse1", "handleMouseClick", call_handle_mouse_click, this); pfw->define_key("e", "handleEarth", call_handle_earth, this); pfw->define_key("s", // message name "togglePlanetSun", // Note: event description call_toggle_planet<P_sun>, // function to call this); // arguments to be passed to togglePlanet // See togglePlanet's definition below for // an explanation of what they are // Repeat the structure above for the other planets pfw->define_key("y", "togglePlanetMercury", call_toggle_planet<P_mercury>, this); pfw->define_key("v", "togglePlanetVenus", call_toggle_planet<P_venus>, this); pfw->define_key("m", "togglePlanetMars", call_toggle_planet<P_mars>, this); EventHandler::get_global_event_handler()->add_hook("newYear", call_inc_year, this); }
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(); }
// The initialization method caused when a world object is created World5::World5(WindowFramework* windowFrameworkPtr) : m_windowFrameworkPtr(windowFrameworkPtr), m_title("title", COnscreenText::TS_plain), m_sizescale(1), m_sky(), m_skyTex(NULL), m_sun(), m_sunTex(NULL), m_orbitscale(1), m_orbitRootMercury(), m_orbitRootVenus(), m_orbitRootMars(), m_orbitRootEarth(), m_orbitRootMoon(), m_mercury(), m_mercuryTex(NULL), m_venus(), m_venusTex(NULL), m_mars(), m_marsTex(NULL), m_earth(), m_earthTex(NULL), m_moon(), m_moonTex(NULL), m_yearscale(1), m_dayscale(1), m_dayPeriodSun(NULL), m_orbitPeriodMercury(NULL), m_dayPeriodMercury(NULL), m_orbitPeriodVenus(NULL), m_dayPeriodVenus(NULL), m_orbitPeriodEarth(NULL), m_dayPeriodEarth(NULL), m_orbitPeriodMoon(NULL), m_dayPeriodMoon(NULL), m_orbitPeriodMars(NULL), m_dayPeriodMars(NULL) { // This is the initialization we had before m_title.set_text("Panda3D: Tutorial 1 - Solar System"); m_title.set_fg(Colorf(1, 1, 1, 1)); m_title.set_pos(LVecBase2f(0.8, -0.95)); m_title.set_scale(0.07); m_title.reparent_to(m_windowFrameworkPtr->get_aspect_2d()); // Set the background to black m_windowFrameworkPtr->set_background_type(WindowFramework::BT_black); // Note: mouse ain't enable by default in C++ // base.disableMouse() // disable mouse control of the camera NodePath camera = m_windowFrameworkPtr->get_camera_group(); camera.set_pos(0, 0, 45); // Set the camera position (X, Y, Z) camera.set_hpr(0, -90, 0); // Set the camera orientation // (heading, pitch, roll) in degrees // Here again is where we put our global variables. Added this time are // variables to control the relative speeds of spinning and orbits in the // simulation // Number of seconds a full rotation of Earth around the sun should take m_yearscale = 60; // Number of seconds a day rotation of Earth should take. // It is scaled from its correct value for easier visability m_dayscale = m_yearscale / 365.0 * 5; m_sizescale = 0.6; // relative size of planets m_orbitscale = 10; // relative size of orbits load_planets(); // Load our models and make them render // Finally, we call the rotatePlanets function which puts the planets, // sun, and moon into motion. rotate_planets(); }