Panda3D

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