Panda3D
pandaFramework.cxx
1 // Filename: pandaFramework.cxx
2 // Created by: drose (02Apr02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pandaFramework.h"
16 #include "clockObject.h"
17 #include "pStatClient.h"
18 #include "eventQueue.h"
19 #include "dataGraphTraverser.h"
20 #include "depthOffsetAttrib.h"
21 #include "collisionNode.h"
22 #include "occluderNode.h"
23 #include "config_framework.h"
24 #include "graphicsPipeSelection.h"
25 #include "nodePathCollection.h"
26 #include "textNode.h"
27 #include "mouseAndKeyboard.h"
28 #include "mouseRecorder.h"
29 #include "throw_event.h"
30 #include "executionEnvironment.h"
31 #include "sceneGraphAnalyzer.h"
32 #include "transformState.h"
33 #include "renderState.h"
34 
35 LoaderOptions PandaFramework::_loader_options;
36 
37 ////////////////////////////////////////////////////////////////////
38 // Function: PandaFramework::Constructor
39 // Access: Public
40 // Description:
41 ////////////////////////////////////////////////////////////////////
42 PandaFramework::
43 PandaFramework() :
44  _event_handler(*EventHandler::get_global_event_handler()),
45  _task_mgr(*AsyncTaskManager::get_global_ptr())
46 {
47  _is_open = false;
48  _made_default_pipe = false;
49  _window_title = string();
50  _engine = (GraphicsEngine *)NULL;
51  _start_time = 0.0;
52  _frame_count = 0;
53  _wireframe_enabled = false;
54  _texture_enabled = true;
55  _two_sided_enabled = false;
56  _lighting_enabled = false;
57  _perpixel_enabled = false;
58  _background_type = WindowFramework::BT_default;
59  _default_keys_enabled = false;
60  _exit_flag = false;
61 }
62 
63 ////////////////////////////////////////////////////////////////////
64 // Function: PandaFramework::Destructor
65 // Access: Public, Virtual
66 // Description:
67 ////////////////////////////////////////////////////////////////////
68 PandaFramework::
69 ~PandaFramework() {
70  if (_is_open) {
72  }
73 }
74 
75 ////////////////////////////////////////////////////////////////////
76 // Function: PandaFramework::open_framework
77 // Access: Public
78 // Description: Should be called once at the beginning of the
79 // application to initialize Panda (and the framework)
80 // for use. The command-line arguments should be passed
81 // in so Panda can remove any arguments that it
82 // recognizes as special control parameters.
83 ////////////////////////////////////////////////////////////////////
85 open_framework(int &argc, char **&argv) {
86  if (_is_open) {
87  return;
88  }
89 
90  _is_open = true;
91 
92 #ifdef LINK_ALL_STATIC
93  // If we're statically linking, we need to explicitly link with
94  // at least one of the available renderers.
95  #if defined(HAVE_GL)
96  extern EXPCL_PANDAGL void init_libpandagl();
97  init_libpandagl();
98  #elif defined(HAVE_DX9)
99  extern EXPCL_PANDADX void init_libpandadx9();
100  init_libpandadx9();
101  #elif defined(HAVE_TINYDISPLAY)
102  extern EXPCL_TINYDISPLAY void init_libtinydisplay();
103  init_libtinydisplay();
104  #endif
105 
106  // Ensure the animation subsystem is available.
107  extern EXPCL_PANDA_CHAR void init_libchar();
108  init_libchar();
109 
110  // We also want the egg loader.
111  #ifdef HAVE_EGG
112  extern EXPCL_PANDAEGG void init_libpandaegg();
113  init_libpandaegg();
114  #endif
115 
116 #endif
117 
118  // Let's explicitly make a call to the image type library to ensure
119  // it gets pulled in by the dynamic linker.
120  extern EXPCL_PANDA_PNMIMAGETYPES void init_libpnmimagetypes();
121  init_libpnmimagetypes();
122 
124 
125  {
126  PT(GenericAsyncTask) task = new GenericAsyncTask("event", task_event, this);
127  _task_mgr.add(task);
128  }
129 
130  _data_root = NodePath("data");
131  {
132  PT(GenericAsyncTask) task = new GenericAsyncTask("data_loop", task_data_loop, this);
133  task->set_sort(-50);
134  _task_mgr.add(task);
135  }
136 
137  if (garbage_collect_states) {
138  PT(GenericAsyncTask) task = new GenericAsyncTask("garbageCollectStates", task_garbage_collect, this);
139  task->set_sort(46);
140  _task_mgr.add(task);
141  }
142 
143  if (!playback_session.empty()) {
144  // If the config file so indicates, create a recorder and start it
145  // playing.
146  _recorder = new RecorderController;
147  _recorder->begin_playback(Filename::from_os_specific(playback_session));
148 
149  PT(GenericAsyncTask) task = new GenericAsyncTask("play_frame", task_play_frame, this);
150  task->set_sort(55);
151  _task_mgr.add(task);
152 
153  } else if (!record_session.empty()) {
154  // If the config file so indicates, create a recorder and start it
155  // recording.
156  _recorder = new RecorderController;
157  _recorder->begin_record(Filename::from_os_specific(record_session));
158 
159  PT(GenericAsyncTask) task = new GenericAsyncTask("record_frame", task_record_frame, this);
160  task->set_sort(45);
161  _task_mgr.add(task);
162  }
163 
164  _event_handler.add_hook("window-event", event_window_event, this);
165 }
166 
167 ////////////////////////////////////////////////////////////////////
168 // Function: PandaFramework::close_framework
169 // Access: Public
170 // Description: Should be called at the end of an application to
171 // close Panda. This is optional, as the destructor
172 // will do the same thing.
173 ////////////////////////////////////////////////////////////////////
174 void PandaFramework::
176  if (!_is_open) {
177  return;
178  }
179 
181  // Also close down any other windows that might have been opened.
182  if (_engine != (GraphicsEngine *)NULL) {
183  _engine->remove_all_windows();
184  _engine = NULL;
185  }
186 
187  _event_handler.remove_all_hooks();
188 
189  _is_open = false;
190  _made_default_pipe = false;
191  _default_pipe.clear();
192 
193  _start_time = 0.0;
194  _frame_count = 0;
195  _wireframe_enabled = false;
196  _two_sided_enabled = false;
197  _lighting_enabled = false;
198  _default_keys_enabled = false;
199  _exit_flag = false;
200 
201  _recorder = NULL;
202 
204 }
205 
206 ////////////////////////////////////////////////////////////////////
207 // Function: PandaFramework::get_default_pipe
208 // Access: Public
209 // Description: Returns the default pipe. This is the GraphicsPipe
210 // that all windows in the framework will be created on,
211 // unless otherwise specified in open_window(). It is
212 // usually the primary graphics interface on the local
213 // machine.
214 //
215 // If the default pipe has not yet been created, this
216 // creates it.
217 //
218 // The return value is the default pipe, or NULL if no
219 // default pipe could be created.
220 ////////////////////////////////////////////////////////////////////
223  nassertr(_is_open, NULL);
224  if (!_made_default_pipe) {
225  make_default_pipe();
226  _made_default_pipe = true;
227  }
228  return _default_pipe;
229 }
230 
231 ////////////////////////////////////////////////////////////////////
232 // Function: PandaFramework::get_mouse
233 // Access: Public
234 // Description: Returns a NodePath to the MouseAndKeyboard associated
235 // with the indicated GraphicsWindow object. If there's
236 // not yet a mouse associated with the window, creates
237 // one.
238 //
239 // This allows multiple WindowFramework objects that
240 // represent different display regions of the same
241 // GraphicsWindow to share the same mouse.
242 ////////////////////////////////////////////////////////////////////
245  Mouses::iterator mi = _mouses.find(window);
246  if (mi != _mouses.end()) {
247  return (*mi).second;
248  }
249 
250  NodePath mouse;
251 
252  if (window->is_of_type(GraphicsWindow::get_class_type())) {
253  NodePath data_root = get_data_root();
254  GraphicsWindow *win = DCAST(GraphicsWindow, window);
255  MouseAndKeyboard *mouse_node = new MouseAndKeyboard(win, 0, "mouse");
256  mouse = data_root.attach_new_node(mouse_node);
257 
258  RecorderController *recorder = get_recorder();
259  if (recorder != (RecorderController *)NULL) {
260  // If we're in recording or playback mode, associate a recorder.
261  MouseRecorder *mouse_recorder = new MouseRecorder("mouse");
262  mouse = mouse.attach_new_node(mouse_recorder);
263  recorder->add_recorder("mouse", mouse_recorder);
264  }
265  }
266 
267  _mouses[window] = mouse;
268 
269  return mouse;
270 }
271 
272 ////////////////////////////////////////////////////////////////////
273 // Function: PandaFramework::remove_mouse
274 // Access: Public
275 // Description: Removes the mouse that may have been created by an
276 // earlier call to get_mouse().
277 ////////////////////////////////////////////////////////////////////
278 void PandaFramework::
279 remove_mouse(const GraphicsOutput *window) {
280  Mouses::iterator mi = _mouses.find(window);
281  if (mi != _mouses.end()) {
282  (*mi).second.remove_node();
283  _mouses.erase(mi);
284  }
285 }
286 
287 ////////////////////////////////////////////////////////////////////
288 // Function: PandaFramework::define_key
289 // Access: Public
290 // Description: Sets up a handler for the indicated key. When the
291 // key is pressed in a window, the given callback will
292 // be called. The description is a one-line description
293 // of the function of the key, for display to the user.
294 ////////////////////////////////////////////////////////////////////
295 void PandaFramework::
296 define_key(const string &event_name, const string &description,
297  EventHandler::EventCallbackFunction *function,
298  void *data) {
299  if (_event_handler.has_hook(event_name)) {
300  // If there is already a hook for the indicated keyname, we're
301  // most likely replacing a previous definition of a key. Search
302  // for the old definition and remove it.
303  KeyDefinitions::iterator di;
304  di = _key_definitions.begin();
305  while (di != _key_definitions.end() && (*di)._event_name != event_name) {
306  ++di;
307  }
308  if (di != _key_definitions.end()) {
309  _key_definitions.erase(di);
310  }
311  }
312 
313  // Now add a new hook for the keyname, and also add the new
314  // description.
315  _event_handler.add_hook(event_name, function, data);
316 
317  if (!description.empty()) {
318  KeyDefinition keydef;
319  keydef._event_name = event_name;
320  keydef._description = description;
321  _key_definitions.push_back(keydef);
322  }
323 }
324 
325 ////////////////////////////////////////////////////////////////////
326 // Function: PandaFramework::get_default_window_props
327 // Access: Public, Virtual
328 // Description: Fills in the indicated window properties structure
329 // according to the normal window properties for this
330 // application.
331 ////////////////////////////////////////////////////////////////////
332 void PandaFramework::
334  // This function is largely vestigial and will be removed soon. We
335  // have moved the default window properties into
336  // WindowProperties::get_default().
337 
339  if (!_window_title.empty()) {
340  props.set_title(_window_title);
341  }
342 }
343 
344 ////////////////////////////////////////////////////////////////////
345 // Function: PandaFramework::open_window
346 // Access: Public
347 // Description: Opens a window on the default graphics pipe. If the
348 // default graphics pipe can't open a window for some
349 // reason, automatically fails over to the next
350 // available graphics pipe, and updates _default_pipe
351 // accordingly. Returns NULL only if all graphics pipes
352 // fail.
353 ////////////////////////////////////////////////////////////////////
356  GraphicsPipe *pipe = get_default_pipe();
357  if (pipe == (GraphicsPipe *)NULL) {
358  // Can't get a pipe.
359  return NULL;
360  }
361 
362  WindowFramework *wf = open_window(pipe, NULL);
363  if (wf == (WindowFramework *)NULL) {
364  // Ok, the default graphics pipe failed; try a little harder.
366  selection->load_aux_modules();
367 
368  int num_pipe_types = selection->get_num_pipe_types();
369  for (int i = 0; i < num_pipe_types; i++) {
370  TypeHandle pipe_type = selection->get_pipe_type(i);
371  if (pipe_type != _default_pipe->get_type()) {
372  PT(GraphicsPipe) new_pipe = selection->make_pipe(pipe_type);
373  if (new_pipe != (GraphicsPipe *)NULL) {
374  wf = open_window(new_pipe, NULL);
375  if (wf != (WindowFramework *)NULL) {
376  // Here's the winner!
377  _default_pipe = new_pipe;
378  return wf;
379  }
380  }
381  }
382  }
383 
384  // Too bad; none of the pipes could open a window. Fall through
385  // and return NULL.
386  }
387 
388  return wf;
389 }
390 
391 ////////////////////////////////////////////////////////////////////
392 // Function: PandaFramework::open_window
393 // Access: Public
394 // Description: Opens a new window on the indicated pipe, using the
395 // default parameters. Returns the new WindowFramework
396 // if successful, or NULL if not.
397 ////////////////////////////////////////////////////////////////////
400  nassertr(_is_open, NULL);
401 
402  WindowProperties props;
404 
405  int flags = GraphicsPipe::BF_require_window;
406  if (window_type == "offscreen") {
407  flags = GraphicsPipe::BF_refuse_window;
408  }
409 
410  return open_window(props, flags, pipe, gsg);
411 }
412 
413 ////////////////////////////////////////////////////////////////////
414 // Function: PandaFramework::open_window
415 // Access: Public
416 // Description: Opens a new window using the indicated properties.
417 // (You may initialize the properties to their default
418 // values by calling get_default_window_props() first.)
419 //
420 // Returns the new WindowFramework if successful, or
421 // NULL if not.
422 ////////////////////////////////////////////////////////////////////
424 open_window(const WindowProperties &props, int flags,
425  GraphicsPipe *pipe, GraphicsStateGuardian *gsg) {
426  if (pipe == (GraphicsPipe *)NULL) {
427  pipe = get_default_pipe();
428  if (pipe == (GraphicsPipe *)NULL) {
429  // Can't get a pipe.
430  return NULL;
431  }
432  }
433 
434  nassertr(_is_open, NULL);
435  PT(WindowFramework) wf = make_window_framework();
436  wf->set_wireframe(get_wireframe());
437  wf->set_texture(get_texture());
438  wf->set_two_sided(get_two_sided());
439  wf->set_lighting(get_lighting());
440  wf->set_perpixel(get_perpixel());
441  wf->set_background_type(get_background_type());
442 
443  GraphicsOutput *win = wf->open_window(props, flags, get_graphics_engine(),
444  pipe, gsg);
445  _engine->open_windows();
446  if (win != (GraphicsOutput *)NULL && !win->is_valid()) {
447  // The window won't open.
448  _engine->remove_window(win);
449  wf->close_window();
450  win = NULL;
451  }
452 
453  if (win == (GraphicsOutput *)NULL) {
454  // Oops, couldn't make a window or buffer.
455  framework_cat.error()
456  << "Unable to create window.\n";
457  return NULL;
458  }
459 
460  _windows.push_back(wf);
461  return wf;
462 }
463 
464 ////////////////////////////////////////////////////////////////////
465 // Function: PandaFramework::find_window
466 // Access: Public
467 // Description: Returns the index of the first WindowFramework object
468 // found that references the indicated GraphicsOutput
469 // pointer, or -1 if none do.
470 ////////////////////////////////////////////////////////////////////
472 find_window(const GraphicsOutput *win) const {
473  int n;
474  for (n = 0; n < (int)_windows.size(); n++) {
475  if (_windows[n]->get_graphics_output() == win) {
476  return n;
477  }
478  }
479 
480  return -1;
481 }
482 
483 ////////////////////////////////////////////////////////////////////
484 // Function: PandaFramework::find_window
485 // Access: Public
486 // Description: Returns the index of the given WindowFramework
487 // object, or -1 if the object does not represent a
488 // window opened with this PandaFramework.
489 ////////////////////////////////////////////////////////////////////
491 find_window(const WindowFramework *wf) const {
492  int n;
493  for (n = 0; n < (int)_windows.size(); n++) {
494  if (_windows[n] == wf) {
495  return n;
496  }
497  }
498 
499  return -1;
500 }
501 
502 
503 ////////////////////////////////////////////////////////////////////
504 // Function: PandaFramework::close_window
505 // Access: Public
506 // Description: Closes the nth window and removes it from the list.
507 ////////////////////////////////////////////////////////////////////
508 void PandaFramework::
509 close_window(int n) {
510  nassertv(n >= 0 && n < (int)_windows.size());
511  WindowFramework *wf = _windows[n];
512 
513  GraphicsOutput *win = wf->get_graphics_output();
514  if (win != (GraphicsOutput *)NULL) {
515  _engine->remove_window(win);
516  }
517 
518  wf->close_window();
519  _windows.erase(_windows.begin() + n);
520 }
521 
522 ////////////////////////////////////////////////////////////////////
523 // Function: PandaFramework::close_all_windows
524 // Access: Public
525 // Description: Closes all currently open windows and empties the
526 // list of windows.
527 ////////////////////////////////////////////////////////////////////
528 void PandaFramework::
530  Windows::iterator wi;
531  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
532  WindowFramework *wf = (*wi);
533 
534  GraphicsOutput *win = wf->get_graphics_output();
535  if (win != (GraphicsOutput *)NULL) {
536  _engine->remove_window(win);
537  }
538 
539  wf->close_window();
540  }
541 
542  Mouses::iterator mi;
543  for (mi = _mouses.begin(); mi != _mouses.end(); ++mi) {
544  (*mi).second.remove_node();
545  }
546 
547  _windows.clear();
548  _mouses.clear();
549 }
550 
551 ////////////////////////////////////////////////////////////////////
552 // Function: PandaFramework::all_windows_closed
553 // Access: Public
554 // Description: Returns true if all of the opened windows have been
555 // closed by the user, false otherwise.
556 ////////////////////////////////////////////////////////////////////
557 bool PandaFramework::
559  Windows::const_iterator wi;
560  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
561  WindowFramework *wf = (*wi);
562  if (wf->get_graphics_output()->is_valid()) {
563  return false;
564  }
565  }
566 
567  return true;
568 }
569 
570 ////////////////////////////////////////////////////////////////////
571 // Function: PandaFramework::get_models
572 // Access: Public
573 // Description: Returns the root of the scene graph normally reserved
574 // for parenting models and such. This scene graph may
575 // be instanced to each window's render tree as the
576 // window is created.
577 ////////////////////////////////////////////////////////////////////
580  if (_models.is_empty()) {
581  _models = NodePath("models");
582  }
583  return _models;
584 }
585 
586 ////////////////////////////////////////////////////////////////////
587 // Function: PandaFramework::report_frame_rate
588 // Access: Public
589 // Description: Reports the currently measured average frame rate to
590 // the indicated ostream.
591 ////////////////////////////////////////////////////////////////////
592 void PandaFramework::
593 report_frame_rate(ostream &out) const {
595  double delta = now - _start_time;
596 
597  int frame_count = ClockObject::get_global_clock()->get_frame_count();
598  int num_frames = frame_count - _frame_count;
599  if (num_frames > 0) {
600  out << num_frames << " frames in " << delta << " seconds.\n";
601  double fps = ((double)num_frames) / delta;
602  out << fps << " fps average (" << 1000.0 / fps << "ms)\n";
603  }
604 }
605 
606 ////////////////////////////////////////////////////////////////////
607 // Function: PandaFramework::reset_frame_rate
608 // Access: Public
609 // Description: Resets the frame rate computation.
610 ////////////////////////////////////////////////////////////////////
611 void PandaFramework::
615 }
616 
617 ////////////////////////////////////////////////////////////////////
618 // Function: PandaFramework::set_wireframe
619 // Access: Public
620 // Description: Sets the wireframe state on all windows.
621 ////////////////////////////////////////////////////////////////////
622 void PandaFramework::
623 set_wireframe(bool enable) {
624  Windows::iterator wi;
625  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
626  WindowFramework *wf = (*wi);
627  wf->set_wireframe(enable);
628  }
629 
630  _wireframe_enabled = enable;
631 }
632 
633 ////////////////////////////////////////////////////////////////////
634 // Function: PandaFramework::set_texture
635 // Access: Public
636 // Description: Sets the texture state on all windows.
637 ////////////////////////////////////////////////////////////////////
638 void PandaFramework::
639 set_texture(bool enable) {
640  Windows::iterator wi;
641  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
642  WindowFramework *wf = (*wi);
643  wf->set_texture(enable);
644  }
645 
646  _texture_enabled = enable;
647 }
648 
649 ////////////////////////////////////////////////////////////////////
650 // Function: PandaFramework::set_two_sided
651 // Access: Public
652 // Description: Sets the two_sided state on all windows.
653 ////////////////////////////////////////////////////////////////////
654 void PandaFramework::
655 set_two_sided(bool enable) {
656  Windows::iterator wi;
657  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
658  WindowFramework *wf = (*wi);
659  wf->set_two_sided(enable);
660  }
661 
662  _two_sided_enabled = enable;
663 }
664 
665 ////////////////////////////////////////////////////////////////////
666 // Function: PandaFramework::set_lighting
667 // Access: Public
668 // Description: Sets the lighting state on all windows.
669 ////////////////////////////////////////////////////////////////////
670 void PandaFramework::
671 set_lighting(bool enable) {
672  Windows::iterator wi;
673  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
674  WindowFramework *wf = (*wi);
675  wf->set_lighting(enable);
676  }
677 
678  _lighting_enabled = enable;
679 }
680 
681 ////////////////////////////////////////////////////////////////////
682 // Function: PandaFramework::set_perpixel
683 // Access: Public
684 // Description: Sets the perpixel state on all windows.
685 ////////////////////////////////////////////////////////////////////
686 void PandaFramework::
687 set_perpixel(bool enable) {
688  Windows::iterator wi;
689  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
690  WindowFramework *wf = (*wi);
691  wf->set_perpixel(enable);
692  }
693 
694  _perpixel_enabled = enable;
695 }
696 
697 ////////////////////////////////////////////////////////////////////
698 // Function: BackgroundFramework::set_background_type
699 // Access: Public
700 // Description: Sets the background type of all windows.
701 ////////////////////////////////////////////////////////////////////
702 void PandaFramework::
703 set_background_type(WindowFramework::BackgroundType type) {
704  Windows::iterator wi;
705  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
706  WindowFramework *wf = (*wi);
707  wf->set_background_type(type);
708  }
709 
710  _background_type = type;
711 }
712 
713 ////////////////////////////////////////////////////////////////////
714 // Function: PandaFramework::hide_collision_solids
715 // Access: Public
716 // Description: Hides any collision solids, or occluders, which are
717 // visible in the indicated scene graph. Returns the
718 // number of nodes hidden.
719 ////////////////////////////////////////////////////////////////////
722  int num_changed = 0;
723 
724  if (node.node()->is_of_type(CollisionNode::get_class_type()) ||
725  node.node()->is_of_type(OccluderNode::get_class_type())) {
726  if (!node.is_hidden()) {
727  node.hide();
728  num_changed++;
729  }
730  }
731 
732  int num_children = node.get_num_children();
733  for (int i = 0; i < num_children; i++) {
734  num_changed += hide_collision_solids(node.get_child(i));
735  }
736 
737  return num_changed;
738 }
739 
740 ////////////////////////////////////////////////////////////////////
741 // Function: PandaFramework::show_collision_solids
742 // Access: Public
743 // Description: Shows any collision solids, or occluders, which are
744 // directly hidden in the indicated scene graph.
745 // Returns the number of nodes shown.
746 ////////////////////////////////////////////////////////////////////
749  int num_changed = 0;
750 
751  if (node.node()->is_of_type(CollisionNode::get_class_type()) ||
752  node.node()->is_of_type(OccluderNode::get_class_type())) {
753  if (node.get_hidden_ancestor() == node) {
754  node.show();
755  num_changed++;
756  }
757  }
758 
759  int num_children = node.get_num_children();
760  for (int i = 0; i < num_children; i++) {
761  num_changed += show_collision_solids(node.get_child(i));
762  }
763 
764  return num_changed;
765 }
766 
767 ////////////////////////////////////////////////////////////////////
768 // Function: PandaFramework::set_highlight
769 // Access: Public
770 // Description: Sets the indicated node (normally a node within the
771 // get_models() tree) up as the highlighted node.
772 // Certain operations affect the highlighted node only.
773 ////////////////////////////////////////////////////////////////////
774 void PandaFramework::
775 set_highlight(const NodePath &node) {
776  clear_highlight();
777  _highlight = node;
778  if (!_highlight.is_empty()) {
779  framework_cat.info(false) << _highlight << "\n";
780  _highlight.show_bounds();
781  _highlight.set_render_mode_filled_wireframe(LColor(1.0f, 0.0f, 0.0f, 1.0f), 200);
782  }
783 }
784 
785 ////////////////////////////////////////////////////////////////////
786 // Function: PandaFramework::clear_highlight
787 // Access: Public
788 // Description: Unhighlights the currently highlighted node, if any.
789 ////////////////////////////////////////////////////////////////////
790 void PandaFramework::
792  if (!_highlight.is_empty()) {
793  _highlight.hide_bounds();
794  _highlight.clear_render_mode();
795  _highlight = NodePath();
796  }
797 }
798 
799 ////////////////////////////////////////////////////////////////////
800 // Function: PandaFramework::enable_default_keys
801 // Access: Public
802 // Description: Sets callbacks on the event handler to handle all of
803 // the normal viewer keys, like t to toggle texture, ESC
804 // or q to quit, etc.
805 ////////////////////////////////////////////////////////////////////
806 void PandaFramework::
808  if (!_default_keys_enabled) {
809  do_enable_default_keys();
810  _default_keys_enabled = true;
811  }
812 }
813 
814 ////////////////////////////////////////////////////////////////////
815 // Function: PandaFramework::do_frame
816 // Access: Public, Virtual
817 // Description: Renders one frame and performs all associated
818 // processing. Returns true if we should continue
819 // rendering, false if we should exit. This is normally
820 // called only from main_loop().
821 ////////////////////////////////////////////////////////////////////
822 bool PandaFramework::
823 do_frame(Thread *current_thread) {
824  nassertr(_is_open, false);
825 
826  _task_mgr.poll();
827 
828  return !_exit_flag;
829 }
830 
831 ////////////////////////////////////////////////////////////////////
832 // Function: PandaFramework::main_loop
833 // Access: Public
834 // Description: Called to yield control to the panda framework. This
835 // function does not return until set_exit_flag() has
836 // been called.
837 ////////////////////////////////////////////////////////////////////
838 void PandaFramework::
840  Thread *current_thread = Thread::get_current_thread();
841  while (do_frame(current_thread)) {
842  }
843 }
844 
845 ////////////////////////////////////////////////////////////////////
846 // Function: PandaFramework::make_window_framework
847 // Access: Protected, Virtual
848 // Description: Creates a new WindowFramework object. This is
849 // provided as a hook so derived PandaFramework classes
850 // can create custom WindowFramework objects.
851 ////////////////////////////////////////////////////////////////////
852 PT(WindowFramework) PandaFramework::
853 make_window_framework() {
854  return new WindowFramework(this);
855 }
856 
857 ////////////////////////////////////////////////////////////////////
858 // Function: PandaFramework::make_default_pipe
859 // Access: Protected, Virtual
860 // Description: Creates the default GraphicsPipe that will contain
861 // all windows that are not opened on a specific pipe.
862 ////////////////////////////////////////////////////////////////////
863 void PandaFramework::
864 make_default_pipe() {
865  // This depends on the shared library or libraries (DLL's to you
866  // Windows folks) that have been loaded in at runtime from the
867  // load-display and/or aux-display Configrc variables.
869  selection->print_pipe_types();
870  _default_pipe = selection->make_default_pipe();
871 
872  if (_default_pipe == (GraphicsPipe*)NULL) {
873  nout << "No graphics pipe is available!\n"
874  << "Your Config.prc file must name at least one valid panda display\n"
875  << "library via load-display or aux-display.\n";
876  }
877 }
878 
879 ////////////////////////////////////////////////////////////////////
880 // Function: PandaFramework::do_enable_default_keys
881 // Access: Protected, Virtual
882 // Description: The implementation of enable_default_keys().
883 ////////////////////////////////////////////////////////////////////
884 void PandaFramework::
885 do_enable_default_keys() {
886  define_key("escape", "close window", event_esc, this);
887  define_key("q", "close window", event_esc, this);
888  define_key("f", "report frame rate", event_f, this);
889  define_key("w", "toggle wireframe mode", event_w, this);
890  define_key("t", "toggle texturing", event_t, this);
891  define_key("b", "toggle backface (double-sided) rendering", event_b, this);
892  define_key("i", "invert (reverse) single-sided faces", event_i, this);
893  define_key("l", "toggle lighting", event_l, this);
894  define_key("p", "toggle per-pixel lighting", event_p, this);
895  define_key("c", "recenter view on object", event_c, this);
896  define_key("a", "toggle animation controls", event_a, this);
897  define_key("shift-c", "toggle collision surfaces", event_C, this);
898  define_key("shift-b", "report bounding volume", event_B, this);
899  define_key("shift-l", "list hierarchy", event_L, this);
900  define_key("shift-a", "analyze hierarchy", event_A, this);
901  define_key("h", "highlight node", event_h, this);
902  define_key("arrow_up", "move highlight to parent", event_arrow_up, this);
903  define_key("arrow_down", "move highlight to child", event_arrow_down, this);
904  define_key("arrow_left", "move highlight to sibling", event_arrow_left, this);
905  define_key("arrow_right", "move highlight to sibling", event_arrow_right, this);
906  define_key("shift-s", "activate PStats", event_S, this);
907  define_key("f9", "Take screenshot", event_f9, this);
908  define_key(",", "change background color", event_comma, this);
909  define_key("?", "", event_question, this);
910  define_key("shift-/", "", event_question, this);
911 }
912 
913 ////////////////////////////////////////////////////////////////////
914 // Function: PandaFramework::clear_text
915 // Access: Protected
916 // Description: Removes any onscreen text (like help text or
917 // screenshot filename). Returns true if there was any
918 // text in the first place, false otherwise.
919 ////////////////////////////////////////////////////////////////////
920 bool PandaFramework::
921 clear_text() {
922  bool any_text = false;
923  if (!_screenshot_text.is_empty()) {
924  _screenshot_text.remove_node();
925  any_text = true;
926  }
927 
928  if (!_help_text.is_empty()) {
929  _help_text.remove_node();
930  any_text = true;
931  }
932 
933  return any_text;
934 }
935 
936 ////////////////////////////////////////////////////////////////////
937 // Function: PandaFramework::event_esc
938 // Access: Public, Static
939 // Description: Default handler for ESC or q key: close the current
940 // window (and exit the application if that was the last
941 // window).
942 ////////////////////////////////////////////////////////////////////
943 void PandaFramework::
944 event_esc(const Event *event, void *data) {
945  if (event->get_num_parameters() == 1) {
946  EventParameter param = event->get_parameter(0);
947  WindowFramework *wf;
948  DCAST_INTO_V(wf, param.get_ptr());
949 
950  PT(GraphicsOutput) win = wf->get_graphics_output();
951 
952  PandaFramework *self = (PandaFramework *)data;
953  self->close_window(wf);
954 
955  // Also close any other WindowFrameworks on the same window.
956  int window_index = self->find_window(win);
957  while (window_index != -1) {
958  self->close_window(window_index);
959  window_index = self->find_window(win);
960  }
961 
962  // Free up the mouse for that window.
963  self->remove_mouse(win);
964 
965  // Make sure the close request propagates through the system.
966  self->_engine->open_windows();
967 
968  // If we closed the last window, shut down.
969  if (self->all_windows_closed()) {
970  self->_exit_flag = true;
971  }
972  }
973 }
974 
975 ////////////////////////////////////////////////////////////////////
976 // Function: PandaFramework::event_f
977 // Access: Public, Static
978 // Description: Default handler for f key: report and reset frame
979 // rate.
980 ////////////////////////////////////////////////////////////////////
981 void PandaFramework::
982 event_f(const Event *, void *data) {
983  PandaFramework *self = (PandaFramework *)data;
984  self->report_frame_rate(nout);
985  self->reset_frame_rate();
986 }
987 
988 ////////////////////////////////////////////////////////////////////
989 // Function: PandaFramework::event_w
990 // Access: Public, Static
991 // Description: Default handler for w key: toggle wireframe.
992 ////////////////////////////////////////////////////////////////////
993 void PandaFramework::
994 event_w(const Event *event, void *) {
995  if (event->get_num_parameters() == 1) {
996  EventParameter param = event->get_parameter(0);
997  WindowFramework *wf;
998  DCAST_INTO_V(wf, param.get_ptr());
999 
1000  if (!wf->get_wireframe()) {
1001  wf->set_wireframe(true, true);
1002  } else if (wf->get_wireframe_filled()) {
1003  wf->set_wireframe(true, false);
1004  } else {
1005  wf->set_wireframe(false, false);
1006  }
1007  }
1008 }
1009 
1010 ////////////////////////////////////////////////////////////////////
1011 // Function: PandaFramework::event_t
1012 // Access: Public, Static
1013 // Description: Default handler for t key: toggle texture.
1014 ////////////////////////////////////////////////////////////////////
1015 void PandaFramework::
1016 event_t(const Event *event, void *) {
1017  if (event->get_num_parameters() == 1) {
1018  EventParameter param = event->get_parameter(0);
1019  WindowFramework *wf;
1020  DCAST_INTO_V(wf, param.get_ptr());
1021 
1022  wf->set_texture(!wf->get_texture());
1023  }
1024 }
1025 
1026 ////////////////////////////////////////////////////////////////////
1027 // Function: PandaFramework::event_b
1028 // Access: Public, Static
1029 // Description: Default handler for b key: toggle backface (two-sided
1030 // rendering).
1031 ////////////////////////////////////////////////////////////////////
1032 void PandaFramework::
1033 event_b(const Event *event, void *) {
1034  if (event->get_num_parameters() == 1) {
1035  EventParameter param = event->get_parameter(0);
1036  WindowFramework *wf;
1037  DCAST_INTO_V(wf, param.get_ptr());
1038 
1039  wf->set_two_sided(!wf->get_two_sided());
1040  }
1041 }
1042 
1043 ////////////////////////////////////////////////////////////////////
1044 // Function: PandaFramework::event_i
1045 // Access: Public, Static
1046 // Description: Default handler for i key: invert one-sided faces.
1047 ////////////////////////////////////////////////////////////////////
1048 void PandaFramework::
1049 event_i(const Event *event, void *) {
1050  if (event->get_num_parameters() == 1) {
1051  EventParameter param = event->get_parameter(0);
1052  WindowFramework *wf;
1053  DCAST_INTO_V(wf, param.get_ptr());
1054 
1056  }
1057 }
1058 
1059 ////////////////////////////////////////////////////////////////////
1060 // Function: PandaFramework::event_l
1061 // Access: Public, Static
1062 // Description: Default handler for l key: toggle lighting.
1063 ////////////////////////////////////////////////////////////////////
1064 void PandaFramework::
1065 event_l(const Event *event, void *) {
1066  if (event->get_num_parameters() == 1) {
1067  EventParameter param = event->get_parameter(0);
1068  WindowFramework *wf;
1069  DCAST_INTO_V(wf, param.get_ptr());
1070 
1071  wf->set_lighting(!wf->get_lighting());
1072  }
1073 }
1074 
1075 ////////////////////////////////////////////////////////////////////
1076 // Function: PandaFramework::event_p
1077 // Access: Public, Static
1078 // Description: Default handler for p key: toggle per-pixel lighting.
1079 ////////////////////////////////////////////////////////////////////
1080 void PandaFramework::
1081 event_p(const Event *event, void *) {
1082  if (event->get_num_parameters() == 1) {
1083  EventParameter param = event->get_parameter(0);
1084  WindowFramework *wf;
1085  DCAST_INTO_V(wf, param.get_ptr());
1086 
1087  wf->set_perpixel(!wf->get_perpixel());
1088  }
1089 }
1090 
1091 ////////////////////////////////////////////////////////////////////
1092 // Function: PandaFramework::event_c
1093 // Access: Public, Static
1094 // Description: Default handler for c key: center the trackball over
1095 // the scene, or over the highlighted part of the scene.
1096 ////////////////////////////////////////////////////////////////////
1097 void PandaFramework::
1098 event_c(const Event *event, void *data) {
1099  if (event->get_num_parameters() == 1) {
1100  EventParameter param = event->get_parameter(0);
1101  WindowFramework *wf;
1102  DCAST_INTO_V(wf, param.get_ptr());
1103 
1104  PandaFramework *self = (PandaFramework *)data;
1105 
1106  NodePath node = self->get_highlight();
1107  if (node.is_empty()) {
1108  node = self->get_models();
1109  }
1110  wf->center_trackball(node);
1111  }
1112 }
1113 
1114 ////////////////////////////////////////////////////////////////////
1115 // Function: PandaFramework::event_a
1116 // Access: Public, Static
1117 // Description: Default handler for a key: toggle the animation
1118 // controls.
1119 ////////////////////////////////////////////////////////////////////
1120 void PandaFramework::
1121 event_a(const Event *event, void *data) {
1122  if (event->get_num_parameters() == 1) {
1123  EventParameter param = event->get_parameter(0);
1124  WindowFramework *wf;
1125  DCAST_INTO_V(wf, param.get_ptr());
1126 
1127  wf->next_anim_control();
1128  }
1129 }
1130 
1131 ////////////////////////////////////////////////////////////////////
1132 // Function: PandaFramework::event_C
1133 // Access: Public, Static
1134 // Description: Default handler for shift-C key: toggle the showing
1135 // of collision solids.
1136 ////////////////////////////////////////////////////////////////////
1137 void PandaFramework::
1138 event_C(const Event *, void *data) {
1139  PandaFramework *self = (PandaFramework *)data;
1140 
1141  NodePath node = self->get_highlight();
1142  if (node.is_empty()) {
1143  node = self->get_models();
1144  }
1145 
1146  if (self->hide_collision_solids(node) == 0) {
1147  self->show_collision_solids(node);
1148  }
1149 }
1150 
1151 ////////////////////////////////////////////////////////////////////
1152 // Function: PandaFramework::event_B
1153 // Access: Public, Static
1154 // Description: Default handler for shift-B key: describe the
1155 // bounding volume of the currently selected object, or
1156 // the entire scene.
1157 ////////////////////////////////////////////////////////////////////
1158 void PandaFramework::
1159 event_B(const Event *, void *data) {
1160  PandaFramework *self = (PandaFramework *)data;
1161 
1162  NodePath node = self->get_highlight();
1163  if (node.is_empty()) {
1164  node = self->get_models();
1165  }
1166 
1167  node.get_bounds()->write(nout);
1168 }
1169 
1170 ////////////////////////////////////////////////////////////////////
1171 // Function: PandaFramework::event_L
1172 // Access: Public, Static
1173 // Description: Default handler for shift-L key: list the contents of
1174 // the scene graph, or the highlighted node.
1175 ////////////////////////////////////////////////////////////////////
1176 void PandaFramework::
1177 event_L(const Event *, void *data) {
1178  PandaFramework *self = (PandaFramework *)data;
1179 
1180  NodePath node = self->get_highlight();
1181  if (node.is_empty()) {
1182  node = self->get_models();
1183  }
1184 
1185  node.ls();
1186 }
1187 
1188 ////////////////////////////////////////////////////////////////////
1189 // Function: PandaFramework::event_A
1190 // Access: Public, Static
1191 // Description: Default handler for shift-A key: analyze the contents
1192 // of the scene graph, or the highlighted node.
1193 ////////////////////////////////////////////////////////////////////
1194 void PandaFramework::
1195 event_A(const Event *, void *data) {
1196  PandaFramework *self = (PandaFramework *)data;
1197 
1198  NodePath node = self->get_highlight();
1199  if (node.is_empty()) {
1200  node = self->get_models();
1201  }
1202 
1203  SceneGraphAnalyzer sga;
1204  sga.add_node(node.node());
1205  sga.write(nout);
1206 }
1207 
1208 ////////////////////////////////////////////////////////////////////
1209 // Function: PandaFramework::event_h
1210 // Access: Public, Static
1211 // Description: Default handler for h key: toggle highlight mode. In
1212 // this mode, you can walk the scene graph with the
1213 // arrow keys to highlight different nodes.
1214 ////////////////////////////////////////////////////////////////////
1215 void PandaFramework::
1216 event_h(const Event *, void *data) {
1217  PandaFramework *self = (PandaFramework *)data;
1218 
1219  if (self->has_highlight()) {
1220  self->clear_highlight();
1221  } else {
1222  self->set_highlight(self->get_models());
1223  }
1224 }
1225 
1226 ////////////////////////////////////////////////////////////////////
1227 // Function: PandaFramework::event_arrow_up
1228 // Access: Public, Static
1229 // Description: Default handler for up arrow key: in highlight mode,
1230 // move the highlight to the node's parent.
1231 ////////////////////////////////////////////////////////////////////
1232 void PandaFramework::
1233 event_arrow_up(const Event *, void *data) {
1234  PandaFramework *self = (PandaFramework *)data;
1235 
1236  if (self->has_highlight()) {
1237  NodePath node = self->get_highlight();
1238  if (node.has_parent() && node != self->get_models()) {
1239  self->set_highlight(node.get_parent());
1240  }
1241  }
1242 }
1243 
1244 ////////////////////////////////////////////////////////////////////
1245 // Function: PandaFramework::event_arrow_down
1246 // Access: Public, Static
1247 // Description: Default handler for up arrow key: in highlight mode,
1248 // move the highlight to the node's first child.
1249 ////////////////////////////////////////////////////////////////////
1250 void PandaFramework::
1251 event_arrow_down(const Event *, void *data) {
1252  PandaFramework *self = (PandaFramework *)data;
1253 
1254  if (self->has_highlight()) {
1255  NodePath node = self->get_highlight();
1256  if (node.get_num_children() > 0) {
1257  self->set_highlight(node.get_child(0));
1258  }
1259  }
1260 }
1261 
1262 ////////////////////////////////////////////////////////////////////
1263 // Function: PandaFramework::event_arrow_left
1264 // Access: Public, Static
1265 // Description: Default handler for up arrow key: in highlight mode,
1266 // move the highlight to the node's nearest sibling on
1267 // the left.
1268 ////////////////////////////////////////////////////////////////////
1269 void PandaFramework::
1270 event_arrow_left(const Event *, void *data) {
1271  PandaFramework *self = (PandaFramework *)data;
1272 
1273  if (self->has_highlight()) {
1274  NodePath node = self->get_highlight();
1275  NodePath parent = node.get_parent();
1276  if (node.has_parent() && node != self->get_models()) {
1277  int index = parent.node()->find_child(node.node());
1278  nassertv(index >= 0);
1279  int sibling = index - 1;
1280  if (sibling >= 0) {
1281  self->set_highlight(NodePath(parent, parent.node()->get_child(sibling)));
1282  }
1283  }
1284  }
1285 }
1286 
1287 ////////////////////////////////////////////////////////////////////
1288 // Function: PandaFramework::event_arrow_right
1289 // Access: Public, Static
1290 // Description: Default handler for up arrow key: in highlight mode,
1291 // move the highlight to the node's nearest sibling on
1292 // the right.
1293 ////////////////////////////////////////////////////////////////////
1294 void PandaFramework::
1295 event_arrow_right(const Event *, void *data) {
1296  PandaFramework *self = (PandaFramework *)data;
1297 
1298  if (self->has_highlight()) {
1299  NodePath node = self->get_highlight();
1300  NodePath parent = node.get_parent();
1301  if (node.has_parent() && node != self->get_models()) {
1302  int index = parent.node()->find_child(node.node());
1303  nassertv(index >= 0);
1304  int num_children = parent.node()->get_num_children();
1305  int sibling = index + 1;
1306  if (sibling < num_children) {
1307  self->set_highlight(NodePath(parent, parent.node()->get_child(sibling)));
1308  }
1309  }
1310  }
1311 }
1312 
1313 ////////////////////////////////////////////////////////////////////
1314 // Function: PandaFramework::event_S
1315 // Access: Public, Static
1316 // Description: Default handler for shift-S key: activate stats.
1317 ////////////////////////////////////////////////////////////////////
1318 void PandaFramework::
1319 event_S(const Event *, void *) {
1320 #ifdef DO_PSTATS
1321  nout << "Connecting to stats host" << endl;
1323 #else
1324  nout << "Stats host not supported." << endl;
1325 #endif
1326 }
1327 
1328 ////////////////////////////////////////////////////////////////////
1329 // Function: PandaFramework::event_f9
1330 // Access: Public, Static
1331 // Description: Default handler for f9 key: take screenshot.
1332 ////////////////////////////////////////////////////////////////////
1333 void PandaFramework::
1334 event_f9(const Event *event, void *data) {
1335  PandaFramework *self = (PandaFramework *)data;
1336 
1337  if (event->get_num_parameters() == 1) {
1338  EventParameter param = event->get_parameter(0);
1339  WindowFramework *wf;
1340  DCAST_INTO_V(wf, param.get_ptr());
1341 
1342  if (self->clear_text()) {
1343  // Render one more frame to remove the text.
1344  self->_engine->render_frame();
1345  }
1346 
1348  string text;
1349  if (filename.empty()) {
1350  text = "Screenshot failed";
1351  } else {
1352  text = filename;
1353  }
1354 
1355  // Adds the full path to the output string
1356  string output_text = (string)ExecutionEnvironment::get_cwd() + "/" + (string)text;
1357 
1358  TextNode *text_node = new TextNode("screenshot");
1359  self->_screenshot_text = NodePath(text_node);
1360  text_node->set_align(TextNode::A_center);
1361  text_node->set_shadow_color(0.0f, 0.0f, 0.0f, 1.0f);
1362  text_node->set_shadow(0.04, 0.04);
1363  text_node->set_text(output_text);
1364  self->_screenshot_text.set_scale(0.06);
1365  self->_screenshot_text.set_pos(0.0, 0.0, -0.7);
1366  self->_screenshot_text.reparent_to(wf->get_aspect_2d());
1367  cout << "Screenshot saved: " + output_text + "\n";
1368 
1369  // Set a do-later to remove the text in 3 seconds.
1370  self->_task_mgr.remove(self->_task_mgr.find_tasks("clear_text"));
1371  PT(GenericAsyncTask) task = new GenericAsyncTask("clear_text", task_clear_text, self);
1372  task->set_delay(3.0);
1373  self->_task_mgr.add(task);
1374  }
1375 }
1376 
1377 ////////////////////////////////////////////////////////////////////
1378 // Function: PandaFramework::event_comma
1379 // Access: Public, Static
1380 // Description: Default handler for comma key: rotate background color.
1381 ////////////////////////////////////////////////////////////////////
1382 void PandaFramework::
1383 event_comma(const Event *event, void *) {
1384  if (event->get_num_parameters() == 1) {
1385  EventParameter param = event->get_parameter(0);
1386  WindowFramework *wf;
1387  DCAST_INTO_V(wf, param.get_ptr());
1388 
1389  switch (wf->get_background_type()) {
1390  case WindowFramework::BT_other:
1391  case WindowFramework::BT_none:
1392  break;
1393 
1394  case WindowFramework::BT_white:
1395  wf->set_background_type(WindowFramework::BT_default);
1396  break;
1397 
1398  default:
1399  wf->set_background_type((WindowFramework::BackgroundType)(wf->get_background_type() + 1));
1400  }
1401  }
1402 }
1403 
1404 ////////////////////////////////////////////////////////////////////
1405 // Function: PandaFramework::event_question
1406 // Access: Public, Static
1407 // Description: Default handler for ? key: show the available keys.
1408 ////////////////////////////////////////////////////////////////////
1409 void PandaFramework::
1410 event_question(const Event *event, void *data) {
1411  PandaFramework *self = (PandaFramework *)data;
1412  if (event->get_num_parameters() == 1) {
1413  EventParameter param = event->get_parameter(0);
1414  WindowFramework *wf;
1415  DCAST_INTO_V(wf, param.get_ptr());
1416 
1417  self->_screenshot_text.remove_node();
1418 
1419  if (!self->_help_text.is_empty()) {
1420  self->_help_text.remove_node();
1421  // This key is a toggle; remove the help text and do nothing else.
1422 
1423  } else {
1424  // Build up a string to display.
1425  ostringstream help;
1426  KeyDefinitions::const_iterator ki;
1427  for (ki = self->_key_definitions.begin();
1428  ki != self->_key_definitions.end();
1429  ++ki) {
1430  const KeyDefinition &keydef = (*ki);
1431  help << keydef._event_name << "\t" << keydef._description << "\n";
1432  }
1433 
1434  string help_text = help.str();
1435 
1436  TextNode *text_node = new TextNode("help");
1437  self->_help_text = NodePath(text_node);
1438  text_node->set_align(TextNode::A_left);
1439  text_node->set_shadow_color(0.0f, 0.0f, 0.0f, 1.0f);
1440  text_node->set_shadow(0.04, 0.04);
1441  text_node->set_text(help_text);
1442 
1443  LVecBase4 frame = text_node->get_frame_actual();
1444 
1445  PN_stdfloat height = frame[3] - frame[2];
1446  PN_stdfloat scale = min(0.06, 1.8 / height);
1447  self->_help_text.set_scale(scale);
1448 
1449  PN_stdfloat pos_scale = scale / -2.0;
1450  self->_help_text.set_pos((frame[0] + frame[1]) * pos_scale,
1451  0.0,
1452  (frame[2] + frame[3]) * pos_scale);
1453 
1454  self->_help_text.reparent_to(wf->get_aspect_2d());
1455  }
1456  }
1457 }
1458 
1459 ////////////////////////////////////////////////////////////////////
1460 // Function: PandaFramework::event_window_event
1461 // Access: Public, Static
1462 // Description: Default handler for window events: window resized or
1463 // closed, etc.
1464 ////////////////////////////////////////////////////////////////////
1465 void PandaFramework::
1466 event_window_event(const Event *event, void *data) {
1467  PandaFramework *self = (PandaFramework *)data;
1468  if (event->get_num_parameters() == 1) {
1469  // The parameter of the window event is the window itself, rather
1470  // than the window framework object (which is the parameter of all
1471  // of the keyboard events).
1472  EventParameter param = event->get_parameter(0);
1473  const GraphicsOutput *win;
1474  DCAST_INTO_V(win, param.get_ptr());
1475 
1476  // Is this a window we've heard about?
1477  int window_index = self->find_window(win);
1478  if (window_index == -1) {
1479  framework_cat.debug()
1480  << "Ignoring message from unknown window.\n";
1481 
1482  } else {
1483  if (!win->is_valid()) {
1484  // The window has been closed.
1485  int window_index = self->find_window(win);
1486  while (window_index != -1) {
1487  self->close_window(window_index);
1488  window_index = self->find_window(win);
1489  }
1490 
1491  // Free up the mouse for that window.
1492  self->remove_mouse(win);
1493 
1494  // If the last window was closed, exit the application.
1495  if (self->all_windows_closed() && !self->_exit_flag) {
1496  framework_cat.info()
1497  << "Last window was closed by user.\n";
1498  self->_exit_flag = true;
1499  }
1500  } else {
1501  // Adjust aspect ratio.
1502  for (int n = 0; n < (int)self->_windows.size(); n++) {
1503  if (self->_windows[n]->get_graphics_output() == win) {
1504  self->_windows[n]->adjust_dimensions();
1505  }
1506  }
1507  }
1508  }
1509  }
1510 }
1511 
1512 ////////////////////////////////////////////////////////////////////
1513 // Function: PandaFramework::task_data_loop
1514 // Access: Public, Static
1515 // Description: Called once per frame to process the data graph (which
1516 // handles user input via the mouse and keyboard, etc.)
1517 ////////////////////////////////////////////////////////////////////
1518 AsyncTask::DoneStatus PandaFramework::
1519 task_data_loop(GenericAsyncTask *task, void *data) {
1520  PandaFramework *self = (PandaFramework *)data;
1521 
1522  DataGraphTraverser dg_trav;
1523  dg_trav.traverse(self->_data_root.node());
1524 
1525  return AsyncTask::DS_cont;
1526 }
1527 
1528 ////////////////////////////////////////////////////////////////////
1529 // Function: PandaFramework::task_event
1530 // Access: Public, Static
1531 // Description: Called once per frame to process the pending events.
1532 ////////////////////////////////////////////////////////////////////
1533 AsyncTask::DoneStatus PandaFramework::
1534 task_event(GenericAsyncTask *task, void *data) {
1535  PandaFramework *self = (PandaFramework *)data;
1536 
1537  throw_event("NewFrame");
1538  self->_event_handler.process_events();
1539 
1540  return AsyncTask::DS_cont;
1541 }
1542 
1543 ////////////////////////////////////////////////////////////////////
1544 // Function: PandaFramework::task_igloop
1545 // Access: Public, Static
1546 // Description: Called once per frame to render the scene.
1547 ////////////////////////////////////////////////////////////////////
1548 AsyncTask::DoneStatus PandaFramework::
1549 task_igloop(GenericAsyncTask *task, void *data) {
1550  PandaFramework *self = (PandaFramework *)data;
1551 
1552  if (self->_engine != (GraphicsEngine *)NULL) {
1553  self->_engine->render_frame();
1554  }
1555 
1556  return AsyncTask::DS_cont;
1557 }
1558 
1559 ////////////////////////////////////////////////////////////////////
1560 // Function: PandaFramework::task_record_frame
1561 // Access: Public, Static
1562 // Description: Called once per frame to ask the recorder to record
1563 // the user input data, if enabled.
1564 ////////////////////////////////////////////////////////////////////
1565 AsyncTask::DoneStatus PandaFramework::
1567  PandaFramework *self = (PandaFramework *)data;
1568 
1569  if (self->_recorder != (RecorderController *)NULL) {
1570  self->_recorder->record_frame();
1571  }
1572 
1573  return AsyncTask::DS_cont;
1574 }
1575 
1576 ////////////////////////////////////////////////////////////////////
1577 // Function: PandaFramework::task_play_frame
1578 // Access: Public, Static
1579 // Description: Called once per frame to ask the recorder to play back
1580 // the user input data, if enabled.
1581 ////////////////////////////////////////////////////////////////////
1582 AsyncTask::DoneStatus PandaFramework::
1583 task_play_frame(GenericAsyncTask *task, void *data) {
1584  PandaFramework *self = (PandaFramework *)data;
1585 
1586  if (self->_recorder != (RecorderController *)NULL) {
1587  self->_recorder->play_frame();
1588  }
1589 
1590  return AsyncTask::DS_cont;
1591 }
1592 
1593 ////////////////////////////////////////////////////////////////////
1594 // Function: PandaFramework::task_clear_text
1595 // Access: Public, Static
1596 // Description: Called once to remove the screenshot text from onscreen.
1597 ////////////////////////////////////////////////////////////////////
1598 AsyncTask::DoneStatus PandaFramework::
1599 task_clear_text(GenericAsyncTask *task, void *data) {
1600  PandaFramework *self = (PandaFramework *)data;
1601 
1602  self->clear_text();
1603  return AsyncTask::DS_cont;
1604 }
1605 
1606 ////////////////////////////////////////////////////////////////////
1607 // Function: PandaFramework::task_garbage_collect
1608 // Access: Public, Static
1609 // Description: This task is created automatically if
1610 // garbage_collect_states is true. It calls the needed
1611 // TransformState::garbage_collect() and
1612 // RenderState::garbage_collect() methods each frame.
1613 ////////////////////////////////////////////////////////////////////
1614 AsyncTask::DoneStatus PandaFramework::
1616  TransformState::garbage_collect();
1618  return AsyncTask::DS_cont;
1619 }
bool get_wireframe_filled() const
Returns the current state of the wireframe_filled flag.
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:271
void remove_all_hooks()
Removes all hooks assigned to all events.
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of children of the referenced node.
Definition: nodePath.I:406
static AsyncTask::DoneStatus task_clear_text(GenericAsyncTask *task, void *data)
Called once to remove the screenshot text from onscreen.
static void event_w(const Event *, void *data)
Default handler for w key: toggle wireframe.
static AsyncTask::DoneStatus task_garbage_collect(GenericAsyncTask *task, void *data)
This task is created automatically if garbage_collect_states is true.
bool begin_playback(const Filename &filename)
Begins playing back data from the indicated filename.
void set_two_sided(bool enable)
Sets the two_sided state on all windows.
static void event_i(const Event *, void *data)
Default handler for i key: invert one-sided faces.
void enable_default_keys()
Sets callbacks on the event handler to handle all of the normal viewer keys, like t to toggle texture...
void set_perpixel(bool enable)
Turns per-pixel lighting on (true) or off (false).
static void event_f9(const Event *, void *data)
Default handler for f9 key: take screenshot.
static bool connect(const string &=string(), int=-1)
Attempts to establish a connection to the indicated PStatServer.
Definition: pStatClient.h:267
WindowFramework::BackgroundType get_background_type() const
Returns the current background type setting.
A class to monitor events from the C++ side of things.
Definition: eventHandler.h:41
void reset_frame_rate()
Resets the frame rate computation.
An optional parameter associated with an event.
bool get_wireframe() const
Returns the current state of the wireframe flag.
NodePath & get_models()
Returns the root of the scene graph normally reserved for parenting models and such.
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
void close_framework()
Should be called at the end of an application to close Panda.
static AsyncTask::DoneStatus task_event(GenericAsyncTask *task, void *data)
Called once per frame to process the pending events.
A class to manage a loose queue of isolated tasks, which can be performed either synchronously (in th...
int find_window(const GraphicsOutput *win) const
Returns the index of the first WindowFramework object found that references the indicated GraphicsOut...
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of child nodes this node has.
Definition: pandaNode.I:68
GraphicsPipe * get_default_pipe()
Returns the default pipe.
void set_text(const string &text)
Changes the text that is displayed under the TextNode.
Definition: textNode.I:1171
static void event_A(const Event *, void *data)
Default handler for shift-A key: analyze the contents of the scene graph, or the highlighted node...
static void event_C(const Event *, void *data)
Default handler for shift-C key: toggle the showing of collision solids.
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:26
static void event_comma(const Event *, void *data)
Default handler for comma key: rotate background color.
NodePath get_aspect_2d()
Returns the node under the 2-d scene graph that is scaled to suit the window&#39;s aspect ratio...
GraphicsOutput * get_graphics_output() const
Returns a pointer to the underlying GraphicsOutput object.
static void event_a(const Event *, void *data)
Default handler for a key: toggle the animation controls.
void add_node(PandaNode *node)
Adds a new node to the set of data for analysis.
Filename save_screenshot_default(const string &prefix="screenshot")
Saves a screenshot of the region to a default filename, and returns the filename, or empty string if ...
bool add_hook(const string &event_name, EventFunction *function)
Adds the indicated function to the list of those that will be called when the named event is thrown...
bool get_perpixel() const
Returns the current state of the perpixel flag.
NodePath get_hidden_ancestor(DrawMask camera_mask=PandaNode::get_overall_bit(), Thread *current_thread=Thread::get_current_thread()) const
Returns the NodePath at or above the referenced node that is hidden to the indicated camera(s)...
Definition: nodePath.cxx:5872
PandaNode * get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the nth child node of this node.
Definition: pandaNode.I:82
This encapsulates the data that is normally associated with a single window, or with a single display...
static void prepare_for_exit()
Should be called by the main thread just before exiting the program, this blocks until any remaining ...
Definition: thread.I:341
virtual void get_default_window_props(WindowProperties &props)
Fills in the indicated window properties structure according to the normal window properties for this...
void print_pipe_types() const
Writes a list of the currently known GraphicsPipe types to nout, for the user&#39;s information.
void hide_bounds()
Stops the rendering of the bounding volume begun with show_bounds().
Definition: nodePath.cxx:6132
void show_bounds()
Causes the bounding volume of the bottom node and all of its descendants (that is, the bounding volume associated with the the bottom arc) to be rendered, if possible.
Definition: nodePath.cxx:6104
void set_lighting(bool enable)
Turns lighting on (true) or off (false).
static void event_arrow_right(const Event *, void *data)
Default handler for up arrow key: in highlight mode, move the highlight to the node&#39;s nearest sibling...
Reads the mouse and/or keyboard data sent from a GraphicsWindow, and transmits it down the data graph...
double get_frame_time(Thread *current_thread=Thread::get_current_thread()) const
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.I:48
bool get_one_sided_reverse() const
Returns the current state of the one_sided_reverse flag.
void show()
Undoes the effect of a previous hide() on this node: makes the referenced node (and the entire subgra...
Definition: nodePath.I:2202
void add_recorder(const string &name, RecorderBase *recorder)
Adds the named recorder to the set of recorders that are in use.
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
static AsyncTask::DoneStatus task_data_loop(GenericAsyncTask *task, void *data)
Called once per frame to process the data graph (which handles user input via the mouse and keyboard...
bool get_texture() const
Returns the current state of the texture flag.
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
void set_wireframe(bool enable, bool filled=false)
Forces wireframe state (true) or restores default rendering (false).
void set_one_sided_reverse(bool enable)
Toggles one-sided reverse mode.
bool is_valid() const
Returns true if the output is fully created and ready for rendering, false otherwise.
static void event_esc(const Event *, void *data)
Default handler for ESC or q key: close the current window (and exit the application if that was the ...
static void event_f(const Event *, void *data)
Default handler for f key: report and reset frame rate.
const NodePath & get_data_root() const
Returns the root of the data graph.
bool get_two_sided() const
Returns the current state of the two_sided flag.
A container for the various kinds of properties we might ask to have on a graphics window before we o...
static void event_arrow_up(const Event *, void *data)
Default handler for up arrow key: in highlight mode, move the highlight to the node&#39;s parent...
NodePath get_mouse(GraphicsOutput *window)
Returns a NodePath to the MouseAndKeyboard associated with the indicated GraphicsWindow object...
void write(ostream &out, int indent_level=0) const
Describes all the data collected.
LVecBase4 get_frame_actual() const
Returns the actual dimensions of the frame around the text.
Definition: textNode.I:377
static void event_L(const Event *, void *data)
Default handler for shift-L key: list the contents of the scene graph, or the highlighted node...
static void event_arrow_down(const Event *, void *data)
Default handler for up arrow key: in highlight mode, move the highlight to the node&#39;s first child...
A handy class that can scrub over a scene graph and collect interesting statistics on it...
bool get_lighting() const
Returns the current state of the lighting flag.
TypeHandle get_pipe_type(int n) const
Returns the nth type of GraphicsPipe available through this interface.
static void event_B(const Event *, void *data)
Default handler for shift-B key: describe the bounding volume of the currently selected object...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
void set_two_sided(bool enable)
Forces two-sided rendering (true) or restores default rendering (false).
void add(AsyncTask *task)
Adds the indicated task to the active queue.
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:723
int get_frame_count(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of times tick() has been called since the ClockObject was created, or since it was last reset.
Definition: clockObject.I:113
static int garbage_collect()
Performs a garbage-collection cycle.
static WindowProperties get_default()
Returns the "default" WindowProperties.
This maintains a list of GraphicsPipes by type that are available for creation.
void center_trackball(const NodePath &object)
Centers the trackball on the indicated object, and scales the trackball motion suitably.
static AsyncTask::DoneStatus task_igloop(GenericAsyncTask *task, void *data)
Called once per frame to render the scene.
void main_loop()
Called to yield control to the panda framework.
void clear_render_mode()
Completely removes any render mode adjustment that may have been set on this node via set_render_mode...
Definition: nodePath.cxx:5046
void next_anim_control()
Rotates the animation controls through all of the available animations.
void clear_highlight()
Unhighlights the currently highlighted node, if any.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
int find_child(PandaNode *node, Thread *current_thread=Thread::get_current_thread()) const
Returns the index of the indicated child node, if it is a child, or -1 if it is not.
Definition: pandaNode.I:111
void set_highlight(const NodePath &node)
Sets the indicated node (normally a node within the get_models() tree) up as the highlighted node...
void define_key(const string &event_name, const string &description, EventHandler::EventCallbackFunction *function, void *data)
Sets up a handler for the indicated key.
void set_texture(bool enable)
Sets the texture state on all windows.
BackgroundType get_background_type() const
Returns the current background type setting.
Associates a generic C-style function pointer with an AsyncTask object.
static AsyncTask::DoneStatus task_record_frame(GenericAsyncTask *task, void *data)
Called once per frame to ask the recorder to record the user input data, if enabled.
WindowFramework * open_window()
Opens a window on the default graphics pipe.
static GraphicsPipeSelection * get_global_ptr()
Returns a pointer to the one global GraphicsPipeSelection object.
void set_background_type(WindowFramework::BackgroundType type)
Sets the background type of all windows.
static int show_collision_solids(NodePath node)
Shows any collision solids, or occluders, which are directly hidden in the indicated scene graph...
void close_window(int n)
Closes the nth window and removes it from the list.
void set_perpixel(bool enable)
Sets the perpixel state on all windows.
static void event_arrow_left(const Event *, void *data)
Default handler for up arrow key: in highlight mode, move the highlight to the node&#39;s nearest sibling...
TypedWritableReferenceCount * get_ptr() const
Retrieves a pointer to the actual value stored in the parameter.
This is a base class for the various different classes that represent the result of a frame of render...
static void event_p(const Event *, void *data)
Default handler for p key: toggle per-pixel lighting.
static void event_b(const Event *, void *data)
Default handler for b key: toggle backface (two-sided rendering).
static void event_S(const Event *, void *data)
Default handler for shift-S key: activate stats.
bool get_lighting() const
Returns the current state of the lighting flag.
void set_render_mode_filled_wireframe(const LColor &wireframe_color, int priority=0)
Sets up the geometry at this level and below (unless overridden) to render in filled, but overlay the wireframe on top with a fixed color.
Definition: nodePath.cxx:4972
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
static void event_window_event(const Event *, void *data)
Default handler for window events: window resized or closed, etc.
void set_background_type(BackgroundType type)
Sets the background of the window to one of the pre-canned background types (or to BT_other...
void open_framework(int &argc, char **&argv)
Should be called once at the beginning of the application to initialize Panda (and the framework) for...
static void event_t(const Event *, void *data)
Default handler for t key: toggle texture.
int get_num_pipe_types() const
Returns the number of different types of GraphicsPipes that are available to create through this inte...
static void event_l(const Event *, void *data)
Default handler for l key: toggle lighting.
bool is_hidden(DrawMask camera_mask=PandaNode::get_overall_bit()) const
Returns true if the referenced node is hidden from the indicated camera(s) either directly...
Definition: nodePath.I:2301
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
void set_texture(bool enable)
Forces textures off (false) or restores default rendering (true).
void load_aux_modules()
Loads all the modules named in the aux-display Configrc variable, making as many graphics pipes as po...
void add_properties(const WindowProperties &other)
Sets any properties that are explicitly specified in other on this object.
A thread; that is, a lightweight process.
Definition: thread.h:51
A named event, possibly with parameters.
Definition: event.h:36
bool has_parent(Thread *current_thread=Thread::get_current_thread()) const
Returns true if the referenced node has a parent; i.e.
Definition: nodePath.I:447
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition: nodePath.cxx:757
void set_lighting(bool enable)
Sets the lighting state on all windows.
bool begin_record(const Filename &filename)
Begins recording data to the indicated filename.
bool has_hook(const string &event_name) const
Returns true if there is any hook added on the indicated event name, false otherwise.
void set_title(const string &title)
Specifies the title that should be assigned to the window.
The primary interface to this module.
Definition: textNode.h:52
Encapsulates all the communication with a particular instance of a given rendering backend...
NodePath get_parent(Thread *current_thread=Thread::get_current_thread()) const
Returns the NodePath to the parent of the referenced node: that is, this NodePath, shortened by one node.
Definition: nodePath.I:460
void report_frame_rate(ostream &out) const
Reports the currently measured average frame rate to the indicated ostream.
static void event_h(const Event *, void *data)
Default handler for h key: toggle highlight mode.
RecorderController * get_recorder() const
Returns the RecorderController that has been associated with the PandaFramework, if any...
static Filename get_cwd()
Returns the name of the current working directory.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
This object manages the process of recording the user&#39;s runtime inputs to a bam file so that the sess...
static void event_question(const Event *event, void *data)
Default handler for ? key: show the available keys.
This object records any data generated by a particular MouseAndKeyboard node on the datagraph for a s...
Definition: mouseRecorder.h:38
void close_all_windows()
Closes all currently open windows and empties the list of windows.
void remove_mouse(const GraphicsOutput *window)
Removes the mouse that may have been created by an earlier call to get_mouse().
This class serves to provide a high-level framework for basic applications that use Panda in simple w...
This class is the main interface to controlling the render process.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
GraphicsEngine * get_graphics_engine()
Returns the GraphicsEngine that is used to render all the windows in the framework.
bool get_two_sided() const
Returns the current state of the two_sided flag.
bool get_perpixel() const
Returns the current state of the perpixel flag.
void hide()
Makes the referenced node (and the entire subgraph below this node) invisible to all cameras...
Definition: nodePath.I:2270
static AsyncTask::DoneStatus task_play_frame(GenericAsyncTask *task, void *data)
Called once per frame to ask the recorder to play back the user input data, if enabled.
void ls() const
Lists the hierarchy at and below the referenced node.
Definition: nodePath.I:492
void set_wireframe(bool enable)
Sets the wireframe state on all windows.
bool all_windows_closed() const
Returns true if all of the opened windows have been closed by the user, false otherwise.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
void set_shadow(PN_stdfloat xoffset, PN_stdfloat yoffset)
Specifies that the text should be drawn with a shadow, by creating a second copy of the text and offs...
Definition: textNode.I:995
NodePath get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns a NodePath representing the nth child of the referenced node.
Definition: nodePath.I:418
static int hide_collision_solids(NodePath node)
Hides any collision solids, or occluders, which are visible in the indicated scene graph...
virtual bool do_frame(Thread *current_thread)
Renders one frame and performs all associated processing.
void traverse(PandaNode *node)
Starts the traversal of the data graph at the indicated root node.
bool get_wireframe() const
Returns the current state of the wireframe flag.
This object supervises the traversal of the data graph and the moving of data from one DataNode to it...
void poll()
Runs through all the tasks in the task list, once, if the task manager is running in single-threaded ...
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:332
static void event_c(const Event *, void *data)
Default handler for c key: center the trackball over the scene, or over the highlighted part of the s...
bool get_texture() const
Returns the current state of the texture flag.