Ejemplo n.º 1
0
// 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();
   }
Ejemplo n.º 2
0
// 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;
   }
Ejemplo n.º 3
0
// 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();
   }
Ejemplo n.º 4
0
// 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();
}
Ejemplo n.º 5
0
// 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);
   }
Ejemplo n.º 6
0
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();
   }
Ejemplo n.º 7
0
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();
   }
Ejemplo n.º 8
0
// 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();
   }