ソースを参照

pgraph tweaks, chancfg, modelpool

David Rose 24 年 前
コミット
a7132f9cf4

+ 21 - 1
panda/src/chancfg/chancfg.I

@@ -17,7 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
   INLINE PT_NamedNode ChanConfig::get_group_node(const int node_index) const {
-         return _group_node[node_index];
+    return _group_node[node_index];
   }
   INLINE int ChanConfig::get_group_membership(const int dr_index) const {
     return _group_membership[dr_index];
@@ -35,3 +35,23 @@
     return _graphics_window;
   }
 
+
+  INLINE PandaNode *qpChanConfig::get_group_node(const int node_index) const {
+    return _group_node[node_index];
+  }
+  INLINE int qpChanConfig::get_group_membership(const int dr_index) const {
+    return _group_membership[dr_index];
+  }
+  INLINE int qpChanConfig::get_num_groups(void) const {
+    return _group_node.size();
+  }
+  INLINE int qpChanConfig::get_num_drs(void) const {
+    return _display_region.size();
+  }
+  INLINE PT(DisplayRegion) qpChanConfig::get_dr(const int dr_index) const {
+    return _display_region[dr_index];
+  }
+  INLINE PT(GraphicsWindow) qpChanConfig::get_win(void) const {
+    return _graphics_window;
+  }
+

+ 293 - 13
panda/src/chancfg/chancfg.cxx

@@ -17,20 +17,23 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "chancfg.h"
-#include <notify.h>
-#include <displayRegion.h>
-#include <graphicsChannel.h>
-#include <hardwareChannel.h>
-#include <camera.h>
-#include <frustum.h>
-#include <perspectiveLens.h>
-#include <renderRelation.h>
-#include <transformTransition.h>
-#include <dSearchPath.h>
-#include <dconfig.h>
-#include <filename.h>
+#include "notify.h"
+#include "displayRegion.h"
+#include "graphicsChannel.h"
+#include "hardwareChannel.h"
+#include "camera.h"
+#include "qpcamera.h"
+#include "frustum.h"
+#include "perspectiveLens.h"
+#include "renderRelation.h"
+#include "transformTransition.h"
+#include "dSearchPath.h"
+#include "dconfig.h"
+#include "filename.h"
+#include "pt_NamedNode.h"
+#include "transformState.h"
+
 #include <algorithm>
-#include <pt_NamedNode.h>
 
 
 Configure(chanconfig);
@@ -483,6 +486,283 @@ ChanConfig::ChanConfig(GraphicsPipe* pipe, std::string cfg, Node *render,
   return;
 }
 
+void qpChanConfig::chan_eval(GraphicsWindow* win, WindowItem& W, LayoutItem& L, 
+                           SVec& S,
+                           ChanViewport& V, int hw_offset, int xsize, int ysize,
+                           const qpNodePath &render, bool want_cameras) {
+  int i = min(L.GetNumRegions(), int(S.size()));
+  int j;
+  SVec::iterator k;
+  std::vector< PT(PandaNode) >camera(W.getNumCameraGroups());
+  //first camera is special cased to name "camera" for older code
+  camera[0] = new PandaNode("camera");
+  for(int icam=1;icam<W.getNumCameraGroups();icam++) {
+    char dummy[10];//if more than 10^11 groups, you've got bigger problems
+    sprintf(dummy,"%d",icam);
+    std::string nodeName = "camera";
+    nodeName.append(dummy);
+    camera[icam] = new PandaNode(nodeName);
+  }
+  for (j=0, k=S.begin(); j<i; ++j, ++k) {
+   ChanViewport v(ChanScaleViewport(V, L[j]));
+   PT(GraphicsChannel) chan;
+   if ((*k).getHWChan() && W.getHWChans()) {
+     if ((*k).getChan() == -1) {
+       chan = win->get_channel(hw_offset);
+     } else
+       chan = win->get_channel((*k).getChan());
+       // HW channels always start with the full area of the channel
+       v = ChanViewport(0.0f, 1.0f, 0.0f, 1.0f);
+   } else {
+     chan = win->get_channel(0);
+   }
+   ChanViewport v2(ChanScaleViewport(v, (*k).getViewport()));
+   PT(GraphicsLayer) layer = chan->make_layer();
+   PT(DisplayRegion) dr = 
+     layer->make_display_region(v2.left(), v2.right(),
+                                v2.bottom(), v2.top());
+   if (want_cameras && camera[0] != (PandaNode *)NULL) {
+     // now make a camera for it
+     PT(qpCamera) cam = new qpCamera("");
+     dr->set_qpcamera(qpNodePath(cam));
+     _display_region.push_back(dr);
+     SetupFOV fov = (*k).getFOV();
+     // The distinction between ConsoleSize and DisplaySize
+     // is to handle display regions with orientations that
+     // are rotated 90 degrees left or right.  For example,
+     // the model shop cave (LAIR) uses projectors on their
+     // sides, so that what's horizontal on the console is 
+     // vertical in the cave (Display).
+     float xConsoleSize = xsize*(v2.right()-v2.left());
+     float yConsoleSize = ysize*(v2.top()-v2.bottom());
+     float xDisplaySize, yDisplaySize;
+     if ( (*k).getOrientation() == SetupItem::Left ||
+          (*k).getOrientation() == SetupItem::Right ) {
+        xDisplaySize = yConsoleSize;
+        yDisplaySize = xConsoleSize;
+     } else {
+        xDisplaySize = xConsoleSize;
+        yDisplaySize = yConsoleSize;
+     }
+     fov = ChanResolveFOV(fov, xDisplaySize, yDisplaySize);
+     if (chancfg_cat->is_debug()) {
+       chancfg_cat->debug() << "ChanEval:: FOVhoriz = " << fov.getHoriz()
+          << "  FOVvert = " << fov.getVert() << endl;
+       chancfg_cat->debug() << "ChanEval:: xsize = " << xsize
+          << "  ysize = " << ysize << endl;
+     }
+
+     // take care of the orientation
+     CPT(TransformState) orient;
+     float hFov, vFov;
+   
+     switch ((*k).getOrientation()) {
+       case SetupItem::Up:
+         hFov = fov.getHoriz(); vFov = fov.getVert();
+         break;
+       case SetupItem::Down:
+         hFov = fov.getHoriz(); vFov = fov.getVert();
+         orient = TransformState::make_mat
+           (LMatrix4f::rotate_mat_normaxis(180.0f, LVector3f::forward()));
+         break;
+       case SetupItem::Left:
+         // vertical and horizontal FOV are being switched
+         hFov = fov.getVert(); vFov = fov.getHoriz();
+         orient = TransformState::make_mat
+           (LMatrix4f::rotate_mat_normaxis(90.0f, LVector3f::forward()));
+         break;
+       case SetupItem::Right:
+         // vertical and horizontal FOV are being switched
+         hFov = fov.getVert(); vFov = fov.getHoriz();
+         orient = TransformState::make_mat
+           (LMatrix4f::rotate_mat_normaxis(-90.0f, LVector3f::forward()));
+         break;
+     }
+
+     PT(Lens) lens = new PerspectiveLens;
+     lens->set_fov(hFov, vFov);
+     lens->set_near_far(1.0f, 10000.0f);
+     cam->set_lens(lens);
+
+     // hfov and vfov for camera are switched from what was specified
+     // if the orientation is sideways.
+     if (chancfg_cat->is_debug())
+       chancfg_cat->debug() << "ChanEval:: camera hfov = "
+         << lens->get_hfov() << "  vfov = "
+         << lens->get_vfov() << endl;
+     cam->set_scene(render);
+
+     camera[W.getCameraGroup(j)]->add_child(cam);
+     if (orient != (TransformState *)NULL) {
+       cam->set_transform(orient);
+     }
+   }
+  }
+  _group_node = camera;
+  return;
+}
+
+qpChanConfig::qpChanConfig(GraphicsPipe* pipe, std::string cfg, const qpNodePath &render,
+                       ChanCfgOverrides& overrides) {
+  ReadChanConfigData();
+  // check to make sure we know everything we need to
+  if (!ConfigDefined(cfg)) {
+    chancfg_cat.error()
+      << "no window configuration called '" << cfg << "'" << endl;
+    _graphics_window = (GraphicsWindow*)0;
+    return;
+  }
+  WindowItem W = (*WindowDB)[cfg];
+
+  std::string l = W.getLayout();
+  if (!LayoutDefined(l)) {
+    chancfg_cat.error()
+      << "No layout called '" << l << "'" << endl;
+    _graphics_window = (GraphicsWindow*)0;
+    return;
+  }
+  LayoutItem L = (*LayoutDB)[l];
+
+  SetupSyms s = W.getSetups();
+  if (!ChanCheckSetups(s)) {
+    chancfg_cat.error() << "Setup failure" << endl;
+    _graphics_window = (GraphicsWindow*)0;
+    return;
+  }
+
+  SVec S;
+  S.reserve(s.size());
+  for (SetupSyms::iterator i=s.begin(); i!=s.end(); ++i)
+    S.push_back((*SetupDB)[(*i)]);
+
+  // get the window data
+  int sizeX = chanconfig.GetInt("win-width", -1);
+  int sizeY = chanconfig.GetInt("win-height", -1);
+  if (overrides.defined(ChanCfgOverrides::SizeX))
+    sizeX = overrides.getInt(ChanCfgOverrides::SizeX);
+  if (overrides.defined(ChanCfgOverrides::SizeY))
+    sizeY = overrides.getInt(ChanCfgOverrides::SizeY);
+  if (sizeX < 0) {
+    if (sizeY < 0) {
+      if(chancfg_cat.is_debug())
+          chancfg_cat.debug() << "Using default chan-window size\n";
+      // take the default size
+      sizeX = W.getSizeX();
+      sizeY = W.getSizeY();
+    } else {
+      // vertical size is defined, compute horizontal keeping the aspect from
+      // the default
+      sizeX = (W.getSizeX() * sizeY) / W.getSizeY();
+    }
+  } else if (sizeY < 0) {
+    // horizontal size is defined, compute vertical keeping the aspect from the
+    // default
+    sizeY = (W.getSizeY() * sizeX) / W.getSizeX();
+  }
+
+  int origX = chanconfig.GetInt("win-origin-x");
+  int origY = chanconfig.GetInt("win-origin-y");
+  origX = overrides.defined(ChanCfgOverrides::OrigX) ?
+            overrides.getInt(ChanCfgOverrides::OrigX) : origX;
+  origY = overrides.defined(ChanCfgOverrides::OrigY) ?
+            overrides.getInt(ChanCfgOverrides::OrigY) : origY;
+
+  bool border = !chanconfig.GetBool("no-border", !W.getBorder());
+  bool fullscreen = chanconfig.GetBool("fullscreen", false);
+  bool use_cursor = chanconfig.GetBool("cursor-visible", W.getCursor());
+  int want_depth_bits = chanconfig.GetInt("want-depth-bits", 1);
+  int want_color_bits = chanconfig.GetInt("want-color-bits", 1);
+
+  // visual?  nope, that's handled with the mode.
+  uint mask = 0x0;  // ?!  this really should come from the win config
+  mask = overrides.defined(ChanCfgOverrides::Mask) ?
+           overrides.getUInt(ChanCfgOverrides::Mask) : mask;
+  std::string title = cfg;
+  title = overrides.defined(ChanCfgOverrides::Title) ?
+            overrides.getString(ChanCfgOverrides::Title) : title;
+
+  GraphicsWindow::Properties props;
+  props._xorg = origX;
+  props._yorg = origY;
+  props._xsize = sizeX;
+  props._ysize = sizeY;
+  props._title = title;
+  props._mask = mask;
+  props._border = border;
+  props._fullscreen = fullscreen;
+  props._want_depth_bits = want_depth_bits;
+  props._want_color_bits = want_color_bits;
+  props._bCursorIsVisible = use_cursor;
+
+  // stereo prep?
+  // DVR prep?
+
+  bool want_cameras = overrides.defined(ChanCfgOverrides::Cameras) ?
+                        overrides.getBool(ChanCfgOverrides::Cameras) : true;
+
+  // open that sucker
+  PT(GraphicsWindow) win = pipe->make_window(props);
+  if(win == (GraphicsWindow *)NULL) {
+    chancfg_cat.error() << "Could not create window" << endl;
+    _graphics_window = (GraphicsWindow *)NULL;
+    return;
+  }
+
+  // make channels and display regions
+  ChanViewport V(0.0f, 1.0f, 0.0f, 1.0f);
+  chan_eval(win, W, L, S, V, W.getChanOffset()+1, sizeX, sizeY, 
+            render, want_cameras);
+  for(size_t dr_index=0; dr_index<_display_region.size(); dr_index++)
+    _group_membership.push_back(W.getCameraGroup(dr_index));
+
+  // sanity check
+  if (config_sanity_check) {
+    nout << "ChanConfig Sanity check:" << endl
+         << "window - " << (void*)win << endl
+         << "  width = " << win->get_width() << "  height = " << win->get_height() << endl
+         << "  xorig = " << win->get_xorg() << "  yorig = " << win->get_yorg()<< endl;
+
+    int max_channel_index = win->get_max_channel_index();
+    for (int c = 0; c < max_channel_index; c++) {
+        if (win->is_channel_defined(c)) {
+          GraphicsChannel *chan = win->get_channel(c);
+          nout << "  Chan - " << (void*)chan << endl
+               << "    window = " << (void*)(chan->get_window()) << endl
+               << "    active = " << chan->is_active() << endl;
+        
+          int num_layers = chan->get_num_layers();
+          for (int l = 0; l < num_layers; l++) {
+            GraphicsLayer *layer = chan->get_layer(l);
+            nout << "    Layer - " << (void*)layer << endl
+                 << "      channel = " << (void*)(layer->get_channel()) << endl
+                 << "      active = " << layer->is_active() << endl;
+        
+            int num_drs = layer->get_num_drs();
+            for (int d = 0; d < num_drs; d++) {
+              DisplayRegion *dr = layer->get_dr(d);
+              nout << "      DR - " << (void*)dr << endl
+                   << "        layer = " << (void*)(dr->get_layer()) << endl;
+              float ll, rr, bb, tt;
+              dr->get_dimensions(ll, rr, bb, tt);
+              nout << "        (" << ll << " " << rr << " " << bb << " " << tt << ")" << endl
+                   << "        camera = " << (void*)(dr->get_camera()) << endl;
+              Camera* cmm = dr->get_camera();
+              if (cmm != (Camera*)0L) {
+                  nout << "          active = " << cmm->is_active() << endl;
+                  int num_cam_drs = cmm->get_num_drs();
+                  for (int cd = 0; cd < num_cam_drs; cd++) 
+                      nout << "          dr = " << (void*)cmm->get_dr(cd) << endl;
+              }
+              nout << "      active = " << dr->is_active() << endl;
+            }
+          }
+        }
+    }
+  }
+  _graphics_window = win;
+  return;
+}
+
 
 
 

+ 28 - 4
panda/src/chancfg/chancfg.h

@@ -19,16 +19,18 @@
 #ifndef __CHANCFG_H__
 #define __CHANCFG_H__
 
-#include <pandabase.h>
+#include "pandabase.h"
 
 #include "chanlayout.h"
 #include "chansetup.h"
 #include "chanwindow.h"
 #include "chanshare.h"
 
-#include <graphicsPipe.h>
-#include <graphicsWindow.h>
-#include <pt_NamedNode.h>
+#include "graphicsPipe.h"
+#include "graphicsWindow.h"
+#include "pt_NamedNode.h"
+#include "pandaNode.h"
+#include "qpnodePath.h"
 
 #include "pmap.h"
 
@@ -104,6 +106,7 @@ extern ChanCfgOverrides ChanOverrideNone;
 
 typedef pvector<SetupItem> SVec;
 class NamedNode;
+
 class EXPCL_PANDA ChanConfig
 {
 private:
@@ -125,6 +128,27 @@ PUBLISHED:
   INLINE PT(GraphicsWindow) get_win(void) const;
 };
 
+class EXPCL_PANDA qpChanConfig
+{
+private:
+  std::vector< PT(PandaNode) > _group_node;
+  std::vector< PT(DisplayRegion) > _display_region;
+  std::vector<int> _group_membership;
+  PT(GraphicsWindow) _graphics_window;
+  void chan_eval(GraphicsWindow* win, WindowItem& W, LayoutItem& L, 
+         SVec& S, ChanViewport& V, int hw_offset, 
+         int xsize, int ysize, const qpNodePath &render, bool want_cameras);
+PUBLISHED:
+  qpChanConfig(GraphicsPipe*, std::string, const qpNodePath &render,
+    ChanCfgOverrides& = ChanOverrideNone);
+  INLINE PandaNode *get_group_node(const int node_index) const;
+  INLINE int get_group_membership(const int dr_index) const;
+  INLINE int get_num_groups(void) const;
+  INLINE int get_num_drs(void) const;
+  INLINE PT(DisplayRegion) get_dr(const int dr_index) const;
+  INLINE PT(GraphicsWindow) get_win(void) const;
+};
+
 #include "chancfg.I"
 
 #endif /* __CHANCFG_H__ */

+ 12 - 8
panda/src/loader/Sources.pp

@@ -9,19 +9,23 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
 
   #define SOURCES \
-     bamFile.I bamFile.h config_loader.h loader.I loader.h  \
-     loaderFileType.h loaderFileTypeBam.h  \
-     loaderFileTypeRegistry.h modelPool.I modelPool.h  
+    bamFile.I bamFile.h config_loader.h loader.I loader.h  \
+    loaderFileType.h loaderFileTypeBam.h  \
+    loaderFileTypeRegistry.h \
+    modelPool.I modelPool.h \
+    qpmodelPool.I qpmodelPool.h  
      
   #define INCLUDED_SOURCES  \
-     bamFile.cxx config_loader.cxx loader.cxx loaderFileType.cxx  \
-     loaderFileTypeBam.cxx loaderFileTypeRegistry.cxx  \
-     modelPool.cxx 
+    bamFile.cxx config_loader.cxx loader.cxx loaderFileType.cxx  \
+    loaderFileTypeBam.cxx loaderFileTypeRegistry.cxx  \
+    modelPool.cxx \
+    qpmodelPool.cxx
 
   #define INSTALL_HEADERS \
     bamFile.I bamFile.h loader.I loader.h loaderFileType.h \
-    loaderFileTypeBam.h loaderFileTypeRegistry.h modelPool.I \
-    modelPool.h
+    loaderFileTypeBam.h loaderFileTypeRegistry.h \
+    modelPool.I modelPool.h \
+    qpmodelPool.I qpmodelPool.h  
 
   #define IGATESCAN all
 

+ 1 - 0
panda/src/loader/loader_composite2.cxx

@@ -1,5 +1,6 @@
 
 #include "modelPool.cxx"
+#include "qpmodelPool.cxx"
 #include "loaderFileType.cxx"
 #include "loaderFileTypeBam.cxx"
 #include "loaderFileTypeRegistry.cxx"

+ 131 - 0
panda/src/loader/qpmodelPool.I

@@ -0,0 +1,131 @@
+// Filename: qpmodelPool.I
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: qpModelPool::has_model
+//       Access: Public, Static
+//  Description: Returns true if the model has ever been loaded,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpModelPool::
+has_model(const string &filename) {
+  return get_ptr()->ns_has_model(filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::verify_model
+//       Access: Public, Static
+//  Description: Loads the given filename up as a model, if it has
+//               not already been loaded, and returns true to indicate
+//               success, or false to indicate failure.  If this
+//               returns true, it is guaranteed that a subsequent call
+//               to load_model() with the same model name will
+//               return a valid Node pointer.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpModelPool::
+verify_model(const string &filename) {
+  return load_model(filename) != (PandaNode *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::load_model
+//       Access: Public, Static
+//  Description: Loads the given filename up as a model, if it has
+//               not already been loaded, and returns the new model.
+//               If a model with the same filename was previously
+//               loaded, returns that one instead.  If the model
+//               file cannot be found, returns NULL.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode *qpModelPool::
+load_model(const string &filename) {
+  return get_ptr()->ns_load_model(filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::add_model
+//       Access: Public, Static
+//  Description: Adds the indicated already-loaded model to the
+//               pool.  The model will always replace any
+//               previously-loaded model in the pool that had the
+//               same filename.
+////////////////////////////////////////////////////////////////////
+INLINE void qpModelPool::
+add_model(const string &filename, PandaNode *model) {
+  get_ptr()->ns_add_model(filename, model);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::release_model
+//       Access: Public, Static
+//  Description: Removes the indicated model from the pool,
+//               indicating it will never be loaded again; the model
+//               may then be freed.  If this function is never called,
+//               a reference count will be maintained on every model
+//               every loaded, and models will never be freed.
+////////////////////////////////////////////////////////////////////
+INLINE void qpModelPool::
+release_model(const string &filename) {
+  get_ptr()->ns_release_model(filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::release_all_models
+//       Access: Public, Static
+//  Description: Releases all models in the pool and restores the
+//               pool to the empty state.
+////////////////////////////////////////////////////////////////////
+INLINE void qpModelPool::
+release_all_models() {
+  get_ptr()->ns_release_all_models();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::garbage_collect
+//       Access: Public, Static
+//  Description: Releases only those models in the pool that have a
+//               reference count of exactly 1; i.e. only those
+//               models that are not being used outside of the pool.
+//               Returns the number of models released.
+////////////////////////////////////////////////////////////////////
+INLINE int qpModelPool::
+garbage_collect() {
+  return get_ptr()->ns_garbage_collect();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::list_contents
+//       Access: Public, Static
+//  Description: Lists the contents of the model pool to the
+//               indicated output stream.
+////////////////////////////////////////////////////////////////////
+INLINE void qpModelPool::
+list_contents(ostream &out) {
+  get_ptr()->ns_list_contents(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::Constructor
+//       Access: Private
+//  Description: The constructor is not intended to be called
+//               directly; there's only supposed to be one qpModelPool
+//               in the universe and it constructs itself.
+////////////////////////////////////////////////////////////////////
+INLINE qpModelPool::
+qpModelPool() {
+}

+ 161 - 0
panda/src/loader/qpmodelPool.cxx

@@ -0,0 +1,161 @@
+// Filename: qpmodelPool.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "qpmodelPool.h"
+#include "loader.h"
+#include "config_loader.h"
+
+
+qpModelPool *qpModelPool::_global_ptr = (qpModelPool *)NULL;
+
+static Loader _qpmodel_loader;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::ns_has_model
+//       Access: Private
+//  Description: The nonstatic implementation of has_model().
+////////////////////////////////////////////////////////////////////
+bool qpModelPool::
+ns_has_model(const string &filename) {
+  Models::const_iterator ti;
+  ti = _models.find(filename);
+  if (ti != _models.end()) {
+    // This model was previously loaded.
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::ns_load_model
+//       Access: Private
+//  Description: The nonstatic implementation of load_model().
+////////////////////////////////////////////////////////////////////
+PandaNode *qpModelPool::
+ns_load_model(const string &filename) {
+  Models::const_iterator ti;
+  ti = _models.find(filename);
+  if (ti != _models.end()) {
+    // This model was previously loaded.
+    return (*ti).second;
+  }
+
+  loader_cat.info()
+    << "Loading model " << filename << "\n";
+  PT(PandaNode) node = _qpmodel_loader.qpload_sync(filename);
+  if (node.is_null()) {
+    // This model was not found.
+    return (PandaNode *)NULL;
+  }
+
+  _models[filename] = node;
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::ns_add_model
+//       Access: Private
+//  Description: The nonstatic implementation of add_model().
+////////////////////////////////////////////////////////////////////
+void qpModelPool::
+ns_add_model(const string &filename, PandaNode *model) {
+  // We blow away whatever model was there previously, if any.
+  _models[filename] = model;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::ns_release_model
+//       Access: Private
+//  Description: The nonstatic implementation of release_model().
+////////////////////////////////////////////////////////////////////
+void qpModelPool::
+ns_release_model(const string &filename) {
+  Models::iterator ti;
+  ti = _models.find(filename);
+  if (ti != _models.end()) {
+    _models.erase(ti);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::ns_release_all_models
+//       Access: Private
+//  Description: The nonstatic implementation of release_all_models().
+////////////////////////////////////////////////////////////////////
+void qpModelPool::
+ns_release_all_models() {
+  _models.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::ns_garbage_collect
+//       Access: Private
+//  Description: The nonstatic implementation of garbage_collect().
+////////////////////////////////////////////////////////////////////
+int qpModelPool::
+ns_garbage_collect() {
+  int num_released = 0;
+  Models new_set;
+
+  Models::iterator ti;
+  for (ti = _models.begin(); ti != _models.end(); ++ti) {
+    PandaNode *node = (*ti).second;
+    if (node->get_ref_count() == 1) {
+      if (loader_cat.is_debug()) {
+        loader_cat.debug()
+          << "Releasing " << (*ti).first << "\n";
+      }
+      num_released++;
+    } else {
+      new_set.insert(new_set.end(), *ti);
+    }
+  }
+
+  _models.swap(new_set);
+  return num_released;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::ns_list_contents
+//       Access: Private
+//  Description: The nonstatic implementation of list_contents().
+////////////////////////////////////////////////////////////////////
+void qpModelPool::
+ns_list_contents(ostream &out) {
+  out << _models.size() << " models:\n";
+  Models::iterator ti;
+  for (ti = _models.begin(); ti != _models.end(); ++ti) {
+    out << "  " << (*ti).first
+        << " (count = " << (*ti).second->get_ref_count() << ")\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelPool::get_ptr
+//       Access: Private, Static
+//  Description: Initializes and/or returns the global pointer to the
+//               one qpModelPool object in the system.
+////////////////////////////////////////////////////////////////////
+qpModelPool *qpModelPool::
+get_ptr() {
+  if (_global_ptr == (qpModelPool *)NULL) {
+    _global_ptr = new qpModelPool;
+  }
+  return _global_ptr;
+}

+ 85 - 0
panda/src/loader/qpmodelPool.h

@@ -0,0 +1,85 @@
+// Filename: qpmodelPool.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 qpMODELPOOL_H
+#define qpMODELPOOL_H
+
+#include "pandabase.h"
+
+#include "filename.h"
+#include "pandaNode.h"
+#include "pointerTo.h"
+
+#include "pmap.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ModelPool
+// Description : This is the preferred interface for loading models.
+//               It unifies all references to the same filename, so
+//               that multiple attempts to load the same model will
+//               return the same pointer.  Note that the default
+//               behavior is thus to make instances: use with caution.
+//               Use the copy_subgraph() method on Node (or use
+//               NodePath::copy_to) to make modifiable copies of the
+//               node.
+//
+//               Unlike TexturePool, this class does not automatically
+//               resolve the model filenames before loading, so a
+//               relative path and an absolute path to the same model
+//               will appear to be different filenames.
+//
+//               This does not presently support asynchronous loading,
+//               although it wouldn't be *too* difficult to add.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpModelPool {
+PUBLISHED:
+  INLINE static bool has_model(const string &filename);
+  INLINE static bool verify_model(const string &filename);
+  INLINE static PandaNode *load_model(const string &filename);
+
+  INLINE static void add_model(const string &filename, PandaNode *model);
+  INLINE static void release_model(const string &filename);
+  INLINE static void release_all_models();
+
+  INLINE static int garbage_collect();
+
+  INLINE static void list_contents(ostream &out);
+
+private:
+  INLINE qpModelPool();
+
+  bool ns_has_model(const string &filename);
+  PandaNode *ns_load_model(const string &filename);
+  void ns_add_model(const string &filename, PandaNode *model);
+  void ns_release_model(const string &filename);
+  void ns_release_all_models();
+  int ns_garbage_collect();
+  void ns_list_contents(ostream &out);
+
+  static qpModelPool *get_ptr();
+
+  static qpModelPool *_global_ptr;
+  typedef pmap<string,  PT(PandaNode) > Models;
+  Models _models;
+};
+
+#include "qpmodelPool.I"
+
+#endif
+
+

+ 1 - 0
panda/src/pgraph/pandaNode.cxx

@@ -487,6 +487,7 @@ add_child(PandaNode *child_node, int sort) {
   {
     PT(PandaNode) keep_child = child_node;
     remove_child(child_node);
+
     CDWriter cdata(_cycler);
     CDWriter cdata_child(child_node->_cycler);
     

+ 15 - 3
panda/src/pgraph/qpnodePath.I

@@ -32,13 +32,25 @@ qpNodePath() :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpNodePath::Constructor
+//       Access: Published
+//  Description: This constructs an empty qpNodePath with a single
+//               node.  An ordinary PandaNode is created with the
+//               indicated name.
+////////////////////////////////////////////////////////////////////
+INLINE qpNodePath::
+qpNodePath(const string &top_node_name) :
+  _error_type(ET_ok)
+{
+  PandaNode *top_node = new PandaNode(top_node_name);
+  _head = top_node->get_generic_component();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpNodePath::Constructor
 //       Access: Published
 //  Description: This constructs an empty qpNodePath with a single node.
-//               This path may now be extended by repeatedly calling
-//               push_back() with each node below that node in
-//               sequence.
 //
 //               If the Node pointer is NULL, this quietly creates an
 //               empty qpNodePath.

+ 1 - 0
panda/src/pgraph/qpnodePath.h

@@ -68,6 +68,7 @@ PUBLISHED:
   };
 
   INLINE qpNodePath();
+  INLINE qpNodePath(const string &top_node_name);
   INLINE qpNodePath(PandaNode *top_node);
   INLINE qpNodePath(const qpNodePath &copy);
   INLINE void operator = (const qpNodePath &copy);

+ 17 - 11
panda/src/putil/pipelineCyclerBase.I

@@ -31,8 +31,7 @@ PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline) :
   _data(initial_data),
   _pipeline(pipeline),
   _read_count(0),
-  _write_count(0),
-  _stage_count(0)
+  _write_count(0)
 {
   if (_pipeline == (Pipeline *)NULL) {
     _pipeline = Pipeline::get_render_pipeline();
@@ -49,8 +48,7 @@ PipelineCyclerBase(const PipelineCyclerBase &copy) :
   _data(copy._data->make_copy()),
   _pipeline(copy._pipeline),
   _read_count(0),
-  _write_count(0),
-  _stage_count(0)
+  _write_count(0)
 {
 }
 
@@ -61,7 +59,7 @@ PipelineCyclerBase(const PipelineCyclerBase &copy) :
 ////////////////////////////////////////////////////////////////////
 INLINE void PipelineCyclerBase::
 operator = (const PipelineCyclerBase &copy) {
-  nassertv(_read_count == 0 && _write_count == 0 && _stage_count == 0);
+  nassertv(_read_count == 0 && _write_count == 0);
   _data = copy._data->make_copy();
   _pipeline = copy._pipeline;
 }
@@ -73,7 +71,7 @@ operator = (const PipelineCyclerBase &copy) {
 ////////////////////////////////////////////////////////////////////
 INLINE PipelineCyclerBase::
 ~PipelineCyclerBase() {
-  nassertv(_read_count == 0 && _write_count == 0 && _stage_count == 0);
+  nassertv(_read_count == 0 && _write_count == 0);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -92,7 +90,9 @@ read() const {
   // This function isn't truly const, but it doesn't change the data
   // in any meaningful way, so we pretend it is.
   ((PipelineCyclerBase *)this)->_read_count++;
-  nassertr(_write_count == 0, _data);
+
+  // It's not an error to grab a read pointer while someone else holds
+  // a read or a write pointer.
   return _data;
 }
 
@@ -146,8 +146,14 @@ release_read(const CycleData *pointer) const {
 INLINE CycleData *PipelineCyclerBase::
 write() {
   _write_count++;
+
+  // It's an error to grab a write pointer while someone else holds a
+  // read pointer, because doing so may invalidate the read pointer.
   nassertr(_read_count == 0, _data);
-  nassertr(_write_count == 1, _data);
+
+  // It's not an error to do this while someone else holds a write
+  // pointer, however.
+
   return _data;
 }
 
@@ -215,7 +221,7 @@ is_stage_unique(int n) const {
 INLINE CycleData *PipelineCyclerBase::
 write_stage(int n) {
   nassertr(n == 0, (CycleData *)NULL);
-  _stage_count++;
+  _write_count++;
   return _data;
 }
 
@@ -228,8 +234,8 @@ write_stage(int n) {
 INLINE void PipelineCyclerBase::
 release_write_stage(int n, CycleData *pointer) {
   nassertv(n == 0 && pointer == _data);
-  nassertv(_stage_count > 0);
-  _stage_count--;
+  nassertv(_write_count > 0);
+  _write_count--;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/putil/pipelineCyclerBase.h

@@ -62,7 +62,7 @@ public:
 private:
   PT(CycleData) _data;
   Pipeline *_pipeline;
-  short _read_count, _write_count, _stage_count;
+  short _read_count, _write_count;
 #endif  // DO_PIPELINING
 };