Panda3D
Loading...
Searching...
No Matches
windowFramework.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file windowFramework.cxx
10 * @author drose
11 * @date 2002-04-02
12 */
13
14#include "windowFramework.h"
15#include "pandaFramework.h"
16#include "displayRegion.h"
17#include "buttonThrower.h"
18#include "transform2sg.h"
19#include "dSearchPath.h"
20#include "filename.h"
21#include "loader.h"
22#include "keyboardButton.h"
23#include "geom.h"
24#include "geomTriangles.h"
25#include "geomTristrips.h"
26#include "geomVertexData.h"
27#include "geomVertexFormat.h"
28#include "geomVertexWriter.h"
29#include "texturePool.h"
30#include "textureAttrib.h"
31#include "colorAttrib.h"
32#include "perspectiveLens.h"
33#include "orthographicLens.h"
34#include "auto_bind.h"
35#include "ambientLight.h"
36#include "directionalLight.h"
37#include "lightAttrib.h"
38#include "boundingSphere.h"
39#include "deg_2_rad.h"
40#include "config_framework.h"
41#include "cullFaceAttrib.h"
42#include "rescaleNormalAttrib.h"
43#include "shadeModelAttrib.h"
44#include "pgTop.h"
45#include "geomNode.h"
46#include "texture.h"
47#include "texturePool.h"
49#include "pnmImage.h"
50#include "virtualFileSystem.h"
51#include "string_utils.h"
52#include "bamFile.h"
53#include "staticTextFont.h"
54#include "mouseButton.h"
55
56// This is generated data for the standard texture we apply to the blue
57// triangle.
58#include "rock_floor.rgb_src.c"
59
60// This is generated data for shuttle_controls.bam, a bamified version of
61// shuttle_controls.egg (found in the models tree). It's compiled in
62// shuttle_controls.bam_src.c.
63#include "shuttle_controls.bam_src.c"
64
65using std::istringstream;
66using std::ostringstream;
67using std::string;
68
69// This number is chosen arbitrarily to override any settings in model files.
70static const int override_priority = 100;
71
72PT(TextFont) WindowFramework::_shuttle_controls_font = nullptr;
73TypeHandle WindowFramework::_type_handle;
74
75/**
76 *
77 */
78WindowFramework::
79WindowFramework(PandaFramework *panda_framework) :
80 _panda_framework(panda_framework)
81{
82 _got_keyboard = false;
83 _got_trackball = false;
84 _got_lights = false;
85 _anim_controls_enabled = false;
86 _anim_index = 0;
87 _wireframe_enabled = false;
88 _wireframe_filled = false;
89 _texture_enabled = true;
90 _two_sided_enabled = false;
91 _one_sided_reverse_enabled = false;
92 _lighting_enabled = false;
93 _perpixel_enabled = false;
94 _background_type = BT_default;
95}
96
97/**
98 *
99 */
100WindowFramework::
101WindowFramework(const WindowFramework &copy, DisplayRegion *display_region) :
102 _panda_framework(copy._panda_framework),
103 _window(copy._window),
104 _display_region_3d(display_region)
105{
106 _got_keyboard = false;
107 _got_trackball = false;
108 _got_lights = false;
109 _anim_controls_enabled = false;
110 _anim_index = 0;
111 _wireframe_enabled = false;
112 _texture_enabled = true;
113 _two_sided_enabled = false;
114 _one_sided_reverse_enabled = false;
115 _lighting_enabled = false;
116 _perpixel_enabled = false;
117 _background_type = BT_default;
118
119 set_background_type(copy._background_type);
120 // Set up a 3-d camera for the window by default.
121 NodePath camera_np = make_camera();
122 _display_region_3d->set_camera(camera_np);
123}
124
125/**
126 *
127 */
128WindowFramework::
129~WindowFramework() {
130 close_window();
131}
132
133/**
134 * Opens the actual window or buffer. This is normally called only from
135 * PandaFramework::open_window().
136 */
137GraphicsOutput *WindowFramework::
138open_window(const WindowProperties &props, int flags, GraphicsEngine *engine,
140 const FrameBufferProperties &fbprops) {
141 nassertr(_window == nullptr, _window);
142
143 static int next_window_index = 1;
144 ostringstream stream;
145 stream << "window" << next_window_index;
146 next_window_index++;
147 string name = stream.str();
148
149 _window = nullptr;
150 GraphicsOutput *winout =
151 engine->make_output(pipe, name, 0, fbprops,
152 props, flags, gsg, nullptr);
153 if (winout != nullptr) {
154 _window = winout;
155 // _window->request_properties(props);
156
157 // Create a display region that covers the entire window.
158 _display_region_3d = _window->make_display_region();
159
160 // Make sure the DisplayRegion does the clearing, not the window, so we
161 // can have multiple DisplayRegions of different colors.
162 _window->set_clear_color_active(false);
163 _window->set_clear_depth_active(false);
164 _window->set_clear_stencil_active(false);
165
166 // Set up a 3-d camera for the window by default.
167 NodePath camera_np = make_camera();
168 _display_region_3d->set_camera(camera_np);
169
170 set_background_type(_background_type);
171
172 if (show_frame_rate_meter) {
173 _frame_rate_meter = new FrameRateMeter("frame_rate_meter");
174 _frame_rate_meter->setup_window(_window);
175 }
176 if (show_scene_graph_analyzer_meter) {
177 _scene_graph_analyzer_meter = new SceneGraphAnalyzerMeter("scene_graph_analyzer_meter", get_render().node());
178 _scene_graph_analyzer_meter->setup_window(_window);
179 }
180 }
181
182 return _window;
183}
184
185/**
186 * Closes the window or buffer. This is normally called from
187 * PandaFramework::close_window().
188 */
189void WindowFramework::
190close_window() {
191 _window.clear();
192 _camera_group.remove_node();
193 _render.remove_node();
194 _render_2d.remove_node();
195 _mouse.remove_node();
196
197 _alight.clear();
198 _dlight.clear();
199 _got_keyboard = false;
200 _got_trackball = false;
201 _got_lights = false;
202
203 _wireframe_enabled = false;
204 _texture_enabled = true;
205 _two_sided_enabled = false;
206 _one_sided_reverse_enabled = false;
207 _lighting_enabled = false;
208 _perpixel_enabled = false;
209
210 if (_frame_rate_meter != nullptr) {
211 _frame_rate_meter->clear_window();
212 _frame_rate_meter = nullptr;
213 }
214 if (_scene_graph_analyzer_meter != nullptr) {
215 _scene_graph_analyzer_meter->clear_window();
216 _scene_graph_analyzer_meter = nullptr;
217 }
218}
219
220/**
221 * Returns the node above the collection of 3-d cameras in the scene graph.
222 * This node may be moved around to represent the viewpoint.
223 */
226 if (_camera_group.is_empty()) {
227 _camera_group = get_render().attach_new_node("camera_group");
228 }
229 return _camera_group;
230}
231
232/**
233 * Returns the root of the 3-d scene graph.
234 */
236get_render() {
237 if (_render.is_empty()) {
238 _render = NodePath("render");
239
240 _render.node()->set_attrib(RescaleNormalAttrib::make_default());
241 _render.node()->set_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_smooth));
242
243 // This is maybe here temporarily, and maybe not.
244 _render.set_two_sided(0);
245 }
246 return _render;
247}
248
249/**
250 * Returns the root of the 2-d scene graph.
251 */
254 if (_render_2d.is_empty()) {
255 _render_2d = NodePath("render_2d");
256
257 // Some standard properties for the 2-d display.
258
259 _render_2d.set_depth_write(0);
260 _render_2d.set_depth_test(0);
261 _render_2d.set_material_off(1);
262 _render_2d.set_two_sided(1);
263
264 // Now set up a 2-d camera to view render_2d.
265
266 // Create a display region that matches the size of the 3-d display
267 // region.
268 PN_stdfloat l, r, b, t;
269 _display_region_3d->get_dimensions(l, r, b, t);
270 _display_region_2d = _window->make_mono_display_region(l, r, b, t);
271 _display_region_2d->set_sort(10);
272
273 // Finally, we need a camera to associate with the display region.
274 PT(Camera) camera = new Camera("camera2d");
275 NodePath camera_np = _render_2d.attach_new_node(camera);
276
277 PT(Lens) lens = new OrthographicLens;
278
279 static const PN_stdfloat left = -1.0f;
280 static const PN_stdfloat right = 1.0f;
281 static const PN_stdfloat bottom = -1.0f;
282 static const PN_stdfloat top = 1.0f;
283 lens->set_film_size(right - left, top - bottom);
284 lens->set_film_offset((right + left) * 0.5, (top + bottom) * 0.5);
285 lens->set_near_far(-1000, 1000);
286
287 camera->set_lens(lens);
288 _display_region_2d->set_camera(camera_np);
289 }
290
291 return _render_2d;
292}
293
294/**
295 * Returns the node under the 2-d scene graph that is scaled to suit the
296 * window's aspect ratio.
297 */
300 if (_aspect_2d.is_empty()) {
301 PGTop *top = new PGTop("aspect_2d");
302 _aspect_2d = get_render_2d().attach_new_node(top);
303
304 // Tell the PGTop about our MouseWatcher object, so the PGui system can
305 // operate.
306 PandaNode *mouse_node = get_mouse().node();
307 if (mouse_node->is_of_type(MouseWatcher::get_class_type())) {
308 top->set_mouse_watcher(DCAST(MouseWatcher, mouse_node));
309 }
310
311 PN_stdfloat this_aspect_ratio = aspect_ratio;
312 if (this_aspect_ratio == 0.0f) {
313 // An aspect ratio of 0.0 means to try to infer it.
314 this_aspect_ratio = 1.0f;
315
316 if (_window->has_size()) {
317 int x_size = _window->get_sbs_left_x_size();
318 int y_size = _window->get_sbs_left_y_size();
319 if (y_size != 0) {
320 this_aspect_ratio = (PN_stdfloat)x_size / (PN_stdfloat)y_size;
321 }
322 }
323 }
324
325 _aspect_2d.set_scale(1.0f / this_aspect_ratio, 1.0f, 1.0f);
326 }
327
328 return _aspect_2d;
329}
330
331/**
332 * Returns a special root that uses units in pixels that are relative to the
333 * window. The upperleft corner of the window is (0, 0), the lowerleft corner
334 * is (xsize, -ysize), in this coordinate system.
335 */
337get_pixel_2d() {
338 if (_pixel_2d.is_empty()) {
339 PGTop *top = new PGTop("pixel_2d");
340 _pixel_2d = get_render_2d().attach_new_node(top);
341 _pixel_2d.set_pos(-1, 0, 1);
342
343 if (_window->has_size()) {
344 int x_size = _window->get_sbs_left_x_size();
345 int y_size = _window->get_sbs_left_y_size();
346 if (x_size > 0){
347 _pixel_2d.set_sx(2.0f / (float)x_size);
348 }
349 _pixel_2d.set_sy(1.0f);
350 if (y_size > 0){
351 _pixel_2d.set_sz(2.0f / (float)y_size);
352 }
353 }
354 }
355
356 return _pixel_2d;
357}
358
359/**
360 * Returns the node in the data graph corresponding to the mouse associated
361 * with this window.
362 */
364get_mouse() {
365 if (_mouse.is_empty()) {
366 NodePath mouse = _panda_framework->get_mouse(_window);
367
368 // Create a MouseWatcher to filter the mouse input. We do this mainly so
369 // we can constrain the mouse input to our particular display region, if
370 // we have one. This means the node we return from get_mouse() is
371 // actually a MouseWatcher, but since it presents the same interface as a
372 // Mouse, no one should mind.
373
374 // Another advantage to using a MouseWatcher is that the PGTop of aspect2d
375 // likes it better.
376 PT(MouseWatcher) mw = new MouseWatcher("watcher");
377
378 if (_window->get_side_by_side_stereo()) {
379 // If the window has side-by-side stereo enabled, then we should
380 // constrain the MouseWatcher to the window's DisplayRegion. This will
381 // enable the MouseWatcher to track the left and right halves of the
382 // screen individually.
383 mw->set_display_region(_window->get_overlay_display_region());
384 }
385
386 _mouse = mouse.attach_new_node(mw);
387 }
388 return _mouse;
389}
390
391/**
392 * Returns the node in the data graph corresponding to the ButtonThrower
393 * object associated with this window.
394 */
397 return _button_thrower;
398}
399
400/**
401 * Creates a ButtonThrower to listen to button presses and throw them as
402 * events.
403 */
406 if (_got_keyboard) {
407 return;
408 }
409
410 if (_window->is_of_type(GraphicsWindow::get_class_type()) &&
411 DCAST(GraphicsWindow, _window)->get_num_input_devices() > 0) {
412 NodePath mouse = get_mouse();
413
414 // Create a button thrower to listen for our keyboard events and associate
415 // this WindowFramework pointer with each one.
416 PT(ButtonThrower) bt = new ButtonThrower("kb-events");
417 bt->add_parameter(EventParameter(this));
418 ModifierButtons mods;
419 mods.add_button(KeyboardButton::shift());
420 mods.add_button(KeyboardButton::control());
421 mods.add_button(KeyboardButton::alt());
422 mods.add_button(KeyboardButton::meta());
423 bt->set_modifier_buttons(mods);
424 _button_thrower = mouse.attach_new_node(bt);
425 }
426
427 _got_keyboard = true;
428}
429
430/**
431 * Sets up the mouse to trackball around the camera.
432 */
435 if (_got_trackball) {
436 return;
437 }
438
439 if (_window->is_of_type(GraphicsWindow::get_class_type()) &&
440 DCAST(GraphicsWindow, _window)->get_num_input_devices() > 0) {
441 NodePath mouse = get_mouse();
442 NodePath camera = get_camera_group();
443
444 _trackball = new Trackball("trackball");
445 _trackball->set_pos(LVector3::forward() * 50.0);
446 mouse.attach_new_node(_trackball);
447
448 PT(Transform2SG) tball2cam = new Transform2SG("tball2cam");
449 tball2cam->set_node(camera.node());
450 _trackball->add_child(tball2cam);
451 }
452
453 _got_trackball = true;
454}
455
456/**
457 * Centers the trackball on the indicated object, and scales the trackball
458 * motion suitably.
459 */
461center_trackball(const NodePath &object) {
462 if (_trackball == nullptr) {
463 return;
464 }
465
466 PT(BoundingVolume) volume = object.get_bounds();
467 // We expect at least a geometric bounding volume around the world.
468 nassertv(volume != nullptr);
469 nassertv(volume->is_of_type(GeometricBoundingVolume::get_class_type()));
470 CPT(GeometricBoundingVolume) gbv = DCAST(GeometricBoundingVolume, volume);
471
472 if (object.has_parent()) {
473 CPT(TransformState) net_transform = object.get_parent().get_net_transform();
474 PT(GeometricBoundingVolume) new_gbv = DCAST(GeometricBoundingVolume, gbv->make_copy());
475 new_gbv->xform(net_transform->get_mat());
476 gbv = new_gbv;
477 }
478
479 // Determine the bounding sphere around the object.
480 if (gbv->is_infinite()) {
481 framework_cat.warning()
482 << "Infinite bounding volume for " << object << "\n";
483 return;
484 }
485
486 if (gbv->is_empty()) {
487 framework_cat.warning()
488 << "Empty bounding volume for " << object << "\n";
489 return;
490 }
491
492 // The BoundingVolume might be a sphere (it's likely), but since it might
493 // not, we'll take no chances and make our own sphere.
494 PT(BoundingSphere) sphere = new BoundingSphere(gbv->get_approx_center(), 0.0f);
495 if (!sphere->extend_by(gbv)) {
496 framework_cat.warning()
497 << "Cannot determine bounding volume of " << object << "\n";
498 return;
499 }
500
501 LPoint3 center = sphere->get_center();
502 PN_stdfloat radius = sphere->get_radius();
503
504 PN_stdfloat distance = 50.0f;
505
506 // Choose a suitable distance to view the whole volume in our frame. This
507 // is based on the camera lens in use. Determine the lens based on the
508 // first camera; this will be the default camera.
509 Lens *lens = nullptr;
510 if (!_cameras.empty()) {
511 Cameras::const_iterator ci;
512 for (ci = _cameras.begin();
513 ci != _cameras.end() && lens == nullptr;
514 ++ci) {
515 lens = (*ci)->get_lens();
516 }
517 }
518
519 if (lens != nullptr) {
520 LVecBase2 fov = lens->get_fov();
521 distance = radius / ctan(deg_2_rad(std::min(fov[0], fov[1]) / 2.0f));
522
523 // Ensure the far plane is far enough back to see the entire object.
524 PN_stdfloat ideal_far_plane = distance + radius * 1.5;
525 lens->set_far(std::max(lens->get_default_far(), ideal_far_plane));
526
527 // And that the near plane is far enough forward.
528 PN_stdfloat ideal_near_plane = distance - radius;
529 lens->set_near(std::min(lens->get_default_near(), ideal_near_plane));
530 }
531
532 _trackball->set_origin(center);
533 _trackball->set_pos(LVector3::forward() * distance);
534
535 // Also set the movement scale on the trackball to be consistent with the
536 // size of the model and the lens field-of-view.
537 _trackball->set_forward_scale(distance * 0.006);
538}
539
540/**
541 * Loads up all the model files listed in the indicated argument list. If
542 * first_arg is supplied, it is the first argument in the list to consider.
543 *
544 * Returns true if all models loaded successfully, or false if at least one of
545 * them had an error.
546 */
548load_models(const NodePath &parent, int argc, char *argv[], int first_arg) {
549 pvector<Filename> files;
550
551 for (int i = first_arg; i < argc && argv[i] != nullptr; i++) {
552 files.push_back(Filename::from_os_specific(argv[i]));
553 }
554
555 return load_models(parent, files);
556}
557
558/**
559 * Loads up all the model files listed in the indicated argument list.
560 *
561 * Returns true if all models loaded successfully, or false if at least one of
562 * them had an error.
563 */
565load_models(const NodePath &parent, const pvector<Filename> &files) {
566 bool all_ok = true;
567
569 for (fi = files.begin(); fi != files.end(); ++fi) {
570 const Filename &filename = (*fi);
571 NodePath model = load_model(parent, filename);
572 if (model.is_empty()) {
573 all_ok = false;
574 }
575 }
576
577 return all_ok;
578}
579
580/**
581 * Loads up the indicated model and returns the new NodePath, or the empty
582 * NodePath if the model could not be loaded.
583 */
585load_model(const NodePath &parent, Filename filename) {
586 framework_cat.info() << "Loading " << filename << "\n";
587
588 // If the filename already exists where it is, or if it is fully qualified,
589 // don't search along the model path for it.
591 bool search = !(filename.is_fully_qualified() || vfs->exists(filename));
592
593 // We allow loading image files here. Check to see if it might be an image
594 // file, based on the filename extension.
595 bool is_image = false;
596 string extension = filename.get_extension();
597#ifdef HAVE_ZLIB
598 if (extension == "pz" || extension == "gz") {
599 extension = Filename(filename.get_basename_wo_extension()).get_extension();
600 }
601#endif // HAVE_ZLIB
603 LoaderFileType *model_type = nullptr;
604
605 if (!extension.empty()) {
607 model_type = reg->get_type_from_extension(extension);
608
609 if (model_type == nullptr) {
610 // The extension isn't a known model file type; is it a known image file
611 // extension?
613 if (texture_pool->get_texture_type(extension) != nullptr) {
614 // It is a known image file extension.
615 is_image = true;
616 }
617 }
618 }
619
620 LoaderOptions options = PandaFramework::_loader_options;
621 if (search) {
622 options.set_flags(options.get_flags() | LoaderOptions::LF_search);
623 } else {
624 options.set_flags(options.get_flags() & ~LoaderOptions::LF_search);
625 }
626
627 Loader loader;
628 PT(PandaNode) node;
629 if (is_image) {
630 node = load_image_as_model(filename);
631 } else {
632 node = loader.load_sync(filename, options);
633
634 // It failed to load. Is it because the extension isn't recognised? If
635 // so, then we just got done printing out the known scene types, and we
636 // should also print out the supported texture types.
637 if (node == nullptr && !is_image && model_type == nullptr) {
638 texture_pool->write_texture_types(nout, 2);
639 }
640 }
641
642 if (node == nullptr) {
643 nout << "Unable to load " << filename << "\n";
644 return NodePath::not_found();
645 }
646
647 return parent.attach_new_node(node);
648}
649
650/**
651 * Loads our favorite blue triangle. This is intended to provide some default
652 * geometry to have *something* to look at for testing, when no other models
653 * are provided.
654 */
656load_default_model(const NodePath &parent) {
657 CPT(RenderState) state = RenderState::make_empty();
658
659 state = state->add_attrib(ColorAttrib::make_flat(LColor(0.5, 0.5, 1.0, 1.0)));
660
661 // Get the default texture to apply to the triangle; it's compiled into the
662 // code these days.
663 string rock_floor_string((const char *)rock_floor, rock_floor_len);
664 istringstream rock_floor_strm(rock_floor_string);
665 PNMImage rock_floor_pnm;
666 if (rock_floor_pnm.read(rock_floor_strm, "rock-floor.rgb")) {
667 PT(Texture) tex = new Texture;
668 tex->set_name("rock-floor.rgb");
669 tex->load(rock_floor_pnm);
670 tex->set_minfilter(SamplerState::FT_linear);
671 tex->set_magfilter(SamplerState::FT_linear);
672 state = state->add_attrib(TextureAttrib::make(tex));
673 }
674
675 GeomNode *geomnode = new GeomNode("tri");
676
677 PT(GeomVertexData) vdata = new GeomVertexData
679 Geom::UH_static);
680 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
681 GeomVertexWriter normal(vdata, InternalName::get_normal());
682 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
683
684 vertex.add_data3(LVertex::rfu(0.0, 0.0, 0.0));
685 vertex.add_data3(LVertex::rfu(1.0, 0.0, 0.0));
686 vertex.add_data3(LVertex::rfu(0.0, 0.0, 1.0));
687
688 normal.add_data3(LNormal::back());
689 normal.add_data3(LNormal::back());
690 normal.add_data3(LNormal::back());
691
692 texcoord.add_data2(0.0, 0.0);
693 texcoord.add_data2(1.0, 0.0);
694 texcoord.add_data2(0.0, 1.0);
695
696 PT(GeomTriangles) tri = new GeomTriangles(Geom::UH_static);
697 tri->add_consecutive_vertices(0, 3);
698 tri->close_primitive();
699
700 PT(Geom) geom = new Geom(vdata);
701 geom->add_primitive(tri);
702
703 geomnode->add_geom(geom, state);
704
705 return parent.attach_new_node(geomnode);
706}
707
708/**
709 * Looks for characters and their matching animation files in the scene graph;
710 * binds and loops any matching animations found.
711 */
713loop_animations(int hierarchy_match_flags) {
714 // If we happened to load up both a character file and its matching
715 // animation file, attempt to bind them together now and start the
716 // animations looping.
717 auto_bind(get_render().node(), _anim_controls, hierarchy_match_flags);
718 _anim_controls.loop_all(true);
719}
720
721/**
722 * Walks through all the animations that were bound by loop_animations() and
723 * staggers their play rate slightly so that they will not remain perfectly in
724 * sync.
725 */
728 for (int i = 0; i < _anim_controls.get_num_anims(); ++i) {
729 AnimControl *control = _anim_controls.get_anim(i);
730 double r = (double)rand() / (double)RAND_MAX;
731 r = r * 0.2 + 0.9;
732 control->set_play_rate(r);
733 }
734}
735
736/**
737 * Rotates the animation controls through all of the available animations. If
738 * the animation controls are not already enabled, enables them at sets to the
739 * first animation; if they are already enabled, steps to the next animation;
740 * if that is the last animation, disables the animation controls.
741 */
744 if (_anim_controls_enabled) {
745 destroy_anim_controls();
746
747 if (_anim_controls.get_num_anims() == 0) {
748 set_anim_controls(false);
749 return;
750 }
751
752 // Stop the active animation.
753 pause_button();
754 ++_anim_index;
755
756 if (_anim_index >= _anim_controls.get_num_anims()) {
757 set_anim_controls(false);
758 _anim_controls.loop_all(true);
759 } else {
760 create_anim_controls();
761 play_button();
762 }
763 } else {
764 _anim_index = 0;
765 set_anim_controls(true);
766 if (_anim_controls.get_num_anims() > 0) {
767 play_button();
768 }
769 }
770}
771
772/**
773 * Creates an onscreen animation slider for frame-stepping through the
774 * animations.
775 */
777set_anim_controls(bool enable) {
778 _anim_controls_enabled = enable;
779 if (_anim_controls_enabled) {
780 create_anim_controls();
781
782 } else {
783 destroy_anim_controls();
784 }
785}
786
787/**
788 * Reevaluates the dimensions of the window, presumably after the window has
789 * been resized by the user or some other force. Adjusts the render film
790 * size, aspect2d scale (aspect ratio) and the dimensionsas of pixel_2d
791 * according to the new window shape, or new config setting.
792 */
795 PN_stdfloat this_aspect_ratio = aspect_ratio;
796
797 int x_size = 0, y_size = 0;
798 if (_window->has_size()) {
799 x_size = _window->get_sbs_left_x_size();
800 y_size = _window->get_sbs_left_y_size();
801 }
802
803 if (this_aspect_ratio == 0.0f) {
804 // An aspect ratio of 0.0 means to try to infer it.
805 this_aspect_ratio = 1.0f;
806 if (y_size != 0) {
807 this_aspect_ratio = (float)x_size / (float)y_size;
808 }
809 }
810
811 if (!_aspect_2d.is_empty()) {
812 _aspect_2d.set_scale(1.0f / this_aspect_ratio, 1.0f, 1.0f);
813 }
814
815 if (!_pixel_2d.is_empty()) {
816 // Adjust the pixel 2d scale
817 if (x_size > 0){
818 _pixel_2d.set_sx(2.0f / (float)x_size);
819 }
820 _pixel_2d.set_sy(1.0f);
821 if (y_size > 0){
822 _pixel_2d.set_sz(2.0f / (float)y_size);
823 }
824 }
825
826 Cameras::iterator ci;
827 for (ci = _cameras.begin(); ci != _cameras.end(); ++ci) {
828 Lens *lens = (*ci)->get_lens();
829 if (lens != nullptr) {
830 if (y_size != 0) {
831 lens->set_film_size(x_size, y_size);
832 } else {
833 lens->set_aspect_ratio(this_aspect_ratio);
834 }
835 }
836 }
837}
838
839/**
840 * Divides the window into two display regions, each of which gets its own
841 * trackball and keyboard events. The new window pointer is returned.
842 *
843 * There is not an interface for recombining divided windows.
844 */
846split_window(SplitType split_type) {
847 DisplayRegion *new_region = nullptr;
848
849 if (split_type == ST_default) {
850 // Choose either horizontal or vertical according to the largest
851 // dimension.
852
853 if (_display_region_3d->get_pixel_width() >
854 _display_region_3d->get_pixel_height()) {
855 split_type = ST_horizontal;
856 } else {
857 split_type = ST_vertical;
858 }
859 }
860
861 PN_stdfloat left, right, bottom, top;
862 _display_region_3d->get_dimensions(left, right, bottom, top);
863 new_region = _display_region_3d->get_window()->make_display_region();
864
865 if (split_type == ST_vertical) {
866 _display_region_3d->set_dimensions(left, right, bottom, (top + bottom) / 2.0f);
867 if (_display_region_2d != nullptr) {
868 _display_region_2d->set_dimensions(left, right, bottom, (top + bottom) / 2.0f);
869 }
870
871 new_region->set_dimensions(left, right, (top + bottom) / 2.0f, top);
872
873 } else {
874 _display_region_3d->set_dimensions(left, (left + right) / 2.0f, bottom, top);
875 if (_display_region_2d != nullptr) {
876 _display_region_2d->set_dimensions(left, (left + right) / 2.0f, bottom, top);
877 }
878
879 new_region->set_dimensions((left + right) / 2.0f, right, bottom, top);
880 }
881
882 PT(WindowFramework) wf = new WindowFramework(*this, new_region);
883 _panda_framework->_windows.push_back(wf);
884
885 return wf;
886}
887
888/**
889 * Forces wireframe state (true) or restores default rendering (false).
890 */
892set_wireframe(bool enable, bool filled) {
893 if (enable == _wireframe_enabled && filled == _wireframe_filled) {
894 return;
895 }
896
897 NodePath render = get_render();
898
899 if (!_two_sided_enabled) {
900 render.clear_two_sided();
901 }
902
903 if (enable) {
904 if (filled) {
905 render.set_attrib(RenderModeAttrib::make(
906 RenderModeAttrib::M_filled_wireframe,
907 1.4f, false, LColor(1, 1, 1, .5f)),
908 override_priority);
909 // Darken the scene so that the wireframe is clearly visible, even when
910 // the scene is completely white.
911 render.set_color_scale(LColor(0.7f, 0.7f, 0.7f, 1), override_priority);
912 } else {
913 render.set_render_mode_wireframe(override_priority);
914 render.set_two_sided(true, override_priority);
915 render.clear_color_scale();
916 }
917 } else {
918 render.clear_render_mode();
919 if (_one_sided_reverse_enabled) {
920 CPT(RenderAttrib) attrib = CullFaceAttrib::make_reverse();
921 render.node()->set_attrib(attrib);
922 }
923 render.clear_color_scale();
924 }
925
926 _wireframe_enabled = enable;
927 _wireframe_filled = filled;
928}
929
930/**
931 * Forces textures off (false) or restores default rendering (true).
932 */
934set_texture(bool enable) {
935 if (enable == _texture_enabled) {
936 return;
937 }
938
939 NodePath render = get_render();
940
941 if (!enable) {
942 render.set_texture_off(override_priority);
943 } else {
944 render.clear_texture();
945 }
946
947 _texture_enabled = enable;
948}
949
950/**
951 * Forces two-sided rendering (true) or restores default rendering (false).
952 */
954set_two_sided(bool enable) {
955 if (enable == _two_sided_enabled) {
956 return;
957 }
958
959 NodePath render = get_render();
960
961 if (enable) {
962 render.set_two_sided(true, override_priority);
963 } else {
964 if (!_wireframe_enabled) {
965 render.clear_two_sided();
966 }
967 }
968
969 _two_sided_enabled = enable;
970 _one_sided_reverse_enabled = false;
971}
972
973/**
974 * Toggles one-sided reverse mode. In this mode, the front sides of one-sided
975 * polygons are culled instead of the back side.
976 */
978set_one_sided_reverse(bool enable) {
979 if (enable == _one_sided_reverse_enabled) {
980 return;
981 }
982
983 NodePath render = get_render();
984
985 if (!_wireframe_enabled) {
986 if (enable) {
987 CPT(RenderAttrib) attrib = CullFaceAttrib::make_reverse();
988 render.node()->set_attrib(attrib);
989 } else {
990 render.clear_two_sided();
991 }
992 }
993
994 _two_sided_enabled = false;
995 _one_sided_reverse_enabled = enable;
996}
997
998/**
999 * Turns lighting on (true) or off (false).
1000 */
1002set_lighting(bool enable) {
1003 if (enable == _lighting_enabled) {
1004 return;
1005 }
1006
1007 NodePath render = get_render();
1008
1009 if (enable) {
1010 if (!_got_lights) {
1011 setup_lights();
1012 }
1013 render.set_light(_alight);
1014 render.set_light(_dlight);
1015 } else {
1016 render.clear_light();
1017 }
1018
1019 _lighting_enabled = enable;
1020}
1021
1022/**
1023 * Turns per-pixel lighting on (true) or off (false).
1024 */
1026set_perpixel(bool enable) {
1027 if (enable == _perpixel_enabled) {
1028 return;
1029 }
1030
1031 NodePath render = get_render();
1032
1033 if (enable) {
1034 render.set_shader_auto();
1035 } else {
1036 render.set_shader_off();
1037 }
1038
1039 _perpixel_enabled = enable;
1040}
1041
1042/**
1043 * Sets the background of the window to one of the pre-canned background types
1044 * (or to BT_other, which indicates the user intends to set up his own special
1045 * background mode).
1046 */
1048set_background_type(WindowFramework::BackgroundType type) {
1049 _background_type = type;
1050
1051 if (_display_region_3d == nullptr) {
1052 return;
1053 }
1054
1055 switch (_background_type) {
1056 case BT_other:
1057 break;
1058
1059 case BT_default:
1060 _display_region_3d->set_clear_color_active(true);
1061 _display_region_3d->set_clear_depth_active(true);
1062 _display_region_3d->set_clear_stencil_active(true);
1063 _display_region_3d->set_clear_color(_window->get_clear_color());
1064 _display_region_3d->set_clear_depth(_window->get_clear_depth());
1065 _display_region_3d->set_clear_stencil(_window->get_clear_stencil());
1066 break;
1067
1068 case BT_black:
1069 _display_region_3d->set_clear_color_active(true);
1070 _display_region_3d->set_clear_depth_active(true);
1071 _display_region_3d->set_clear_stencil_active(true);
1072 _display_region_3d->set_clear_color(LColor(0.0f, 0.0f, 0.0f, 0.0f));
1073 _display_region_3d->set_clear_depth(1.0f);
1074 _display_region_3d->set_clear_stencil(0);
1075 break;
1076
1077 case BT_gray:
1078 _display_region_3d->set_clear_color_active(true);
1079 _display_region_3d->set_clear_depth_active(true);
1080 _display_region_3d->set_clear_stencil_active(true);
1081 _display_region_3d->set_clear_color(LColor(0.3, 0.3, 0.3, 0.0f));
1082 _display_region_3d->set_clear_depth(1.0f);
1083 _display_region_3d->set_clear_stencil(0);
1084 break;
1085
1086 case BT_white:
1087 _display_region_3d->set_clear_color_active(true);
1088 _display_region_3d->set_clear_depth_active(true);
1089 _display_region_3d->set_clear_stencil_active(true);
1090 _display_region_3d->set_clear_color(LColor(1.0f, 1.0f, 1.0f, 0.0f));
1091 _display_region_3d->set_clear_depth(1.0f);
1092 _display_region_3d->set_clear_stencil(0);
1093 break;
1094
1095 case BT_none:
1096 _display_region_3d->set_clear_color_active(false);
1097 _display_region_3d->set_clear_depth_active(false);
1098 _display_region_3d->set_clear_stencil_active(false);
1099 break;
1100 }
1101}
1102
1103/**
1104 * Returns a font that contains the shuttle controls icons.
1105 */
1108 if (_shuttle_controls_font == nullptr) {
1109 PT(TextFont) font;
1110
1111 string shuttle_controls_string((const char *)shuttle_controls, shuttle_controls_len);
1112 istringstream in(shuttle_controls_string);
1113 BamFile bam_file;
1114 if (bam_file.open_read(in, "shuttle_controls font stream")) {
1115 PT(PandaNode) node = bam_file.read_node();
1116 if (node != nullptr) {
1117 _shuttle_controls_font = new StaticTextFont(node);
1118 }
1119 }
1120 }
1121
1122 return _shuttle_controls_font;
1123}
1124
1125/**
1126 * Makes a new 3-d camera for the window.
1127 */
1129make_camera() {
1130 // Finally, we need a camera to associate with the display region.
1131 PT(Camera) camera = new Camera("camera");
1132 NodePath camera_np = get_camera_group().attach_new_node(camera);
1133 _cameras.push_back(camera);
1134
1135 PT(Lens) lens = new PerspectiveLens;
1136
1137 if (aspect_ratio != 0.0) {
1138 // If we're given an explict aspect ratio, use it
1139 lens->set_aspect_ratio(aspect_ratio);
1140
1141 } else {
1142 // Otherwise, infer the aspect ratio from the window size. This does
1143 // assume we have square pixels on our output device.
1144 if (_window->has_size()) {
1145 int x_size = _window->get_sbs_left_x_size();
1146 int y_size = _window->get_sbs_left_y_size();
1147 if (y_size != 0) {
1148 lens->set_film_size(x_size, y_size);
1149 }
1150 }
1151 }
1152
1153 camera->set_lens(lens);
1154
1155 return camera_np;
1156}
1157
1158/**
1159 * Makes light nodes and attaches them to the camera for viewing the scene.
1160 */
1161void WindowFramework::
1162setup_lights() {
1163 if (_got_lights) {
1164 return;
1165 }
1166
1167 NodePath camera_group = get_camera_group();
1168 NodePath light_group = camera_group.attach_new_node("lights");
1169
1170 AmbientLight *alight = new AmbientLight("ambient");
1171 alight->set_color(LColor(0.2, 0.2, 0.2, 1.0f));
1172 DirectionalLight *dlight = new DirectionalLight("directional");
1173 dlight->set_color(LColor(0.8f, 0.8f, 0.8f, 1.0f));
1174
1175 _alight = light_group.attach_new_node(alight);
1176 _dlight = light_group.attach_new_node(dlight);
1177 _dlight.set_hpr(-10, -20, 0);
1178
1179 _got_lights = true;
1180}
1181
1182/**
1183 * Loads the indicated image file as a texture, and creates a polygon to
1184 * render it. Returns the new model.
1185 */
1186PT(PandaNode) WindowFramework::
1187load_image_as_model(const Filename &filename) {
1188 PT(Texture) tex = TexturePool::load_texture(filename);
1189 if (tex == nullptr) {
1190 return nullptr;
1191 }
1192
1193 // Yes, it is an image file; make a texture out of it.
1194 tex->set_minfilter(SamplerState::FT_linear_mipmap_linear);
1195 tex->set_magfilter(SamplerState::FT_linear);
1196 tex->set_wrap_u(SamplerState::WM_clamp);
1197 tex->set_wrap_v(SamplerState::WM_clamp);
1198 tex->set_wrap_w(SamplerState::WM_clamp);
1199
1200 // Ok, now make a polygon to show the texture.
1201 bool has_alpha = true;
1202 LVecBase2 tex_scale = tex->get_tex_scale();
1203
1204 // Get the size from the original image (the texture may have scaled it to
1205 // make a power of 2).
1206 int x_size = tex->get_orig_file_x_size();
1207 int y_size = tex->get_orig_file_y_size();
1208
1209 // Choose the dimensions of the polygon appropriately.
1210 PN_stdfloat left,right,top,bottom;
1211 static const PN_stdfloat scale = 10.0;
1212 if (x_size > y_size) {
1213 left = -scale;
1214 right = scale;
1215 top = (scale * y_size) / x_size;
1216 bottom = -(scale * y_size) / x_size;
1217 } else if (y_size != 0) {
1218 left = -(scale * x_size) / y_size;
1219 right = (scale * x_size) / y_size;
1220 top = scale;
1221 bottom = -scale;
1222 } else {
1223 framework_cat.warning()
1224 << "Texture size is 0 0: " << *tex << "\n";
1225
1226 left = -scale;
1227 right = scale;
1228 top = scale;
1229 bottom = -scale;
1230 }
1231
1232 PT(GeomNode) card_node = new GeomNode("card");
1233 card_node->set_attrib(TextureAttrib::make(tex));
1234 if (has_alpha) {
1235 card_node->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
1236 }
1237
1238 bool is_3d = false;
1239 if (tex->get_texture_type() == Texture::TT_3d_texture ||
1240 tex->get_texture_type() == Texture::TT_cube_map) {
1241 // For a 3-d texture, generate a cube, instead of a plain card.
1242 is_3d = true;
1243 }
1244
1245 CPT(GeomVertexFormat) vformat;
1246 if (!is_3d) {
1247 // Vertices and 2-d texture coordinates, all we need.
1248 vformat = GeomVertexFormat::get_v3t2();
1249
1250 } else {
1251 // Vertices and 3-d texture coordinates.
1252 vformat = GeomVertexFormat::register_format
1254 (InternalName::get_vertex(), 3,
1255 GeomEnums::NT_stdfloat, GeomEnums::C_point,
1256 InternalName::get_texcoord(), 3,
1257 GeomEnums::NT_stdfloat, GeomEnums::C_texcoord));
1258 }
1259
1260 PT(GeomVertexData) vdata = new GeomVertexData
1261 ("card", vformat, Geom::UH_static);
1262 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
1263 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
1264
1265 if (!is_3d) {
1266 // A normal 2-d card.
1267 vertex.add_data3(LVertex::rfu(left, 0.02, top));
1268 vertex.add_data3(LVertex::rfu(left, 0.02, bottom));
1269 vertex.add_data3(LVertex::rfu(right, 0.02, top));
1270 vertex.add_data3(LVertex::rfu(right, 0.02, bottom));
1271
1272 texcoord.add_data2(0.0f, tex_scale[1]);
1273 texcoord.add_data2(0.0f, 0.0f);
1274 texcoord.add_data2(tex_scale[0], tex_scale[1]);
1275 texcoord.add_data2(tex_scale[0], 0.0f);
1276
1277 } else {
1278 // The eight vertices of a 3-d cube.
1279 vertex.add_data3(-1.0f, -1.0f, 1.0f); // 0
1280 vertex.add_data3(-1.0f, -1.0f, -1.0f); // 1
1281 vertex.add_data3(1.0f, -1.0f, -1.0f); // 2
1282 vertex.add_data3(1.0f, -1.0f, 1.0f); // 3
1283 vertex.add_data3(1.0f, 1.0f, 1.0f); // 4
1284 vertex.add_data3(1.0f, 1.0f, -1.0f); // 5
1285 vertex.add_data3(-1.0f, 1.0f, -1.0f); // 6
1286 vertex.add_data3(-1.0f, 1.0f, 1.0f); // 7
1287
1288 texcoord.add_data3(-1.0f, -1.0f, 1.0f); // 0
1289 texcoord.add_data3(-1.0f, -1.0f, -1.0f); // 1
1290 texcoord.add_data3(1.0f, -1.0f, -1.0f); // 2
1291 texcoord.add_data3(1.0f, -1.0f, 1.0f); // 3
1292 texcoord.add_data3(1.0f, 1.0f, 1.0f); // 4
1293 texcoord.add_data3(1.0f, 1.0f, -1.0f); // 5
1294 texcoord.add_data3(-1.0f, 1.0f, -1.0f); // 6
1295 texcoord.add_data3(-1.0f, 1.0f, 1.0f); // 7
1296 }
1297
1298 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
1299
1300 if (!is_3d) {
1301 // The two triangles that make up a quad.
1302 strip->add_consecutive_vertices(0, 4);
1303 strip->close_primitive();
1304
1305 } else {
1306 // The twelve triangles (six quads) that make up a cube.
1307 strip->add_vertex(7);
1308 strip->add_vertex(0);
1309 strip->add_vertex(4);
1310 strip->add_vertex(3);
1311 strip->close_primitive();
1312
1313 strip->add_vertex(1);
1314 strip->add_vertex(6);
1315 strip->add_vertex(2);
1316 strip->add_vertex(5);
1317 strip->close_primitive();
1318
1319 strip->add_vertex(5);
1320 strip->add_vertex(4);
1321 strip->add_vertex(2);
1322 strip->add_vertex(3);
1323 strip->add_vertex(1);
1324 strip->add_vertex(0);
1325 strip->add_vertex(6);
1326 strip->add_vertex(7);
1327 strip->add_vertex(5);
1328 strip->add_vertex(4);
1329 strip->close_primitive();
1330 }
1331
1332 PT(Geom) geom = new Geom(vdata);
1333 geom->add_primitive(strip);
1334
1335 card_node->add_geom(geom);
1336
1337 return card_node;
1338}
1339
1340/**
1341 * Creates an onscreen animation slider for frame-stepping through the
1342 * animations.
1343 */
1344void WindowFramework::
1345create_anim_controls() {
1346 destroy_anim_controls();
1347
1348 PT(PGItem) group = new PGItem("anim_controls_group");
1349 PGFrameStyle style;
1350 style.set_type(PGFrameStyle::T_flat);
1351 style.set_color(0.0f, 0.0f, 0.0f, 0.3);
1352 group->set_frame(-1.0f, 1.0f, 0.0f, 0.2);
1353 group->set_frame_style(0, style);
1354 group->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
1355 group->set_active(true);
1356
1357 _anim_controls_group = get_aspect_2d().attach_new_node(group);
1358 _anim_controls_group.set_pos(0.0f, 0.0f, -0.9f);
1359
1360 if (_anim_index >= _anim_controls.get_num_anims()) {
1361 PT(TextNode) label = new TextNode("label");
1362 label->set_align(TextNode::A_center);
1363 label->set_text("No animation.");
1364 NodePath tnp = _anim_controls_group.attach_new_node(label);
1365 tnp.set_pos(0.0f, 0.0f, 0.07f);
1366 tnp.set_scale(0.1f);
1367
1368 return;
1369 }
1370
1371 AnimControl *control = _anim_controls.get_anim(_anim_index);
1372 nassertv(control != nullptr);
1373
1374 if (control->get_num_frames() <= 1) {
1375 // Don't show the controls when the animation has only 0 or 1 frames.
1376 ostringstream text;
1377 text << _anim_controls.get_anim_name(_anim_index);
1378 text << " (" << control->get_num_frames() << " frame"
1379 << ((control->get_num_frames() == 1) ? "" : "s") << ")";
1380
1381 PT(TextNode) label = new TextNode("label");
1382 label->set_align(TextNode::A_center);
1383 label->set_text(text.str());
1384 NodePath tnp = _anim_controls_group.attach_new_node(label);
1385 tnp.set_pos(0.0f, 0.0f, 0.07f);
1386 tnp.set_scale(0.1f);
1387
1388 return;
1389 }
1390
1391 PT(TextNode) label = new TextNode("anim_name");
1392 label->set_align(TextNode::A_left);
1393 label->set_text(_anim_controls.get_anim_name(_anim_index));
1394 NodePath tnp = _anim_controls_group.attach_new_node(label);
1395 tnp.set_pos(-0.95f, 0.0f, 0.15f);
1396 tnp.set_scale(0.05f);
1397
1398 _anim_slider = new PGSliderBar("anim_slider");
1399 _anim_slider->setup_slider(false, 1.9f, 0.1f, 0.005f);
1400 _anim_slider->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
1401 _anim_slider->get_thumb_button()->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
1402
1403 _anim_slider->set_range(0.0f, (PN_stdfloat)(control->get_num_frames() - 1));
1404 _anim_slider->set_scroll_size(0.0f);
1405 _anim_slider->set_page_size(1.0f);
1406 NodePath snp = _anim_controls_group.attach_new_node(_anim_slider);
1407 snp.set_pos(0.0f, 0.0f, 0.06f);
1408
1409 _frame_number = new TextNode("frame_number");
1410 _frame_number->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
1411 _frame_number->set_align(TextNode::A_center);
1412 _frame_number->set_text(format_string(control->get_frame()));
1413 NodePath fnp = NodePath(_anim_slider->get_thumb_button()).attach_new_node(_frame_number);
1414 fnp.set_scale(0.05f);
1415 fnp.set_pos(0.0f, 0.0f, -0.01f);
1416
1417 _play_rate_slider = new PGSliderBar("play_rate_slider");
1418 _play_rate_slider->setup_slider(false, 0.4, 0.05f, 0.005f);
1419 _play_rate_slider->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
1420 _play_rate_slider->get_thumb_button()->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
1421 _play_rate_slider->set_value(control->get_play_rate());
1422 NodePath pnp = _anim_controls_group.attach_new_node(_play_rate_slider);
1423 pnp.set_pos(0.75f, 0.0f, 0.15f);
1424
1425 // Set up the jogshuttle buttons. These use symbols from the
1426 // shuttle_controls_font file.
1427 setup_shuttle_button("9", 0, st_back_button);
1428 setup_shuttle_button(";", 1, st_pause_button);
1429 setup_shuttle_button("4", 2, st_play_button);
1430 setup_shuttle_button(":", 3, st_forward_button);
1431
1432 _update_anim_controls_task = new GenericAsyncTask("controls", st_update_anim_controls, (void *)this);
1433 _panda_framework->get_task_mgr().add(_update_anim_controls_task);
1434}
1435
1436/**
1437 * Removes the previously-created anim controls, if any.
1438 */
1439void WindowFramework::
1440destroy_anim_controls() {
1441 if (!_anim_controls_group.is_empty()) {
1442 _anim_controls_group.remove_node();
1443
1444 _panda_framework->get_event_handler().remove_hooks_with((void *)this);
1445 if (_update_anim_controls_task != nullptr) {
1446 _panda_framework->get_task_mgr().remove(_update_anim_controls_task);
1447 _update_anim_controls_task.clear();
1448 }
1449 }
1450}
1451
1452/**
1453 * A per-frame callback to update the anim slider for the current frame.
1454 */
1455void WindowFramework::
1456update_anim_controls() {
1457 AnimControl *control = _anim_controls.get_anim(_anim_index);
1458 nassertv(control != nullptr);
1459
1460 if (_anim_slider != nullptr) {
1461 if (_anim_slider->is_button_down()) {
1462 control->pose((int)(_anim_slider->get_value() + 0.5));
1463 } else {
1464 _anim_slider->set_value((PN_stdfloat)control->get_frame());
1465 }
1466 }
1467
1468 if (_frame_number != nullptr) {
1469 _frame_number->set_text(format_string(control->get_frame()));
1470 }
1471
1472 control->set_play_rate(_play_rate_slider->get_value());
1473}
1474
1475/**
1476 * Creates a PGButton to implement the indicated shuttle event (play, pause,
1477 * etc.).
1478 */
1479void WindowFramework::
1480setup_shuttle_button(const string &label, int index,
1481 EventHandler::EventCallbackFunction *func) {
1482 PT(PGButton) button = new PGButton(label);
1483 button->set_frame(-0.05f, 0.05f, 0.0f, 0.07f);
1484
1485 PN_stdfloat bevel = 0.005f;
1486
1487 PGFrameStyle style;
1488 style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
1489 style.set_width(bevel, bevel);
1490
1491 style.set_type(PGFrameStyle::T_bevel_out);
1492 button->set_frame_style(PGButton::S_ready, style);
1493
1494 style.set_type(PGFrameStyle::T_bevel_in);
1495 button->set_frame_style(PGButton::S_depressed, style);
1496
1497 style.set_color(0.9f, 0.9f, 0.9f, 1.0f);
1498 style.set_type(PGFrameStyle::T_bevel_out);
1499 button->set_frame_style(PGButton::S_rollover, style);
1500
1501 if (get_shuttle_controls_font() != nullptr) {
1502 PT(TextNode) tn = new TextNode("label");
1503 tn->set_align(TextNode::A_center);
1504 tn->set_font(get_shuttle_controls_font());
1505 tn->set_text(label);
1506 tn->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
1507 LMatrix4 xform = LMatrix4::scale_mat(0.07f);
1508 xform.set_row(3, LVecBase3(0.0f, 0.0f, 0.016f));
1509 tn->set_transform(xform);
1510
1511 button->get_state_def(PGButton::S_ready).attach_new_node(tn);
1512 button->get_state_def(PGButton::S_depressed).attach_new_node(tn);
1513 button->get_state_def(PGButton::S_rollover).attach_new_node(tn);
1514 }
1515
1516 NodePath np = _anim_controls_group.attach_new_node(button);
1517 np.set_pos(0.1f * index - 0.15f, 0.0f, 0.12);
1518
1519 _panda_framework->get_event_handler().add_hook(button->get_click_event(MouseButton::one()), func, (void *)this);
1520}
1521
1522/**
1523 * Handler for a shuttle button.
1524 */
1525void WindowFramework::
1526back_button() {
1527 AnimControl *control = _anim_controls.get_anim(_anim_index);
1528 nassertv(control != nullptr);
1529 control->pose(control->get_frame() - 1);
1530}
1531
1532/**
1533 * Handler for a shuttle button.
1534 */
1535void WindowFramework::
1536pause_button() {
1537 AnimControl *control = _anim_controls.get_anim(_anim_index);
1538 nassertv(control != nullptr);
1539 control->stop();
1540}
1541
1542/**
1543 * Handler for a shuttle button.
1544 */
1545void WindowFramework::
1546play_button() {
1547 AnimControl *control = _anim_controls.get_anim(_anim_index);
1548 nassertv(control != nullptr);
1549 control->loop(false);
1550}
1551
1552/**
1553 * Handler for a shuttle button.
1554 */
1555void WindowFramework::
1556forward_button() {
1557 AnimControl *control = _anim_controls.get_anim(_anim_index);
1558 nassertv(control != nullptr);
1559 control->pose(control->get_frame() + 1);
1560}
1561
1562
1563/**
1564 * The static task function.
1565 */
1566AsyncTask::DoneStatus WindowFramework::
1567st_update_anim_controls(GenericAsyncTask *, void *data) {
1568 WindowFramework *self = (WindowFramework *)data;
1569 self->update_anim_controls();
1570 return AsyncTask::DS_cont;
1571}
1572
1573
1574/**
1575 * The static event handler function.
1576 */
1577void WindowFramework::
1578st_back_button(const Event *, void *data) {
1579 WindowFramework *self = (WindowFramework *)data;
1580 self->back_button();
1581}
1582
1583/**
1584 * The static event handler function.
1585 */
1586void WindowFramework::
1587st_pause_button(const Event *, void *data) {
1588 WindowFramework *self = (WindowFramework *)data;
1589 self->pause_button();
1590}
1591
1592/**
1593 * The static event handler function.
1594 */
1595void WindowFramework::
1596st_play_button(const Event *, void *data) {
1597 WindowFramework *self = (WindowFramework *)data;
1598 self->play_button();
1599}
1600
1601/**
1602 * The static event handler function.
1603 */
1604void WindowFramework::
1605st_forward_button(const Event *, void *data) {
1606 WindowFramework *self = (WindowFramework *)data;
1607 self->forward_button();
1608}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void auto_bind(PandaNode *root_node, AnimControlCollection &controls, int hierarchy_match_flags)
Walks the scene graph or subgraph beginning at the indicated node, and attempts to bind any AnimBundl...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A light source that seems to illuminate all points in space at once.
get_num_anims
Returns the number of AnimControls associated with this collection.
get_anim
Returns the nth AnimControl associated with this collection.
void loop_all(bool restart)
Starts all animations looping.
get_anim_name
Returns the name of the nth AnimControl associated with this collection.
Controls the timing of a character animation.
Definition animControl.h:38
void pose(double frame)
Sets the animation to the indicated frame and holds it there.
set_play_rate
Changes the rate at which the animation plays.
get_frame
Returns the current integer frame number.
get_num_frames
Returns the number of frames in the animation.
get_play_rate
Returns the rate at which the animation plays.
void stop()
Stops a currently playing or looping animation right where it is.
void loop(bool restart)
Starts the entire animation looping.
bool remove(AsyncTask *task)
Removes the indicated task from the active queue.
void add(AsyncTask *task)
Adds the indicated task to the active queue.
The principle public interface to reading and writing Bam disk files.
Definition bamFile.h:41
bool open_read(const Filename &bam_filename, bool report_errors=true)
Attempts to open the indicated filename for reading.
Definition bamFile.cxx:51
This defines a bounding sphere, consisting of a center and a radius.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
Throws Panda Events for button down/up events generated within the data graph.
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition camera.h:35
A light shining from infinitely far away in a particular direction, like sunlight.
A rectangular subregion within a window for rendering into.
get_window
Returns the GraphicsOutput that this DisplayRegion is ultimately associated with, or NULL if no windo...
set_dimensions
Changes the portion of the framebuffer this DisplayRegion corresponds to.
void set_clear_color_active(bool clear_color_active)
Toggles the flag that indicates whether the color buffer should be cleared every frame.
bool remove_hooks_with(void *data)
Removes all CallbackFunction hooks that have the indicated pointer as the associated data pointer.
bool add_hook(const std::string &event_name, EventFunction *function)
Adds the indicated function to the list of those that will be called when the named event is thrown.
An optional parameter associated with an event.
A named event, possibly with parameters.
Definition event.h:33
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition filename.I:562
std::string get_extension() const
Returns the file extension.
Definition filename.I:400
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition filename.I:386
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
This is a special TextNode that automatically updates itself with the current frame rate.
Associates a generic C-style function pointer with an AsyncTask object.
A node that holds Geom objects, renderable pieces of geometry.
Definition geomNode.h:34
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition geomNode.cxx:612
Defines a series of disconnected triangles.
Defines a series of triangle strips.
This describes the structure of a single array within a Geom data.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
static const GeomVertexFormat * get_v3n3cpt2()
Returns a standard vertex format with a 2-component texture coordinate pair, a packed color,...
static const GeomVertexFormat * get_v3t2()
Returns a standard vertex format with a 2-component texture coordinate pair and a 3-component vertex ...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
void add_data2(PN_stdfloat x, PN_stdfloat y)
Sets the write row to a particular 2-component value, and advances the write row.
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row.
A container for geometry primitives.
Definition geom.h:54
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
This class is the main interface to controlling the render process.
GraphicsOutput * make_output(GraphicsPipe *pipe, const std::string &name, int sort, const FrameBufferProperties &fb_prop, const WindowProperties &win_prop, int flags, GraphicsStateGuardian *gsg=nullptr, GraphicsOutput *host=nullptr)
Creates a new window (or buffer) and returns it.
This is a base class for the various different classes that represent the result of a frame of render...
DisplayRegion * make_display_region()
Creates a new DisplayRegion that covers the entire window.
An object to create GraphicsOutputs that share a particular 3-D API.
Encapsulates all the communication with a particular instance of a given rendering backend.
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
A base class for any number of different kinds of lenses, linear and otherwise.
Definition lens.h:41
set_aspect_ratio
Sets the aspect ratio of the lens.
Definition lens.h:106
static PN_stdfloat get_default_near()
Returns the default near plane distance that will be assigned to each newly-created lens.
Definition lens.cxx:148
static PN_stdfloat get_default_far()
Returns the default far plane distance that will be assigned to each newly- created lens.
Definition lens.cxx:157
set_near
Defines the position of the near plane (or cylinder, sphere, whatever).
Definition lens.h:113
get_fov
Returns the horizontal and vertical film size of the virtual film.
Definition lens.h:101
set_film_size
Sets the horizontal size of the film without changing its shape.
Definition lens.h:82
set_far
Defines the position of the far plane (or cylinder, sphere, whatever).
Definition lens.h:114
set_color
Sets the basic color of the light.
Definition light.h:49
This class maintains the set of all known LoaderFileTypes in the universe.
static LoaderFileTypeRegistry * get_global_ptr()
Returns a pointer to the global LoaderFileTypeRegistry object.
LoaderFileType * get_type_from_extension(const std::string &extension)
Determines the type of the file based on the indicated extension (without a leading dot).
This is the base class for a family of scene-graph file types that the Loader supports.
Specifies parameters that may be passed to the loader.
A convenient class for loading models from disk, in bam or egg format (or any of a number of other fo...
Definition loader.h:42
This class monitors the state of a number of individual buttons and tracks whether each button is kno...
bool add_button(ButtonHandle button)
Adds the indicated button to the set of buttons that will be monitored for upness and downness.
static ButtonHandle one()
Returns the ButtonHandle associated with the first mouse button.
This TFormer maintains a list of rectangular regions on the screen that are considered special mouse ...
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
void set_light(const NodePath &light, int priority=0)
Adds the indicated Light or PolylightNode to the list of lights that illuminate geometry at this node...
void set_sz(PN_stdfloat sz)
Sets the z-scale component of the transform, leaving other components untouched.
void clear_two_sided()
Completely removes any two-sided adjustment that may have been set on this node via set_two_sided().
static NodePath not_found()
Creates a NodePath with the ET_not_found error type set.
Definition nodePath.I:129
void set_depth_write(bool depth_write, int priority=0)
Specifically sets or disables the writing to the depth buffer on this particular node.
void set_sy(PN_stdfloat sy)
Sets the y-scale component of the transform, leaving other components untouched.
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition nodePath.cxx:627
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition nodePath.I:188
void set_sx(PN_stdfloat sx)
Sets the x-scale component of the transform, leaving other components untouched.
void clear_light()
Completely removes any lighting operations that may have been set via set_light() or set_light_off() ...
PandaNode * node() const
Returns the referenced node of the path.
Definition nodePath.I:227
void clear_texture()
Completely removes any texture adjustment that may have been set via set_texture() or set_texture_off...
void clear_render_mode()
Completely removes any render mode adjustment that may have been set on this node via set_render_mode...
void set_texture_off(int priority=0)
Sets the geometry at this level and below to render using no texture, on any stage.
void set_depth_test(bool depth_test, int priority=0)
Specifically sets or disables the testing of the depth buffer on this particular node.
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition nodePath.cxx:599
void set_material_off(int priority=0)
Sets the geometry at this level and below to render using no material.
void set_scale(PN_stdfloat scale)
Sets the scale component of the transform, leaving translation and rotation untouched.
Definition nodePath.I:675
void set_two_sided(bool two_sided, int priority=0)
Specifically sets or disables two-sided rendering mode on this particular node.
void set_render_mode_wireframe(int priority=0)
Sets up the geometry at this level and below (unless overridden) to render in wireframe mode.
void clear_color_scale()
Completely removes any color scale from the referenced node.
void set_hpr(PN_stdfloat h, PN_stdfloat p, PN_stdfloat r)
Sets the rotation component of the transform, leaving translation and scale untouched.
Definition nodePath.I:651
void clear()
Sets this NodePath to the empty NodePath.
Definition nodePath.I:119
void set_color_scale(const LVecBase4 &scale, int priority=0)
Sets the color scale component of the transform, leaving translation and rotation untouched.
An orthographic lens.
This is a particular kind of PGItem that is specialized to behave like a normal button object.
Definition pgButton.h:29
void set_type(Type type)
Sets the basic type of frame.
void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a)
Sets the dominant color of the frame.
void set_width(PN_stdfloat x, PN_stdfloat y)
Sets the width parameter, which has meaning only for certain frame types.
This is the base class for all the various kinds of gui widget objects.
Definition pgItem.h:53
This is a particular kind of PGItem that draws a little bar with a slider that moves from left to rig...
Definition pgSliderBar.h:31
The "top" node of the new Panda GUI system.
Definition pgTop.h:38
void set_mouse_watcher(MouseWatcher *watcher)
Sets the MouseWatcher pointer that the PGTop object registers its PG items with.
Definition pgTop.cxx:132
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition pnmImage.h:58
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition pnmImage.cxx:278
This class serves to provide a high-level framework for basic applications that use Panda in simple w...
NodePath get_mouse(GraphicsOutput *window)
Returns a NodePath to the MouseAndKeyboard associated with the indicated GraphicsWindow object.
EventHandler & get_event_handler()
Returns the EventHandler object that serves events in the framework.
AsyncTaskManager & get_task_mgr()
Returns the Task Manager object that manages tasks in the framework.
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
void set_attrib(const RenderAttrib *attrib, int override=0)
Adds the indicated render attribute to the scene graph on this node.
A perspective-type lens: a normal camera.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
This is a special TextNode that automatically updates itself with output from a SceneGraphAnalyzer in...
A StaticTextFont is loaded up from a model that was previously generated via egg-mkfont,...
An encapsulation of a font; i.e.
Definition textFont.h:32
The primary interface to this module.
Definition textNode.h:48
This is the preferred interface for loading textures from image files.
Definition texturePool.h:37
static Texture * load_texture(const Filename &filename, int primary_file_num_channels=0, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads the given filename up into a texture, if it has not already been loaded, and returns the new te...
Definition texturePool.I:70
static TexturePool * get_global_ptr()
Initializes and/or returns the global pointer to the one TexturePool object in the system.
void write_texture_types(std::ostream &out, int indent_level) const
Outputs a list of the available texture types to the indicated output stream.
MakeTextureFunc * get_texture_type(const std::string &extension) const
Returns the factory function to construct a new texture of the type appropriate for the indicated fil...
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
Trackball acts like Performer in trackball mode.
Definition trackball.h:35
input: Transform (matrix)
Indicates a coordinate-system transform on vertices.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition typedObject.I:28
A hierarchy of directories and files that appears to be one continuous file system,...
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists in the virtual file system hierarchy.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
This encapsulates the data that is normally associated with a single window, or with a single display...
void set_wireframe(bool enable, bool filled=false)
Forces wireframe state (true) or restores default rendering (false).
NodePath make_camera()
Makes a new 3-d camera for the window.
NodePath load_default_model(const NodePath &parent)
Loads our favorite blue triangle.
void setup_trackball()
Sets up the mouse to trackball around the camera.
NodePath get_aspect_2d()
Returns the node under the 2-d scene graph that is scaled to suit the window's aspect ratio.
WindowFramework * split_window(SplitType split_type=ST_default)
Divides the window into two display regions, each of which gets its own trackball and keyboard events...
void stagger_animations()
Walks through all the animations that were bound by loop_animations() and staggers their play rate sl...
NodePath get_button_thrower()
Returns the node in the data graph corresponding to the ButtonThrower object associated with this win...
void set_one_sided_reverse(bool enable)
Toggles one-sided reverse mode.
NodePath get_render_2d()
Returns the root of the 2-d scene graph.
void loop_animations(int hierarchy_match_flags=PartGroup::HMF_ok_part_extra|PartGroup::HMF_ok_anim_extra)
Looks for characters and their matching animation files in the scene graph; binds and loops any match...
void set_two_sided(bool enable)
Forces two-sided rendering (true) or restores default rendering (false).
void set_texture(bool enable)
Forces textures off (false) or restores default rendering (true).
void next_anim_control()
Rotates the animation controls through all of the available animations.
void enable_keyboard()
Creates a ButtonThrower to listen to button presses and throw them as events.
NodePath get_render()
Returns the root of the 3-d scene graph.
void center_trackball(const NodePath &object)
Centers the trackball on the indicated object, and scales the trackball motion suitably.
NodePath get_mouse()
Returns the node in the data graph corresponding to the mouse associated with this window.
void set_anim_controls(bool enable)
Creates an onscreen animation slider for frame-stepping through the animations.
void adjust_dimensions()
Reevaluates the dimensions of the window, presumably after the window has been resized by the user or...
bool load_models(const NodePath &parent, int argc, char *argv[], int first_arg=1)
Loads up all the model files listed in the indicated argument list.
NodePath get_camera_group()
Returns the node above the collection of 3-d cameras in the scene graph.
void set_background_type(BackgroundType type)
Sets the background of the window to one of the pre-canned background types (or to BT_other,...
NodePath get_pixel_2d()
Returns a special root that uses units in pixels that are relative to the window.
static TextFont * get_shuttle_controls_font()
Returns a font that contains the shuttle controls icons.
void set_lighting(bool enable)
Turns lighting on (true) or off (false).
void set_perpixel(bool enable)
Turns per-pixel lighting on (true) or off (false).
NodePath load_model(const NodePath &parent, Filename filename)
Loads up the indicated model and returns the new NodePath, or the empty NodePath if the model could n...
A container for the various kinds of properties we might ask to have on a graphics window before we o...
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.