Panda3D
 All Classes Functions Variables Enumerations
windowFramework.cxx
00001 // Filename: windowFramework.cxx
00002 // Created by:  drose (02Apr02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "windowFramework.h"
00016 #include "pandaFramework.h"
00017 #include "displayRegion.h"
00018 #include "buttonThrower.h"
00019 #include "transform2sg.h"
00020 #include "dSearchPath.h"
00021 #include "filename.h"
00022 #include "loader.h"
00023 #include "keyboardButton.h"
00024 #include "geom.h"
00025 #include "geomTriangles.h"
00026 #include "geomTristrips.h"
00027 #include "geomVertexData.h"
00028 #include "geomVertexFormat.h"
00029 #include "geomVertexWriter.h"
00030 #include "texturePool.h"
00031 #include "textureAttrib.h"
00032 #include "colorAttrib.h"
00033 #include "perspectiveLens.h"
00034 #include "orthographicLens.h"
00035 #include "auto_bind.h"
00036 #include "ambientLight.h"
00037 #include "directionalLight.h"
00038 #include "lightAttrib.h"
00039 #include "boundingSphere.h"
00040 #include "deg_2_rad.h"
00041 #include "config_framework.h"
00042 #include "cullFaceAttrib.h"
00043 #include "rescaleNormalAttrib.h"
00044 #include "shadeModelAttrib.h"
00045 #include "pgTop.h"
00046 #include "geomNode.h"
00047 #include "texture.h"
00048 #include "texturePool.h"
00049 #include "loaderFileTypeRegistry.h"
00050 #include "pnmImage.h"
00051 #include "virtualFileSystem.h"
00052 #include "string_utils.h"
00053 #include "bamFile.h"
00054 #include "staticTextFont.h"
00055 #include "mouseButton.h"
00056 
00057 // This is generated data for the standard texture we apply to the
00058 // blue triangle.
00059 #include "rock_floor.rgb_src.c"
00060 
00061 // This is generated data for shuttle_controls.bam, a bamified version
00062 // of shuttle_controls.egg (found in the models tree).  It's
00063 // compiled in shuttle_controls.bam_src.c.
00064 #include "shuttle_controls.bam_src.c"
00065 
00066 // This number is chosen arbitrarily to override any settings in model
00067 // files.
00068 static const int override_priority = 100;
00069 
00070 PT(TextFont) WindowFramework::_shuttle_controls_font = NULL;
00071 TypeHandle WindowFramework::_type_handle;
00072 
00073 ////////////////////////////////////////////////////////////////////
00074 //     Function: WindowFramework::Constructor
00075 //       Access: Protected
00076 //  Description:
00077 ////////////////////////////////////////////////////////////////////
00078 WindowFramework::
00079 WindowFramework(PandaFramework *panda_framework) :
00080   _panda_framework(panda_framework)
00081 {
00082   _alight = (AmbientLight *)NULL;
00083   _dlight = (DirectionalLight *)NULL;
00084   _got_keyboard = false;
00085   _got_trackball = false;
00086   _got_lights = false;
00087   _anim_controls_enabled = false;
00088   _anim_index = 0;
00089   _wireframe_enabled = false;
00090   _texture_enabled = true;
00091   _two_sided_enabled = false;
00092   _one_sided_reverse_enabled = false;
00093   _lighting_enabled = false;
00094   _perpixel_enabled = false;
00095   _background_type = BT_default;
00096 }
00097 
00098 ////////////////////////////////////////////////////////////////////
00099 //     Function: WindowFramework::Copy Constructor
00100 //       Access: Protected
00101 //  Description:
00102 ////////////////////////////////////////////////////////////////////
00103 WindowFramework::
00104 WindowFramework(const WindowFramework &copy, DisplayRegion *display_region) :
00105   _panda_framework(copy._panda_framework),
00106   _window(copy._window),
00107   _display_region_3d(display_region)
00108 {
00109   _alight = (AmbientLight *)NULL;
00110   _dlight = (DirectionalLight *)NULL;
00111   _got_keyboard = false;
00112   _got_trackball = false;
00113   _got_lights = false;
00114   _anim_controls_enabled = false;
00115   _anim_index = 0;
00116   _wireframe_enabled = false;
00117   _texture_enabled = true;
00118   _two_sided_enabled = false;
00119   _one_sided_reverse_enabled = false;
00120   _lighting_enabled = false;
00121   _perpixel_enabled = false;
00122   _background_type = BT_default;
00123 
00124   set_background_type(copy._background_type);
00125   // Set up a 3-d camera for the window by default.
00126   NodePath camera_np = make_camera();
00127   _display_region_3d->set_camera(camera_np);
00128 }
00129 
00130 ////////////////////////////////////////////////////////////////////
00131 //     Function: WindowFramework::Destructor
00132 //       Access: Public, Virtual
00133 //  Description:
00134 ////////////////////////////////////////////////////////////////////
00135 WindowFramework::
00136 ~WindowFramework() {
00137   close_window();
00138 }
00139 
00140 ////////////////////////////////////////////////////////////////////
00141 //     Function: WindowFramework::open_window
00142 //       Access: Protected
00143 //  Description: Opens the actual window or buffer.  This is normally
00144 //               called only from PandaFramework::open_window().
00145 ////////////////////////////////////////////////////////////////////
00146 GraphicsOutput *WindowFramework::
00147 open_window(const WindowProperties &props, int flags, GraphicsEngine *engine,
00148             GraphicsPipe *pipe, GraphicsStateGuardian *gsg) {
00149   nassertr(_window == (GraphicsOutput *)NULL, _window);
00150 
00151   static int next_window_index = 1;
00152   ostringstream stream;
00153   stream << "window" << next_window_index;
00154   next_window_index++;
00155   string name = stream.str();
00156 
00157   _window = 0;
00158   GraphicsOutput *winout = 
00159     engine->make_output(pipe, name, 0,
00160                         FrameBufferProperties::get_default(),
00161                         props, flags, gsg, NULL);
00162   if (winout != (GraphicsOutput *)NULL) {
00163     _window = winout;
00164     //    _window->request_properties(props);
00165 
00166     // Create a display region that covers the entire window.
00167     _display_region_3d = _window->make_display_region();
00168 
00169     // Make sure the DisplayRegion does the clearing, not the window,
00170     // so we can have multiple DisplayRegions of different colors.
00171     _window->set_clear_color_active(false);
00172     _window->set_clear_depth_active(false);
00173     _window->set_clear_stencil_active(false);
00174 
00175     // Set up a 3-d camera for the window by default.
00176     NodePath camera_np = make_camera();
00177     _display_region_3d->set_camera(camera_np);
00178 
00179     set_background_type(_background_type);
00180 
00181     if (show_frame_rate_meter) {
00182       _frame_rate_meter = new FrameRateMeter("frame_rate_meter");
00183       _frame_rate_meter->setup_window(_window);
00184     }
00185     if (show_scene_graph_analyzer_meter) {
00186       _scene_graph_analyzer_meter = new SceneGraphAnalyzerMeter("scene_graph_analyzer_meter", get_render().node());
00187       _scene_graph_analyzer_meter->setup_window(_window);
00188     }
00189   }
00190 
00191   return _window;
00192 }
00193 
00194 ////////////////////////////////////////////////////////////////////
00195 //     Function: WindowFramework::close_window
00196 //       Access: Protected
00197 //  Description: Closes the window or buffer.  This is normally called
00198 //               from PandaFramework::close_window().
00199 ////////////////////////////////////////////////////////////////////
00200 void WindowFramework::
00201 close_window() {
00202   _window.clear();
00203   _camera_group.remove_node();
00204   _render.remove_node();
00205   _render_2d.remove_node();
00206   _mouse.remove_node();
00207 
00208   _alight = (AmbientLight *)NULL;
00209   _dlight = (DirectionalLight *)NULL;
00210   _got_keyboard = false;
00211   _got_trackball = false;
00212   _got_lights = false;
00213 
00214   _wireframe_enabled = false;
00215   _texture_enabled = true;
00216   _two_sided_enabled = false;
00217   _one_sided_reverse_enabled = false;
00218   _lighting_enabled = false;
00219   _perpixel_enabled = false;
00220 
00221   if (_frame_rate_meter != (FrameRateMeter *)NULL) {
00222     _frame_rate_meter->clear_window();
00223     _frame_rate_meter = (FrameRateMeter *)NULL;
00224   }
00225   if (_scene_graph_analyzer_meter != (SceneGraphAnalyzerMeter *)NULL) {
00226     _scene_graph_analyzer_meter->clear_window();
00227     _scene_graph_analyzer_meter = (SceneGraphAnalyzerMeter *)NULL;
00228   }
00229 }
00230 
00231 ////////////////////////////////////////////////////////////////////
00232 //     Function: WindowFramework::get_camera_group
00233 //       Access: Public
00234 //  Description: Returns the node above the collection of 3-d cameras
00235 //               in the scene graph.  This node may be moved around to
00236 //               represent the viewpoint.
00237 ////////////////////////////////////////////////////////////////////
00238 NodePath WindowFramework::
00239 get_camera_group() {
00240   if (_camera_group.is_empty()) {
00241     _camera_group = get_render().attach_new_node("camera_group");
00242   }
00243   return _camera_group;
00244 }
00245 
00246 ////////////////////////////////////////////////////////////////////
00247 //     Function: WindowFramework::get_render
00248 //       Access: Public
00249 //  Description: Returns the root of the 3-d scene graph.
00250 ////////////////////////////////////////////////////////////////////
00251 NodePath WindowFramework::
00252 get_render() {
00253   if (_render.is_empty()) {
00254     _render = NodePath("render");
00255 
00256     _render.node()->set_attrib(RescaleNormalAttrib::make_default());
00257     _render.node()->set_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_smooth));
00258 
00259     // This is maybe here temporarily, and maybe not.
00260     _render.set_two_sided(0);
00261   }
00262   return _render;
00263 }
00264 
00265 ////////////////////////////////////////////////////////////////////
00266 //     Function: WindowFramework::get_render_2d
00267 //       Access: Public
00268 //  Description: Returns the root of the 2-d scene graph.
00269 ////////////////////////////////////////////////////////////////////
00270 NodePath WindowFramework::
00271 get_render_2d() {
00272   if (_render_2d.is_empty()) {
00273     _render_2d = NodePath("render_2d");
00274 
00275     // Some standard properties for the 2-d display.
00276 
00277     _render_2d.set_depth_write(0);
00278     _render_2d.set_depth_test(0);
00279     _render_2d.set_material_off(1);
00280     _render_2d.set_two_sided(1);
00281 
00282     // Now set up a 2-d camera to view render_2d.
00283 
00284     // Create a display region that matches the size of the 3-d
00285     // display region.
00286     PN_stdfloat l, r, b, t;
00287     _display_region_3d->get_dimensions(l, r, b, t);
00288     _display_region_2d = _window->make_mono_display_region(l, r, b, t);
00289     _display_region_2d->set_sort(10);
00290 
00291     // Finally, we need a camera to associate with the display region.
00292     PT(Camera) camera = new Camera("camera2d");
00293     NodePath camera_np = _render_2d.attach_new_node(camera);
00294 
00295     PT(Lens) lens = new OrthographicLens;
00296 
00297     static const PN_stdfloat left = -1.0f;
00298     static const PN_stdfloat right = 1.0f;
00299     static const PN_stdfloat bottom = -1.0f;
00300     static const PN_stdfloat top = 1.0f;
00301     lens->set_film_size(right - left, top - bottom);
00302     lens->set_film_offset((right + left) * 0.5, (top + bottom) * 0.5);
00303     lens->set_near_far(-1000, 1000);
00304 
00305     camera->set_lens(lens);
00306     _display_region_2d->set_camera(camera_np);
00307   }
00308 
00309   return _render_2d;
00310 }
00311 
00312 ////////////////////////////////////////////////////////////////////
00313 //     Function: WindowFramework::get_aspect_2d
00314 //       Access: Public
00315 //  Description: Returns the node under the 2-d scene graph that is
00316 //               scaled to suit the window's aspect ratio.
00317 ////////////////////////////////////////////////////////////////////
00318 NodePath WindowFramework::
00319 get_aspect_2d() {
00320   if (_aspect_2d.is_empty()) {
00321     PGTop *top = new PGTop("aspect_2d");
00322     _aspect_2d = get_render_2d().attach_new_node(top);
00323 
00324     // Tell the PGTop about our MouseWatcher object, so the PGui
00325     // system can operate.
00326     PandaNode *mouse_node = get_mouse().node();
00327     if (mouse_node->is_of_type(MouseWatcher::get_class_type())) {
00328       top->set_mouse_watcher(DCAST(MouseWatcher, mouse_node));
00329     }
00330 
00331     PN_stdfloat this_aspect_ratio = aspect_ratio;
00332     if (this_aspect_ratio == 0.0f) {
00333       // An aspect ratio of 0.0 means to try to infer it.
00334       this_aspect_ratio = 1.0f;
00335 
00336       if (_window->has_size()) {
00337         int x_size = _window->get_sbs_left_x_size();
00338         int y_size = _window->get_sbs_left_y_size();
00339         if (y_size != 0) {
00340           this_aspect_ratio = (PN_stdfloat)x_size / (PN_stdfloat)y_size;
00341         }
00342       }
00343     }
00344 
00345     _aspect_2d.set_scale(1.0f / this_aspect_ratio, 1.0f, 1.0f);
00346   }
00347 
00348   return _aspect_2d;
00349 }
00350 
00351 ////////////////////////////////////////////////////////////////////
00352 //     Function: WindowFramework::get_pixel_2d
00353 //       Access: Public
00354 //  Description: Returns a special root that uses units in pixels that
00355 //               are relative to the window. The upperleft corner of
00356 //               the window is (0, 0), the lowerleft corner is
00357 //               (xsize, -ysize), in this coordinate system.
00358 ////////////////////////////////////////////////////////////////////
00359 NodePath WindowFramework::
00360 get_pixel_2d() {
00361   if (_pixel_2d.is_empty()) {
00362     PGTop *top = new PGTop("pixel_2d");
00363     _pixel_2d = get_render_2d().attach_new_node(top);
00364     _pixel_2d.set_pos(-1, 0, 1);
00365   
00366     if (_window->has_size()) {
00367       int x_size = _window->get_sbs_left_x_size();
00368       int y_size = _window->get_sbs_left_y_size();
00369       if (x_size > 0){
00370         _pixel_2d.set_sx(2.0f / (float)x_size);
00371       }
00372       _pixel_2d.set_sy(1.0f);
00373       if (y_size > 0){
00374         _pixel_2d.set_sz(2.0f / (float)y_size);
00375       }
00376     }
00377   }
00378 
00379   return _pixel_2d;
00380 }
00381 
00382 ////////////////////////////////////////////////////////////////////
00383 //     Function: WindowFramework::get_mouse
00384 //       Access: Public
00385 //  Description: Returns the node in the data graph corresponding to
00386 //               the mouse associated with this window.
00387 ////////////////////////////////////////////////////////////////////
00388 NodePath WindowFramework::
00389 get_mouse() {
00390   if (_mouse.is_empty()) {
00391     NodePath mouse = _panda_framework->get_mouse(_window);
00392 
00393     // Create a MouseWatcher to filter the mouse input.  We do this
00394     // mainly so we can constrain the mouse input to our particular
00395     // display region, if we have one.  This means the node we return
00396     // from get_mouse() is actually a MouseWatcher, but since it
00397     // presents the same interface as a Mouse, no one should mind.
00398 
00399     // Another advantage to using a MouseWatcher is that the PGTop of
00400     // aspect2d likes it better.
00401     PT(MouseWatcher) mw = new MouseWatcher("watcher");
00402 
00403     if (_window->get_side_by_side_stereo()) {
00404       // If the window has side-by-side stereo enabled, then
00405       // we should constrain the MouseWatcher to the window's
00406       // DisplayRegion.  This will enable the MouseWatcher to
00407       // track the left and right halves of the screen
00408       // individually.
00409       mw->set_display_region(_window->get_overlay_display_region());
00410     }
00411 
00412     _mouse = mouse.attach_new_node(mw);
00413   }
00414   return _mouse;
00415 }
00416 
00417 ////////////////////////////////////////////////////////////////////
00418 //     Function: WindowFramework::get_button_thrower
00419 //       Access: Public
00420 //  Description: Returns the node in the data graph corresponding to
00421 //               the ButtonThrower object associated with this window.
00422 ////////////////////////////////////////////////////////////////////
00423 NodePath WindowFramework::
00424 get_button_thrower() {
00425   return _button_thrower;
00426 }
00427 
00428 ////////////////////////////////////////////////////////////////////
00429 //     Function: WindowFramework::enable_keyboard
00430 //       Access: Public
00431 //  Description: Creates a ButtonThrower to listen to button presses
00432 //               and throw them as events.
00433 ////////////////////////////////////////////////////////////////////
00434 void WindowFramework::
00435 enable_keyboard() {
00436   if (_got_keyboard) {
00437     return;
00438   }
00439 
00440   if (_window->is_of_type(GraphicsWindow::get_class_type()) &&
00441       DCAST(GraphicsWindow, _window)->get_num_input_devices() > 0) {
00442     NodePath mouse = get_mouse();
00443 
00444     // Create a button thrower to listen for our keyboard events and
00445     // associate this WindowFramework pointer with each one.
00446     PT(ButtonThrower) bt = new ButtonThrower("kb-events");
00447     bt->add_parameter(EventParameter(this));
00448     ModifierButtons mods;
00449     mods.add_button(KeyboardButton::shift());
00450     mods.add_button(KeyboardButton::control());
00451     mods.add_button(KeyboardButton::alt());
00452     mods.add_button(KeyboardButton::meta());
00453     bt->set_modifier_buttons(mods);
00454     _button_thrower = mouse.attach_new_node(bt);
00455   }
00456 
00457   _got_keyboard = true;
00458 }
00459 
00460 ////////////////////////////////////////////////////////////////////
00461 //     Function: WindowFramework::setup_trackball
00462 //       Access: Public
00463 //  Description: Sets up the mouse to trackball around the camera.
00464 ////////////////////////////////////////////////////////////////////
00465 void WindowFramework::
00466 setup_trackball() {
00467   if (_got_trackball) {
00468     return;
00469   }
00470 
00471   if (_window->is_of_type(GraphicsWindow::get_class_type()) &&
00472       DCAST(GraphicsWindow, _window)->get_num_input_devices() > 0) {
00473     NodePath mouse = get_mouse();
00474     NodePath camera = get_camera_group();
00475 
00476     _trackball = new Trackball("trackball");
00477     _trackball->set_pos(LVector3::forward() * 50.0);
00478     mouse.attach_new_node(_trackball);
00479 
00480     PT(Transform2SG) tball2cam = new Transform2SG("tball2cam");
00481     tball2cam->set_node(camera.node());
00482     _trackball->add_child(tball2cam);
00483   }
00484 
00485   _got_trackball = true;
00486 }
00487 
00488 ////////////////////////////////////////////////////////////////////
00489 //     Function: WindowFramework::center_trackball
00490 //       Access: Public
00491 //  Description: Centers the trackball on the indicated object, and
00492 //               scales the trackball motion suitably.
00493 ////////////////////////////////////////////////////////////////////
00494 void WindowFramework::
00495 center_trackball(const NodePath &object) {
00496   if (_trackball == (Trackball *)NULL) {
00497     return;
00498   }
00499 
00500   PT(BoundingVolume) volume = object.get_bounds();
00501   // We expect at least a geometric bounding volume around the world.
00502   nassertv(volume != (BoundingVolume *)NULL);
00503   nassertv(volume->is_of_type(GeometricBoundingVolume::get_class_type()));
00504   CPT(GeometricBoundingVolume) gbv = DCAST(GeometricBoundingVolume, volume);
00505 
00506   if (object.has_parent()) {
00507     CPT(TransformState) net_transform = object.get_parent().get_net_transform();
00508     PT(GeometricBoundingVolume) new_gbv = DCAST(GeometricBoundingVolume, gbv->make_copy());
00509     new_gbv->xform(net_transform->get_mat());
00510     gbv = new_gbv;
00511   }
00512 
00513   // Determine the bounding sphere around the object.
00514   if (gbv->is_infinite()) {
00515     framework_cat.warning()
00516       << "Infinite bounding volume for " << object << "\n";
00517     return;
00518   }
00519 
00520   if (gbv->is_empty()) {
00521     framework_cat.warning()
00522       << "Empty bounding volume for " << object << "\n";
00523     return;
00524   }
00525 
00526   // The BoundingVolume might be a sphere (it's likely), but since it
00527   // might not, we'll take no chances and make our own sphere.
00528   PT(BoundingSphere) sphere = new BoundingSphere(gbv->get_approx_center(), 0.0f);
00529   if (!sphere->extend_by(gbv)) {
00530     framework_cat.warning()
00531       << "Cannot determine bounding volume of " << object << "\n";
00532     return;
00533   }
00534 
00535   LPoint3 center = sphere->get_center();
00536   PN_stdfloat radius = sphere->get_radius();
00537 
00538   PN_stdfloat distance = 50.0f;
00539 
00540   // Choose a suitable distance to view the whole volume in our frame.
00541   // This is based on the camera lens in use.  Determine the lens
00542   // based on the first camera; this will be the default camera.
00543   Lens *lens = (Lens *)NULL;
00544   if (!_cameras.empty()) {
00545     Cameras::const_iterator ci;
00546     for (ci = _cameras.begin();
00547          ci != _cameras.end() && lens == (Lens *)NULL;
00548          ++ci) {
00549       lens = (*ci)->get_lens();
00550     }
00551   }
00552 
00553   if (lens != (Lens *)NULL) {
00554     LVecBase2 fov = lens->get_fov();
00555     distance = radius / ctan(deg_2_rad(min(fov[0], fov[1]) / 2.0f));
00556 
00557     // Ensure the far plane is far enough back to see the entire object.
00558     PN_stdfloat ideal_far_plane = distance + radius * 1.5;
00559     lens->set_far(max(lens->get_default_far(), ideal_far_plane));
00560 
00561     // And that the near plane is far enough forward.
00562     PN_stdfloat ideal_near_plane = distance - radius;
00563     lens->set_near(min(lens->get_default_near(), ideal_near_plane));
00564   }
00565 
00566   _trackball->set_origin(center);
00567   _trackball->set_pos(LVector3::forward() * distance);
00568 
00569   // Also set the movement scale on the trackball to be consistent
00570   // with the size of the model and the lens field-of-view.
00571   _trackball->set_forward_scale(distance * 0.006);
00572 }
00573 
00574 ////////////////////////////////////////////////////////////////////
00575 //     Function: WindowFramework::load_models
00576 //       Access: Public
00577 //  Description: Loads up all the model files listed in the indicated
00578 //               argument list.  If first_arg is supplied, it is the
00579 //               first argument in the list to consider.
00580 //
00581 //               Returns true if all models loaded successfully, or
00582 //               false if at least one of them had an error.
00583 ////////////////////////////////////////////////////////////////////
00584 bool WindowFramework::
00585 load_models(const NodePath &parent, int argc, char *argv[], int first_arg) {
00586   pvector<Filename> files;
00587 
00588   for (int i = first_arg; i < argc && argv[i] != (char *)NULL; i++) {
00589     files.push_back(Filename::from_os_specific(argv[i]));
00590   }
00591 
00592   return load_models(parent, files);
00593 }
00594 
00595 ////////////////////////////////////////////////////////////////////
00596 //     Function: WindowFramework::load_models
00597 //       Access: Public
00598 //  Description: Loads up all the model files listed in the indicated
00599 //               argument list.
00600 //
00601 //               Returns true if all models loaded successfully, or
00602 //               false if at least one of them had an error.
00603 ////////////////////////////////////////////////////////////////////
00604 bool WindowFramework::
00605 load_models(const NodePath &parent, const pvector<Filename> &files) {
00606   bool all_ok = true;
00607 
00608   pvector<Filename>::const_iterator fi;
00609   for (fi = files.begin(); fi != files.end(); ++fi) {
00610     const Filename &filename = (*fi);
00611     NodePath model = load_model(parent, filename);
00612     if (model.is_empty()) {
00613       all_ok = false;
00614     }
00615   }
00616 
00617   return all_ok;
00618 }
00619 
00620 ////////////////////////////////////////////////////////////////////
00621 //     Function: WindowFramework::load_model
00622 //       Access: Public
00623 //  Description: Loads up the indicated model and returns the new
00624 //               NodePath, or the empty NodePath if the model could
00625 //               not be loaded.
00626 ////////////////////////////////////////////////////////////////////
00627 NodePath WindowFramework::
00628 load_model(const NodePath &parent, Filename filename) {
00629   nout << "Loading " << filename << "\n";
00630 
00631   // If the filename already exists where it is, or if it is fully
00632   // qualified, don't search along the model path for it.
00633   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00634   bool search = !(filename.is_fully_qualified() || vfs->exists(filename));
00635 
00636   // We allow loading image files here.  Check to see if it might be
00637   // an image file, based on the filename extension.
00638   bool is_image = false;
00639   string extension = filename.get_extension();
00640 #ifdef HAVE_ZLIB
00641   if (extension == "pz") {
00642     extension = Filename(filename.get_basename_wo_extension()).get_extension();
00643   }
00644 #endif  // HAVE_ZLIB
00645   if (!extension.empty()) {
00646     LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
00647     LoaderFileType *model_type =
00648       reg->get_type_from_extension(extension);
00649     if (model_type == (LoaderFileType *)NULL) {
00650       // The extension isn't a known model file type; is it a known
00651       // image file extension?
00652       if (extension == "txo" || downcase(extension) == "dds") {
00653         // A texture object.  Not exactly an image, but certainly a
00654         // texture.
00655         is_image = true;
00656         
00657       } else {
00658         TexturePool *texture_pool = TexturePool::get_global_ptr();
00659         if (texture_pool->get_texture_type(extension) != NULL) {
00660           // It is a known image file extension.
00661           is_image = true;
00662         }
00663       }
00664     }
00665   }
00666 
00667   LoaderOptions options = PandaFramework::_loader_options;
00668   if (search) {
00669     options.set_flags(options.get_flags() | LoaderOptions::LF_search);
00670   } else {
00671     options.set_flags(options.get_flags() & ~LoaderOptions::LF_search);
00672   }
00673 
00674   Loader loader;
00675   PT(PandaNode) node;
00676   if (is_image) {
00677     node = load_image_as_model(filename);
00678   } else {
00679     node = loader.load_sync(filename, options);
00680   }
00681 
00682   if (node == (PandaNode *)NULL) {
00683     nout << "Unable to load " << filename << "\n";
00684     return NodePath::not_found();
00685   }
00686 
00687   return parent.attach_new_node(node);
00688 }
00689 
00690 ////////////////////////////////////////////////////////////////////
00691 //     Function: WindowFramework::load_default_model
00692 //       Access: Public
00693 //  Description: Loads our favorite blue triangle.  This is intended
00694 //               to provide some default geometry to have *something*
00695 //               to look at for testing, when no other models are
00696 //               provided.
00697 ////////////////////////////////////////////////////////////////////
00698 NodePath WindowFramework::
00699 load_default_model(const NodePath &parent) {
00700   CPT(RenderState) state = RenderState::make_empty();
00701 
00702   state = state->add_attrib(ColorAttrib::make_flat(LColor(0.5, 0.5, 1.0, 1.0)));
00703 
00704   // Get the default texture to apply to the triangle; it's compiled
00705   // into the code these days.
00706   string rock_floor_string((const char *)rock_floor, rock_floor_len);
00707   istringstream rock_floor_strm(rock_floor_string);
00708   PNMImage rock_floor_pnm;
00709   if (rock_floor_pnm.read(rock_floor_strm, "rock-floor.rgb")) {
00710     PT(Texture) tex = new Texture;
00711     tex->set_name("rock-floor.rgb");
00712     tex->load(rock_floor_pnm);
00713     tex->set_minfilter(Texture::FT_linear);
00714     tex->set_magfilter(Texture::FT_linear);
00715     state = state->add_attrib(TextureAttrib::make(tex));
00716   }
00717 
00718   GeomNode *geomnode = new GeomNode("tri");
00719 
00720   PT(GeomVertexData) vdata = new GeomVertexData
00721     ("tri", GeomVertexFormat::get_v3n3cpt2(),
00722      Geom::UH_static);
00723   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00724   GeomVertexWriter normal(vdata, InternalName::get_normal());
00725   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00726 
00727   vertex.add_data3(LVertex::rfu(0.0, 0.0, 0.0));
00728   vertex.add_data3(LVertex::rfu(1.0, 0.0, 0.0));
00729   vertex.add_data3(LVertex::rfu(0.0, 0.0, 1.0));
00730 
00731   normal.add_data3(LNormal::back());
00732   normal.add_data3(LNormal::back());
00733   normal.add_data3(LNormal::back());
00734 
00735   texcoord.add_data2(0.0, 0.0);
00736   texcoord.add_data2(1.0, 0.0);
00737   texcoord.add_data2(0.0, 1.0);
00738 
00739   PT(GeomTriangles) tri = new GeomTriangles(Geom::UH_static);
00740   tri->add_consecutive_vertices(0, 3);
00741   tri->close_primitive();
00742 
00743   PT(Geom) geom = new Geom(vdata);
00744   geom->add_primitive(tri);
00745 
00746   geomnode->add_geom(geom, state);
00747 
00748   return parent.attach_new_node(geomnode);
00749 }
00750 
00751 ////////////////////////////////////////////////////////////////////
00752 //     Function: WindowFramework::loop_animations
00753 //       Access: Public
00754 //  Description: Looks for characters and their matching animation
00755 //               files in the scene graph; binds and loops any
00756 //               matching animations found.
00757 ////////////////////////////////////////////////////////////////////
00758 void WindowFramework::
00759 loop_animations(int hierarchy_match_flags) {
00760   // If we happened to load up both a character file and its matching
00761   // animation file, attempt to bind them together now and start the
00762   // animations looping.
00763   auto_bind(get_render().node(), _anim_controls, hierarchy_match_flags);
00764   _anim_controls.loop_all(true);
00765 }
00766 
00767 ////////////////////////////////////////////////////////////////////
00768 //     Function: WindowFramework::stagger_animations
00769 //       Access: Public
00770 //  Description: Walks through all the animations that were bound by
00771 //               loop_animations() and staggers their play rate
00772 //               slightly so that they will not remain perfectly in
00773 //               sync.
00774 ////////////////////////////////////////////////////////////////////
00775 void WindowFramework::
00776 stagger_animations() {
00777   for (int i = 0; i < _anim_controls.get_num_anims(); ++i) {
00778     AnimControl *control = _anim_controls.get_anim(i);
00779     double r = (double)rand() / (double)RAND_MAX;
00780     r = r * 0.2 + 0.9;
00781     control->set_play_rate(r);
00782   }
00783 }
00784 
00785 ////////////////////////////////////////////////////////////////////
00786 //     Function: WindowFramework::next_anim_control
00787 //       Access: Public
00788 //  Description: Rotates the animation controls through all of the
00789 //               available animations.  If the animation controls are
00790 //               not already enabled, enables them at sets to the
00791 //               first animation; if they are already enabled, steps
00792 //               to the next animation; if that is the last animation,
00793 //               disables the animation controls.
00794 ////////////////////////////////////////////////////////////////////
00795 void WindowFramework::
00796 next_anim_control() {
00797   if (_anim_controls_enabled) {
00798     destroy_anim_controls();
00799 
00800     ++_anim_index;
00801     if (_anim_index >= _anim_controls.get_num_anims()) {
00802       set_anim_controls(false);
00803     } else {
00804       create_anim_controls();
00805     }
00806   } else {
00807     _anim_index = 0;
00808     set_anim_controls(true);
00809   }
00810 }
00811 
00812 ////////////////////////////////////////////////////////////////////
00813 //     Function: WindowFramework::set_anim_controls
00814 //       Access: Public
00815 //  Description: Creates an onscreen animation slider for
00816 //               frame-stepping through the animations.
00817 ////////////////////////////////////////////////////////////////////
00818 void WindowFramework::
00819 set_anim_controls(bool enable) {
00820   _anim_controls_enabled = enable;
00821   if (_anim_controls_enabled) {
00822     create_anim_controls();
00823 
00824   } else {
00825     destroy_anim_controls();
00826   }
00827 }
00828 
00829 ////////////////////////////////////////////////////////////////////
00830 //     Function: WindowFramework::adjust_dimensions
00831 //       Access: Public
00832 //  Description: Reevaluates the dimensions of the window,
00833 //               presumably after the window has been resized by the
00834 //               user or some other force.  Adjusts the render film
00835 //               size, aspect2d scale (aspect ratio) and the
00836 //               dimensionsas of pixel_2d according to the
00837 //               new window shape, or new config setting.
00838 ////////////////////////////////////////////////////////////////////
00839 void WindowFramework::
00840 adjust_dimensions() {
00841   PN_stdfloat this_aspect_ratio = aspect_ratio;
00842 
00843   int x_size = 0, y_size = 0;
00844   if (_window->has_size()) {
00845     x_size = _window->get_sbs_left_x_size();
00846     y_size = _window->get_sbs_left_y_size();
00847   }
00848   
00849   if (this_aspect_ratio == 0.0f) {
00850     // An aspect ratio of 0.0 means to try to infer it.
00851     this_aspect_ratio = 1.0f;
00852     if (y_size != 0) {
00853       this_aspect_ratio = (float)x_size / (float)y_size;
00854     }
00855   }
00856 
00857   if (!_aspect_2d.is_empty()) {
00858     _aspect_2d.set_scale(1.0f / this_aspect_ratio, 1.0f, 1.0f);
00859   }
00860 
00861   if (!_pixel_2d.is_empty()) {
00862     // Adjust the pixel 2d scale
00863     if (x_size > 0){
00864       _pixel_2d.set_sx(2.0f / (float)x_size);
00865     }
00866     _pixel_2d.set_sy(1.0f);
00867     if (y_size > 0){
00868       _pixel_2d.set_sz(2.0f / (float)y_size);
00869     }
00870   }
00871 
00872   Cameras::iterator ci;
00873   for (ci = _cameras.begin(); ci != _cameras.end(); ++ci) {
00874     Lens *lens = (*ci)->get_lens();
00875     if (lens != (Lens *)NULL) {
00876       if (y_size != 0) {
00877         lens->set_film_size(x_size, y_size);
00878       } else {
00879         lens->set_aspect_ratio(this_aspect_ratio);
00880       }
00881     }
00882   }
00883 }
00884 
00885 ////////////////////////////////////////////////////////////////////
00886 //     Function: WindowFramework::split_window
00887 //       Access: Public
00888 //  Description: Divides the window into two display regions, each of
00889 //               which gets its own trackball and keyboard events.
00890 //               The new window pointer is returned.
00891 //
00892 //               There is not an interface for recombining divided
00893 //               windows.
00894 ////////////////////////////////////////////////////////////////////
00895 WindowFramework *WindowFramework::
00896 split_window(SplitType split_type) {
00897   DisplayRegion *new_region = NULL;
00898 
00899   if (split_type == ST_default) {
00900     // Choose either horizontal or vertical according to the largest
00901     // dimension.
00902 
00903     if (_display_region_3d->get_pixel_width() >
00904         _display_region_3d->get_pixel_height()) {
00905       split_type = ST_horizontal;
00906     } else {
00907       split_type = ST_vertical;
00908     }
00909   }
00910 
00911   PN_stdfloat left, right, bottom, top;
00912   _display_region_3d->get_dimensions(left, right, bottom, top);
00913   new_region = _display_region_3d->get_window()->make_display_region();
00914 
00915   if (split_type == ST_vertical) {
00916     _display_region_3d->set_dimensions(left, right, bottom, (top + bottom) / 2.0f);
00917     if (_display_region_2d != (DisplayRegion *)NULL) {
00918       _display_region_2d->set_dimensions(left, right, bottom, (top + bottom) / 2.0f);
00919     }
00920 
00921     new_region->set_dimensions(left, right, (top + bottom) / 2.0f, top);
00922 
00923   } else {
00924     _display_region_3d->set_dimensions(left, (left + right) / 2.0f, bottom, top);
00925     if (_display_region_2d != (DisplayRegion *)NULL) {
00926       _display_region_2d->set_dimensions(left, (left + right) / 2.0f, bottom, top);
00927     }
00928 
00929     new_region->set_dimensions((left + right) / 2.0f, right, bottom, top);
00930   }
00931 
00932   PT(WindowFramework) wf = new WindowFramework(*this, new_region);
00933   _panda_framework->_windows.push_back(wf);
00934 
00935   return wf;
00936 }
00937 
00938 ////////////////////////////////////////////////////////////////////
00939 //     Function: WindowFramework::set_wireframe
00940 //       Access: Public
00941 //  Description: Forces wireframe state (true) or restores default
00942 //               rendering (false).
00943 ////////////////////////////////////////////////////////////////////
00944 void WindowFramework::
00945 set_wireframe(bool enable) {
00946   if (enable == _wireframe_enabled) {
00947     return;
00948   }
00949 
00950   NodePath render = get_render();
00951 
00952   if (enable) {
00953     render.set_render_mode_wireframe(override_priority);
00954     render.set_two_sided(true, override_priority);
00955   } else {
00956     render.clear_render_mode();
00957     if (!_two_sided_enabled) {
00958       render.clear_two_sided();
00959     }
00960     if (_one_sided_reverse_enabled) {
00961       CPT(RenderAttrib) attrib = CullFaceAttrib::make_reverse();
00962       render.node()->set_attrib(attrib);
00963     }
00964   }
00965 
00966   _wireframe_enabled = enable;
00967 }
00968 
00969 ////////////////////////////////////////////////////////////////////
00970 //     Function: WindowFramework::set_texture
00971 //       Access: Public
00972 //  Description: Forces textures off (false) or restores default
00973 //               rendering (true).
00974 ////////////////////////////////////////////////////////////////////
00975 void WindowFramework::
00976 set_texture(bool enable) {
00977   if (enable == _texture_enabled) {
00978     return;
00979   }
00980 
00981   NodePath render = get_render();
00982 
00983   if (!enable) {
00984     render.set_texture_off(override_priority);
00985   } else {
00986     render.clear_texture();
00987   }
00988 
00989   _texture_enabled = enable;
00990 }
00991 
00992 ////////////////////////////////////////////////////////////////////
00993 //     Function: WindowFramework::set_two_sided
00994 //       Access: Public
00995 //  Description: Forces two-sided rendering (true) or restores default
00996 //               rendering (false).
00997 ////////////////////////////////////////////////////////////////////
00998 void WindowFramework::
00999 set_two_sided(bool enable) {
01000   if (enable == _two_sided_enabled) {
01001     return;
01002   }
01003 
01004   NodePath render = get_render();
01005 
01006   if (enable) {
01007     render.set_two_sided(true, override_priority);
01008   } else {
01009     if (!_wireframe_enabled) {
01010       render.clear_two_sided();
01011     }
01012   }
01013 
01014   _two_sided_enabled = enable;
01015   _one_sided_reverse_enabled = false;
01016 }
01017 
01018 ////////////////////////////////////////////////////////////////////
01019 //     Function: WindowFramework::set_one_sided_reverse
01020 //       Access: Public
01021 //  Description: Toggles one-sided reverse mode.  In this mode, the
01022 //               front sides of one-sided polygons are culled instead
01023 //               of the back side.
01024 ////////////////////////////////////////////////////////////////////
01025 void WindowFramework::
01026 set_one_sided_reverse(bool enable) {
01027   if (enable == _one_sided_reverse_enabled) {
01028     return;
01029   }
01030 
01031   NodePath render = get_render();
01032 
01033   if (!_wireframe_enabled) {
01034     if (enable) {
01035       CPT(RenderAttrib) attrib = CullFaceAttrib::make_reverse();
01036       render.node()->set_attrib(attrib);
01037     } else {
01038       render.clear_two_sided();
01039     }
01040   }
01041 
01042   _two_sided_enabled = false;
01043   _one_sided_reverse_enabled = enable;
01044 }
01045 
01046 ////////////////////////////////////////////////////////////////////
01047 //     Function: WindowFramework::set_lighting
01048 //       Access: Public
01049 //  Description: Turns lighting on (true) or off (false).
01050 ////////////////////////////////////////////////////////////////////
01051 void WindowFramework::
01052 set_lighting(bool enable) {
01053   if (enable == _lighting_enabled) {
01054     return;
01055   }
01056 
01057   NodePath render = get_render();
01058 
01059   if (enable) {
01060     if (!_got_lights) {
01061       setup_lights();
01062     }
01063     render.set_light(_alight);
01064     render.set_light(_dlight);
01065   } else {
01066     render.clear_light();
01067   }
01068 
01069   _lighting_enabled = enable;
01070 }
01071 
01072 ////////////////////////////////////////////////////////////////////
01073 //     Function: WindowFramework::set_perpixel
01074 //       Access: Public
01075 //  Description: Turns per-pixel lighting on (true) or off (false).
01076 ////////////////////////////////////////////////////////////////////
01077 void WindowFramework::
01078 set_perpixel(bool enable) {
01079   if (enable == _perpixel_enabled) {
01080     return;
01081   }
01082 
01083   NodePath render = get_render();
01084 
01085   if (enable) {
01086     render.set_shader_auto();
01087   } else {
01088     render.set_shader_off();
01089   }
01090 
01091   _perpixel_enabled = enable;
01092 }
01093 
01094 ////////////////////////////////////////////////////////////////////
01095 //     Function: WindowFramework::set_background_type
01096 //       Access: Public
01097 //  Description: Sets the background of the window to one of the
01098 //               pre-canned background types (or to BT_other, which
01099 //               indicates the user intends to set up his own special
01100 //               background mode).
01101 ////////////////////////////////////////////////////////////////////
01102 void WindowFramework::
01103 set_background_type(WindowFramework::BackgroundType type) {
01104   _background_type = type;
01105 
01106   if (_display_region_3d == (DisplayRegion *)NULL) {
01107     return;
01108   }
01109 
01110   switch (_background_type) {
01111   case BT_other:
01112     break;
01113 
01114   case BT_default:
01115     _display_region_3d->set_clear_color_active(true);
01116     _display_region_3d->set_clear_depth_active(true);
01117     _display_region_3d->set_clear_stencil_active(true);
01118     _display_region_3d->set_clear_color(_window->get_clear_color());
01119     _display_region_3d->set_clear_depth(_window->get_clear_depth());
01120     _display_region_3d->set_clear_stencil(_window->get_clear_stencil());
01121     break;
01122 
01123   case BT_black:
01124     _display_region_3d->set_clear_color_active(true);
01125     _display_region_3d->set_clear_depth_active(true);
01126     _display_region_3d->set_clear_stencil_active(true);
01127     _display_region_3d->set_clear_color(LColor(0.0f, 0.0f, 0.0f, 0.0f));
01128     _display_region_3d->set_clear_depth(1.0f);
01129     _display_region_3d->set_clear_stencil(0);
01130     break;
01131 
01132   case BT_gray:
01133     _display_region_3d->set_clear_color_active(true);
01134     _display_region_3d->set_clear_depth_active(true);
01135     _display_region_3d->set_clear_stencil_active(true);
01136     _display_region_3d->set_clear_color(LColor(0.3, 0.3, 0.3, 0.0f));
01137     _display_region_3d->set_clear_depth(1.0f);
01138     _display_region_3d->set_clear_stencil(0);
01139     break;
01140 
01141   case BT_white:
01142     _display_region_3d->set_clear_color_active(true);
01143     _display_region_3d->set_clear_depth_active(true);
01144     _display_region_3d->set_clear_stencil_active(true);
01145     _display_region_3d->set_clear_color(LColor(1.0f, 1.0f, 1.0f, 0.0f));
01146     _display_region_3d->set_clear_depth(1.0f);
01147     _display_region_3d->set_clear_stencil(0);
01148     break;
01149 
01150   case BT_none:
01151     _display_region_3d->set_clear_color_active(false);
01152     _display_region_3d->set_clear_depth_active(false);
01153     _display_region_3d->set_clear_stencil_active(false);
01154     break;
01155   }
01156 }
01157 
01158 ////////////////////////////////////////////////////////////////////
01159 //     Function: WindowFramework::get_shuttle_controls_font
01160 //       Access: Public, Static
01161 //  Description: Returns a font that contains the shuttle controls
01162 //               icons.
01163 ////////////////////////////////////////////////////////////////////
01164 TextFont *WindowFramework::
01165 get_shuttle_controls_font() {
01166   if (_shuttle_controls_font == (TextFont *)NULL) {
01167     PT(TextFont) font;
01168 
01169     string shuttle_controls_string((const char *)shuttle_controls, shuttle_controls_len);
01170     istringstream in(shuttle_controls_string);
01171     BamFile bam_file;
01172     if (bam_file.open_read(in, "shuttle_controls font stream")) {
01173       PT(PandaNode) node = bam_file.read_node();
01174       if (node != (PandaNode *)NULL) {
01175         _shuttle_controls_font = new StaticTextFont(node);
01176       }
01177     }
01178   }
01179 
01180   return _shuttle_controls_font;
01181 }
01182 
01183 ////////////////////////////////////////////////////////////////////
01184 //     Function: WindowFramework::make_camera
01185 //       Access: Public
01186 //  Description: Makes a new 3-d camera for the window.
01187 ////////////////////////////////////////////////////////////////////
01188 NodePath WindowFramework::
01189 make_camera() {
01190   // Finally, we need a camera to associate with the display region.
01191   PT(Camera) camera = new Camera("camera");
01192   NodePath camera_np = get_camera_group().attach_new_node(camera);
01193   _cameras.push_back(camera);
01194 
01195   PT(Lens) lens = new PerspectiveLens;
01196 
01197   if (aspect_ratio != 0.0) {
01198     // If we're given an explict aspect ratio, use it
01199     lens->set_aspect_ratio(aspect_ratio);
01200 
01201   } else {
01202     // Otherwise, infer the aspect ratio from the window size.  This
01203     // does assume we have square pixels on our output device.
01204     if (_window->has_size()) {
01205       int x_size = _window->get_sbs_left_x_size();
01206       int y_size = _window->get_sbs_left_y_size();
01207       if (y_size != 0) {
01208         lens->set_film_size(x_size, y_size);
01209       }
01210     }
01211   }
01212 
01213   camera->set_lens(lens);
01214 
01215   return camera_np;
01216 }
01217 
01218 ////////////////////////////////////////////////////////////////////
01219 //     Function: WindowFramework::setup_lights
01220 //       Access: Protected
01221 //  Description: Makes light nodes and attaches them to the camera for
01222 //               viewing the scene.
01223 ////////////////////////////////////////////////////////////////////
01224 void WindowFramework::
01225 setup_lights() {
01226   if (_got_lights) {
01227     return;
01228   }
01229 
01230   NodePath camera_group = get_camera_group();
01231   NodePath light_group = camera_group.attach_new_node("lights");
01232 
01233   AmbientLight *alight = new AmbientLight("ambient");
01234   alight->set_color(LColor(0.2, 0.2, 0.2, 1.0f));
01235   DirectionalLight *dlight = new DirectionalLight("directional");
01236   dlight->set_color(LColor(0.8f, 0.8f, 0.8f, 1.0f));
01237 
01238   _alight = light_group.attach_new_node(alight);
01239   _dlight = light_group.attach_new_node(dlight);
01240   _dlight.set_hpr(-10, -20, 0);
01241 
01242   _got_lights = true;
01243 }
01244 
01245 ////////////////////////////////////////////////////////////////////
01246 //     Function: WindowFramework::load_image_as_model
01247 //       Access: Private
01248 //  Description: Loads the indicated image file as a texture, and
01249 //               creates a polygon to render it.  Returns the new
01250 //               model.
01251 ////////////////////////////////////////////////////////////////////
01252 PT(PandaNode) WindowFramework::
01253 load_image_as_model(const Filename &filename) {
01254   PT(Texture) tex = TexturePool::load_texture(filename);
01255   if (tex == NULL) {
01256     return NULL;
01257   }
01258 
01259   // Yes, it is an image file; make a texture out of it.
01260   tex->set_minfilter(Texture::FT_linear_mipmap_linear);
01261   tex->set_magfilter(Texture::FT_linear);
01262 
01263   // Ok, now make a polygon to show the texture.
01264   bool has_alpha = true;
01265   LVecBase2 tex_scale = tex->get_tex_scale();
01266 
01267   // Get the size from the original image (the texture may have
01268   // scaled it to make a power of 2).
01269   int x_size = tex->get_orig_file_x_size();
01270   int y_size = tex->get_orig_file_y_size();
01271 
01272   // Choose the dimensions of the polygon appropriately.
01273   PN_stdfloat left,right,top,bottom;
01274   static const PN_stdfloat scale = 10.0;
01275   if (x_size > y_size) {
01276     left   = -scale;
01277     right  =  scale;
01278     top    =  (scale * y_size) / x_size;
01279     bottom = -(scale * y_size) / x_size;
01280   } else if (y_size != 0) {
01281     left   = -(scale * x_size) / y_size;
01282     right  =  (scale * x_size) / y_size;
01283     top    =  scale;
01284     bottom = -scale;
01285   } else {
01286     framework_cat.warning()
01287       << "Texture size is 0 0: " << *tex << "\n";
01288     
01289     left   = -scale;
01290     right  =  scale;
01291     top    =  scale;
01292     bottom = -scale;
01293   }
01294 
01295   PT(GeomNode) card_node = new GeomNode("card");
01296   card_node->set_attrib(TextureAttrib::make(tex));
01297   if (has_alpha) {
01298     card_node->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
01299   }
01300 
01301   bool is_3d = false;
01302   if (tex->get_texture_type() == Texture::TT_3d_texture ||
01303       tex->get_texture_type() == Texture::TT_cube_map) {
01304     // For a 3-d texture, generate a cube, instead of a plain card.
01305     is_3d = true;
01306   }
01307 
01308   CPT(GeomVertexFormat) vformat;
01309   if (!is_3d) {
01310     // Vertices and 2-d texture coordinates, all we need.
01311     vformat = GeomVertexFormat::get_v3t2();
01312 
01313   } else {
01314     // Vertices and 3-d texture coordinates.
01315     vformat = GeomVertexFormat::register_format
01316       (new GeomVertexArrayFormat
01317        (InternalName::get_vertex(), 3, 
01318         GeomEnums::NT_stdfloat, GeomEnums::C_point,
01319         InternalName::get_texcoord(), 3, 
01320         GeomEnums::NT_stdfloat, GeomEnums::C_texcoord));
01321   }
01322 
01323   PT(GeomVertexData) vdata = new GeomVertexData
01324     ("card", vformat, Geom::UH_static);
01325   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
01326   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
01327 
01328   if (!is_3d) {
01329     // A normal 2-d card.
01330     vertex.add_data3(LVertex::rfu(left, 0.02, top));
01331     vertex.add_data3(LVertex::rfu(left, 0.02, bottom));
01332     vertex.add_data3(LVertex::rfu(right, 0.02, top));
01333     vertex.add_data3(LVertex::rfu(right, 0.02, bottom));
01334     
01335     texcoord.add_data2(0.0f, tex_scale[1]);
01336     texcoord.add_data2(0.0f, 0.0f);
01337     texcoord.add_data2(tex_scale[0], tex_scale[1]);
01338     texcoord.add_data2(tex_scale[0], 0.0f);
01339 
01340   } else {
01341     // The eight vertices of a 3-d cube.
01342     vertex.add_data3(-1.0f, -1.0f, 1.0f);   // 0
01343     vertex.add_data3(-1.0f, -1.0f, -1.0f);  // 1
01344     vertex.add_data3(1.0f, -1.0f, -1.0f);   // 2
01345     vertex.add_data3(1.0f, -1.0f, 1.0f);    // 3
01346     vertex.add_data3(1.0f, 1.0f, 1.0f);     // 4
01347     vertex.add_data3(1.0f, 1.0f, -1.0f);    // 5
01348     vertex.add_data3(-1.0f, 1.0f, -1.0f);   // 6
01349     vertex.add_data3(-1.0f, 1.0f, 1.0f);    // 7
01350 
01351     texcoord.add_data3(-1.0f, -1.0f, 1.0f);   // 0
01352     texcoord.add_data3(-1.0f, -1.0f, -1.0f);  // 1
01353     texcoord.add_data3(1.0f, -1.0f, -1.0f);   // 2
01354     texcoord.add_data3(1.0f, -1.0f, 1.0f);    // 3
01355     texcoord.add_data3(1.0f, 1.0f, 1.0f);     // 4
01356     texcoord.add_data3(1.0f, 1.0f, -1.0f);    // 5
01357     texcoord.add_data3(-1.0f, 1.0f, -1.0f);   // 6
01358     texcoord.add_data3(-1.0f, 1.0f, 1.0f);    // 7
01359   }
01360 
01361   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
01362 
01363   if (!is_3d) {
01364     // The two triangles that make up a quad.
01365     strip->add_consecutive_vertices(0, 4);
01366     strip->close_primitive();
01367 
01368   } else {
01369     // The twelve triangles (six quads) that make up a cube.
01370     strip->add_vertex(7);
01371     strip->add_vertex(0);
01372     strip->add_vertex(4);
01373     strip->add_vertex(3);
01374     strip->close_primitive();
01375 
01376     strip->add_vertex(1);
01377     strip->add_vertex(6);
01378     strip->add_vertex(2);
01379     strip->add_vertex(5);
01380     strip->close_primitive();
01381 
01382     strip->add_vertex(5);
01383     strip->add_vertex(4);
01384     strip->add_vertex(2);
01385     strip->add_vertex(3);
01386     strip->add_vertex(1);
01387     strip->add_vertex(0);
01388     strip->add_vertex(6);
01389     strip->add_vertex(7);
01390     strip->add_vertex(5);
01391     strip->add_vertex(4);
01392     strip->close_primitive();
01393   }
01394 
01395   PT(Geom) geom = new Geom(vdata);
01396   geom->add_primitive(strip);
01397 
01398   card_node->add_geom(geom);
01399 
01400   return card_node.p();
01401 }
01402 
01403 ////////////////////////////////////////////////////////////////////
01404 //     Function: WindowFramework::create_anim_controls
01405 //       Access: Private
01406 //  Description: Creates an onscreen animation slider for
01407 //               frame-stepping through the animations.
01408 ////////////////////////////////////////////////////////////////////
01409 void WindowFramework::
01410 create_anim_controls() {
01411   destroy_anim_controls();
01412 
01413   PT(PGItem) group = new PGItem("anim_controls_group");
01414   PGFrameStyle style;
01415   style.set_type(PGFrameStyle::T_flat);
01416   style.set_color(0.0f, 0.0f, 0.0f, 0.3);
01417   group->set_frame(-1.0f, 1.0f, 0.0f, 0.2);
01418   group->set_frame_style(0, style);
01419   group->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
01420   group->set_active(true);
01421 
01422   _anim_controls_group = get_aspect_2d().attach_new_node(group);
01423   _anim_controls_group.set_pos(0.0f, 0.0f, -0.9f);
01424 
01425   if (_anim_index >= _anim_controls.get_num_anims()) {
01426     PT(TextNode) label = new TextNode("label");
01427     label->set_align(TextNode::A_center);
01428     label->set_text("No animation.");
01429     NodePath tnp = _anim_controls_group.attach_new_node(label);
01430     tnp.set_pos(0.0f, 0.0f, 0.07f);
01431     tnp.set_scale(0.1f);
01432 
01433     return;
01434   }
01435 
01436   AnimControl *control = _anim_controls.get_anim(_anim_index);
01437   nassertv(control != (AnimControl *)NULL);
01438 
01439   PT(TextNode) label = new TextNode("anim_name");
01440   label->set_align(TextNode::A_left);
01441   label->set_text(_anim_controls.get_anim_name(_anim_index));
01442   NodePath tnp = _anim_controls_group.attach_new_node(label);
01443   tnp.set_pos(-0.95f, 0.0f, 0.15f);
01444   tnp.set_scale(0.05f);
01445 
01446   _anim_slider = new PGSliderBar("anim_slider");
01447   _anim_slider->setup_slider(false, 1.9f, 0.1f, 0.005f);
01448   _anim_slider->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
01449   _anim_slider->get_thumb_button()->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
01450 
01451   _anim_slider->set_range(0.0f, (PN_stdfloat)(control->get_num_frames() - 1));
01452   _anim_slider->set_scroll_size(0.0f);
01453   _anim_slider->set_page_size(1.0f);
01454   NodePath snp = _anim_controls_group.attach_new_node(_anim_slider);
01455   snp.set_pos(0.0f, 0.0f, 0.06f);
01456 
01457   _frame_number = new TextNode("frame_number");
01458   _frame_number->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
01459   _frame_number->set_align(TextNode::A_center);
01460   _frame_number->set_text(format_string(control->get_frame()));
01461   NodePath fnp = NodePath(_anim_slider->get_thumb_button()).attach_new_node(_frame_number);
01462   fnp.set_scale(0.05f);
01463   fnp.set_pos(0.0f, 0.0f, -0.01f);
01464 
01465   _play_rate_slider = new PGSliderBar("play_rate_slider");
01466   _play_rate_slider->setup_slider(false, 0.4, 0.05f, 0.005f);
01467   _play_rate_slider->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
01468   _play_rate_slider->get_thumb_button()->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
01469   _play_rate_slider->set_value(control->get_play_rate());
01470   NodePath pnp = _anim_controls_group.attach_new_node(_play_rate_slider);
01471   pnp.set_pos(0.75f, 0.0f, 0.15f);
01472 
01473   // Set up the jog/shuttle buttons.  These use symbols from the
01474   // shuttle_controls_font file.
01475   setup_shuttle_button("9", 0, st_back_button);
01476   setup_shuttle_button(";", 1, st_pause_button);
01477   setup_shuttle_button("4", 2, st_play_button);
01478   setup_shuttle_button(":", 3, st_forward_button);
01479 
01480   _update_anim_controls_task = new GenericAsyncTask("controls", st_update_anim_controls, (void *)this);
01481   _panda_framework->get_task_mgr().add(_update_anim_controls_task);
01482 }
01483 
01484 ////////////////////////////////////////////////////////////////////
01485 //     Function: WindowFramework::destroy_anim_controls
01486 //       Access: Private
01487 //  Description: Removes the previously-created anim controls, if any.
01488 ////////////////////////////////////////////////////////////////////
01489 void WindowFramework::
01490 destroy_anim_controls() {
01491   if (!_anim_controls_group.is_empty()) {
01492     _anim_controls_group.remove_node();
01493 
01494     _panda_framework->get_event_handler().remove_hooks_with((void *)this);
01495     if (_update_anim_controls_task != NULL) {
01496       _panda_framework->get_task_mgr().remove(_update_anim_controls_task);
01497       _update_anim_controls_task.clear();
01498     }
01499   }
01500 }
01501 
01502 ////////////////////////////////////////////////////////////////////
01503 //     Function: WindowFramework::update_anim_controls
01504 //       Access: Private
01505 //  Description: A per-frame callback to update the anim slider for
01506 //               the current frame.
01507 ////////////////////////////////////////////////////////////////////
01508 void WindowFramework::
01509 update_anim_controls() {
01510   AnimControl *control = _anim_controls.get_anim(_anim_index);
01511   nassertv(control != (AnimControl *)NULL);
01512 
01513   if (_anim_slider->is_button_down()) {
01514     control->pose((int)(_anim_slider->get_value() + 0.5));
01515   } else {
01516     _anim_slider->set_value((PN_stdfloat)control->get_frame());
01517   }
01518 
01519   _frame_number->set_text(format_string(control->get_frame()));
01520 
01521   control->set_play_rate(_play_rate_slider->get_value());
01522 }
01523 
01524 ////////////////////////////////////////////////////////////////////
01525 //     Function: WindowFramework::setup_shuttle_button
01526 //       Access: Private
01527 //  Description: Creates a PGButton to implement the indicated shuttle
01528 //               event (play, pause, etc.).
01529 ////////////////////////////////////////////////////////////////////
01530 void WindowFramework::
01531 setup_shuttle_button(const string &label, int index,
01532                      EventHandler::EventCallbackFunction *func) {
01533   PT(PGButton) button = new PGButton(label);
01534   button->set_frame(-0.05f, 0.05f, 0.0f, 0.07f);
01535 
01536   PN_stdfloat bevel = 0.005f;
01537 
01538   PGFrameStyle style;
01539   style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
01540   style.set_width(bevel, bevel);
01541 
01542   style.set_type(PGFrameStyle::T_bevel_out);
01543   button->set_frame_style(PGButton::S_ready, style);
01544 
01545   style.set_type(PGFrameStyle::T_bevel_in);
01546   button->set_frame_style(PGButton::S_depressed, style);
01547 
01548   style.set_color(0.9f, 0.9f, 0.9f, 1.0f);
01549   style.set_type(PGFrameStyle::T_bevel_out);
01550   button->set_frame_style(PGButton::S_rollover, style);
01551 
01552   if (get_shuttle_controls_font() != (TextFont *)NULL) {
01553     PT(TextNode) tn = new TextNode("label");
01554     tn->set_align(TextNode::A_center);
01555     tn->set_font(get_shuttle_controls_font());
01556     tn->set_text(label);
01557     tn->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
01558     LMatrix4 xform = LMatrix4::scale_mat(0.07f);
01559     xform.set_row(3, LVecBase3(0.0f, 0.0f, 0.016f));
01560     tn->set_transform(xform);
01561 
01562     button->get_state_def(PGButton::S_ready).attach_new_node(tn);
01563     button->get_state_def(PGButton::S_depressed).attach_new_node(tn);
01564     button->get_state_def(PGButton::S_rollover).attach_new_node(tn);
01565   }
01566 
01567   NodePath np = _anim_controls_group.attach_new_node(button);
01568   np.set_pos(0.1f * index - 0.15f, 0.0f, 0.12);
01569 
01570   _panda_framework->get_event_handler().add_hook(button->get_click_event(MouseButton::one()), func, (void *)this);
01571 }
01572 
01573 ////////////////////////////////////////////////////////////////////
01574 //     Function: WindowFramework::back_button
01575 //       Access: Private, Static
01576 //  Description: Handler for a shuttle button.
01577 ////////////////////////////////////////////////////////////////////
01578 void WindowFramework::
01579 back_button() {
01580   AnimControl *control = _anim_controls.get_anim(_anim_index);
01581   nassertv(control != (AnimControl *)NULL);
01582   control->pose(control->get_frame() - 1);
01583 }
01584 
01585 ////////////////////////////////////////////////////////////////////
01586 //     Function: WindowFramework::pause_button
01587 //       Access: Private, Static
01588 //  Description: Handler for a shuttle button.
01589 ////////////////////////////////////////////////////////////////////
01590 void WindowFramework::
01591 pause_button() {
01592   AnimControl *control = _anim_controls.get_anim(_anim_index);
01593   nassertv(control != (AnimControl *)NULL);
01594   control->stop();
01595 }
01596 
01597 ////////////////////////////////////////////////////////////////////
01598 //     Function: WindowFramework::play_button
01599 //       Access: Private, Static
01600 //  Description: Handler for a shuttle button.
01601 ////////////////////////////////////////////////////////////////////
01602 void WindowFramework::
01603 play_button() {
01604   AnimControl *control = _anim_controls.get_anim(_anim_index);
01605   nassertv(control != (AnimControl *)NULL);
01606   control->loop(false);
01607 }
01608 
01609 ////////////////////////////////////////////////////////////////////
01610 //     Function: WindowFramework::forward_button
01611 //       Access: Private, Static
01612 //  Description: Handler for a shuttle button.
01613 ////////////////////////////////////////////////////////////////////
01614 void WindowFramework::
01615 forward_button() {
01616   AnimControl *control = _anim_controls.get_anim(_anim_index);
01617   nassertv(control != (AnimControl *)NULL);
01618   control->pose(control->get_frame() + 1);
01619 }
01620 
01621 
01622 ////////////////////////////////////////////////////////////////////
01623 //     Function: WindowFramework::st_update_anim_controls
01624 //       Access: Private, Static
01625 //  Description: The static task function.
01626 ////////////////////////////////////////////////////////////////////
01627 AsyncTask::DoneStatus WindowFramework::
01628 st_update_anim_controls(GenericAsyncTask *, void *data) {
01629   WindowFramework *self = (WindowFramework *)data;
01630   self->update_anim_controls();
01631   return AsyncTask::DS_cont;
01632 }
01633 
01634 
01635 ////////////////////////////////////////////////////////////////////
01636 //     Function: WindowFramework::st_back_button
01637 //       Access: Private, Static
01638 //  Description: The static event handler function.
01639 ////////////////////////////////////////////////////////////////////
01640 void WindowFramework::
01641 st_back_button(const Event *, void *data) {
01642   WindowFramework *self = (WindowFramework *)data;
01643   self->back_button();
01644 }
01645 
01646 ////////////////////////////////////////////////////////////////////
01647 //     Function: WindowFramework::st_pause_button
01648 //       Access: Private, Static
01649 //  Description: The static event handler function.
01650 ////////////////////////////////////////////////////////////////////
01651 void WindowFramework::
01652 st_pause_button(const Event *, void *data) {
01653   WindowFramework *self = (WindowFramework *)data;
01654   self->pause_button();
01655 }
01656 
01657 ////////////////////////////////////////////////////////////////////
01658 //     Function: WindowFramework::st_play_button
01659 //       Access: Private, Static
01660 //  Description: The static event handler function.
01661 ////////////////////////////////////////////////////////////////////
01662 void WindowFramework::
01663 st_play_button(const Event *, void *data) {
01664   WindowFramework *self = (WindowFramework *)data;
01665   self->play_button();
01666 }
01667 
01668 ////////////////////////////////////////////////////////////////////
01669 //     Function: WindowFramework::st_forward_button
01670 //       Access: Private, Static
01671 //  Description: The static event handler function.
01672 ////////////////////////////////////////////////////////////////////
01673 void WindowFramework::
01674 st_forward_button(const Event *, void *data) {
01675   WindowFramework *self = (WindowFramework *)data;
01676   self->forward_button();
01677 }
 All Classes Functions Variables Enumerations