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