Panda3D
|
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 ©, 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 }