Browse Source

abstract out PandaFramework etc.

David Rose 24 years ago
parent
commit
e286a73dbb

+ 9 - 27
panda/src/framework/Sources.pp

@@ -1,38 +1,20 @@
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
                    dtoolutil:c dtoolbase:c dtool:m
                    dtoolutil:c dtoolbase:c dtool:m
 
 
-#begin static_lib_target
+#begin lib_target
   #define TARGET framework
   #define TARGET framework
+  #define BUILDING_DLL BUILDING_FRAMEWORK
   #define LOCAL_LIBS \
   #define LOCAL_LIBS \
-    putil collide loader sgmanip chan text chancfg cull \
+    pgraph putil collide loader sgmanip chan text chancfg cull \
     pnmimage pnmimagetypes event
     pnmimage pnmimagetypes event
 
 
   #define SOURCES \
   #define SOURCES \
-    config_framework.cxx config_framework.h framework.cxx framework.h
+    config_framework.cxx config_framework.h \
+    pandaFramework.cxx pandaFramework.I pandaFramework.h \
+    windowFramework.cxx windowFramework.I windowFramework.h
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
-    framework.h
-
-#end static_lib_target
-
-
-#if $[HAVE_DX]
-  // USE_DX ensures DirectX dirs are on include&link lines
-  #define USE_DX yes
-  
-  #begin static_lib_target
-    #define TARGET framework_multimon
-    #define LOCAL_LIBS \
-      putil collide loader sgmanip chan text chancfg cull \
-      pnmimage pnmimagetypes event wdxdisplay
-
-    #define SOURCES \
-      config_framework.cxx config_framework.h framework_multimon.cxx framework.h
-
-    #define INSTALL_HEADERS \
-      framework.h
-
-  #end static_lib_target
-#endif
-
+    pandaFramework.I pandaFramework.h \
+    windowFramework.I windowFramework.h
 
 
+#end lib_target

+ 3 - 4
panda/src/framework/config_framework.cxx

@@ -26,7 +26,6 @@ NotifyCategoryDef(framework, "");
 ConfigureFn(config_framework) {
 ConfigureFn(config_framework) {
 }
 }
 
 
-// This is the height above the ground your eye should maintain while
-// driving using the "D" interface.
-const double drive_height = config_framework.GetDouble("drive-height", 6.0);
-const CollideMask drive_mask = config_framework.GetInt("drive-mask", ~0);
+const int win_width = config_framework.GetInt("win-width", 640);
+const int win_height = config_framework.GetInt("win-height", 480);
+const bool fullscreen = config_framework.GetBool("fullscreen", false);

+ 6 - 6
panda/src/framework/config_framework.h

@@ -19,14 +19,14 @@
 #ifndef CONFIG_FRAMEWORK_H
 #ifndef CONFIG_FRAMEWORK_H
 #define CONFIG_FRAMEWORK_H
 #define CONFIG_FRAMEWORK_H
 
 
-#include <pandabase.h>
-#include <notifyCategoryProxy.h>
-#include <collideMask.h>
+#include "pandabase.h"
+#include "notifyCategoryProxy.h"
 
 
-NotifyCategoryDecl(framework, EXPCL_EMPTY, EXPCL_EMPTY);
+NotifyCategoryDecl(framework, EXPCL_FRAMEWORK, EXPCL_FRAMEWORK);
 
 
 // Configure variables for framework package.
 // Configure variables for framework package.
-extern const double drive_height;
-extern const CollideMask drive_mask;
+extern const int win_width;
+extern const int win_height;
+extern const bool fullscreen;
 
 
 #endif
 #endif

+ 139 - 0
panda/src/framework/pandaFramework.I

@@ -0,0 +1,139 @@
+// Filename: pandaFramework.I
+// Created by:  drose (02Apr02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_graphics_engine
+//       Access: Public
+//  Description: Returns the GraphicsEngine that is used to render all
+//               the windows in the framework.  Normally there's no
+//               reason for user code to mess with this.
+////////////////////////////////////////////////////////////////////
+INLINE GraphicsEngine *PandaFramework::
+get_graphics_engine() {
+  return &_engine;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_data_root
+//       Access: Public
+//  Description: Returns the root of the data graph.  This is the
+//               graph of nodes that is traversed to control the
+//               inputs from user devices like the mouse and keyboard.
+////////////////////////////////////////////////////////////////////
+INLINE const qpNodePath &PandaFramework::
+get_data_root() const {
+  return _data_root;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_event_handler
+//       Access: Public
+//  Description: Returns the EventHandler object that serves events in
+//               the framework.  This is primarily used to dispatch on
+//               keypresses and such.
+////////////////////////////////////////////////////////////////////
+INLINE EventHandler &PandaFramework::
+get_event_handler() {
+  return _event_handler;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::set_window_title
+//       Access: Public
+//  Description: Specifies the title that is set for all subsequently
+//               created windows.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaFramework::
+set_window_title(const string &title) {
+  _window_title = title;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_num_windows
+//       Access: Public
+//  Description: Returns the number of windows that are currently
+//               open.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaFramework::
+get_num_windows() const {
+  return _windows.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_window
+//       Access: Public
+//  Description: Returns the nth window currently open.
+////////////////////////////////////////////////////////////////////
+INLINE WindowFramework *PandaFramework::
+get_window(int n) const {
+  nassertr(n >= 0 && n < (int)_windows.size(), NULL);
+  return _windows[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_wireframe
+//       Access: Public
+//  Description: Returns the current state of the wireframe flag.
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaFramework::
+get_wireframe() const {
+  return _wireframe_enabled;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_texture
+//       Access: Public
+//  Description: Returns the current state of the texture flag.
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaFramework::
+get_texture() const {
+  return _texture_enabled;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_two_sided
+//       Access: Public
+//  Description: Returns the current state of the two_sided flag.
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaFramework::
+get_two_sided() const {
+  return _two_sided_enabled;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_lighting
+//       Access: Public
+//  Description: Returns the current state of the lighting flag.
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaFramework::
+get_lighting() const {
+  return _lighting_enabled;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::set_exit_flag
+//       Access: Public
+//  Description: Sets the flag that indicates it is time for the
+//               application to exit.  The application will actually
+//               exit at the end of the current frame.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaFramework::
+set_exit_flag() {
+  _exit_flag = true;
+}

+ 530 - 0
panda/src/framework/pandaFramework.cxx

@@ -0,0 +1,530 @@
+// Filename: pandaFramework.cxx
+// Created by:  drose (02Apr02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandaFramework.h"
+#include "clockObject.h"
+#include "pStatClient.h"
+#include "eventQueue.h"
+#include "qpdataGraphTraverser.h"
+#include "interactiveGraphicsPipe.h"
+#include "config_framework.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PandaFramework::
+PandaFramework() :
+  _event_handler(EventQueue::get_global_event_queue())
+{
+  _is_open = false;
+  _made_default_pipe = false;
+  _data_root = qpNodePath("data");
+  _window_title = "Panda";
+  _start_time = 0.0;
+  _frame_count = 0;
+  _wireframe_enabled = false;
+  _texture_enabled = true;
+  _two_sided_enabled = false;
+  _lighting_enabled = false;
+  _default_keys_enabled = false;
+  _exit_flag = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PandaFramework::
+~PandaFramework() {
+  if (_is_open) {
+    close_framework();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::open_framework
+//       Access: Public
+//  Description: Should be called once at the beginning of the
+//               application to initialize Panda (and the framework)
+//               for use.  The command-line arguments should be passed
+//               in so Panda can remove any arguments that it
+//               recognizes as special control parameters.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+open_framework(int &argc, char **&argv) {
+  if (_is_open) {
+    return;
+  }
+
+  _is_open = true;
+
+  reset_frame_rate();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::close_framework
+//       Access: Public
+//  Description: Should be called at the end of an application to
+//               close Panda.  This is optional, as the destructor
+//               will do the same thing.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+close_framework() {
+  if (!_is_open) {
+    return;
+  }
+
+  close_all_windows();
+  // We should define this function on GraphicsEngine.
+  //  _engine.remove_all_windows();
+  _event_handler.remove_all_hooks();
+
+  _is_open = false;
+  _made_default_pipe = false;
+  _default_pipe.clear();
+
+  _start_time = 0.0;
+  _frame_count = 0;
+  _wireframe_enabled = false;
+  _two_sided_enabled = false;
+  _lighting_enabled = false;
+  _default_keys_enabled = false;
+  _exit_flag = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_default_pipe
+//       Access: Public
+//  Description: Returns the default pipe.  This is the GraphicsPipe
+//               that all windows in the framework will be created on,
+//               unless otherwise specified in open_window().  It is
+//               usually the primary graphics interface on the local
+//               machine.
+//
+//               If the default pipe has not yet been created, this
+//               creates it.
+//
+//               The return value is the default pipe, or NULL if no
+//               default pipe could be created.
+////////////////////////////////////////////////////////////////////
+GraphicsPipe *PandaFramework::
+get_default_pipe() {
+  nassertr(_is_open, NULL);
+  if (!_made_default_pipe) {
+    make_default_pipe();
+    _made_default_pipe = true;
+  }
+  return _default_pipe;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::get_default_window_props
+//       Access: Public, Virtual
+//  Description: Fills in the indicated window properties structure
+//               according to the normal window properties for this
+//               application.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+get_default_window_props(GraphicsWindow::Properties &props) {
+  props._xorg = 0;
+  props._yorg = 0;
+  props._xsize = win_width;
+  props._ysize = win_height;
+  props._fullscreen = fullscreen;
+  props._title = _window_title;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::open_window
+//       Access: Public
+//  Description: Opens a new window, using the default parameters.
+//               Returns the new WindowFramework if successful, or
+//               NULL if not.
+////////////////////////////////////////////////////////////////////
+WindowFramework *PandaFramework::
+open_window(GraphicsPipe *pipe) {
+  nassertr(_is_open, NULL);
+
+  GraphicsWindow::Properties props;
+  get_default_window_props(props);
+
+  return open_window(props, pipe);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::open_window
+//       Access: Public
+//  Description: Opens a new window using the indicated properties.
+//               (You may initialize the properties to their default
+//               values by calling get_default_window_props() first.)
+//
+//               Returns the new WindowFramework if successful, or
+//               NULL if not.
+////////////////////////////////////////////////////////////////////
+WindowFramework *PandaFramework::
+open_window(const GraphicsWindow::Properties &props, GraphicsPipe *pipe) {
+  if (pipe == (GraphicsPipe *)NULL) {
+    pipe = get_default_pipe();
+    if (pipe == (GraphicsPipe *)NULL) {
+      // Can't get a pipe.
+      return NULL;
+    }
+  }
+
+  nassertr(_is_open, NULL);
+  WindowFramework *wf = make_window_framework();
+  _windows.push_back(wf);
+
+  GraphicsWindow *win = wf->open_window(props, pipe);
+  if (win != (GraphicsWindow *)NULL) {
+    _engine.add_window(win);
+  }
+
+  return wf;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::close_window
+//       Access: Public
+//  Description: Closes the nth window and removes it from the list.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+close_window(int n) {
+  nassertv(n >= 0 && n < (int)_windows.size());
+  WindowFramework *wf = _windows[n];
+
+  GraphicsWindow *win = wf->get_graphics_window();
+  if (win != (GraphicsWindow *)NULL) {
+    _engine.remove_window(win);
+  }
+
+  wf->close_window();
+  delete wf;
+  _windows.erase(_windows.begin() + n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::close_all_windows
+//       Access: Public
+//  Description: Closes all currently open windows and empties the
+//               list of windows.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+close_all_windows() {
+  Windows::iterator wi;
+  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
+    WindowFramework *wf = (*wi);
+
+    GraphicsWindow *win = wf->get_graphics_window();
+    if (win != (GraphicsWindow *)NULL) {
+      _engine.remove_window(win);
+    }
+    
+    wf->close_window();
+    delete wf;
+  }
+
+  _windows.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::report_frame_rate
+//       Access: Public
+//  Description: Reports the currently measured average frame rate to
+//               the indicated ostream.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+report_frame_rate(ostream &out) const {
+  double now = ClockObject::get_global_clock()->get_frame_time();
+  double delta = now - _start_time;
+  
+  int frame_count = ClockObject::get_global_clock()->get_frame_count();
+  int num_frames = frame_count - _frame_count;
+  if (num_frames > 0) {
+    out << num_frames << " frames in " << delta << " seconds.\n";
+    double fps = ((double)num_frames) / delta;
+    out << fps << " fps average (" << 1000.0 / fps << "ms)\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::reset_frame_rate
+//       Access: Public
+//  Description: Resets the frame rate computation.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+reset_frame_rate() {
+  _start_time = ClockObject::get_global_clock()->get_frame_time();
+  _frame_count = ClockObject::get_global_clock()->get_frame_count();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::set_wireframe
+//       Access: Public
+//  Description: Sets the wireframe state on all windows.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+set_wireframe(bool enable) {
+  Windows::iterator wi;
+  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
+    WindowFramework *wf = (*wi);
+    wf->set_wireframe(enable);
+  }
+
+  _wireframe_enabled = enable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::set_texture
+//       Access: Public
+//  Description: Sets the texture state on all windows.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+set_texture(bool enable) {
+  Windows::iterator wi;
+  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
+    WindowFramework *wf = (*wi);
+    wf->set_texture(enable);
+  }
+
+  _texture_enabled = enable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::set_two_sided
+//       Access: Public
+//  Description: Sets the two_sided state on all windows.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+set_two_sided(bool enable) {
+  Windows::iterator wi;
+  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
+    WindowFramework *wf = (*wi);
+    wf->set_two_sided(enable);
+  }
+
+  _two_sided_enabled = enable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::set_lighting
+//       Access: Public
+//  Description: Sets the lighting state on all windows.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+set_lighting(bool enable) {
+  Windows::iterator wi;
+  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
+    WindowFramework *wf = (*wi);
+    wf->set_lighting(enable);
+  }
+
+  _lighting_enabled = enable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::enable_default_keys
+//       Access: Public
+//  Description: Sets callbacks on the event handler to handle all of
+//               the normal viewer keys, like t to toggle texture, ESC
+//               or q to quit, etc.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+enable_default_keys() {
+  if (!_default_keys_enabled) {
+    do_enable_default_keys();
+    _default_keys_enabled = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::do_frame
+//       Access: Public, Virtual
+//  Description: Renders one frame and performs all associated
+//               processing.  Returns true if we should continue
+//               rendering, false if we should exit.  This is normally
+//               called only from main_loop().
+////////////////////////////////////////////////////////////////////
+bool PandaFramework::
+do_frame() {
+  nassertr(_is_open, false);
+  qpDataGraphTraverser dg_trav;
+  dg_trav.traverse(_data_root.node());
+  _event_handler.process_events();
+  _engine.render_frame();
+
+  return !_exit_flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::main_loop
+//       Access: Public
+//  Description: Called to yield control to the panda framework.  This
+//               function does not return until set_exit_flag() has
+//               been called.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+main_loop() {
+  while (do_frame()) {
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::make_window_framework
+//       Access: Protected, Virtual
+//  Description: Creates a new WindowFramework object.  This is
+//               provided as a hook so derived PandaFramework classes
+//               can create custom WindowFramework objects.
+////////////////////////////////////////////////////////////////////
+WindowFramework *PandaFramework::
+make_window_framework() {
+  return new WindowFramework(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::make_default_pipe
+//       Access: Protected, Virtual
+//  Description: Creates the default GraphicsPipe that will contain
+//               all windows that are not opened on a specific pipe.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+make_default_pipe() {
+  // We use the GraphicsPipe factory to make us a renderable pipe
+  // without knowing exactly what kind of pipes we have available.  We
+  // don't care, so long as we can render to it interactively.
+
+  // This depends on the shared library or libraries (DLL's to you
+  // Windows folks) that have been loaded in at runtime from the
+  // load-display Configrc variable.
+  GraphicsPipe::resolve_modules();
+
+  nout << "Known pipe types:" << endl;
+  GraphicsPipe::get_factory().write_types(nout, 2);
+
+  _default_pipe = GraphicsPipe::get_factory().
+    make_instance(InteractiveGraphicsPipe::get_class_type());
+
+  if (_default_pipe == (GraphicsPipe*)NULL) {
+    nout << "No interactive pipe is available!  Check your Configrc!\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::do_enable_default_keys
+//       Access: Protected, Virtual
+//  Description: The implementation of enable_default_keys().
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+do_enable_default_keys() {
+  _event_handler.add_hook("escape", event_esc, this);
+  _event_handler.add_hook("q", event_esc, this);
+  _event_handler.add_hook("f", event_f, this);
+  _event_handler.add_hook("w", event_w, this);
+  _event_handler.add_hook("t", event_t, this);
+  _event_handler.add_hook("b", event_b, this);
+  _event_handler.add_hook("l", event_l, this);
+  _event_handler.add_hook("shift-s", event_S, this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::event_esc
+//       Access: Protected, Static
+//  Description: Default handler for ESC or q key: exit the
+//               application.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+event_esc(CPT_Event, void *data) {
+  PandaFramework *self = (PandaFramework *)data;
+  self->_exit_flag = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::event_f
+//       Access: Protected, Static
+//  Description: Default handler for f key: report and reset frame
+//               rate.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+event_f(CPT_Event, void *data) {
+  PandaFramework *self = (PandaFramework *)data;
+  self->report_frame_rate(nout);
+  self->reset_frame_rate();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::event_w
+//       Access: Protected, Static
+//  Description: Default handler for w key: toggle wireframe.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+event_w(CPT_Event, void *data) {
+  PandaFramework *self = (PandaFramework *)data;
+  self->set_wireframe(!self->get_wireframe());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::event_t
+//       Access: Protected, Static
+//  Description: Default handler for t key: toggle texture.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+event_t(CPT_Event, void *data) {
+  PandaFramework *self = (PandaFramework *)data;
+  self->set_texture(!self->get_texture());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::event_b
+//       Access: Protected, Static
+//  Description: Default handler for b key: toggle backface (two-sided
+//               rendering).
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+event_b(CPT_Event, void *data) {
+  PandaFramework *self = (PandaFramework *)data;
+  self->set_two_sided(!self->get_two_sided());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::event_l
+//       Access: Protected, Static
+//  Description: Default handler for l key: toggle lighting.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+event_l(CPT_Event, void *data) {
+  PandaFramework *self = (PandaFramework *)data;
+  self->set_lighting(!self->get_lighting());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::event_S
+//       Access: Protected, Static
+//  Description: Default handler for shift-S key: activate stats.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+event_S(CPT_Event, void *data) {
+#ifdef DO_PSTATS
+  nout << "Connecting to stats host" << endl;
+  PStatClient::connect();
+#else
+  nout << "Stats host not supported." << endl;
+#endif
+}

+ 131 - 0
panda/src/framework/pandaFramework.h

@@ -0,0 +1,131 @@
+// Filename: pandaFramework.h
+// Created by:  drose (02Apr02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PANDAFRAMEWORK_H
+#define PANDAFRAMEWORK_H
+
+#include "pandabase.h"
+
+#include "windowFramework.h"
+
+#include "qpnodePath.h"
+#include "eventHandler.h"
+#include "graphicsPipe.h"
+#include "graphicsEngine.h"
+#include "graphicsWindow.h"
+#include "pointerTo.h"
+
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : PandaFramework
+// Description : This class serves to provide a high-level framework
+//               for basic applications that use Panda in simple ways
+//               (like opening a window to view models, etc.).
+////////////////////////////////////////////////////////////////////
+class EXPCL_FRAMEWORK PandaFramework {
+public:
+  PandaFramework();
+  virtual ~PandaFramework();
+
+  void open_framework(int &argc, char **&argv);
+  void close_framework();
+
+  GraphicsPipe *get_default_pipe();
+  INLINE GraphicsEngine *get_graphics_engine();
+  INLINE const qpNodePath &get_data_root() const;
+  INLINE EventHandler &get_event_handler();
+
+  INLINE void set_window_title(const string &title);
+  virtual void get_default_window_props(GraphicsWindow::Properties &props);
+
+  WindowFramework *open_window(GraphicsPipe *pipe = NULL);
+  WindowFramework *open_window(const GraphicsWindow::Properties &props,
+                               GraphicsPipe *pipe = NULL);
+
+  INLINE int get_num_windows() const;
+  INLINE WindowFramework *get_window(int n) const;
+  void close_window(int n);
+  void close_all_windows();
+
+  void report_frame_rate(ostream &out) const;
+  void reset_frame_rate();
+
+  void set_wireframe(bool enable);
+  void set_texture(bool enable);
+  void set_two_sided(bool enable);
+  void set_lighting(bool enable);
+
+  INLINE bool get_wireframe() const;
+  INLINE bool get_texture() const;
+  INLINE bool get_two_sided() const;
+  INLINE bool get_lighting() const;
+
+  void enable_default_keys();
+
+  virtual bool do_frame();
+  void main_loop();
+
+  INLINE void set_exit_flag();
+
+protected:
+  virtual WindowFramework *make_window_framework();
+  virtual void make_default_pipe();
+  virtual void do_enable_default_keys();
+
+  static void event_esc(CPT_Event, void *data);
+  static void event_f(CPT_Event, void *data);
+  static void event_w(CPT_Event, void *data);
+  static void event_t(CPT_Event, void *data);
+  static void event_b(CPT_Event, void *data);
+  static void event_l(CPT_Event, void *data);
+  static void event_S(CPT_Event, void *data);
+
+
+private:
+  bool _is_open;
+  bool _made_default_pipe;
+
+  string _window_title;
+
+  PT(GraphicsPipe) _default_pipe;
+  GraphicsEngine _engine;
+
+  qpNodePath _data_root;
+  EventHandler _event_handler;
+
+  typedef pvector<WindowFramework *> Windows;
+  Windows _windows;
+
+  // For counting frame rate.
+  double _start_time;
+  int _frame_count;
+
+  bool _wireframe_enabled;
+  bool _texture_enabled;
+  bool _two_sided_enabled;
+  bool _lighting_enabled;
+
+  bool _default_keys_enabled;
+
+  bool _exit_flag;
+};
+
+#include "pandaFramework.I"
+
+#endif

+ 104 - 0
panda/src/framework/windowFramework.I

@@ -0,0 +1,104 @@
+// Filename: windowFramework.I
+// Created by:  drose (02Apr02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_panda_framework
+//       Access: Public
+//  Description: Returns a pointer to the associated PandaFramework
+//               object.
+////////////////////////////////////////////////////////////////////
+INLINE PandaFramework *WindowFramework::
+get_panda_framework() const {
+  return _panda_framework;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_graphics_window
+//       Access: Public
+//  Description: Returns a pointer to the underlying GraphicsWindow
+//               object.
+////////////////////////////////////////////////////////////////////
+INLINE GraphicsWindow *WindowFramework::
+get_graphics_window() const {
+  return _window;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_num_cameras
+//       Access: Public
+//  Description: Returns the number of 3-d cameras associated with the
+//               window.  A window usually has only one camera, but it
+//               may have multiple cameras if there are multiple
+//               display regions within the window.
+////////////////////////////////////////////////////////////////////
+INLINE int WindowFramework::
+get_num_cameras() const {
+  return _cameras.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_camera
+//       Access: Public
+//  Description: Returns the nth camera associated with the window.
+////////////////////////////////////////////////////////////////////
+INLINE qpCamera *WindowFramework::
+get_camera(int n) const {
+  nassertr(n >= 0 && n < (int)_cameras.size(), NULL);
+  return _cameras[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_wireframe
+//       Access: Public
+//  Description: Returns the current state of the wireframe flag.
+////////////////////////////////////////////////////////////////////
+INLINE bool WindowFramework::
+get_wireframe() const {
+  return _wireframe_enabled;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_texture
+//       Access: Public
+//  Description: Returns the current state of the texture flag.
+////////////////////////////////////////////////////////////////////
+INLINE bool WindowFramework::
+get_texture() const {
+  return _texture_enabled;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_two_sided
+//       Access: Public
+//  Description: Returns the current state of the two_sided flag.
+////////////////////////////////////////////////////////////////////
+INLINE bool WindowFramework::
+get_two_sided() const {
+  return _two_sided_enabled;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_lighting
+//       Access: Public
+//  Description: Returns the current state of the lighting flag.
+////////////////////////////////////////////////////////////////////
+INLINE bool WindowFramework::
+get_lighting() const {
+  return _lighting_enabled;
+}

+ 520 - 0
panda/src/framework/windowFramework.cxx

@@ -0,0 +1,520 @@
+// Filename: windowFramework.cxx
+// Created by:  drose (02Apr02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "windowFramework.h"
+#include "pandaFramework.h"
+#include "mouseAndKeyboard.h"
+#include "qpbuttonThrower.h"
+#include "qptrackball.h"
+#include "qptransform2sg.h"
+#include "dSearchPath.h"
+#include "filename.h"
+#include "loader.h"
+#include "keyboardButton.h"
+#include "geomTri.h"
+#include "texturePool.h"
+#include "textureAttrib.h"
+#include "perspectiveLens.h"
+#include "auto_bind.h"
+#include "ambientLight.h"
+#include "directionalLight.h"
+#include "lightAttrib.h"
+
+// This number is chosen arbitrarily to override any settings in model
+// files.
+static const int override_priority = 100;
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::Constructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+WindowFramework::
+WindowFramework(PandaFramework *panda_framework) :
+  _panda_framework(panda_framework)
+{
+  _alight = (AmbientLight *)NULL;
+  _dlight = (DirectionalLight *)NULL;
+  _got_keyboard = false;
+  _got_trackball = false;
+  _got_lights = false;
+  _wireframe_enabled = false;
+  _texture_enabled = true;
+  _two_sided_enabled = false;
+  _lighting_enabled = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::Destructor
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+WindowFramework::
+~WindowFramework() {
+  close_window();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::open_window
+//       Access: Protected
+//  Description: Opens the actual window.  This is normally called
+//               only from PandaFramework::open_window().
+////////////////////////////////////////////////////////////////////
+GraphicsWindow *WindowFramework::
+open_window(const GraphicsWindow::Properties &props, GraphicsPipe *pipe) {
+  nassertr(_window == (GraphicsWindow *)NULL, _window);
+
+  _window = pipe->make_window(props);
+
+  // Set up a 3-d camera for the window by default.
+  make_camera();
+  return _window;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::close_window
+//       Access: Protected
+//  Description: Closes the window.  This is normally called
+//               from PandaFramework::close_window().
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+close_window() {
+  _window.clear();
+  _camera_group.remove_node();
+  _render.remove_node();
+  _render_2d.remove_node();
+  _mouse.remove_node();
+
+  _alight = (AmbientLight *)NULL;
+  _dlight = (DirectionalLight *)NULL;
+  _got_keyboard = false;
+  _got_trackball = false;
+  _got_lights = false;
+
+  _wireframe_enabled = false;
+  _texture_enabled = true;
+  _two_sided_enabled = false;
+  _lighting_enabled = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_camera_group
+//       Access: Public
+//  Description: Returns the node above the collection of 3-d cameras
+//               in the scene graph.  This node may be moved around to
+//               represent the viewpoint.
+////////////////////////////////////////////////////////////////////
+const qpNodePath &WindowFramework::
+get_camera_group() {
+  if (_camera_group.is_empty()) {
+    _camera_group = get_render().attach_new_node("camera_group");
+  }
+  return _camera_group;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_render
+//       Access: Public
+//  Description: Returns the root of the 3-d scene graph.
+////////////////////////////////////////////////////////////////////
+const qpNodePath &WindowFramework::
+get_render() {
+  if (_render.is_empty()) {
+    _render = qpNodePath("render");
+
+    // This is maybe here temporarily, and maybe not.
+    _render.set_two_sided(0);
+  }
+  return _render;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_render_2d
+//       Access: Public
+//  Description: Returns the root of the 2-d scene graph.
+////////////////////////////////////////////////////////////////////
+const qpNodePath &WindowFramework::
+get_render_2d() {
+  if (_render_2d.is_empty()) {
+    _render_2d = qpNodePath("render_2d");
+  }
+  return _render_2d;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_mouse
+//       Access: Public
+//  Description: Returns the node in the data graph corresponding to
+//               the mouse associated with this window.
+////////////////////////////////////////////////////////////////////
+const qpNodePath &WindowFramework::
+get_mouse() {
+  if (_mouse.is_empty()) {
+    qpNodePath data_root = _panda_framework->get_data_root();
+    qpMouseAndKeyboard *mouse_node = 
+      new qpMouseAndKeyboard(_window, 0, "mouse");
+    _mouse = data_root.attach_new_node(mouse_node);
+  }
+  return _mouse;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::enable_keyboard
+//       Access: Public
+//  Description: Creates a ButtonThrower to listen to button presses
+//               and throw them as events.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+enable_keyboard() {
+  if (_got_keyboard) {
+    return;
+  }
+
+  qpNodePath mouse = get_mouse();
+
+  PT(qpButtonThrower) bt = new qpButtonThrower("kb-events");
+  ModifierButtons mods;
+  mods.add_button(KeyboardButton::shift());
+  mods.add_button(KeyboardButton::control());
+  mods.add_button(KeyboardButton::alt());
+  bt->set_modifier_buttons(mods);
+  mouse.attach_new_node(bt);
+
+  _got_keyboard = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::setup_trackball
+//       Access: Public
+//  Description: Sets up the mouse to trackball around the camera.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+setup_trackball() {
+  if (_got_trackball) {
+    return;
+  }
+
+  qpNodePath mouse = get_mouse();
+  qpNodePath camera = get_camera_group();
+
+  PT(qpTrackball) trackball = new qpTrackball("trackball");
+  trackball->set_pos(LVector3f::forward() * 50.0);
+  mouse.attach_new_node(trackball);
+
+  PT(qpTransform2SG) tball2cam = new qpTransform2SG("tball2cam");
+  tball2cam->set_node(camera.node());
+  trackball->add_child(tball2cam);
+
+  _got_trackball = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::load_models
+//       Access: Public
+//  Description: Loads up all the model files listed in the indicated
+//               argument list.  If first_arg is supplied, it is the
+//               first argument in the list to consider.
+//
+//               Returns true if all models loaded successfully, or
+//               false if at least one of them had an error.
+////////////////////////////////////////////////////////////////////
+bool WindowFramework::
+load_models(const qpNodePath &parent, int argc, char *argv[], int first_arg) {
+  pvector<Filename> files;
+
+  for (int i = first_arg; i < argc && argv[i] != (char *)NULL; i++) {
+    files.push_back(Filename::from_os_specific(argv[i]));
+  }
+
+  return load_models(parent, files);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::load_models
+//       Access: Public
+//  Description: Loads up all the model files listed in the indicated
+//               argument list.
+//
+//               Returns true if all models loaded successfully, or
+//               false if at least one of them had an error.
+////////////////////////////////////////////////////////////////////
+bool WindowFramework::
+load_models(const qpNodePath &parent, const pvector<Filename> &files) {
+  bool all_ok = true;
+
+  qpNodePath render = get_render();
+
+  pvector<Filename>::const_iterator fi;
+  for (fi = files.begin(); fi != files.end(); ++fi) {
+    const Filename &filename = (*fi);
+    qpNodePath model = load_model(parent, filename);
+    if (model.is_empty()) {
+      all_ok = false;
+    }
+  }
+
+  return all_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::load_model
+//       Access: Public
+//  Description: Loads up the indicated model and returns the new
+//               NodePath, or the empty NodePath if the model could
+//               not be loaded.
+////////////////////////////////////////////////////////////////////
+qpNodePath WindowFramework::
+load_model(const qpNodePath &parent, Filename filename) {
+  nout << "Loading " << filename << "\n";
+  
+  // First, we always try to resolve a filename from the current
+  // directory.  This means a local filename will always be found
+  // before the model path is searched.
+  DSearchPath local_path(".");
+  filename.resolve_filename(local_path);
+  
+  Loader loader;
+  PT(PandaNode) node = loader.qpload_sync(filename);
+  if (node == (PandaNode *)NULL) {
+    nout << "Unable to load " << filename << "\n";
+    return qpNodePath::not_found();
+  }    
+
+  return parent.attach_new_node(node);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::load_default_model
+//       Access: Public
+//  Description: Loads our favorite blue triangle.  This is intended
+//               to provide some default geometry to have *something*
+//               to look at for testing, when no other models are
+//               provided.
+////////////////////////////////////////////////////////////////////
+qpNodePath WindowFramework::
+load_default_model(const qpNodePath &parent) {
+  PTA_Vertexf coords;
+  PTA_TexCoordf uvs;
+  PTA_Normalf norms;
+  PTA_Colorf colors;
+  PTA_ushort cindex;
+  
+  coords.push_back(Vertexf::rfu(0.0, 0.0, 0.0));
+  coords.push_back(Vertexf::rfu(1.0, 0.0, 0.0));
+  coords.push_back(Vertexf::rfu(0.0, 0.0, 1.0));
+  uvs.push_back(TexCoordf(0.0, 0.0));
+  uvs.push_back(TexCoordf(1.0, 0.0));
+  uvs.push_back(TexCoordf(0.0, 1.0));
+  norms.push_back(Normalf::back());
+  colors.push_back(Colorf(0.5, 0.5, 1.0, 1.0));
+  cindex.push_back(0);
+  cindex.push_back(0);
+  cindex.push_back(0);
+  
+  PT(GeomTri) geom = new GeomTri;
+  geom->set_num_prims(1);
+  geom->set_coords(coords);
+  geom->set_texcoords(uvs, G_PER_VERTEX);
+  geom->set_normals(norms, G_PER_PRIM);
+  geom->set_colors(colors, G_PER_VERTEX, cindex);
+
+  CPT(RenderState) state = RenderState::make_empty();
+  Texture *tex = TexturePool::load_texture("rock-floor.rgb");
+  if (tex != (Texture *)NULL) {
+    tex->set_minfilter(Texture::FT_linear);
+    tex->set_magfilter(Texture::FT_linear);
+    state = state->add_attrib(TextureAttrib::make(tex));
+  }
+  
+  qpGeomNode *geomnode = new qpGeomNode("tri");
+  geomnode->add_geom(geom, state);
+
+  return parent.attach_new_node(geomnode);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::loop_animations
+//       Access: Public
+//  Description: Looks for characters and their matching animation
+//               files in the scene graph; binds and loops any
+//               matching animations found.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+loop_animations() {
+  // If we happened to load up both a character file and its matching
+  // animation file, attempt to bind them together now and start the
+  // animations looping.
+  auto_bind(get_render().node(), _anim_controls, ~0);
+  _anim_controls.loop_all(true);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::set_wireframe
+//       Access: Public
+//  Description: Forces wireframe state (true) or restores default
+//               rendering (false).
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+set_wireframe(bool enable) {
+  if (enable == _wireframe_enabled) {
+    return;
+  }
+
+  qpNodePath render = get_render();
+
+  if (enable) {
+    render.set_render_mode_wireframe(override_priority);
+    render.set_two_sided(true, override_priority);
+  } else {
+    render.clear_render_mode();
+    if (!_two_sided_enabled) {
+      render.clear_two_sided();
+    }
+  }
+
+  _wireframe_enabled = enable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::set_texture
+//       Access: Public
+//  Description: Forces textures off (false) or restores default
+//               rendering (true).
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+set_texture(bool enable) {
+  if (enable == _texture_enabled) {
+    return;
+  }
+
+  qpNodePath render = get_render();
+
+  if (!enable) {
+    render.set_texture_off(override_priority);
+  } else {
+    render.clear_texture();
+  }
+
+  _texture_enabled = enable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::set_two_sided
+//       Access: Public
+//  Description: Forces two-sided rendering (true) or restores default
+//               rendering (false).
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+set_two_sided(bool enable) {
+  if (enable == _two_sided_enabled) {
+    return;
+  }
+
+  qpNodePath render = get_render();
+
+  if (enable) {
+    render.set_two_sided(true, override_priority);
+  } else {
+    if (!_wireframe_enabled) {
+      render.clear_two_sided();
+    }
+  }
+
+  _two_sided_enabled = enable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::set_lighting
+//       Access: Public
+//  Description: Turns lighting on (true) or off (false).
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+set_lighting(bool enable) {
+  if (enable == _lighting_enabled) {
+    return;
+  }
+
+  qpNodePath render = get_render();
+
+  if (enable) {
+    if (!_got_lights) {
+      setup_lights();
+    }
+    render.node()->set_attrib(LightAttrib::make(LightAttrib::O_add, 
+                                                _alight, _dlight));
+  } else {
+    render.node()->clear_attrib(LightAttrib::get_class_type());
+  }
+
+  _lighting_enabled = enable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::make_camera
+//       Access: Protected
+//  Description: Makes a new 3-d camera for the window.
+////////////////////////////////////////////////////////////////////
+PT(qpCamera) WindowFramework::
+make_camera() {
+  // Get the first channel on the window.  This will be the only
+  // channel on non-SGI hardware.
+  PT(GraphicsChannel) channel = _window->get_channel(0);
+
+  // Make a layer on the channel to hold our display region.
+  PT(GraphicsLayer) layer = channel->make_layer();
+
+  // And create a display region that covers the entire window.
+  PT(DisplayRegion) dr = layer->make_display_region();
+
+  // Finally, we need a camera to associate with the display region.
+  PT(qpCamera) camera = new qpCamera("camera");
+  qpNodePath camera_np = get_camera_group().attach_new_node(camera);
+  _cameras.push_back(camera);
+
+  PT(Lens) lens = new PerspectiveLens;
+  lens->set_film_size(_window->get_width(), _window->get_height());
+  camera->set_lens(lens);
+  camera->set_scene(get_render());
+  dr->set_qpcamera(camera_np);
+
+  return camera;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::setup_lights
+//       Access: Protected
+//  Description: Makes light nodes and attaches them to the camera for
+//               viewing the scene.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+setup_lights() {
+  if (_got_lights) {
+    return;
+  }
+
+  qpNodePath camera_group = get_camera_group();
+  qpNodePath light_group = camera_group.attach_new_node("lights");
+
+  _alight = new AmbientLight("ambient");
+  _alight->set_color(Colorf(0.2f, 0.2f, 0.2f, 1.0f));
+  _dlight = new DirectionalLight("directional");
+  light_group.attach_new_node(_alight);
+  light_group.attach_new_node(_dlight);
+  
+  _got_lights = true;
+}

+ 117 - 0
panda/src/framework/windowFramework.h

@@ -0,0 +1,117 @@
+// Filename: windowFramework.h
+// Created by:  drose (02Apr02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WINDOWFRAMEWORK_H
+#define WINDOWFRAMEWORK_H
+
+#include "pandabase.h"
+#include "qpnodePath.h"
+#include "qpcamera.h"
+#include "graphicsWindow.h"
+#include "animControlCollection.h"
+#include "filename.h"
+#include "pointerTo.h"
+#include "pvector.h"
+
+class PandaFramework;
+class AmbientLight;
+class DirectionalLight;
+
+////////////////////////////////////////////////////////////////////
+//       Class : WindowFramework
+// Description : This encapsulates the data that is normally
+//               associated with a single window that we've opened.
+////////////////////////////////////////////////////////////////////
+class EXPCL_FRAMEWORK WindowFramework {
+protected:
+  WindowFramework(PandaFramework *panda_framework);
+  virtual ~WindowFramework();
+
+  GraphicsWindow *open_window(const GraphicsWindow::Properties &props,
+                              GraphicsPipe *pipe);
+  void close_window();
+
+public:
+  INLINE PandaFramework *get_panda_framework() const;
+  INLINE GraphicsWindow *get_graphics_window() const;
+  const qpNodePath &get_camera_group();
+
+  INLINE int get_num_cameras() const;
+  INLINE qpCamera *get_camera(int n) const;
+
+  const qpNodePath &get_render();
+  const qpNodePath &get_render_2d();
+  const qpNodePath &get_mouse();
+
+  void enable_keyboard();
+  void setup_trackball();
+
+  bool load_models(const qpNodePath &parent,
+                   int argc, char *argv[], int first_arg = 1);
+  bool load_models(const qpNodePath &parent,
+                   const pvector<Filename> &files);
+  qpNodePath load_model(const qpNodePath &parent, Filename filename);
+  qpNodePath load_default_model(const qpNodePath &parent);
+  void loop_animations();
+
+  void set_wireframe(bool enable);
+  void set_texture(bool enable);
+  void set_two_sided(bool enable);
+  void set_lighting(bool enable);
+
+  INLINE bool get_wireframe() const;
+  INLINE bool get_texture() const;
+  INLINE bool get_two_sided() const;
+  INLINE bool get_lighting() const;
+
+protected:
+  PT(qpCamera) make_camera();
+  void setup_lights();
+
+private:
+  PandaFramework *_panda_framework;
+  PT(GraphicsWindow) _window;
+
+  qpNodePath _camera_group;
+  typedef pvector< PT(qpCamera) > Cameras;
+  Cameras _cameras;
+
+  qpNodePath _render;
+  qpNodePath _render_2d;
+  AnimControlCollection _anim_controls;
+
+  qpNodePath _mouse;
+
+  AmbientLight *_alight;
+  DirectionalLight *_dlight;
+  
+  bool _got_keyboard;
+  bool _got_trackball;
+  bool _got_lights;
+
+  bool _wireframe_enabled;
+  bool _texture_enabled;
+  bool _two_sided_enabled;
+  bool _lighting_enabled;
+
+  friend class PandaFramework;
+};
+
+#include "windowFramework.I"
+
+#endif

+ 48 - 35
panda/src/pandabase/pandasymbols.h

@@ -21,8 +21,37 @@
 
 
 /* See dtoolsymbols.h for a rant on the purpose of this file.  */
 /* See dtoolsymbols.h for a rant on the purpose of this file.  */
 
 
+/* Note that the symbols declared in this file appear in alphabetical
+   order.  Also note that we must use C-style comments only here, not
+   C++-style comments, since this file is occasionally included by a C
+   file. */
+
 #if defined(WIN32_VC) && !defined(CPPPARSER) && !defined(LINK_ALL_STATIC)
 #if defined(WIN32_VC) && !defined(CPPPARSER) && !defined(LINK_ALL_STATIC)
 
 
+#ifdef BUILDING_FRAMEWORK
+  #define EXPCL_FRAMEWORK __declspec(dllexport)
+  #define EXPTP_FRAMEWORK
+#else
+  #define EXPCL_FRAMEWORK __declspec(dllimport)
+  #define EXPTP_FRAMEWORK extern
+#endif
+
+#ifdef BUILDING_LINUX_AUDIO
+  #define EXPCL_LINUX_AUDIO __declspec(dllexport)
+  #define EXPTP_LINUX_AUDIO
+#else
+  #define EXPCL_LINUX_AUDIO __declspec(dllimport)
+  #define EXPTP_LINUX_AUDIO extern
+#endif
+
+#ifdef BUILDING_MILES_AUDIO
+  #define EXPCL_MILES_AUDIO __declspec(dllexport)
+  #define EXPTP_MILES_AUDIO
+#else
+  #define EXPCL_MILES_AUDIO __declspec(dllimport)
+  #define EXPTP_MILES_AUDIO extern
+#endif
+
 #ifdef BUILDING_PANDA
 #ifdef BUILDING_PANDA
   #define EXPCL_PANDA __declspec(dllexport)
   #define EXPCL_PANDA __declspec(dllexport)
   #define EXPTP_PANDA
   #define EXPTP_PANDA
@@ -31,6 +60,14 @@
   #define EXPTP_PANDA extern
   #define EXPTP_PANDA extern
 #endif
 #endif
 
 
+#ifdef BUILDING_PANDACR
+  #define EXPCL_PANDACR __declspec(dllexport)
+  #define EXPTP_PANDACR
+#else
+  #define EXPCL_PANDACR __declspec(dllimport)
+  #define EXPTP_PANDACR extern
+#endif
+
 #ifdef BUILDING_PANDADX
 #ifdef BUILDING_PANDADX
   #define EXPCL_PANDADX __declspec(dllexport)
   #define EXPCL_PANDADX __declspec(dllexport)
   #define EXPTP_PANDADX
   #define EXPTP_PANDADX
@@ -71,14 +108,6 @@
   #define EXPTP_PANDAGL extern
   #define EXPTP_PANDAGL extern
 #endif
 #endif
 
 
-#ifdef BUILDING_PANDACR
-  #define EXPCL_PANDACR __declspec(dllexport)
-  #define EXPTP_PANDACR
-#else
-  #define EXPCL_PANDACR __declspec(dllimport)
-  #define EXPTP_PANDACR extern
-#endif
-
 #ifdef BUILDING_PANDAGLUT
 #ifdef BUILDING_PANDAGLUT
   #define EXPCL_PANDAGLUT __declspec(dllexport)
   #define EXPCL_PANDAGLUT __declspec(dllexport)
   #define EXPTP_PANDAGLUT
   #define EXPTP_PANDAGLUT
@@ -111,27 +140,23 @@
   #define EXPTP_SHADER extern
   #define EXPTP_SHADER extern
 #endif
 #endif
 
 
-#ifdef BUILDING_MILES_AUDIO
-  #define EXPCL_MILES_AUDIO __declspec(dllexport)
-  #define EXPTP_MILES_AUDIO
-#else
-  #define EXPCL_MILES_AUDIO __declspec(dllimport)
-  #define EXPTP_MILES_AUDIO extern
-#endif
+#else   /* !WIN32_VC */
 
 
-#ifdef BUILDING_LINUX_AUDIO
-  #define EXPCL_LINUX_AUDIO __declspec(dllexport)
-  #define EXPTP_LINUX_AUDIO
-#else
-  #define EXPCL_LINUX_AUDIO __declspec(dllimport)
-  #define EXPTP_LINUX_AUDIO extern
-#endif
+#define EXPCL_FRAMEWORK
+#define EXPTP_FRAMEWORK
 
 
-#else   /* !WIN32_VC */
+#define EXPCL_LINUX_AUDIO
+#define EXPTP_LINUX_AUDIO
+
+#define EXPCL_MILES_AUDIO
+#define EXPTP_MILES_AUDIO
 
 
 #define EXPCL_PANDA
 #define EXPCL_PANDA
 #define EXPTP_PANDA
 #define EXPTP_PANDA
 
 
+#define EXPCL_PANDACR
+#define EXPTP_PANDACR
+
 #define EXPCL_PANDADX
 #define EXPCL_PANDADX
 #define EXPTP_PANDADX
 #define EXPTP_PANDADX
 
 
@@ -147,9 +172,6 @@
 #define EXPCL_PANDAGL
 #define EXPCL_PANDAGL
 #define EXPTP_PANDAGL
 #define EXPTP_PANDAGL
 
 
-#define EXPCL_PANDACR
-#define EXPTP_PANDACR
-
 #define EXPCL_PANDAGLUT
 #define EXPCL_PANDAGLUT
 #define EXPTP_PANDAGLUT
 #define EXPTP_PANDAGLUT
 
 
@@ -162,15 +184,6 @@
 #define EXPCL_SHADER
 #define EXPCL_SHADER
 #define EXPTP_SHADER
 #define EXPTP_SHADER
 
 
-#define EXPCL_MILES_AUDIO
-#define EXPTP_MILES_AUDIO
-
-#define EXPCL_LINUX_AUDIO
-#define EXPTP_LINUX_AUDIO
-
-#define EXPCL_FRAMEWORK
-#define EXPTP_FRAMEWORK
-
 #endif  /* WIN32_VC */
 #endif  /* WIN32_VC */
 
 
 #if defined(WIN32_VC) && !defined(CPPPARSER)
 #if defined(WIN32_VC) && !defined(CPPPARSER)

+ 3 - 142
panda/src/testbed/Sources.pp

@@ -10,21 +10,12 @@
 #if $[LINK_ALL_STATIC]
 #if $[LINK_ALL_STATIC]
   // If we're statically linking, we need to explicitly link with
   // If we're statically linking, we need to explicitly link with
   // at least one graphics renderer.
   // at least one graphics renderer.
-  #define LOCAL_LIBS pandacr pandagl pandadx $[LOCAL_LIBS]
+  #define LOCAL_LIBS pandagl pandadx $[LOCAL_LIBS]
 
 
   // And we might like to have the egg loader available.
   // And we might like to have the egg loader available.
   #define LOCAL_LIBS pandaegg $[LOCAL_LIBS]
   #define LOCAL_LIBS pandaegg $[LOCAL_LIBS]
 #endif
 #endif
 
 
-#define UNIX_SYS_LIBS m
-
-#begin bin_target
-  #define TARGET demo
-
-  #define SOURCES \
-    demo.cxx
-
-#end bin_target
 
 
 #begin bin_target
 #begin bin_target
   #define TARGET pview
   #define TARGET pview
@@ -34,138 +25,8 @@
 #end bin_target
 #end bin_target
 
 
 #begin test_bin_target
 #begin test_bin_target
-  #define TARGET open_window
-
-  #define SOURCES \
-    open_window.cxx
-
-  #define LOCAL_LIBS $[LOCAL_LIBS] pandacr pandagl pandadx
-#end test_bin_target
-
-#if $[HAVE_DX]
-  #begin test_bin_target
-    #define TARGET demo_multimon
-
-    #define SOURCES \
-      demo.cxx
-
-    #define LOCAL_LIBS $[LOCAL_LIBS] pandadx framework_multimon
-  #end test_bin_target
-#endif
-
-#begin test_bin_target
-  #define TARGET downloader
-
-  #define SOURCES \
-    downloader_test.cxx
-
-  #define USE_ZLIB yes
-  #define TARGET_IF_ZLIB yes
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET herc
-
-  #define SOURCES \
-    herc.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET loader
-
-  #define SOURCES \
-    loader_test.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET lod
-
-  #define SOURCES \
-    lod_test.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET min_herc
-
-  #define SOURCES \
-    min_herc.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET min_shader
-
-  #define SOURCES \
-    min_shader.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET panda
-
-  #define SOURCES \
-    panda.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET shader
-
-  #define SOURCES \
-    shader_test.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET test_eggWrite
-
-  #define SOURCES \
-    test_eggWrite.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET test_particles
-
-  #define SOURCES \
-    test_particles.cxx
-  #define LOCAL_LIBS $[LOCAL_LIBS] physics particlesystem
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET test_sprite_particles
-
-  #define SOURCES \
-    test_sprite_particles.cxx
-  #define LOCAL_LIBS $[LOCAL_LIBS] physics particlesystem
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET test_recparticles
-
-  #define SOURCES \
-    test_recparticles.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET text
-
-  #define SOURCES \
-    text_test.cxx
-
-#end test_bin_target
-
-#begin test_bin_target
-  #define TARGET vrpn_demo
+  #define TARGET pgrid
 
 
   #define SOURCES \
   #define SOURCES \
-    vrpn_demo.cxx
-
+    pgrid.cxx
 #end test_bin_target
 #end test_bin_target

+ 429 - 0
panda/src/testbed/pgrid.cxx

@@ -0,0 +1,429 @@
+// Filename: pgrid.cxx
+// Created by:  drose (03Apr02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandaFramework.h"
+#include "pandaNode.h"
+#include "transformState.h"
+#include "clockObject.h"
+#include "string_utils.h"
+#include "pvector.h"
+
+#include <getopt.h>
+
+
+#define RANDFRAC (rand()/(float)(RAND_MAX))
+
+class GriddedFilename {
+public:
+  Filename _filename;
+  int _count;
+  qpNodePath _model;
+};
+typedef pvector<GriddedFilename> GriddedFilenames;
+
+typedef struct {
+  // for rot moving
+  float xcenter,ycenter;
+  float xoffset,yoffset;
+  float ang1,ang1_vel;
+  float ang2,ang2_vel;
+  
+  float radius;
+  
+  // for moving
+  float xstart,ystart;
+  float xend,yend;
+  float xdel,ydel,timedel;
+  double starttime,endtime;
+  double vel;
+  LMatrix4f rotmat;
+
+  PandaNode *node;
+} gridded_file_info;
+
+typedef pvector<gridded_file_info> GriddedInfoArray;
+
+typedef enum {None,Rotation,LinearMotion} GriddedMotionType;
+
+#define GRIDCELLSIZE 5.0
+static int gridwidth;  // cells/side
+
+#define MIN_WANDERAREA_DIMENSION 120.0
+
+static float grid_pos_offset;  // origin of grid
+static float wander_area_pos_offset;
+
+static GriddedMotionType gridmotiontype = None;
+
+
+// making these fns to get around ridiculous VC++ matrix inlining bugs at Opt2
+static void
+move_gridded_stuff(GriddedMotionType gridmotiontype,
+                   gridded_file_info *InfoArr, int size) {
+  double now = ClockObject::get_global_clock()->get_frame_time();
+
+  LMatrix4f tmat1,tmat2,xfm_mat;
+
+  for(int i = 0; i < size; i++) {
+    double time_delta = (now-InfoArr[i].starttime);
+#define DO_FP_MODULUS(VAL,MAXVAL)  \
+    {if(VAL > MAXVAL) {int idivresult = (int)(VAL / (float)MAXVAL);  VAL=VAL-idivresult*MAXVAL;} else  \
+    if(VAL < -MAXVAL) {int idivresult = (int)(VAL / (float)MAXVAL);  VAL=VAL+idivresult*MAXVAL;}}
+  
+    // probably should use panda lerps for this stuff, but I dont understand how
+
+    if(gridmotiontype==Rotation) {
+
+      InfoArr[i].ang1=time_delta*InfoArr[i].ang1_vel;
+      DO_FP_MODULUS(InfoArr[i].ang1,360.0);
+      InfoArr[i].ang2=time_delta*InfoArr[i].ang2_vel;
+      DO_FP_MODULUS(InfoArr[i].ang2,360.0);
+
+      // xforms happen left to right
+      LVector2f new_center = LVector2f(InfoArr[i].radius,0.0) *
+        LMatrix3f::rotate_mat(InfoArr[i].ang1);
+
+      LVector3f translate_vec(InfoArr[i].xcenter+new_center._v.v._0,
+                              InfoArr[i].ycenter+new_center._v.v._1,
+                              0.0);
+
+      const LVector3f rotation_axis(0.0, 0.0, 1.0);
+
+      tmat1 = LMatrix4f::rotate_mat_normaxis(InfoArr[i].ang2,rotation_axis);
+      tmat2 = LMatrix4f::translate_mat(translate_vec);
+      xfm_mat = tmat1 * tmat2;
+    } else {
+
+      float xpos,ypos;
+
+      if(now>InfoArr[i].endtime) {
+        InfoArr[i].starttime = now;
+
+        xpos = InfoArr[i].xstart = InfoArr[i].xend;
+        ypos = InfoArr[i].ystart = InfoArr[i].yend;
+
+        InfoArr[i].xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
+        InfoArr[i].yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
+
+        float xdel = InfoArr[i].xdel = InfoArr[i].xend-InfoArr[i].xstart;
+        float ydel = InfoArr[i].ydel = InfoArr[i].yend-InfoArr[i].ystart;
+
+        InfoArr[i].endtime = now + csqrt(xdel*xdel+ydel*ydel)/InfoArr[i].vel;
+        InfoArr[i].timedel = InfoArr[i].endtime - InfoArr[i].starttime;
+
+        const LVector3f rotate_axis(0.0, 0.0, 1.0);
+
+        float ang = rad_2_deg(atan2(-xdel,ydel));
+
+        InfoArr[i].rotmat= LMatrix4f::rotate_mat_normaxis(ang,rotate_axis);
+      } else {
+        float timefrac= time_delta/InfoArr[i].timedel;
+
+        xpos = InfoArr[i].xdel*timefrac+InfoArr[i].xstart;
+        ypos = InfoArr[i].ydel*timefrac+InfoArr[i].ystart;
+      }
+
+      LVector3f translate_vec(xpos, ypos, 0.0);
+      LMatrix4f tmat2 = LMatrix4f::translate_mat(translate_vec);
+
+      xfm_mat = InfoArr[i].rotmat * tmat2;
+    }
+    InfoArr[i].node->set_transform(TransformState::make_mat(xfm_mat));
+  }
+}
+
+bool
+get_command_line_opts(int &argc, char **&argv) {
+  // Use getopt() to decode the optional command-line parameters.
+  //  extern char *optarg;
+  extern int optind;
+  const char *options = "rm";
+  int flag = getopt(argc, argv, options);
+  while (flag != EOF) {
+    switch (flag) {
+    case 'r':
+      gridmotiontype = Rotation;
+      break;
+
+    case 'm':
+      gridmotiontype = LinearMotion;
+      break;
+
+    case '?':
+      nout << "Invalid parameter.\n";
+      return false;
+    }
+
+    flag = getopt(argc, argv, options);
+  }
+
+  argv += (optind - 1);
+  argc -= (optind - 1);
+
+  return true;
+}
+
+void
+get_command_line_filenames(int argc, char *argv[],
+                           pvector<Filename> &static_filenames,
+                           GriddedFilenames &gridded_filenames) {
+  for (int i = 1; i < argc && argv[i] != (char *)NULL; i++) {
+    const string &arg = argv[i];
+    size_t comma = arg.find(',');
+    if (comma == string::npos) {
+      // No comma in the filename, so it must be an ordinary static file.
+      static_filenames.push_back(Filename::from_os_specific(arg));
+
+    } else {
+      // A comma in the filename indicates a gridded file.  The syntax
+      // is filename,count where count represents the number of times
+      // the file is repeated.
+      string name = arg.substr(0, comma);
+      string count_str = arg.substr(comma + 1);
+      int count;
+      if (!string_to_int(count_str, count)) {
+        nout << "Ignoring invalid number: " << count_str << "\n";
+        count = 1;
+      } else if (count <= 0) {
+        nout << "Ignoring inappropriate number: " << count << "\n";
+        count = 1;
+      }
+
+      GriddedFilename gf;
+      gf._filename = Filename::from_os_specific(name);
+      gf._count = count;
+      gridded_filenames.push_back(gf);
+    }
+  }
+}
+
+void
+load_gridded_models(WindowFramework *window, 
+                    GriddedFilenames &filenames,
+                    GriddedInfoArray &info_arr) {
+  // Load up all the files indicated in the list of gridded filenames
+  // and store them in the given vector.
+
+  // First, load up each model from disk once, and store them all
+  // separate from the scene graph.  Also count up the total number of
+  // models we'll be putting in the grid.
+  int grid_count = 0;
+  qpNodePath models("models");
+  GriddedFilenames::iterator fi;
+  for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
+    GriddedFilename &gf = (*fi);
+    gf._model = window->load_model(models, gf._filename);
+    if (!gf._model.is_empty()) {
+      grid_count += gf._count;
+    }
+  }
+
+  info_arr.clear();
+  info_arr.reserve(grid_count);
+
+  // Compute the integer square root of grid_count, so that we put our
+  // models in a nice square grid.
+      
+  gridwidth=1;
+  while(gridwidth*gridwidth < grid_count) {
+    gridwidth++;
+  }
+  
+  grid_pos_offset = -gridwidth*GRIDCELLSIZE/2.0;
+  wander_area_pos_offset = -max(fabs(grid_pos_offset),MIN_WANDERAREA_DIMENSION/2.0);
+
+  // Now walk through the list again, copying models into the scene
+  // graph as we go.
+  
+  float xpos = grid_pos_offset;
+  float ypos = grid_pos_offset;
+      
+  srand( (unsigned)time( NULL ) );
+  double now = ClockObject::get_global_clock()->get_frame_time();
+
+  int model_count = 0;
+  int passnum = 0;
+  bool loaded_any;
+
+  qpNodePath render = window->get_render();
+  do {
+    loaded_any = false;
+
+    for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
+      const GriddedFilename &gf = (*fi);
+      if (!gf._model.is_empty() && gf._count > passnum) {
+        loaded_any = true;
+        // Copy this model into the scene graph, and assign it a
+        // position on the grid.
+
+        string model_name = format_string(++model_count);
+        qpNodePath model = render.attach_new_node(model_name);
+        gf._model.copy_to(model);
+
+        gridded_file_info info;
+        info.node = model.node();
+
+        LMatrix4f xfm_mat,tmat1,tmat2;
+
+        if(gridmotiontype==Rotation) {
+
+#define MIN_REVOLUTION_ANGVEL 30
+#define MAX_REVOLUTION_ANGVEL 60
+
+#define MIN_ROTATION_ANGVEL 30
+#define MAX_ROTATION_ANGVEL 600
+
+#define MAX_RADIUS 4.0*GRIDCELLSIZE
+#define MIN_RADIUS 0.1*GRIDCELLSIZE
+
+          info.starttime = now;
+
+          info.xcenter=xpos;
+          info.ycenter=ypos;
+          info.ang1=RANDFRAC * 360.0;
+          info.ang1_vel=((MAX_REVOLUTION_ANGVEL-MIN_REVOLUTION_ANGVEL) * RANDFRAC) + MIN_REVOLUTION_ANGVEL;
+
+          info.ang2=RANDFRAC * 360.0;
+          info.ang2_vel=((MAX_ROTATION_ANGVEL-MIN_ROTATION_ANGVEL) * RANDFRAC) + MIN_ROTATION_ANGVEL;
+
+          info.radius = (RANDFRAC * (MAX_RADIUS-MIN_RADIUS)) + MIN_RADIUS;
+
+          if(RANDFRAC>0.5) {
+            info.ang1_vel=-info.ang1_vel;
+          }
+
+          if(RANDFRAC>0.5) {
+            info.ang2_vel=-info.ang2_vel;
+          }
+
+          // xforms happen left to right
+          LVector2f new_center = LVector2f(info.radius,0.0) *
+            LMatrix3f::rotate_mat(info.ang1);
+
+          const LVector3f rotate_axis(0.0, 0.0, 1.0);
+
+          LVector3f translate_vec(xpos+new_center._v.v._0,
+                                  ypos+new_center._v.v._1,
+                                  0.0);
+
+          LMatrix4f::rotate_mat_normaxis(info.ang2,rotate_axis,tmat1);
+          tmat2 = LMatrix4f::translate_mat(translate_vec);
+          xfm_mat = tmat1 * tmat2;
+        } else if(gridmotiontype==LinearMotion) {
+
+#define MIN_VEL 2.0
+#define MAX_VEL (fabs(wander_area_pos_offset))
+
+          info.vel=((MAX_VEL-MIN_VEL) * RANDFRAC) + MIN_VEL;
+
+          info.xstart=xpos;
+          info.ystart=ypos;
+
+          info.xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
+          info.yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
+
+          info.starttime = now;
+
+          float xdel = info.xdel = info.xend-info.xstart;
+          float ydel = info.ydel = info.yend-info.ystart;
+
+          info.endtime = csqrt(xdel*xdel+ydel*ydel)/info.vel;
+
+          info.timedel = info.endtime - info.starttime;
+
+          const LVector3f rotate_axis(0.0, 0.0, 1.0);
+          float ang = rad_2_deg(atan2(-xdel,ydel));
+
+          LMatrix4f::rotate_mat_normaxis(ang,rotate_axis,info.rotmat);
+
+          LVector3f translate_vec(xpos, ypos, 0.0);
+          LMatrix4f tmat2 = LMatrix4f::translate_mat(translate_vec);
+
+          xfm_mat = info.rotmat * tmat2;
+        } else {
+          LVector3f translate_vec(xpos, ypos, 0.0);
+          xfm_mat = LMatrix4f::translate_mat(translate_vec);
+        }
+
+        info.node->set_transform(TransformState::make_mat(xfm_mat));
+
+        info_arr.push_back(info);
+
+        if((model_count % gridwidth) == 0) {
+          xpos= -gridwidth*GRIDCELLSIZE/2.0;
+          ypos+=GRIDCELLSIZE;
+        } else {
+          xpos+=GRIDCELLSIZE;
+        }
+      }
+    }
+
+    passnum++;
+  } while (loaded_any);
+
+  // Finally, remove the source models we loaded up.  Not a real big deal.
+  for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
+    GriddedFilename &gf = (*fi);
+    if (!gf._model.is_empty()) {
+      gf._model.remove_node();
+    }
+  }
+}
+
+int
+main(int argc, char *argv[]) {
+  PandaFramework framework;
+  vector_string args;
+  framework.open_framework(argc, argv);
+  framework.set_window_title("Gridded Object Viewer");
+
+  if (!get_command_line_opts(argc, argv)) {
+    return (1);
+  }
+
+  // Extract the remaining arguments into two lists of files: those
+  // with a grid parameter, and those without.
+  pvector<Filename> static_filenames;
+  GriddedFilenames gridded_filenames;
+  get_command_line_filenames(argc, argv, static_filenames, gridded_filenames);
+
+  WindowFramework *window = framework.open_window();
+  if (window != (WindowFramework *)NULL) {
+    // We've successfully opened a window.
+
+    window->enable_keyboard();
+    window->setup_trackball();
+    window->load_models(window->get_render(), static_filenames);
+
+    GriddedInfoArray info_arr;
+    load_gridded_models(window, gridded_filenames, info_arr);
+
+    window->loop_animations();
+
+    framework.enable_default_keys();
+
+    while (framework.do_frame()) {
+      if (!info_arr.empty() && gridmotiontype) {
+        move_gridded_stuff(gridmotiontype, &info_arr[0], info_arr.size());
+      }
+    }
+  }
+
+  framework.report_frame_rate(nout);
+  return (0);
+}

+ 15 - 441
panda/src/testbed/pview.cxx

@@ -16,453 +16,27 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-#include "graphicsWindow.h"
-#include "graphicsChannel.h"
-#include "graphicsLayer.h"
-#include "graphicsEngine.h"
-#include "graphicsPipe.h"
-#include "interactiveGraphicsPipe.h"
-#include "displayRegion.h"
-#include "qpcamera.h"
-#include "perspectiveLens.h"
-#include "pandaNode.h"
-#include "textureAttrib.h"
-#include "clockObject.h"
-#include "qpgeomNode.h"
-#include "geomTri.h"
-#include "texture.h"
-#include "texturePool.h"
-#include "dSearchPath.h"
-#include "loader.h"
-#include "auto_bind.h"
-#include "pStatClient.h"
-#include "notify.h"
-#include "qpnodePath.h"
-#include "cullBinManager.h"
-#include "mouseAndKeyboard.h"
-#include "qpbuttonThrower.h"
-#include "qptrackball.h"
-#include "qptransform2sg.h"
-#include "modifierButtons.h"
-#include "keyboardButton.h"
-#include "event.h"
-#include "eventQueue.h"
-#include "eventHandler.h"
-#include "qpdataGraphTraverser.h"
-#include "lightAttrib.h"
-#include "ambientLight.h"
-#include "directionalLight.h"
-
-// Use dconfig to read a few Configrc variables.
-Configure(config_pview);
-ConfigureFn(config_pview) {
-}
-
-static const int win_width = config_pview.GetInt("win-width", 640);
-static const int win_height = config_pview.GetInt("win-height", 480);
-
-// As long as this is true, the main loop will continue running.
-bool run_flag = true;
-
-// These are referenced in both event_w() and in event_b().
-bool wireframe = false;
-bool two_sided = false;
-bool lighting = false;
-
-
-// These are used by report_frame_rate().
-double start_time = 0.0;
-int start_frame_count = 0;
-
-// A priority number high enough to override any model file settings.
-static const int override_priority = 100;
-
-// This is the main scene graph.
-qpNodePath render;
-
-// And this is the camera node.
-qpNodePath camera_np;
-
-void 
-report_frame_rate() {
-  double now = ClockObject::get_global_clock()->get_frame_time();
-  double delta = now - start_time;
-  
-  int frame_count = ClockObject::get_global_clock()->get_frame_count();
-  int num_frames = frame_count - start_frame_count;
-  if (num_frames > 0) {
-    nout << endl << num_frames << " frames in " << delta << " seconds" << endl;
-    double x = ((double)num_frames) / delta;
-    nout << x << " fps average (" << 1000.0 / x << "ms)" << endl;
-
-    // Reset the frame rate counter for the next press of 'f'.
-    start_time = now;
-    start_frame_count = frame_count;
-  }
-}
-
-PT(GraphicsPipe)
-make_pipe() {
-  // We use the GraphicsPipe factory to make us a renderable pipe
-  // without knowing exactly what kind of pipes we have available.  We
-  // don't care, so long as we can render to it interactively.
-
-  // This depends on the shared library or libraries (DLL's to you
-  // Windows folks) that have been loaded in at runtime from the
-  // load-display Configrc variable.
-  GraphicsPipe::resolve_modules();
-
-  nout << "Known pipe types:" << endl;
-  GraphicsPipe::get_factory().write_types(nout, 2);
-
-  PT(GraphicsPipe) pipe;
-  pipe = GraphicsPipe::get_factory().
-    make_instance(InteractiveGraphicsPipe::get_class_type());
-
-  if (pipe == (GraphicsPipe*)0L) {
-    nout << "No interactive pipe is available!  Check your Configrc!\n";
-    exit(1);
-  }
-
-  return pipe;
-}
-
-PT(GraphicsWindow)
-make_window(GraphicsPipe *pipe, GraphicsEngine *engine) {
-  // Now create a window on that pipe.
-  GraphicsWindow::Properties window_prop;
-  window_prop._xorg = 0;
-  window_prop._yorg = 0;
-  window_prop._xsize = win_width;
-  window_prop._ysize = win_height;
-  window_prop._title = "Panda Viewer";
-  //  window_prop._fullscreen = true;
-
-  PT(GraphicsWindow) window = pipe->make_window(window_prop);
-  engine->add_window(window);
-  
-  return window;
-}
-
-PT(qpCamera)
-make_camera(GraphicsWindow *window) {
-  // Get the first channel on the window.  This will be the only
-  // channel on non-SGI hardware.
-  PT(GraphicsChannel) channel = window->get_channel(0);
-
-  // Make a layer on the channel to hold our display region.
-  PT(GraphicsLayer) layer = channel->make_layer();
-
-  // And create a display region that covers the entire window.
-  PT(DisplayRegion) dr = layer->make_display_region();
-
-  // Finally, we need a camera to associate with the display region.
-  PT(qpCamera) camera = new qpCamera("camera");
-  PT(Lens) lens = new PerspectiveLens;
-  lens->set_film_size(win_width, win_height);
-  camera->set_lens(lens);
-  dr->set_qpcamera(qpNodePath(camera));
-
-  return camera;
-}
-
-
-void
-make_default_geometry(PandaNode *parent) {
-  PTA_Vertexf coords;
-  PTA_TexCoordf uvs;
-  PTA_Normalf norms;
-  PTA_Colorf colors;
-  PTA_ushort cindex;
-  
-  coords.push_back(Vertexf::rfu(0.0, 0.0, 0.0));
-  coords.push_back(Vertexf::rfu(1.0, 0.0, 0.0));
-  coords.push_back(Vertexf::rfu(0.0, 0.0, 1.0));
-  uvs.push_back(TexCoordf(0.0, 0.0));
-  uvs.push_back(TexCoordf(1.0, 0.0));
-  uvs.push_back(TexCoordf(0.0, 1.0));
-  norms.push_back(Normalf::back());
-  colors.push_back(Colorf(0.5, 0.5, 1.0, 1.0));
-  cindex.push_back(0);
-  cindex.push_back(0);
-  cindex.push_back(0);
-  
-  PT(GeomTri) geom = new GeomTri;
-  geom->set_num_prims(1);
-  geom->set_coords(coords);
-  geom->set_texcoords(uvs, G_PER_VERTEX);
-  geom->set_normals(norms, G_PER_PRIM);
-  geom->set_colors(colors, G_PER_VERTEX, cindex);
-
-  CPT(RenderState) state = RenderState::make_empty();
-  Texture *tex = TexturePool::load_texture("rock-floor.rgb");
-  if (tex != (Texture *)NULL) {
-    tex->set_minfilter(Texture::FT_linear);
-    tex->set_magfilter(Texture::FT_linear);
-    state = state->add_attrib(TextureAttrib::make(tex));
-  }
-  
-  qpGeomNode *geomnode = new qpGeomNode("tri");
-  parent->add_child(geomnode);
-  geomnode->add_geom(geom, state);
-}
-
-void
-get_models(PandaNode *parent, int argc, char *argv[]) {
-  if (argc < 2) {
-    // In the absence of any models on the command line, load up a
-    // default triangle so we at least have something to look at.
-    make_default_geometry(parent);
-
-  } else {
-    Loader loader;
-    DSearchPath local_path(".");
-
-    for (int i = 1; i < argc; i++) {
-      Filename filename = argv[i];
-      
-      nout << "Loading " << filename << "\n";
-
-      // First, we always try to resolve a filename from the current
-      // directory.  This means a local filename will always be found
-      // before the model path is searched.
-      filename.resolve_filename(local_path);
-
-      PT(PandaNode) node = loader.qpload_sync(filename);
-      if (node == (PandaNode *)NULL) {
-        nout << "Unable to load " << filename << "\n";
-
-      } else {
-        node->ls(nout, 0);
-        parent->add_child(node);
-      }
-    }
-  }
-}
-
-PandaNode * 
-setup_mouse(PandaNode *data_root, GraphicsWindow *window) {
-  qpMouseAndKeyboard *mouse = new qpMouseAndKeyboard(window, 0, "mouse");
-  data_root->add_child(mouse);
-
-  // Create a ButtonThrower to throw events from the keyboard.
-  PT(qpButtonThrower) bt = new qpButtonThrower("kb-events");
-  ModifierButtons mods;
-  mods.add_button(KeyboardButton::shift());
-  mods.add_button(KeyboardButton::control());
-  mods.add_button(KeyboardButton::alt());
-  bt->set_modifier_buttons(mods);
-  mouse->add_child(bt);
-
-  return mouse;
-}
-
-void 
-setup_trackball(PandaNode *mouse, qpCamera *camera) {
-  PT(qpTrackball) trackball = new qpTrackball("trackball");
-  trackball->set_pos(LVector3f::forward() * 50.0);
-  mouse->add_child(trackball);
-
-  PT(qpTransform2SG) tball2cam = new qpTransform2SG("tball2cam");
-  tball2cam->set_node(camera);
-  trackball->add_child(tball2cam);
-}
-
-void
-event_esc(CPT_Event) {
-  // The Escape or q key was pressed.  Exit the application.
-  run_flag = false;
-}
-
-void
-event_f(CPT_Event) {
-  // 'f' : report frame rate.
-  report_frame_rate();
-}
-
-void
-event_t(CPT_Event) {
-  // 't' : toggle texture.
-  static bool texture_off = false;
-
-  texture_off = !texture_off;
-  if (texture_off) {
-    nout << "Disabling texturing.\n";
-    render.set_texture_off(override_priority);
-  } else {
-    nout << "Enabling texturing.\n";
-    render.clear_texture();
-  }
-}
-
-void
-event_w(CPT_Event) {
-  // 'w' : toggle wireframe.
-  wireframe = !wireframe;
-  if (wireframe) {
-    nout << "Setting wireframe mode.\n";
-    render.set_render_mode_wireframe(override_priority);
-    render.set_two_sided(true, override_priority);
-  } else {
-    nout << "Clearing wireframe mode.\n";
-    render.clear_render_mode();
-    if (!two_sided) {
-      render.clear_two_sided();
-    }
-  }
-}
-
-void
-event_b(CPT_Event) {
-  // 'b' : toggle backfacing.
-  two_sided = !two_sided;
-  if (two_sided) {
-    nout << "Showing back-facing polygons.\n";
-    render.set_two_sided(true, override_priority);
-  } else {
-    nout << "Hiding back-facing polygons.\n";
-    if (!wireframe) {
-      render.clear_two_sided();
-    }
-  }
-}
-
-void
-event_s(CPT_Event) {
-  // 's' : toggle state sorting by putting everything into the 'unsorted' bin.
-  static bool sorting_off = false;
-
-  sorting_off = !sorting_off;
-  if (sorting_off) {
-    nout << "Disabling state sorting.\n";
-    render.set_bin("unsorted", 0, override_priority);
-  } else {
-    nout << "Enabling state sorting.\n";
-    render.clear_bin();
-  }
-}
-
-void
-event_S(CPT_Event) {
-  // shift 'S' : active PStats.
-#ifdef DO_PSTATS
-  nout << "Connecting to stats host" << endl;
-  PStatClient::connect();
-#else
-  nout << "Stats host not supported." << endl;
-#endif
-}
-
-void
-event_A(CPT_Event) {
-  // shift 'A' : deactive PStats.
-#ifdef DO_PSTATS
-  if (PStatClient::is_connected()) {
-    nout << "Disconnecting from stats host" << endl;
-    PStatClient::disconnect();
-  } else {
-    nout << "Stats host is already disconnected." << endl;
-  }
-#else
-  nout << "Stats host not supported." << endl;
-#endif
-}
-
-void
-event_l(CPT_Event) {
-  // 'l' : toggle lighting.
-  lighting = !lighting;
-  if (lighting) {
-    nout << "Enabling lighting.\n";
-    static bool lights_created = false;
-
-    static PT(AmbientLight) alight;
-    static PT(DirectionalLight) dlight;
-
-    if (!lights_created) {
-      alight = new AmbientLight("ambient");
-      alight->set_color(Colorf(0.2f, 0.2f, 0.2f, 1.0f));
-      dlight = new DirectionalLight("directional");
-      camera_np.attach_new_node(alight);
-      camera_np.attach_new_node(dlight);
-
-      lights_created = true;
-    }
-
-    // Enable lights on the top node.
-    render.node()->set_attrib(LightAttrib::make(LightAttrib::O_add, alight, dlight));
-
-  } else {
-    nout << "Disabling lighting.\n";
-    // Remove the lights from the top node.
-    render.node()->clear_attrib(LightAttrib::get_class_type());
-  }
-}
+#include "pandaFramework.h"
 
 
 int
 int
 main(int argc, char *argv[]) {
 main(int argc, char *argv[]) {
-  // First, we need a GraphicsPipe, before we can open a window.
-  PT(GraphicsPipe) pipe = make_pipe();
-
-  // We also need a GraphicsEngine to manage the rendering process.
-  GraphicsEngine *engine = new GraphicsEngine;
-
-  // Now open a window and get a camera.
-  PT(GraphicsWindow) window = make_window(pipe, engine);
-  PT(qpCamera) camera = make_camera(window);
-
-  // Now we just need to make a scene graph for the camera to render.
-  render = qpNodePath("render");
-  camera_np = render.attach_new_node(camera);
-  camera->set_scene(render);
-
-  // This is maybe here temporarily, and maybe not.
-  render.set_two_sided(0);
-
-  // Set up a data graph for tracking user input.
-  PT(PandaNode) data_root = new PandaNode("data_root");
-  PandaNode *mouse = setup_mouse(data_root, window);
-  setup_trackball(mouse, camera);
-  qpDataGraphTraverser dg_trav;
+  PandaFramework framework;
+  framework.open_framework(argc, argv);
+  framework.set_window_title("Panda Viewer");
 
 
-  // Use an event handler to manage keyboard events.
-  EventHandler event_handler(EventQueue::get_global_event_queue());
-  event_handler.add_hook("escape", event_esc);
-  event_handler.add_hook("q", event_esc);
-  event_handler.add_hook("f", event_f);
-  event_handler.add_hook("t", event_t);
-  event_handler.add_hook("w", event_w);
-  event_handler.add_hook("b", event_b);
-  event_handler.add_hook("s", event_s);
-  event_handler.add_hook("shift-s", event_S);
-  event_handler.add_hook("shift-a", event_A);
-  event_handler.add_hook("l", event_l);
+  WindowFramework *window = framework.open_window();
+  if (window != (WindowFramework *)NULL) {
+    // We've successfully opened a window.
 
 
+    window->enable_keyboard();
+    window->setup_trackball();
+    window->load_models(window->get_render(), argc, argv);
+    window->loop_animations();
 
 
-  // Put something in the scene graph to look at.
-  get_models(render.node(), argc, argv);
-
-  // If we happened to load up both a character file and its matching
-  // animation file, attempt to bind them together now and start the
-  // animations looping.
-  AnimControlCollection anim_controls;
-  auto_bind(render.node(), anim_controls, ~0);
-  anim_controls.loop_all(true);
-
-
-  // Tick the clock once so we won't count the time spent loading up
-  // files, above, in our frame rate average.
-  ClockObject::get_global_clock()->tick();
-
-  // This is our main update loop.  Loop here until someone
-  // (e.g. event_esc) sets run_flag to false.
-  while (run_flag) {
-    dg_trav.traverse(data_root);
-    event_handler.process_events();
-    engine->render_frame();
-  } 
+    framework.enable_default_keys();
+    framework.main_loop();
+  }
 
 
-  report_frame_rate();
-  delete engine;
+  framework.report_frame_rate(nout);
   return (0);
   return (0);
 }
 }