Browse Source

several new pgraph classes

David Rose 24 years ago
parent
commit
4c5a5d591c

+ 35 - 22
panda/src/collide/Sources.pp

@@ -10,28 +10,37 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx    
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx    
 
 
   #define SOURCES \
   #define SOURCES \
-     collisionEntry.I collisionEntry.h collisionHandler.h  \
-     collisionHandlerEvent.I collisionHandlerEvent.h  \
-     collisionHandlerFloor.I collisionHandlerFloor.h  \
-     collisionHandlerPhysical.I collisionHandlerPhysical.h  \
-     collisionHandlerPusher.I collisionHandlerPusher.h  \
-     collisionHandlerQueue.h collisionLevelState.I  \
-     collisionLevelState.N collisionLevelState.h collisionNode.I  \
-     collisionNode.h collisionPlane.I collisionPlane.h  \
-     collisionPolygon.I collisionPolygon.h collisionRay.I  \
-     collisionRay.h collisionSegment.I collisionSegment.h  \
-     collisionSolid.I collisionSolid.h collisionSphere.I  \
-     collisionSphere.h collisionTraverser.I collisionTraverser.h  \
-     config_collide.h
+    collisionEntry.I collisionEntry.h collisionHandler.h  \
+    collisionHandlerEvent.I collisionHandlerEvent.h  \
+    collisionHandlerFloor.I collisionHandlerFloor.h  \
+    collisionHandlerPhysical.I collisionHandlerPhysical.h  \
+    collisionHandlerPusher.I collisionHandlerPusher.h  \
+    collisionHandlerQueue.h collisionLevelState.I  \
+    collisionLevelState.N collisionLevelState.h \
+    collisionNode.I collisionNode.h \
+    qpcollisionNode.I qpcollisionNode.h \
+    collisionPlane.I collisionPlane.h  \
+    collisionPolygon.I collisionPolygon.h collisionRay.I  \
+    collisionRay.h collisionSegment.I collisionSegment.h  \
+    collisionSolid.I collisionSolid.h collisionSphere.I  \
+    collisionSphere.h \
+    collisionTraverser.I collisionTraverser.h  \
+    qpcollisionTraverser.I qpcollisionTraverser.h  \
+    config_collide.h
     
     
  #define INCLUDED_SOURCES \
  #define INCLUDED_SOURCES \
-     collisionEntry.cxx collisionHandler.cxx collisionHandlerEvent.cxx  \
-     collisionHandlerFloor.cxx collisionHandlerPhysical.cxx  \
-     collisionHandlerPusher.cxx collisionHandlerQueue.cxx  \
-     collisionLevelState.cxx collisionNode.cxx collisionPlane.cxx  \
-     collisionPolygon.cxx collisionRay.cxx collisionSegment.cxx  \
-     collisionSolid.cxx collisionSphere.cxx  \
-     collisionTraverser.cxx config_collide.cxx 
+    collisionEntry.cxx collisionHandler.cxx collisionHandlerEvent.cxx  \
+    collisionHandlerFloor.cxx collisionHandlerPhysical.cxx  \
+    collisionHandlerPusher.cxx collisionHandlerQueue.cxx  \
+    collisionLevelState.cxx \
+    collisionNode.cxx \
+    qpcollisionNode.cxx \
+    collisionPlane.cxx  \
+    collisionPolygon.cxx collisionRay.cxx collisionSegment.cxx  \
+    collisionSolid.cxx collisionSphere.cxx  \
+    collisionTraverser.cxx \
+    qpcollisionTraverser.cxx \
+    config_collide.cxx 
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     collisionEntry.I collisionEntry.h collisionHandler.h \
     collisionEntry.I collisionEntry.h collisionHandler.h \
@@ -40,11 +49,15 @@
     collisionHandlerPhysical.I collisionHandlerPhysical.h \
     collisionHandlerPhysical.I collisionHandlerPhysical.h \
     collisionHandlerPusher.I collisionHandlerPusher.h \
     collisionHandlerPusher.I collisionHandlerPusher.h \
     collisionHandlerQueue.h collisionLevelState.I collisionLevelState.h \
     collisionHandlerQueue.h collisionLevelState.I collisionLevelState.h \
-    collisionNode.I collisionNode.h collisionPlane.I collisionPlane.h \
+    collisionNode.I collisionNode.h \
+    qpcollisionNode.I qpcollisionNode.h \
+    collisionPlane.I collisionPlane.h \
     collisionPolygon.I collisionPolygon.h collisionRay.I collisionRay.h \
     collisionPolygon.I collisionPolygon.h collisionRay.I collisionRay.h \
     collisionSegment.I collisionSegment.h \
     collisionSegment.I collisionSegment.h \
     collisionSolid.I collisionSolid.h collisionSphere.I \
     collisionSolid.I collisionSolid.h collisionSphere.I \
-    collisionSphere.h collisionTraverser.I collisionTraverser.h
+    collisionSphere.h \
+    collisionTraverser.I collisionTraverser.h \
+    qpcollisionTraverser.I qpcollisionTraverser.h
 
 
   #define IGATESCAN all
   #define IGATESCAN all
 
 

+ 1 - 0
panda/src/collide/collide_composite1.cxx

@@ -9,4 +9,5 @@
 #include "collisionHandlerQueue.cxx"
 #include "collisionHandlerQueue.cxx"
 #include "collisionLevelState.cxx"
 #include "collisionLevelState.cxx"
 #include "collisionNode.cxx"
 #include "collisionNode.cxx"
+#include "qpcollisionNode.cxx"
 
 

+ 1 - 0
panda/src/collide/collide_composite2.cxx

@@ -6,5 +6,6 @@
 #include "collisionSolid.cxx"
 #include "collisionSolid.cxx"
 #include "collisionSphere.cxx"
 #include "collisionSphere.cxx"
 #include "collisionTraverser.cxx"
 #include "collisionTraverser.cxx"
+#include "qpcollisionTraverser.cxx"
 
 
 
 

+ 3 - 3
panda/src/collide/collisionEntry.h

@@ -102,9 +102,6 @@ private:
   LVector3f _into_surface_normal;
   LVector3f _into_surface_normal;
   float _into_depth;
   float _into_depth;
 
 
-  friend class CollisionTraverser;
-
-
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;
@@ -121,6 +118,9 @@ public:
 
 
 private:
 private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
+
+  friend class CollisionTraverser;
+  friend class qpCollisionTraverser;
 };
 };
 
 
 #include "collisionEntry.I"
 #include "collisionEntry.I"

+ 3 - 0
panda/src/collide/config_collide.cxx

@@ -25,6 +25,7 @@
 #include "collisionHandlerPusher.h"
 #include "collisionHandlerPusher.h"
 #include "collisionHandlerQueue.h"
 #include "collisionHandlerQueue.h"
 #include "collisionNode.h"
 #include "collisionNode.h"
+#include "qpcollisionNode.h"
 #include "collisionPlane.h"
 #include "collisionPlane.h"
 #include "collisionPolygon.h"
 #include "collisionPolygon.h"
 #include "collisionRay.h"
 #include "collisionRay.h"
@@ -64,6 +65,7 @@ init_libcollide() {
   CollisionHandlerPusher::init_type();
   CollisionHandlerPusher::init_type();
   CollisionHandlerQueue::init_type();
   CollisionHandlerQueue::init_type();
   CollisionNode::init_type();
   CollisionNode::init_type();
+  qpCollisionNode::init_type();
   CollisionPlane::init_type();
   CollisionPlane::init_type();
   CollisionPolygon::init_type();
   CollisionPolygon::init_type();
   CollisionRay::init_type();
   CollisionRay::init_type();
@@ -74,6 +76,7 @@ init_libcollide() {
   //Registration of writeable object's creation
   //Registration of writeable object's creation
   //functions with BamReader's factory
   //functions with BamReader's factory
   CollisionNode::register_with_read_factory();
   CollisionNode::register_with_read_factory();
+  qpCollisionNode::register_with_read_factory();
   CollisionPlane::register_with_read_factory();
   CollisionPlane::register_with_read_factory();
   CollisionPolygon::register_with_read_factory();
   CollisionPolygon::register_with_read_factory();
   CollisionSphere::register_with_read_factory();
   CollisionSphere::register_with_read_factory();

+ 165 - 0
panda/src/collide/qpcollisionNode.I

@@ -0,0 +1,165 @@
+// Filename: qpcollisionNode.I
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: qpCollisionNode::set_collide_mask
+//       Access: Public
+//  Description: Simultaneously sets both the "from" and "into"
+//               CollideMask values to the same thing.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCollisionNode::
+set_collide_mask(CollideMask mask) {
+  _from_collide_mask = mask;
+  _into_collide_mask = mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::set_from_collide_mask
+//       Access: Public
+//  Description: Sets the "from" CollideMask.  In order for a
+//               collision to be detected from this object into
+//               another object, the intersection of this object's
+//               "from" mask and the other object's "into" mask must
+//               be nonzero.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCollisionNode::
+set_from_collide_mask(CollideMask mask) {
+  _from_collide_mask = mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::set_into_collide_mask
+//       Access: Public
+//  Description: Sets the "into" CollideMask.  In order for a
+//               collision to be detected from another object into
+//               this object, the intersection of the other object's
+//               "from" mask and this object's "into" mask must be
+//               nonzero.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCollisionNode::
+set_into_collide_mask(CollideMask mask) {
+  _into_collide_mask = mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::get_from_collide_mask
+//       Access: Public
+//  Description: Returns the current "from" CollideMask.  In order for
+//               a collision to be detected from this object into
+//               another object, the intersection of this object's
+//               "from" mask and the other object's "into" mask must
+//               be nonzero.
+////////////////////////////////////////////////////////////////////
+INLINE CollideMask qpCollisionNode::
+get_from_collide_mask() const {
+  return _from_collide_mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::get_into_collide_mask
+//       Access: Public
+//  Description: Returns the current "into" CollideMask.  In order for
+//               a collision to be detected from another object into
+//               this object, the intersection of the other object's
+//               "from" mask and this object's "into" mask must be
+//               nonzero.
+////////////////////////////////////////////////////////////////////
+INLINE CollideMask qpCollisionNode::
+get_into_collide_mask() const {
+  return _into_collide_mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::set_collide_geom
+//       Access: Public
+//  Description: Sets the state of the "collide geom" flag for this
+//               qpCollisionNode.  Normally, this is false; when this is
+//               set true, the CollisionSolids in this node will test
+//               for collisions with actual renderable geometry, in
+//               addition to whatever CollisionSolids may be indicated
+//               by the from_collide_mask.
+//
+//               Setting this to true causes this to test *all*
+//               GeomNodes for collisions.  It is an all-or-none
+//               thing; there is no way to collide with only some
+//               GeomNodes, as GeomNodes have no into_collide_mask.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCollisionNode::
+set_collide_geom(bool flag) {
+  _collide_geom = flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::get_collide_geom
+//       Access: Public
+//  Description: Returns the current state of the collide_geom flag.
+//               See set_collide_geom().
+////////////////////////////////////////////////////////////////////
+INLINE bool qpCollisionNode::
+get_collide_geom() const {
+  return _collide_geom;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::get_num_solids
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE int qpCollisionNode::
+get_num_solids() const {
+  return _solids.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::get_solid
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CollisionSolid *qpCollisionNode::
+get_solid(int n) const {
+  nassertr(n >= 0 && n < get_num_solids(), NULL);
+  return _solids[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::remove_solid
+//       Access: Public
+//  Description: Removes the solid with the indicated index.  This
+//               will shift all subsequent indices down by one.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCollisionNode::
+remove_solid(int n) {
+  nassertv(n >= 0 && n < get_num_solids());
+  _solids.erase(_solids.begin() + n);
+  mark_bound_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::add_solid
+//       Access: Public
+//  Description: Adds the indicated solid to the node.  Returns the
+//               index of the new solid within the node's list of
+//               solids.
+////////////////////////////////////////////////////////////////////
+INLINE int qpCollisionNode::
+add_solid(CollisionSolid *solid) {
+  _solids.push_back(solid);
+  mark_bound_stale();
+  return _solids.size() - 1;
+}

+ 360 - 0
panda/src/collide/qpcollisionNode.cxx

@@ -0,0 +1,360 @@
+// Filename: qpcollisionNode.cxx
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "collisionNode.h"
+#include "config_collide.h"
+
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+
+TypeHandle qpCollisionNode::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpCollisionNode::
+qpCollisionNode(const string &name) :
+  PandaNode(name),
+  _from_collide_mask(CollideMask::all_on()),
+  _into_collide_mask(CollideMask::all_on()),
+  _collide_geom(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpCollisionNode::
+qpCollisionNode(const qpCollisionNode &copy) :
+  PandaNode(copy),
+  _from_collide_mask(copy._from_collide_mask),
+  _into_collide_mask(copy._into_collide_mask),
+  _collide_geom(copy._collide_geom),
+  _solids(copy._solids)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpCollisionNode::
+~qpCollisionNode() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated Node that is a shallow copy
+//               of this one.  It will be a different Node pointer,
+//               but its internal data may or may not be shared with
+//               that of the original Node.
+////////////////////////////////////////////////////////////////////
+PandaNode *qpCollisionNode::
+make_copy() const {
+  return new qpCollisionNode(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::preserve_name
+//       Access: Public, Virtual
+//  Description: Returns true if the node's name has extrinsic meaning
+//               and must be preserved across a flatten operation,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool qpCollisionNode::
+preserve_name() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::xform
+//       Access: Public, Virtual
+//  Description: Transforms the contents of this node by the indicated
+//               matrix, if it means anything to do so.  For most
+//               kinds of nodes, this does nothing.
+////////////////////////////////////////////////////////////////////
+void qpCollisionNode::
+xform(const LMatrix4f &mat) {
+  nassertv(!mat.is_nan());
+
+  if (mat.almost_equal(LMatrix4f::ident_mat())) {
+    return;
+  }
+
+  Solids::iterator si;
+  for (si = _solids.begin(); si != _solids.end(); ++si) {
+    CollisionSolid *solid = (*si);
+
+    // We may have to copy each of our solids as we transform them if
+    // someone else is sharing their pointers.
+    if (solid->get_ref_count() > 1) {
+      solid = solid->make_copy();
+      (*si) = solid;
+    }
+
+    solid->xform(mat);
+  }
+  mark_bound_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::combine_with
+//       Access: Public, Virtual
+//  Description: Collapses this node with the other node, if possible,
+//               and returns a pointer to the combined node, or NULL
+//               if the two nodes cannot safely be combined.
+//
+//               The return value may be this, other, or a new node
+//               altogether.
+//
+//               This function is called from GraphReducer::flatten(),
+//               and need not deal with children; its job is just to
+//               decide whether to collapse the two nodes and what the
+//               collapsed node should look like.
+////////////////////////////////////////////////////////////////////
+PandaNode *qpCollisionNode::
+combine_with(PandaNode *other) {
+  if (is_exact_type(get_class_type()) &&
+      other->is_exact_type(get_class_type())) {
+    // Two qpCollisionNodes can combine, but only if they have the same
+    // name, because the name is often meaningful.
+    qpCollisionNode *cother = DCAST(qpCollisionNode, other);
+    if (get_name() == cother->get_name()) {
+      const PT(CollisionSolid) *solids_begin = &cother->_solids[0];
+      const PT(CollisionSolid) *solids_end = solids_begin + cother->_solids.size();
+      _solids.insert(_solids.end(), solids_begin, solids_end);
+      mark_bound_stale();
+      return this;
+    }
+
+    // Two qpCollisionNodes with different names can't combine.
+    return (PandaNode *)NULL;
+  }
+
+  return PandaNode::combine_with(other);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::has_cull_callback
+//       Access: Public, Virtual
+//  Description: Should be overridden by derived classes to return
+//               true if cull_callback() has been defined.  Otherwise,
+//               returns false to indicate cull_callback() does not
+//               need to be called for this node during the cull
+//               traversal.
+////////////////////////////////////////////////////////////////////
+bool qpCollisionNode::
+has_cull_callback() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::cull_callback
+//       Access: Public, Virtual
+//  Description: If has_cull_callback() returns true, this function
+//               will be called during the cull traversal to perform
+//               any additional operations that should be performed at
+//               cull time.  This may include additional manipulation
+//               of render state or additional visible/invisible
+//               decisions, or any other arbitrary operation.
+//
+//               By the time this function is called, the node has
+//               already passed the bounding-volume test for the
+//               viewing frustum, and the node's transform and state
+//               have already been applied to the indicated
+//               CullTraverserData object.
+//
+//               The return value is true if this node should be
+//               visible, or false if it should be culled.
+////////////////////////////////////////////////////////////////////
+bool qpCollisionNode::
+cull_callback(qpCullTraverser *, CullTraverserData &) {
+  /*
+  Solids::iterator si;
+  for (si = _solids.begin(); si != _solids.end(); ++si) {
+    (*si)->update_viz(this);
+  }
+  */
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::output
+//       Access: Public, Virtual
+//  Description: Writes a brief description of the node to the
+//               indicated output stream.  This is invoked by the <<
+//               operator.  It may be overridden in derived classes to
+//               include some information relevant to the class.
+////////////////////////////////////////////////////////////////////
+void qpCollisionNode::
+output(ostream &out) const {
+  PandaNode::output(out);
+  out << " (" << _solids.size() << " solids)";
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::recompute_internal_bound
+//       Access: Protected, Virtual
+//  Description: Called when needed to recompute the node's
+//               _internal_bound object.  Nodes that contain anything
+//               of substance should redefine this to do the right
+//               thing.
+////////////////////////////////////////////////////////////////////
+BoundingVolume *qpCollisionNode::
+recompute_internal_bound() {
+  // First, get ourselves a fresh, empty bounding volume.
+  BoundingVolume *bound = PandaNode::recompute_bound();
+  nassertr(bound != (BoundingVolume *)NULL, bound);
+
+  // Now actually compute the bounding volume by putting it around all
+  // of our solids' bounding volumes.
+  pvector<const BoundingVolume *> child_volumes;
+
+  Solids::const_iterator gi;
+  for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
+    child_volumes.push_back(&(*gi)->get_bound());
+  }
+
+  const BoundingVolume **child_begin = &child_volumes[0];
+  const BoundingVolume **child_end = child_begin + child_volumes.size();
+
+  bool success =
+    bound->around(child_begin, child_end);
+
+#ifdef NOTIFY_DEBUG
+  if (!success) {
+    collide_cat.error()
+      << "Unable to generate bounding volume for " << *this << ":\n"
+      << "Cannot put " << bound->get_type() << " around:\n";
+    for (int i = 0; i < (int)child_volumes.size(); i++) {
+      collide_cat.error(false)
+        << "  " << *child_volumes[i] << "\n";
+    }
+  }
+#endif
+
+  return bound;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               qpCollisionNode.
+////////////////////////////////////////////////////////////////////
+void qpCollisionNode::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpCollisionNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PandaNode::write_datagram(manager, dg);
+
+  int num_solids = _solids.size();
+  dg.add_uint16(num_solids);
+  for(int i = 0; i < num_solids; i++) {
+    manager->write_pointer(dg, _solids[i]);
+  }
+
+  dg.add_uint32(_from_collide_mask.get_word());
+  dg.add_uint32(_into_collide_mask.get_word());
+  dg.add_bool(_collide_geom);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int qpCollisionNode::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = PandaNode::complete_pointers(p_list, manager);
+
+  int num_solids = _solids.size();
+  for (int i = 0; i < num_solids; i++) {
+    _solids[i] = DCAST(CollisionSolid, p_list[pi++]);
+  }
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type qpCollisionNode is encountered
+//               in the Bam file.  It should create the qpCollisionNode
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *qpCollisionNode::
+make_from_bam(const FactoryParams &params) {
+  qpCollisionNode *node = new qpCollisionNode("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionNode::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new qpCollisionNode.
+////////////////////////////////////////////////////////////////////
+void qpCollisionNode::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  PandaNode::fillin(scan, manager);
+
+  int num_solids = scan.get_uint16();
+  _solids.clear();
+  _solids.reserve(num_solids);
+  for(int i = 0; i < num_solids; i++) {
+    manager->read_pointer(scan);
+    // Push back a NULL for each solid, for now.  We'll fill them in
+    // later.
+    _solids.push_back((CollisionSolid *)NULL);
+  }
+
+  _from_collide_mask.set_word(scan.get_uint32());
+  _into_collide_mask.set_word(scan.get_uint32());
+  _collide_geom = scan.get_bool();
+}

+ 114 - 0
panda/src/collide/qpcollisionNode.h

@@ -0,0 +1,114 @@
+// Filename: qpcollisionNode.h
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 qpCOLLISIONNODE_H
+#define qpCOLLISIONNODE_H
+
+#include "pandabase.h"
+
+#include "collisionSolid.h"
+
+#include "collideMask.h"
+#include "pandaNode.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CollisionNode
+// Description : A node in the scene graph that can hold any number of
+//               CollisionSolids.  This may either represent a bit of
+//               static geometry in the scene that things will collide
+//               with, or an animated object twirling around in the
+//               world and running into things.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpCollisionNode : public PandaNode {
+PUBLISHED:
+  qpCollisionNode(const string &name);
+
+protected:
+  qpCollisionNode(const qpCollisionNode &copy);
+
+public:
+  virtual ~qpCollisionNode();
+  virtual PandaNode *make_copy() const;
+  virtual bool preserve_name() const;
+  virtual void xform(const LMatrix4f &mat);
+  virtual PandaNode *combine_with(PandaNode *other); 
+
+  virtual bool has_cull_callback() const;
+  virtual bool cull_callback(qpCullTraverser *trav, CullTraverserData &data);
+
+  virtual void output(ostream &out) const;
+
+PUBLISHED:
+  INLINE void set_collide_mask(CollideMask mask);
+  INLINE void set_from_collide_mask(CollideMask mask);
+  INLINE void set_into_collide_mask(CollideMask mask);
+  INLINE CollideMask get_from_collide_mask() const;
+  INLINE CollideMask get_into_collide_mask() const;
+
+  INLINE void set_collide_geom(bool flag);
+  INLINE bool get_collide_geom() const;
+
+  INLINE int get_num_solids() const;
+  INLINE CollisionSolid *get_solid(int n) const;
+  INLINE void remove_solid(int n);
+  INLINE int add_solid(CollisionSolid *solid);
+
+protected:
+  virtual BoundingVolume *recompute_internal_bound();
+
+private:
+  // This data is not cycled, for now.  We assume the collision
+  // traversal will take place in App only.  Perhaps we will revisit
+  // this later.
+  CollideMask _from_collide_mask;
+  CollideMask _into_collide_mask;
+  bool _collide_geom;
+
+  typedef pvector< PT(CollisionSolid) > Solids;
+  Solids _solids;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type(void) {
+    return _type_handle;
+  }
+  static void init_type(void) {
+    PandaNode::init_type();
+    register_type(_type_handle, "qpCollisionNode",
+                  PandaNode::get_class_type());
+  }
+  virtual TypeHandle get_type(void) const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qpcollisionNode.I"
+
+#endif

+ 17 - 0
panda/src/collide/qpcollisionTraverser.I

@@ -0,0 +1,17 @@
+// Filename: qpcollisionTraverser.I
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////

+ 631 - 0
panda/src/collide/qpcollisionTraverser.cxx

@@ -0,0 +1,631 @@
+// Filename: qpcollisionTraverser.cxx
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "qpcollisionTraverser.h"
+#include "qpcollisionNode.h"
+#include "collisionEntry.h"
+#include "collisionPolygon.h"
+#include "config_collide.h"
+
+#include "transformState.h"
+#include "qpgeomNode.h"
+#include "geom.h"
+#include "qpnodePath.h"
+#include "pStatTimer.h"
+
+#ifndef CPPPARSER
+PStatCollector qpCollisionTraverser::_collisions_pcollector("App:Collisions");
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpCollisionTraverser::
+qpCollisionTraverser() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpCollisionTraverser::
+~qpCollisionTraverser() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::add_collider
+//       Access: Public
+//  Description: Adds a new qpCollisionNode, representing an object that
+//               will be tested for collisions into other objects,
+//               along with the handler that will serve each detected
+//               collision.  Each qpCollisionNode may be served by only
+//               one handler at a time, but a given handler may serve
+//               many qpCollisionNodes.
+//
+//               The handler that serves a particular node may be
+//               changed from time to time by calling add_collider()
+//               again on the same node.
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+add_collider(qpCollisionNode *node, CollisionHandler *handler) {
+  nassertv(_ordered_colliders.size() == _colliders.size());
+  nassertv(node != (qpCollisionNode *)NULL);
+  nassertv(handler != (CollisionHandler *)NULL);
+
+  Colliders::iterator ci = _colliders.find(node);
+  if (ci != _colliders.end()) {
+    // We already knew about this collider.
+    if ((*ci).second != handler) {
+      // Change the handler.
+      PT(CollisionHandler) old_handler = (*ci).second;
+      (*ci).second = handler;
+
+      // Now update our own reference counts within our handler set.
+      Handlers::iterator hi = _handlers.find(old_handler);
+      nassertv(hi != _handlers.end());
+      (*hi).second--;
+      nassertv((*hi).second >= 0);
+      if ((*hi).second == 0) {
+        _handlers.erase(hi);
+      }
+
+      hi = _handlers.find(handler);
+      if (hi == _handlers.end()) {
+        _handlers.insert(Handlers::value_type(handler, 1));
+      } else {
+        (*hi).second++;
+      }
+    }
+
+  } else {
+    // We hadn't already known about this collider.
+    _colliders.insert(Colliders::value_type(node, handler));
+    _ordered_colliders.push_back(node);
+
+    Handlers::iterator hi = _handlers.find(handler);
+    if (hi == _handlers.end()) {
+      _handlers.insert(Handlers::value_type(handler, 1));
+    } else {
+      (*hi).second++;
+    }
+  }
+
+  nassertv(_ordered_colliders.size() == _colliders.size());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::remove_collider
+//       Access: Public
+//  Description: Removes the collider (and its associated handler)
+//               from the set of qpCollisionNodes that will be tested
+//               each frame for collisions into other objects.
+//               Returns true if the definition was found and removed,
+//               false if it wasn't present to begin with.
+////////////////////////////////////////////////////////////////////
+bool qpCollisionTraverser::
+remove_collider(qpCollisionNode *node) {
+  nassertr(_ordered_colliders.size() == _colliders.size(), false);
+
+  Colliders::iterator ci = _colliders.find(node);
+  if (ci == _colliders.end()) {
+    // We didn't know about this node.
+    return false;
+  }
+
+  CollisionHandler *handler = (*ci).second;
+
+  // Update the set of handlers.
+  Handlers::iterator hi = _handlers.find(handler);
+  nassertr(hi != _handlers.end(), false);
+  (*hi).second--;
+  nassertr((*hi).second >= 0, false);
+  if ((*hi).second == 0) {
+    _handlers.erase(hi);
+  }
+
+  _colliders.erase(ci);
+
+  OrderedColliders::iterator oci =
+    find(_ordered_colliders.begin(), _ordered_colliders.end(), node);
+  nassertr(oci != _ordered_colliders.end(), false);
+  _ordered_colliders.erase(oci);
+
+  nassertr(_ordered_colliders.size() == _colliders.size(), false);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::has_collider
+//       Access: Public
+//  Description: Returns true if the indicated node is current in the
+//               set of nodes that will be tested each frame for
+//               collisions into other objects.
+////////////////////////////////////////////////////////////////////
+bool qpCollisionTraverser::
+has_collider(qpCollisionNode *node) const {
+  Colliders::const_iterator ci = _colliders.find(node);
+  return (ci != _colliders.end());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::get_num_colliders
+//       Access: Public
+//  Description: Returns the number of qpCollisionNodes that have been
+//               added to the traverser via add_collider().
+////////////////////////////////////////////////////////////////////
+int qpCollisionTraverser::
+get_num_colliders() const {
+  nassertr(_ordered_colliders.size() == _colliders.size(), 0);
+  return _ordered_colliders.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::get_collider
+//       Access: Public
+//  Description: Returns the nth qpCollisionNode that has been
+//               added to the traverser via add_collider().
+////////////////////////////////////////////////////////////////////
+qpCollisionNode *qpCollisionTraverser::
+get_collider(int n) const {
+  nassertr(_ordered_colliders.size() == _colliders.size(), NULL);
+  nassertr(n >= 0 && n < (int)_ordered_colliders.size(), NULL);
+  return _ordered_colliders[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::get_handler
+//       Access: Public
+//  Description: Returns the handler that is currently assigned to
+//               serve the indicated collision node, or NULL if the
+//               node is not on the traverser's set of active nodes.
+////////////////////////////////////////////////////////////////////
+CollisionHandler *qpCollisionTraverser::
+get_handler(qpCollisionNode *node) const {
+  Colliders::const_iterator ci = _colliders.find(node);
+  if (ci != _colliders.end()) {
+    return (*ci).second;
+  };
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::clear_colliders
+//       Access: Public
+//  Description: Completely empties the set of collision nodes and
+//               their associated handlers.
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+clear_colliders() {
+  _colliders.clear();
+  _ordered_colliders.clear();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::traverse
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+traverse(const qpNodePath &root) {
+  /*
+  PStatTimer timer(_collisions_pcollector);
+
+  CollisionLevelState level_state(root);
+  prepare_colliders(level_state);
+
+  Handlers::iterator hi;
+  for (hi = _handlers.begin(); hi != _handlers.end(); ++hi) {
+    (*hi).first->begin_group();
+  }
+
+  df_traverse(root.node(), *this, NullTransitionWrapper(),
+              level_state, _graph_type);
+
+  hi = _handlers.begin();
+  while (hi != _handlers.end()) {
+    if (!(*hi).first->end_group()) {
+      // This handler wants to remove itself from the traversal list.
+      Handlers::iterator hnext = hi;
+      ++hnext;
+      _handlers.erase(hi);
+      hi = hnext;
+    } else {
+      ++hi;
+    }
+  }
+  */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::output
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+output(ostream &out) const {
+  out << "qpCollisionTraverser, " << _colliders.size()
+      << " colliders and " << _handlers.size() << " handlers.\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::write
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level)
+    << "qpCollisionTraverser, " << _colliders.size()
+    << " colliders and " << _handlers.size() << " handlers:\n";
+  Colliders::const_iterator ci;
+  for (ci = _colliders.begin(); ci != _colliders.end(); ++ci) {
+    qpCollisionNode *cnode = (*ci).first;
+    CollisionHandler *handler = (*ci).second;
+    nassertv(cnode != (qpCollisionNode *)NULL);
+    nassertv(handler != (CollisionHandler *)NULL);
+
+    indent(out, indent_level + 2)
+      << *cnode << " handled by " << handler->get_type() << "\n";
+    int num_solids = cnode->get_num_solids();
+    for (int i = 0; i < num_solids; i++) {
+      cnode->get_solid(i)->write(out, indent_level + 4);
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::prepare_colliders
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+prepare_colliders(CollisionLevelState &level_state) {
+  /*
+  level_state.clear();
+  level_state.reserve(_colliders.size());
+
+  int i = 0;
+  while (i < (int)_ordered_colliders.size()) {
+    qpCollisionNode *cnode = _ordered_colliders[i];
+
+    CollisionLevelState::ColliderDef def;
+    def._node = cnode;
+    get_rel_mat(cnode, NULL, def._space, _graph_type);
+    def._inv_space.invert_from(def._space);
+
+#ifndef NDEBUG
+    if (def._space.is_nan()) {
+      collide_cat.error()
+        << "Collider " << *cnode
+        << " has become NaN.  Dropping from traverser.\n";
+      // This is safe to do while traversing the list of colliders,
+      // because we do not increment i in this case.
+      remove_collider(cnode);
+    } else
+#endif
+      {
+        int num_solids = cnode->get_num_solids();
+        for (int s = 0; s < num_solids; s++) {
+          CollisionSolid *collider = cnode->get_solid(s);
+          def._collider = collider;
+          level_state.prepare_collider(def);
+        }
+        i++;
+      }
+  }
+  */
+}
+
+/*
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::reached_node
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+bool qpCollisionTraverser::
+reached_node(Node *node, NullTransitionWrapper &,
+             CollisionLevelState &level_state) {
+  if (node->is_of_type(qpCollisionNode::get_class_type())) {
+    level_state.reached_collision_node();
+
+    qpCollisionNode *cnode;
+    DCAST_INTO_R(cnode, node, false);
+    const BoundingVolume &node_bv = cnode->get_bound();
+    const GeometricBoundingVolume *node_gbv = NULL;
+    if (node_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
+      DCAST_INTO_R(node_gbv, &node_bv, false);
+    }
+
+    CollisionEntry entry;
+    entry._into_node = cnode;
+    entry._into_node_path = qpNodePath(level_state.get_arc_chain(), _graph_type);
+    entry._into_space = entry._into_node_path.get_mat(qpNodePath());
+
+    int num_colliders = level_state.get_num_colliders();
+    for (int c = 0; c < num_colliders; c++) {
+      if (level_state.has_collider(c)) {
+        entry._from_node = level_state.get_node(c);
+
+        if ((entry._from_node->get_from_collide_mask() &
+             cnode->get_into_collide_mask()) != 0) {
+          entry._from = level_state.get_collider(c);
+          entry._from_space = level_state.get_space(c);
+
+          qpNodePath root;
+          LMatrix4f into_space_inv = root.get_mat(entry._into_node_path);
+          entry._wrt_space = entry._from_space * into_space_inv;
+          entry._inv_wrt_space =
+            entry._into_space * level_state.get_inv_space(c);
+
+          const GeometricBoundingVolume *col_gbv =
+            level_state.get_local_bound(c);
+
+          compare_collider_to_node(entry, col_gbv, node_gbv);
+        }
+      }
+    }
+
+  } else if (node->is_of_type(qpGeomNode::get_class_type()) &&
+             level_state.has_any_collide_geom()) {
+    qpGeomNode *gnode;
+    DCAST_INTO_R(gnode, node, false);
+    const BoundingVolume &node_bv = gnode->get_bound();
+    const GeometricBoundingVolume *node_gbv = NULL;
+    if (node_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
+      DCAST_INTO_R(node_gbv, &node_bv, false);
+    }
+
+    CollisionEntry entry;
+    entry._into_node = gnode;
+    get_rel_mat(node, NULL, entry._into_space, _graph_type);
+
+    int num_colliders = level_state.get_num_colliders();
+    for (int c = 0; c < num_colliders; c++) {
+      if (level_state.has_collider_with_geom(c)) {
+        entry._from_node = level_state.get_node(c);
+
+        entry._from = level_state.get_collider(c);
+        entry._from_space = level_state.get_space(c);
+
+        LMatrix4f into_space_inv;
+        get_rel_mat(NULL, node, into_space_inv, _graph_type);
+        entry._wrt_space = entry._from_space * into_space_inv;
+        entry._inv_wrt_space =
+          entry._into_space * level_state.get_inv_space(c);
+
+        const GeometricBoundingVolume *col_gbv =
+          level_state.get_local_bound(c);
+
+        compare_collider_to_geom_node(entry, col_gbv, node_gbv);
+      }
+    }
+  }
+
+  return true;
+}
+*/
+
+/*
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::forward_arc
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+bool qpCollisionTraverser::
+forward_arc(NodeRelation *arc, NullTransitionWrapper &,
+            NullTransitionWrapper &, NullTransitionWrapper &,
+            CollisionLevelState &level_state) {
+  // Check the bounding volume on the arc against each of our
+  // colliders.
+  const BoundingVolume &arc_bv = arc->get_bound();
+  if (arc_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
+    const GeometricBoundingVolume *arc_gbv;
+    DCAST_INTO_R(arc_gbv, &arc_bv, false);
+
+    int num_colliders = level_state.get_num_colliders();
+    for (int c = 0; c < num_colliders; c++) {
+      if (level_state.has_collider(c)) {
+        const GeometricBoundingVolume *col_gbv =
+          level_state.get_local_bound(c);
+        if (col_gbv != (GeometricBoundingVolume *)NULL) {
+          if (arc_gbv->contains(col_gbv) == 0) {
+            // This collider certainly does not intersect with any
+            // geometry at this arc or below.
+            level_state.omit_collider(c);
+          }
+        }
+      }
+    }
+  }
+
+  if (!level_state.has_any_collider()) {
+    // No colliders intersect with the geometry at this arc or below,
+    // so don't bother traversing them.
+    return false;
+  }
+
+  TransformState *tt;
+  if (get_transition_into(tt, arc)) {
+    // There's a transform on the arc; apply it to our colliders'
+    // bounding volumes.
+    LMatrix4f inv_mat;
+    inv_mat.invert_from(tt->get_matrix());
+    level_state.xform(inv_mat);
+  }
+
+  level_state.forward_arc(arc);
+
+  return true;
+}
+*/
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::compare_collider_to_node
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+compare_collider_to_node(CollisionEntry &entry,
+                         const GeometricBoundingVolume *from_node_gbv,
+                         const GeometricBoundingVolume *into_node_gbv) {
+  /*
+  bool within_node_bounds = true;
+  if (from_node_gbv != (GeometricBoundingVolume *)NULL &&
+      into_node_gbv != (GeometricBoundingVolume *)NULL) {
+    within_node_bounds = (into_node_gbv->contains(from_node_gbv) != 0);
+  }
+
+  if (within_node_bounds) {
+    qpCollisionNode *cnode;
+    DCAST_INTO_V(cnode, entry._into_node);
+    int num_solids = cnode->get_num_solids();
+    for (int s = 0; s < num_solids; s++) {
+      entry._into = cnode->get_solid(s);
+      if (entry._from != entry._into) {
+        const BoundingVolume &solid_bv = entry._into->get_bound();
+        const GeometricBoundingVolume *solid_gbv = NULL;
+        if (num_solids > 1 &&
+            solid_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
+          // Only bother to test against each solid's bounding
+          // volume if we have more than one solid in the node, as a
+          // slight optimization.  (If the node contains just one
+          // solid, then the node's bounding volume, which we just
+          // tested, is the same as the solid's bounding volume.)
+          DCAST_INTO_V(solid_gbv, &solid_bv);
+        }
+
+        compare_collider_to_solid(entry, from_node_gbv, solid_gbv);
+      }
+    }
+  }
+  */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::compare_collider_to_geom_node
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+compare_collider_to_geom_node(CollisionEntry &entry,
+                              const GeometricBoundingVolume *from_node_gbv,
+                              const GeometricBoundingVolume *into_node_gbv) {
+  /*
+  bool within_node_bounds = true;
+  if (from_node_gbv != (GeometricBoundingVolume *)NULL &&
+      into_node_gbv != (GeometricBoundingVolume *)NULL) {
+    within_node_bounds = (into_node_gbv->contains(from_node_gbv) != 0);
+  }
+
+  if (within_node_bounds) {
+    qpGeomNode *gnode;
+    DCAST_INTO_V(gnode, entry._into_node);
+    int num_geoms = gnode->get_num_geoms();
+    for (int s = 0; s < num_geoms; s++) {
+      entry._into = (CollisionSolid *)NULL;
+      Geom *geom = DCAST(Geom, gnode->get_geom(s));
+      if (geom != (Geom *)NULL) {
+        const BoundingVolume &geom_bv = geom->get_bound();
+        const GeometricBoundingVolume *geom_gbv = NULL;
+        if (num_geoms > 1 &&
+            geom_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
+          // Only bother to test against each geom's bounding
+          // volume if we have more than one geom in the node, as a
+          // slight optimization.  (If the node contains just one
+          // geom, then the node's bounding volume, which we just
+          // tested, is the same as the geom's bounding volume.)
+          DCAST_INTO_V(geom_gbv, &geom_bv);
+        }
+
+        compare_collider_to_geom(entry, geom, from_node_gbv, geom_gbv);
+      }
+    }
+  }
+  */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::compare_collider_to_solid
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+compare_collider_to_solid(CollisionEntry &entry,
+                          const GeometricBoundingVolume *from_node_gbv,
+                          const GeometricBoundingVolume *solid_gbv) {
+  /*
+  bool within_solid_bounds = true;
+  if (from_node_gbv != (GeometricBoundingVolume *)NULL &&
+      solid_gbv != (GeometricBoundingVolume *)NULL) {
+    within_solid_bounds = (solid_gbv->contains(from_node_gbv) != 0);
+  }
+  if (within_solid_bounds) {
+    Colliders::const_iterator ci;
+    ci = _colliders.find(entry.get_from_node());
+    nassertv(ci != _colliders.end());
+    entry.get_from()->test_intersection((*ci).second, entry, entry.get_into());
+  }
+  */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCollisionTraverser::compare_collider_to_geom
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpCollisionTraverser::
+compare_collider_to_geom(CollisionEntry &entry, Geom *geom,
+                         const GeometricBoundingVolume *from_node_gbv,
+                         const GeometricBoundingVolume *geom_gbv) {
+  /*
+  bool within_geom_bounds = true;
+  if (from_node_gbv != (GeometricBoundingVolume *)NULL &&
+      geom_gbv != (GeometricBoundingVolume *)NULL) {
+    within_geom_bounds = (geom_gbv->contains(from_node_gbv) != 0);
+  }
+  if (within_geom_bounds) {
+    Colliders::const_iterator ci;
+    ci = _colliders.find(entry.get_from_node());
+    nassertv(ci != _colliders.end());
+
+    PTA_Vertexf coords;
+    PTA_ushort vindex;
+    geom->get_coords(coords, vindex);
+    PTA_ushort tris = geom->get_tris();
+
+    for (int i = 0; i < (int)tris.size(); i += 3) {
+      if (CollisionPolygon::verify_points(coords[tris[i]],
+                                          coords[tris[i + 1]],
+                                          coords[tris[i + 2]])) {
+        // Generate a temporary CollisionPolygon on the fly for each
+        // triangle in the Geom.
+        CollisionPolygon poly(coords[tris[i]], coords[tris[i + 1]],
+                              coords[tris[i + 2]]);
+        if (entry.get_from()->test_intersection((*ci).second, entry, &poly) != 0) {
+          return;
+        }
+      }
+    }
+  }
+  */
+}

+ 102 - 0
panda/src/collide/qpcollisionTraverser.h

@@ -0,0 +1,102 @@
+// Filename: qpcollisionTraverser.h
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 qpCOLLISIONTRAVERSER_H
+#define qpCOLLISIONTRAVERSER_H
+
+#include "pandabase.h"
+
+#include "collisionHandler.h"
+#include "collisionLevelState.h"
+
+#include "traverserVisitor.h"
+#include "nullTransitionWrapper.h"
+#include "pointerTo.h"
+#include "renderRelation.h"
+#include "pointerTo.h"
+#include "pStatCollector.h"
+
+#include "pset.h"
+
+class qpCollisionNode;
+class Geom;
+class qpNodePath;
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpCollisionTraverser
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpCollisionTraverser {
+PUBLISHED:
+  qpCollisionTraverser();
+  ~qpCollisionTraverser();
+
+  void add_collider(qpCollisionNode *node, CollisionHandler *handler);
+  bool remove_collider(qpCollisionNode *node);
+  bool has_collider(qpCollisionNode *node) const;
+  int get_num_colliders() const;
+  qpCollisionNode *get_collider(int n) const;
+  CollisionHandler *get_handler(qpCollisionNode *node) const;
+  void clear_colliders();
+
+  void traverse(const qpNodePath &root);
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level) const;
+
+private:
+  void prepare_colliders(CollisionLevelState &state);
+
+  void compare_collider_to_node(CollisionEntry &entry,
+                                const GeometricBoundingVolume *from_node_gbv,
+                                const GeometricBoundingVolume *into_node_gbv);
+  void compare_collider_to_geom_node(CollisionEntry &entry,
+                                     const GeometricBoundingVolume *from_node_gbv,
+                                     const GeometricBoundingVolume *into_node_gbv);
+  void compare_collider_to_solid(CollisionEntry &entry,
+                                 const GeometricBoundingVolume *from_node_gbv,
+                                 const GeometricBoundingVolume *solid_gbv);
+  void compare_collider_to_geom(CollisionEntry &entry, Geom *geom,
+                                const GeometricBoundingVolume *from_node_gbv,
+                                const GeometricBoundingVolume *solid_gbv);
+
+private:
+  PT(CollisionHandler) _default_handler;
+  TypeHandle _graph_type;
+
+  typedef pmap<PT(qpCollisionNode),  PT(CollisionHandler) > Colliders;
+  Colliders _colliders;
+  typedef pvector<qpCollisionNode *> OrderedColliders;
+  OrderedColliders _ordered_colliders;
+
+  typedef pmap<PT(CollisionHandler), int> Handlers;
+  Handlers _handlers;
+
+  // Statistics
+  static PStatCollector _collisions_pcollector;
+};
+
+INLINE ostream &operator << (ostream &out, const qpCollisionTraverser &trav) {
+  trav.output(out);
+  return out;
+}
+
+#include "qpcollisionTraverser.I"
+
+#endif
+

+ 22 - 38
panda/src/egg2pg/qpeggLoader.cxx

@@ -38,6 +38,8 @@
 #include "qpgeomNode.h"
 #include "qpgeomNode.h"
 #include "qpsequenceNode.h"
 #include "qpsequenceNode.h"
 #include "qplodNode.h"
 #include "qplodNode.h"
+#include "qpmodelNode.h"
+#include "qpmodelRoot.h"
 #include "string_utils.h"
 #include "string_utils.h"
 #include "eggPrimitive.h"
 #include "eggPrimitive.h"
 #include "eggPoint.h"
 #include "eggPoint.h"
@@ -53,6 +55,10 @@
 #include "qpcharacter.h"
 #include "qpcharacter.h"
 #include "animBundleMaker.h"
 #include "animBundleMaker.h"
 #include "qpanimBundleNode.h"
 #include "qpanimBundleNode.h"
+#include "qpcollisionNode.h"
+#include "collisionSphere.h"
+#include "collisionPlane.h"
+#include "collisionPolygon.h"
 
 
 #include <ctype.h>
 #include <ctype.h>
 #include <algorithm>
 #include <algorithm>
@@ -129,7 +135,7 @@ build_graph() {
   load_textures();
   load_textures();
 
 
   // Now build up the scene graph.
   // Now build up the scene graph.
-  _root = new PandaNode(_data.get_egg_filename().get_basename());
+  _root = new qpModelRoot(_data.get_egg_filename().get_basename());
   make_node(&_data, _root);
   make_node(&_data, _root);
   _builder.qpbuild();
   _builder.qpbuild();
 
 
@@ -1248,7 +1254,7 @@ make_node(EggBin *egg_bin, PandaNode *parent) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *qpEggLoader::
 PandaNode *qpEggLoader::
 make_node(EggGroup *egg_group, PandaNode *parent) {
 make_node(EggGroup *egg_group, PandaNode *parent) {
-  PandaNode *node = NULL;
+  PT(PandaNode) node = NULL;
 
 
   if (egg_group->has_objecttype()) {
   if (egg_group->has_objecttype()) {
     // We'll allow recursive expansion of ObjectType strings--but we
     // We'll allow recursive expansion of ObjectType strings--but we
@@ -1342,12 +1348,10 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
 
 
   } else if (egg_group->get_cs_type() != EggGroup::CST_none &&
   } else if (egg_group->get_cs_type() != EggGroup::CST_none &&
              egg_group->get_cs_type() != EggGroup::CST_geode) {
              egg_group->get_cs_type() != EggGroup::CST_geode) {
-    /*
     // A collision group: create collision geometry.
     // A collision group: create collision geometry.
-    node = new CollisionNode;
-    node->set_name(egg_group->get_name());
+    node = new qpCollisionNode(egg_group->get_name());
 
 
-    make_collision_solids(egg_group, egg_group, (CollisionNode *)node);
+    make_collision_solids(egg_group, egg_group, (qpCollisionNode *)node.p());
     if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) {
     if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) {
       // If we also specified to keep the geometry, continue the
       // If we also specified to keep the geometry, continue the
       // traversal.
       // traversal.
@@ -1357,13 +1361,12 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
       }
       }
     }
     }
 
 
-    PandaNode *arc = create_group_arc(egg_group, parent, node);
+    node = create_group_arc(egg_group, parent, node);
 
 
     if (!egg_show_collision_solids) {
     if (!egg_show_collision_solids) {
-      arc->set_transition(new PruneTransition());
+      node->set_draw_mask(DrawMask::all_off());
     }
     }
-    return arc;
-    */
+    return node;
 
 
   } else if (egg_group->get_switch_flag() &&
   } else if (egg_group->get_switch_flag() &&
              egg_group->get_switch_fps() != 0.0) {
              egg_group->get_switch_fps() != 0.0) {
@@ -1378,9 +1381,8 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
 
 
   } else if (egg_group->get_model_flag() || egg_group->get_dcs_flag()) {
   } else if (egg_group->get_model_flag() || egg_group->get_dcs_flag()) {
     // A model or DCS flag; create a model node.
     // A model or DCS flag; create a model node.
-    node = new PandaNode(egg_group->get_name());
-
-    //    DCAST(ModelNode, node)->set_preserve_transform(egg_group->get_dcs_flag());
+    node = new qpModelNode(egg_group->get_name());
+    DCAST(qpModelNode, node)->set_preserve_transform(egg_group->get_dcs_flag());
 
 
     EggGroup::const_iterator ci;
     EggGroup::const_iterator ci;
     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
@@ -1522,7 +1524,6 @@ make_node(EggGroupNode *egg_group, PandaNode *parent) {
   return node;
   return node;
 }
 }
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::make_collision_solids
 //     Function: qpEggLoader::make_collision_solids
 //       Access: Private
 //       Access: Private
@@ -1532,7 +1533,7 @@ make_node(EggGroupNode *egg_group, PandaNode *parent) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void qpEggLoader::
 void qpEggLoader::
 make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
 make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
-                      CollisionNode *cnode) {
+                      qpCollisionNode *cnode) {
   if (egg_group->get_cs_type() != EggGroup::CST_none) {
   if (egg_group->get_cs_type() != EggGroup::CST_none) {
     start_group = egg_group;
     start_group = egg_group;
   }
   }
@@ -1578,9 +1579,7 @@ make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
     }
     }
   }
   }
 }
 }
-*/
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::make_collision_plane
 //     Function: qpEggLoader::make_collision_plane
 //       Access: Private
 //       Access: Private
@@ -1588,7 +1587,7 @@ make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
 //               to the first polygon associated with this group.
 //               to the first polygon associated with this group.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void qpEggLoader::
 void qpEggLoader::
-make_collision_plane(EggGroup *egg_group, CollisionNode *cnode,
+make_collision_plane(EggGroup *egg_group, qpCollisionNode *cnode,
                      EggGroup::CollideFlags flags) {
                      EggGroup::CollideFlags flags) {
   EggGroup *geom_group = find_collision_geometry(egg_group);
   EggGroup *geom_group = find_collision_geometry(egg_group);
   if (geom_group != (EggGroup *)NULL) {
   if (geom_group != (EggGroup *)NULL) {
@@ -1606,9 +1605,7 @@ make_collision_plane(EggGroup *egg_group, CollisionNode *cnode,
     }
     }
   }
   }
 }
 }
-*/
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::make_collision_polygon
 //     Function: qpEggLoader::make_collision_polygon
 //       Access: Private
 //       Access: Private
@@ -1616,7 +1613,7 @@ make_collision_plane(EggGroup *egg_group, CollisionNode *cnode,
 //               to the first polygon associated with this group.
 //               to the first polygon associated with this group.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void qpEggLoader::
 void qpEggLoader::
-make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode,
+make_collision_polygon(EggGroup *egg_group, qpCollisionNode *cnode,
                        EggGroup::CollideFlags flags) {
                        EggGroup::CollideFlags flags) {
 
 
   EggGroup *geom_group = find_collision_geometry(egg_group);
   EggGroup *geom_group = find_collision_geometry(egg_group);
@@ -1630,9 +1627,7 @@ make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode,
     }
     }
   }
   }
 }
 }
-*/
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::make_collision_polyset
 //     Function: qpEggLoader::make_collision_polyset
 //       Access: Private
 //       Access: Private
@@ -1640,7 +1635,7 @@ make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode,
 //               to the polygons associated with this group.
 //               to the polygons associated with this group.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void qpEggLoader::
 void qpEggLoader::
-make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode,
+make_collision_polyset(EggGroup *egg_group, qpCollisionNode *cnode,
                        EggGroup::CollideFlags flags) {
                        EggGroup::CollideFlags flags) {
   EggGroup *geom_group = find_collision_geometry(egg_group);
   EggGroup *geom_group = find_collision_geometry(egg_group);
   if (geom_group != (EggGroup *)NULL) {
   if (geom_group != (EggGroup *)NULL) {
@@ -1653,9 +1648,7 @@ make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode,
     }
     }
   }
   }
 }
 }
-*/
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::make_collision_sphere
 //     Function: qpEggLoader::make_collision_sphere
 //       Access: Private
 //       Access: Private
@@ -1663,7 +1656,7 @@ make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode,
 //               to the polygons associated with this group.
 //               to the polygons associated with this group.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void qpEggLoader::
 void qpEggLoader::
-make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
+make_collision_sphere(EggGroup *egg_group, qpCollisionNode *cnode,
                       EggGroup::CollideFlags flags) {
                       EggGroup::CollideFlags flags) {
   EggGroup *geom_group = find_collision_geometry(egg_group);
   EggGroup *geom_group = find_collision_geometry(egg_group);
   if (geom_group != (EggGroup *)NULL) {
   if (geom_group != (EggGroup *)NULL) {
@@ -1729,9 +1722,7 @@ make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
     }
     }
   }
   }
 }
 }
-*/
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::apply_collision_flags
 //     Function: qpEggLoader::apply_collision_flags
 //       Access: Private
 //       Access: Private
@@ -1746,9 +1737,7 @@ apply_collision_flags(CollisionSolid *solid,
     solid->set_tangible(false);
     solid->set_tangible(false);
   }
   }
 }
 }
-*/
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::find_collision_geometry
 //     Function: qpEggLoader::find_collision_geometry
 //       Access: Private
 //       Access: Private
@@ -1786,9 +1775,7 @@ find_collision_geometry(EggGroup *egg_group) {
   // We got nothing.
   // We got nothing.
   return NULL;
   return NULL;
 }
 }
-*/
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::create_collision_plane
 //     Function: qpEggLoader::create_collision_plane
 //       Access: Private
 //       Access: Private
@@ -1832,18 +1819,16 @@ create_collision_plane(EggPolygon *egg_poly, EggGroup *parent_group) {
   Planef plane(vertices[0], vertices[1], vertices[2]);
   Planef plane(vertices[0], vertices[1], vertices[2]);
   return new CollisionPlane(plane);
   return new CollisionPlane(plane);
 }
 }
-*/
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::create_collision_polygons
 //     Function: qpEggLoader::create_collision_polygons
 //       Access: Private
 //       Access: Private
 //  Description: Creates one or more CollisionPolygons from the
 //  Description: Creates one or more CollisionPolygons from the
 //               indicated EggPolygon, and adds them to the indicated
 //               indicated EggPolygon, and adds them to the indicated
-//               CollisionNode.
+//               qpCollisionNode.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void qpEggLoader::
 void qpEggLoader::
-create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
+create_collision_polygons(qpCollisionNode *cnode, EggPolygon *egg_poly,
                           EggGroup *parent_group,
                           EggGroup *parent_group,
                           EggGroup::CollideFlags flags) {
                           EggGroup::CollideFlags flags) {
 
 
@@ -1899,7 +1884,6 @@ create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
     }
     }
   }
   }
 }
 }
-*/
 
 
 
 
 /*
 /*

+ 8 - 8
panda/src/egg2pg/qpeggLoader.h

@@ -47,7 +47,7 @@ class EggMaterial;
 class ComputedVerticesMaker;
 class ComputedVerticesMaker;
 class RenderRelation;
 class RenderRelation;
 class CollisionSolid;
 class CollisionSolid;
-class CollisionNode;
+class qpCollisionNode;
 class CollisionPlane;
 class CollisionPlane;
 class CollisionPolygon;
 class CollisionPolygon;
 
 
@@ -102,26 +102,26 @@ private:
   PandaNode *make_node(EggTable *egg_table, PandaNode *parent);
   PandaNode *make_node(EggTable *egg_table, PandaNode *parent);
   PandaNode *make_node(EggGroupNode *egg_group, PandaNode *parent);
   PandaNode *make_node(EggGroupNode *egg_group, PandaNode *parent);
 
 
-  /*
   void make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
   void make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
-                             CollisionNode *cnode);
-  void make_collision_plane(EggGroup *egg_group, CollisionNode *cnode,
+                             qpCollisionNode *cnode);
+  void make_collision_plane(EggGroup *egg_group, qpCollisionNode *cnode,
                             EggGroup::CollideFlags flags);
                             EggGroup::CollideFlags flags);
-  void make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode,
+  void make_collision_polygon(EggGroup *egg_group, qpCollisionNode *cnode,
                               EggGroup::CollideFlags flags);
                               EggGroup::CollideFlags flags);
-  void make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode,
+  void make_collision_polyset(EggGroup *egg_group, qpCollisionNode *cnode,
                               EggGroup::CollideFlags flags);
                               EggGroup::CollideFlags flags);
-  void make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
+  void make_collision_sphere(EggGroup *egg_group, qpCollisionNode *cnode,
                              EggGroup::CollideFlags flags);
                              EggGroup::CollideFlags flags);
   void apply_collision_flags(CollisionSolid *solid,
   void apply_collision_flags(CollisionSolid *solid,
                              EggGroup::CollideFlags flags);
                              EggGroup::CollideFlags flags);
   EggGroup *find_collision_geometry(EggGroup *egg_group);
   EggGroup *find_collision_geometry(EggGroup *egg_group);
   CollisionPlane *create_collision_plane(EggPolygon *egg_poly,
   CollisionPlane *create_collision_plane(EggPolygon *egg_poly,
                                          EggGroup *parent_group);
                                          EggGroup *parent_group);
-  void create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
+  void create_collision_polygons(qpCollisionNode *cnode, EggPolygon *egg_poly,
                                  EggGroup *parent_group,
                                  EggGroup *parent_group,
                                  EggGroup::CollideFlags flags);
                                  EggGroup::CollideFlags flags);
 
 
+  /*
   void apply_deferred_arcs(PandaNode *root);
   void apply_deferred_arcs(PandaNode *root);
   */
   */
 
 

+ 11 - 3
panda/src/grutil/Sources.pp

@@ -10,15 +10,23 @@
 
 
   #define SOURCES \
   #define SOURCES \
     cardMaker.I cardMaker.h \
     cardMaker.I cardMaker.h \
+    qpcardMaker.I qpcardMaker.h \
     config_grutil.h \
     config_grutil.h \
-    lineSegs.I lineSegs.h
+    lineSegs.I lineSegs.h \
+    qplineSegs.I qplineSegs.h
     
     
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
-    cardMaker.cxx config_grutil.cxx lineSegs.cxx
+    cardMaker.cxx \
+    qpcardMaker.cxx \
+    config_grutil.cxx \
+    qplineSegs.cxx \
+    lineSegs.cxx
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     cardMaker.I cardMaker.h \
     cardMaker.I cardMaker.h \
-    lineSegs.I lineSegs.h
+    qpcardMaker.I qpcardMaker.h \
+    lineSegs.I lineSegs.h \
+    qplineSegs.I qplineSegs.h
 
 
   #define IGATESCAN all
   #define IGATESCAN all
 
 

+ 2 - 0
panda/src/grutil/grutil_composite1.cxx

@@ -1,7 +1,9 @@
 
 
 #include "cardMaker.cxx"
 #include "cardMaker.cxx"
+#include "qpcardMaker.cxx"
 #include "config_grutil.cxx"
 #include "config_grutil.cxx"
 #include "lineSegs.cxx"
 #include "lineSegs.cxx"
+#include "qplineSegs.cxx"
  
  
  
  
      
      

+ 136 - 0
panda/src/grutil/qpcardMaker.I

@@ -0,0 +1,136 @@
+// Filename: qpcardMaker.I
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: qpCardMaker::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpCardMaker::
+qpCardMaker(const string &name) : Namable(name) {
+  reset();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpCardMaker::
+~qpCardMaker() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::set_uv_range
+//       Access: Public
+//  Description: Sets the range of UV's that will be applied to the
+//               vertices.  If set_has_uvs() is true (as it is by
+//               default), the vertices will be generated with the
+//               indicated range of UV's, which will be useful if a
+//               texture is applied.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCardMaker::
+set_uv_range(const TexCoordf &ll, const TexCoordf &ur) {
+  _ll = ll;
+  _ur = ur;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::set_has_uvs
+//       Access: Public
+//  Description: Sets the flag indicating whether vertices will be
+//               generated with UV's or not.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCardMaker::
+set_has_uvs(bool flag) {
+  _has_uvs = flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::set_frame
+//       Access: Public
+//  Description: Sets the size of the card.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCardMaker::
+set_frame(float left, float right, float bottom, float top) {
+  set_frame(LVecBase4f(left, right, bottom, top));
+} 
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::set_frame
+//       Access: Public
+//  Description: Sets the size of the card.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCardMaker::
+set_frame(const LVecBase4f &frame) {
+  _frame = frame;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::set_color
+//       Access: Public
+//  Description: Sets the color of the card.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCardMaker::
+set_color(float r, float g, float b, float a) {
+  set_color(LVecBase4f(r, g, b, a));
+} 
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::set_color
+//       Access: Public
+//  Description: Sets the color of the card.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCardMaker::
+set_color(const LVecBase4f &color) {
+  _color = color;
+  _has_color = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::set_source_geometry
+//       Access: Published
+//  Description: Sets a node that will be copied (and scaled and
+//               translated) to generate the frame, instead of
+//               generating a new polygon.  The node may contain
+//               arbitrary geometry that describes a flat polygon
+//               contained within the indicated left, right, bottom,
+//               top frame.
+//
+//               When generate() is called, the geometry in this node
+//               will be scaled and translated appropriately to give
+//               it the size and aspect ratio specified by
+//               set_frame().
+////////////////////////////////////////////////////////////////////
+INLINE void qpCardMaker::
+set_source_geometry(PandaNode *node, const LVecBase4f &frame) {
+  _source_geometry = node;
+  _source_frame = frame;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::clear_source_geometry
+//       Access: Published
+//  Description: Removes the node specified by an earlier call to
+//               set_source_geometry().
+////////////////////////////////////////////////////////////////////
+INLINE void qpCardMaker::
+clear_source_geometry() {
+  _source_geometry = (PandaNode *)NULL;
+}

+ 137 - 0
panda/src/grutil/qpcardMaker.cxx

@@ -0,0 +1,137 @@
+// Filename: qpcardMaker.cxx
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "qpcardMaker.h"
+#include "qpgeomNode.h"
+#include "geomTristrip.h"
+#include "transformState.h"
+#include "colorAttrib.h"
+#include "qpsceneGraphReducer.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::reset
+//       Access: Public
+//  Description: Resets all the parameters to their initial defaults.
+////////////////////////////////////////////////////////////////////
+void qpCardMaker::
+reset() {
+  _has_uvs = true;
+  _ll.set(0.0f, 0.0f);
+  _ur.set(1.0f, 1.0f);
+  _frame.set(0.0f, 1.0f, 0.0f, 1.0f);
+  _has_color = false;
+  _color.set(1.0f, 1.0f, 1.0f, 1.0f);
+  _source_geometry = (PandaNode *)NULL;
+  _source_frame.set(0.0f, 0.0f, 0.0f, 0.0f);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::generate
+//       Access: Public
+//  Description: Generates a GeomNode that renders the specified
+//               geometry.
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) qpCardMaker::
+generate() {
+  if (_source_geometry != (PandaNode *)NULL) {
+    return rescale_source_geometry();
+  }
+
+  PT(qpGeomNode) gnode = new qpGeomNode(get_name());
+  Geom *geom = new GeomTristrip;
+  gnode->add_geom(geom);
+
+  float left = _frame[0];
+  float right = _frame[1];
+  float bottom = _frame[2];
+  float top = _frame[3];
+
+  PTA_int lengths=PTA_int::empty_array(0);
+  lengths.push_back(4);
+
+  PTA_Vertexf verts;
+  verts.push_back(Vertexf::rfu(left, 0.0f, top));
+  verts.push_back(Vertexf::rfu(left, 0.0f, bottom));
+  verts.push_back(Vertexf::rfu(right, 0.0f, top));
+  verts.push_back(Vertexf::rfu(right, 0.0f, bottom));
+
+  geom->set_num_prims(1);
+  geom->set_lengths(lengths);
+
+  geom->set_coords(verts);
+
+  PTA_Colorf colors;
+  colors.push_back(_color);
+  geom->set_colors(colors, G_OVERALL);
+
+  if (_has_uvs) {
+    PTA_TexCoordf uvs;
+    uvs.push_back(TexCoordf(_ll[0], _ur[1]));
+    uvs.push_back(TexCoordf(_ll[0], _ll[1]));
+    uvs.push_back(TexCoordf(_ur[0], _ur[1]));
+    uvs.push_back(TexCoordf(_ur[0], _ll[1]));
+    geom->set_texcoords(uvs, G_PER_VERTEX);
+  }
+  
+  return gnode.p();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCardMaker::rescale_source_geometry
+//       Access: Private
+//  Description: Generates the card by rescaling the source geometry
+//               appropriately.
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) qpCardMaker::
+rescale_source_geometry() {
+  PT(PandaNode) root = _source_geometry->copy_subgraph();
+
+  // Determine the translate and scale appropriate for our geometry.
+  float geom_center_x = (_source_frame[0] + _source_frame[1]) * 0.5f;
+  float geom_center_y = (_source_frame[2] + _source_frame[3]) * 0.5f;
+
+  float frame_center_x = (_frame[0] + _frame[1]) * 0.5f;
+  float frame_center_y = (_frame[2] + _frame[3]) * 0.5f;
+
+  float scale_x = 
+    (_frame[1] - _frame[0]) / (_source_frame[1] - _source_frame[0]);
+  float scale_y = 
+    (_frame[3] - _frame[2]) / (_source_frame[3] - _source_frame[2]);
+
+  LVector3f trans = LVector3f::rfu(frame_center_x - geom_center_x, 0.0f, 
+                                   frame_center_y - geom_center_y);
+  LVector3f scale = LVector3f::rfu(scale_x, 1.0f, scale_y);
+
+  CPT(TransformState) transform = 
+    TransformState::make_pos_hpr_scale(trans, LPoint3f(0.0f, 0.0f, 0.0f),
+                                       scale);
+  root->set_transform(transform);
+
+  if (_has_color) {
+    root->set_attrib(ColorAttrib::make_flat(_color));
+  }
+
+  // Now flatten out the geometry as much as we can.
+  qpSceneGraphReducer reducer;
+  reducer.apply_attribs(root);
+  reducer.flatten(root, true);
+
+  return root;
+}

+ 72 - 0
panda/src/grutil/qpcardMaker.h

@@ -0,0 +1,72 @@
+// Filename: qpcardMaker.h
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 qpCARDMAKER_H
+#define qpCARDMAKER_H
+
+#include "pandabase.h"
+
+#include "luse.h"
+#include "pandaNode.h"
+#include "pointerTo.h"
+#include "namable.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpCardMaker
+// Description : This class generates 2-d "cards", that is,
+//               rectangular polygons, particularly useful for showing
+//               textures etc. in the 2-d scene graph.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpCardMaker : public Namable {
+PUBLISHED:
+  INLINE qpCardMaker(const string &name);
+  INLINE ~qpCardMaker();
+
+  void reset();
+  INLINE void set_uv_range(const TexCoordf &ll, const TexCoordf &ur);
+  INLINE void set_has_uvs(bool flag);
+
+  INLINE void set_frame(float left, float right, float bottom, float top);
+  INLINE void set_frame(const LVecBase4f &frame);
+
+  INLINE void set_color(float r, float g, float b, float a);
+  INLINE void set_color(const Colorf &color);
+
+  INLINE void set_source_geometry(PandaNode *node, const LVecBase4f &frame);
+  INLINE void clear_source_geometry();
+
+  PT(PandaNode) generate();
+
+private:
+  PT(PandaNode) rescale_source_geometry();
+
+  bool _has_uvs;
+  TexCoordf _ll, _ur;
+  LVecBase4f _frame;
+
+  bool _has_color;
+  Colorf _color;
+
+  PT(PandaNode) _source_geometry;
+  LVecBase4f _source_frame;
+};
+
+#include "qpcardMaker.I"
+
+#endif
+

+ 238 - 0
panda/src/grutil/qplineSegs.I

@@ -0,0 +1,238 @@
+// Filename: qplineSegs.I
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: qpLineSegs::Point::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpLineSegs::Point::
+Point() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::Point::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpLineSegs::Point::
+Point(const LVecBase3f &point, const Colorf &color) :
+  _point(point[0], point[1], point[2]),
+  _color(color)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::Point::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpLineSegs::Point::
+Point(const qpLineSegs::Point &copy) :
+  _point(copy._point),
+  _color(copy._color)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::Point::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::Point::
+operator = (const qpLineSegs::Point &copy) {
+  _point = copy._point;
+  _color = copy._color;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::set_color
+//       Access: Public
+//  Description: Establishes the color that will be assigned to all
+//               vertices created by future calls to move_to() and
+//               draw_to().
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::
+set_color(float r, float g, float b, float a) {
+  _color.set(r, g, b, a);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::set_color
+//       Access: Public
+//  Description: Establishes the color that will be assigned to all
+//               vertices created by future calls to move_to() and
+//               draw_to().
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::
+set_color(const Colorf &color) {
+  _color = color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::set_thickness
+//       Access: Public
+//  Description: Establishes the line thickness or point size in
+//               pixels that will be assigned to all lines and points
+//               created by future calls to create().
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::
+set_thickness(float thick) {
+  _thick = thick;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::move_to
+//       Access: Public
+//  Description: Moves the pen to the given point without drawing a
+//               line.  When followed by draw_to(), this marks the
+//               first point of a line segment; when followed by
+//               move_to() or create(), this creates a single point.
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::
+move_to(float x, float y, float z) {
+  move_to(Vertexf(x, y, z));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::draw_to
+//       Access: Public
+//  Description: Draws a line segment from the pen's last position
+//               (the last call to move_to or draw_to) to the
+//               indicated point.  move_to() and draw_to() only update
+//               tables; the actual drawing is performed when create()
+//               is called.
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::
+draw_to(float x, float y, float z) {
+  draw_to(Vertexf(x, y, z));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::create
+//       Access: Public
+//  Description: Creates a new GeomNode that will render the series of
+//               line segments and points described via calls to
+//               move_to() and draw_to().  The lines and points are
+//               created with the color and thickness established by
+//               calls to set_color() and set_thick().
+//
+//               If frame_accurate is true, the line segments will be
+//               created as a frame-accurate index, so that later
+//               calls to set_vertex or set_vertex_color will be
+//               visually correct.
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomNode *qpLineSegs::
+create(bool frame_accurate) {
+  qpGeomNode *gnode = new qpGeomNode(get_name());
+  return create(gnode, frame_accurate);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::get_num_vertices
+//       Access: Public
+//  Description: Returns the total number of line segment and point
+//               vertices generated by the last call to create().  The
+//               positions of these vertices may be read and adjusted
+//               through get_vertex() and set_vertex().
+////////////////////////////////////////////////////////////////////
+INLINE int qpLineSegs::
+get_num_vertices() const {
+  return _created_verts.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::get_vertex
+//       Access: Public
+//  Description: Returns the nth point or vertex of the line segment
+//               sequence generated by the last call to create().  The
+//               first move_to() generates vertex 0; subsequent
+//               move_to() and draw_to() calls generate consecutively
+//               higher vertex numbers.
+////////////////////////////////////////////////////////////////////
+INLINE Vertexf qpLineSegs::
+get_vertex(int vertex) const {
+  nassertr(vertex >= 0 && vertex < (int)_created_verts.size(),
+           Vertexf(0.0f, 0.0f, 0.0f));
+  return _created_verts[vertex];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::set_vertex
+//       Access: Public
+//  Description: Moves the nth point or vertex of the line segment
+//               sequence generated by the last call to create().  The
+//               first move_to() generates vertex 0; subsequent
+//               move_to() and draw_to() calls generate consecutively
+//               higher vertex numbers.
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::
+set_vertex(int vertex, const Vertexf &vert) {
+  nassertv(vertex >= 0 && vertex < (int)_created_verts.size());
+  _created_verts[vertex] = vert;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::set_vertex
+//       Access: Public
+//  Description: Moves the nth point or vertex of the line segment
+//               sequence generated by the last call to create().  The
+//               first move_to() generates vertex 0; subsequent
+//               move_to() and draw_to() calls generate consecutively
+//               higher vertex numbers.
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::
+set_vertex(int vertex, float x, float y, float z) {
+  set_vertex(vertex, Vertexf(x, y, z));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::get_vertex_color
+//       Access: Public
+//  Description: Returns the color of the nth point or vertex/
+////////////////////////////////////////////////////////////////////
+INLINE Colorf qpLineSegs::
+get_vertex_color(int vertex) const {
+  nassertr(vertex >= 0 && vertex < (int)_created_colors.size(),
+           Colorf(0.0f, 0.0f, 0.0f, 0.0f));
+  return _created_colors[vertex];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::set_vertex_color
+//       Access: Public
+//  Description: Changes the vertex color of the nth point or vertex.
+//               See set_vertex().
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::
+set_vertex_color(int vertex, const Colorf &color) {
+  nassertv(vertex >= 0 && vertex < (int)_created_verts.size());
+  _created_colors[vertex] = color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::set_vertex_color
+//       Access: Public
+//  Description: Changes the vertex color of the nth point or vertex.
+//               See set_vertex().
+////////////////////////////////////////////////////////////////////
+INLINE void qpLineSegs::
+set_vertex_color(int vertex, float r, float g, float b, float a) {
+  set_vertex_color(vertex, Colorf(r, g, b, a));
+}

+ 228 - 0
panda/src/grutil/qplineSegs.cxx

@@ -0,0 +1,228 @@
+// Filename: qplineSegs.cxx
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "qplineSegs.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::Constructor
+//       Access: Public
+//  Description: Constructs a qpLineSegs object, which can be used to
+//               create any number of disconnected lines or points of
+//               various thicknesses and colors through the visible
+//               scene.  After creating the object, call move_to() and
+//               draw_to() repeatedly to describe the path, then call
+//               create() to create a GeomNode which will render the
+//               described path.
+////////////////////////////////////////////////////////////////////
+qpLineSegs::
+qpLineSegs(const string &name) : Namable(name) {
+  _color.set(1.0f, 1.0f, 1.0f, 1.0f);
+  _thick = 1.0f;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::Destructor
+//       Access: Public
+////////////////////////////////////////////////////////////////////
+qpLineSegs::
+~qpLineSegs() {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::reset
+//       Access: Public
+//  Description: Removes any lines in progress and resets to the
+//               initial empty state.
+////////////////////////////////////////////////////////////////////
+void qpLineSegs::
+reset() {
+  _list.clear();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::move_to
+//       Access: Public
+//  Description: Moves the pen to the given point without drawing a
+//               line.  When followed by draw_to(), this marks the
+//               first point of a line segment; when followed by
+//               move_to() or create(), this creates a single point.
+////////////////////////////////////////////////////////////////////
+void qpLineSegs::
+move_to(const LVecBase3f &v) {
+  // We create a new SegmentList with the initial point in it.
+  SegmentList segs;
+  segs.push_back(Point(v, _color));
+
+  // And add this list to the list of segments.
+  _list.push_back(segs);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::draw_to
+//       Access: Public
+//  Description: Draws a line segment from the pen's last position
+//               (the last call to move_to or draw_to) to the
+//               indicated point.  move_to() and draw_to() only update
+//               tables; the actual drawing is performed when create()
+//               is called.
+////////////////////////////////////////////////////////////////////
+void qpLineSegs::
+draw_to(const LVecBase3f &v) {
+  if (_list.empty()) {
+    // Let our first call to draw_to() be an implicit move_to().
+    move_to(v);
+
+  } else {
+    // Get the current SegmentList, which was the last one we added to
+    // the LineList.
+    SegmentList &segs = _list.back();
+
+    // Add the new point.
+    segs.push_back(Point(v, _color));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::empty
+//       Access: Public
+//  Description: Returns true if move_to() or draw_to() have not been
+//               called since the last reset() or create(), false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool qpLineSegs::
+is_empty() {
+  return _list.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::get_current_position
+//       Access: Public
+//  Description: Returns the pen's current position.  The next call to
+//               draw_to() will draw a line segment from this point.
+////////////////////////////////////////////////////////////////////
+const Vertexf &qpLineSegs::
+get_current_position() {
+  if (_list.empty()) {
+    // Our pen isn't anywhere.  We'll put it somewhere.
+    move_to(Vertexf(0.0f, 0.0f, 0.0f));
+  }
+
+  return _list.back().back()._point;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLineSegs::create
+//       Access: Public
+//  Description: Appends to an existing GeomNode a new Geom that
+//               will render the series of line segments and points
+//               described via calls to move_to() and draw_to().  The
+//               lines and points are created with the color and
+//               thickness established by calls to set_color() and
+//               set_thick().
+//
+//               If frame_accurate is true, the line segments will be
+//               created as a frame-accurate index, so that later
+//               calls to set_vertex or set_vertex_color will be
+//               visually correct.
+////////////////////////////////////////////////////////////////////
+qpGeomNode *qpLineSegs::
+create(qpGeomNode *previous, bool) {
+  if (!_list.empty()) {
+    _created_verts.clear();
+    _created_colors.clear();
+
+    // One array each for the indices into these arrays for points
+    // and lines, and one for our line-segment lengths array.
+    PTA_ushort point_index;
+    PTA_ushort line_index;
+    PTA_int lengths;
+
+    // Now fill up the arrays.
+    int v = 0;
+    LineList::const_iterator ll;
+    SegmentList::const_iterator sl;
+
+    for (ll = _list.begin(); ll != _list.end(); ll++) {
+      const SegmentList &segs = (*ll);
+
+      if (segs.size() < 2) {
+        point_index.push_back(v);
+      } else {
+        lengths.push_back(segs.size());
+      }
+
+      for (sl = segs.begin(); sl != segs.end(); sl++) {
+        if (segs.size() >= 2) {
+          line_index.push_back(v);
+        }
+        _created_verts.push_back((*sl)._point);
+        _created_colors.push_back((*sl)._color);
+        v++;
+        nassertr(v == (int)_created_verts.size(), previous);
+      }
+    }
+
+    // Now create the lines.
+    if (line_index.size() > 0) {
+      // Create a new Geom and add the line segments.
+      Geom *geom;
+      if (line_index.size() <= 2) {
+        // Here's a special case: just one line segment.
+        GeomLine *geom_line = new GeomLine;
+        geom_line->set_num_prims(1);
+        geom_line->set_width(_thick);
+        geom = geom_line;
+
+      } else {
+        // The more normal case: multiple line segments, connected
+        // end-to-end like a series of linestrips.
+        GeomLinestrip *geom_linestrip = new GeomLinestrip;
+        geom_linestrip->set_num_prims(lengths.size());
+        geom_linestrip->set_lengths(lengths);
+        geom_linestrip->set_width(_thick);
+        geom = geom_linestrip;
+      }
+
+      geom->set_colors(_created_colors, G_PER_VERTEX, line_index);
+      geom->set_coords(_created_verts, line_index);
+
+      previous->add_geom(geom);
+    }
+
+    // And now create the points.
+    if (point_index.size() > 0) {
+      // Create a new Geom and add the points.
+      GeomPoint *geom = new GeomPoint;
+
+      geom->set_num_prims(point_index.size());
+      geom->set_size(_thick);
+      geom->set_colors(_created_colors, G_PER_VERTEX, point_index);
+      geom->set_coords(_created_verts, point_index);
+
+      previous->add_geom(geom);
+    }
+
+    // And reset for next time.
+    reset();
+  }
+
+  return previous;
+}

+ 100 - 0
panda/src/grutil/qplineSegs.h

@@ -0,0 +1,100 @@
+// Filename: qplineSegs.h
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 qpLINESEGS_H
+#define qpLINESEGS_H
+
+#include "pandabase.h"
+
+#include "luse.h"
+#include "geom.h"
+#include "geomPoint.h"
+#include "geomLine.h"
+#include "geomLinestrip.h"
+#include "qpgeomNode.h"
+#include "namable.h"
+
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpLineSegs
+// Description : Encapsulates creation of a series of connected or
+//               disconnected line segments or points, for drawing
+//               paths or rays.  This class doesn't attempt to be the
+//               smartest it could possibly be; it's intended
+//               primarily as a visualization and editing tool.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpLineSegs : public Namable {
+PUBLISHED:
+  qpLineSegs(const string &name = "lines");
+  ~qpLineSegs();
+
+  void reset();
+  INLINE void set_color(float r, float g, float b, float a = 1.0f);
+  INLINE void set_color(const Colorf &color);
+  INLINE void set_thickness(float thick);
+
+  INLINE void move_to(float x, float y, float z);
+  void move_to(const LVecBase3f &v);
+
+  INLINE void draw_to(float x, float y, float z);
+  void draw_to(const LVecBase3f &v);
+
+  const Vertexf &get_current_position();
+  bool is_empty();
+
+  INLINE qpGeomNode *create(bool frame_accurate = false);
+  qpGeomNode *create(qpGeomNode *previous, bool frame_accurate = false);
+
+  // Functions to move the line vertices after they have been created.
+  INLINE int get_num_vertices() const;
+
+  INLINE Vertexf get_vertex(int vertex) const;
+  INLINE void set_vertex(int vertex, const Vertexf &vert);
+  INLINE void set_vertex(int vertex, float x, float y, float z);
+
+  INLINE Colorf get_vertex_color(int vertex) const;
+  INLINE void set_vertex_color(int vertex, const Colorf &color);
+  INLINE void set_vertex_color(int vertex, float r, float g, float b, float a = 1.0f);
+
+private:
+  class Point {
+  public:
+    INLINE Point();
+    INLINE Point(const LVecBase3f &point, const Colorf &color);
+    INLINE Point(const Point &copy);
+    INLINE void operator = (const Point &copy);
+
+    Vertexf _point;
+    Colorf _color;
+  };
+
+  typedef pvector<Point> SegmentList;
+  typedef pvector<SegmentList> LineList;
+
+  LineList _list;
+  Colorf _color;
+  float _thick;
+
+  PTA_Vertexf _created_verts;
+  PTA_Colorf _created_colors;
+};
+
+#include "qplineSegs.I"
+
+#endif

+ 6 - 0
panda/src/pgraph/Sources.pp

@@ -39,6 +39,8 @@
     qplensNode.I qplensNode.h \
     qplensNode.I qplensNode.h \
     qplodNode.I qplodNode.h \
     qplodNode.I qplodNode.h \
     materialAttrib.I materialAttrib.h \
     materialAttrib.I materialAttrib.h \
+    qpmodelNode.I qpmodelNode.h \
+    qpmodelRoot.I qpmodelRoot.h \
     qpnodePath.I qpnodePath.h \
     qpnodePath.I qpnodePath.h \
     qpnodePathCollection.I qpnodePathCollection.h \
     qpnodePathCollection.I qpnodePathCollection.h \
     qpnodePathComponent.I qpnodePathComponent.h \
     qpnodePathComponent.I qpnodePathComponent.h \
@@ -92,6 +94,8 @@
     qplensNode.cxx \
     qplensNode.cxx \
     qplodNode.cxx \
     qplodNode.cxx \
     materialAttrib.cxx \
     materialAttrib.cxx \
+    qpmodelNode.cxx \
+    qpmodelRoot.cxx \
     qpnodePath.cxx \
     qpnodePath.cxx \
     qpnodePathCollection.cxx \
     qpnodePathCollection.cxx \
     qpnodePathComponent.cxx \
     qpnodePathComponent.cxx \
@@ -147,6 +151,8 @@
     qplensNode.I qplensNode.h \
     qplensNode.I qplensNode.h \
     qplodNode.I qplodNode.h \
     qplodNode.I qplodNode.h \
     materialAttrib.I materialAttrib.h \
     materialAttrib.I materialAttrib.h \
+    qpmodelNode.I qpmodelNode.h \
+    qpmodelRoot.I qpmodelRoot.h \
     qpnodePath.I qpnodePath.h \
     qpnodePath.I qpnodePath.h \
     qpnodePathCollection.I qpnodePathCollection.h \
     qpnodePathCollection.I qpnodePathCollection.h \
     qpnodePathComponent.I qpnodePathComponent.h \
     qpnodePathComponent.I qpnodePathComponent.h \

+ 6 - 0
panda/src/pgraph/config_pgraph.cxx

@@ -40,6 +40,8 @@
 #include "qplensNode.h"
 #include "qplensNode.h"
 #include "qplodNode.h"
 #include "qplodNode.h"
 #include "materialAttrib.h"
 #include "materialAttrib.h"
+#include "qpmodelNode.h"
+#include "qpmodelRoot.h"
 #include "qpnodePath.h"
 #include "qpnodePath.h"
 #include "qpnodePathComponent.h"
 #include "qpnodePathComponent.h"
 #include "pandaNode.h"
 #include "pandaNode.h"
@@ -113,6 +115,8 @@ init_libpgraph() {
   qpLensNode::init_type();
   qpLensNode::init_type();
   qpLODNode::init_type();
   qpLODNode::init_type();
   MaterialAttrib::init_type();
   MaterialAttrib::init_type();
+  qpModelNode::init_type();
+  qpModelRoot::init_type();
   qpNodePath::init_type();
   qpNodePath::init_type();
   qpNodePathComponent::init_type();
   qpNodePathComponent::init_type();
   PandaNode::init_type();
   PandaNode::init_type();
@@ -146,6 +150,8 @@ init_libpgraph() {
   qpLensNode::register_with_read_factory();
   qpLensNode::register_with_read_factory();
   qpLODNode::register_with_read_factory();
   qpLODNode::register_with_read_factory();
   MaterialAttrib::register_with_read_factory();
   MaterialAttrib::register_with_read_factory();
+  qpModelNode::register_with_read_factory();
+  qpModelRoot::register_with_read_factory();
   PandaNode::register_with_read_factory();
   PandaNode::register_with_read_factory();
   RenderEffects::register_with_read_factory();
   RenderEffects::register_with_read_factory();
   RenderModeAttrib::register_with_read_factory();
   RenderModeAttrib::register_with_read_factory();

+ 2 - 0
panda/src/pgraph/pgraph_composite2.cxx

@@ -8,6 +8,8 @@
 #include "qplensNode.cxx"
 #include "qplensNode.cxx"
 #include "qplodNode.cxx"
 #include "qplodNode.cxx"
 #include "materialAttrib.cxx"
 #include "materialAttrib.cxx"
+#include "qpmodelNode.cxx"
+#include "qpmodelRoot.cxx"
 #include "qpnodePath.cxx"
 #include "qpnodePath.cxx"
 #include "qpnodePathCollection.cxx"
 #include "qpnodePathCollection.cxx"
 #include "qpnodePathComponent.cxx"
 #include "qpnodePathComponent.cxx"

+ 67 - 0
panda/src/pgraph/qpmodelNode.I

@@ -0,0 +1,67 @@
+// Filename: qpmodelNode.I
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: qpModelNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpModelNode::
+qpModelNode(const string &name) :
+  PandaNode(name)
+{
+  _preserve_transform = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::set_preserve_transform
+//       Access: Public
+//  Description: Sets the preserve_transform flag.  When this flag is
+//               true, flattening the scene graph will not flatten out
+//               any transformation assigned above this node;
+//               otherwise, any transforms applying to this node may
+//               or may not be flattened.
+////////////////////////////////////////////////////////////////////
+INLINE void qpModelNode::
+set_preserve_transform(bool preserve_transform) {
+  _preserve_transform = preserve_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::get_preserve_transform
+//       Access: Public
+//  Description: Returns the current setting of the preserve_transform
+//               flag.  See set_preserve_transform().
+////////////////////////////////////////////////////////////////////
+INLINE bool qpModelNode::
+get_preserve_transform() const {
+  return _preserve_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpModelNode::
+qpModelNode(const qpModelNode &copy) :
+  PandaNode(copy),
+  _preserve_transform(copy._preserve_transform)
+{
+}

+ 140 - 0
panda/src/pgraph/qpmodelNode.cxx

@@ -0,0 +1,140 @@
+// Filename: qpmodelNode.cxx
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "qpmodelNode.h"
+
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle qpModelNode::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated Node that is a shallow copy
+//               of this one.  It will be a different Node pointer,
+//               but its internal data may or may not be shared with
+//               that of the original Node.
+////////////////////////////////////////////////////////////////////
+PandaNode *qpModelNode::
+make_copy() const {
+  return new qpModelNode(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::safe_to_flatten
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to flatten out
+//               this particular kind of Node by duplicating
+//               instances, false otherwise (for instance, a Camera
+//               cannot be safely flattened, because the Camera
+//               pointer itself is meaningful).
+////////////////////////////////////////////////////////////////////
+bool qpModelNode::
+safe_to_flatten() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::safe_to_transform
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to transform
+//               this particular kind of Node by calling the xform()
+//               method, false otherwise.  For instance, it's usually
+//               a bad idea to attempt to xform a Character.
+////////////////////////////////////////////////////////////////////
+bool qpModelNode::
+safe_to_transform() const {
+  return !_preserve_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::preserve_name
+//       Access: Public, Virtual
+//  Description: Returns true if the node's name has extrinsic meaning
+//               and must be preserved across a flatten operation,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool qpModelNode::
+preserve_name() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               qpModelNode.
+////////////////////////////////////////////////////////////////////
+void qpModelNode::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpModelNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PandaNode::write_datagram(manager, dg);
+  dg.add_bool(_preserve_transform);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type qpModelNode is encountered
+//               in the Bam file.  It should create the qpModelNode
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *qpModelNode::
+make_from_bam(const FactoryParams &params) {
+  qpModelNode *node = new qpModelNode("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new qpModelNode.
+////////////////////////////////////////////////////////////////////
+void qpModelNode::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  PandaNode::fillin(scan, manager);
+
+  if (manager->get_file_minor_ver() < 2) {
+    // No _preserve_transform before bams 3.2.
+    _preserve_transform = false;
+  } else {
+    _preserve_transform = scan.get_bool();
+  }
+}

+ 89 - 0
panda/src/pgraph/qpmodelNode.h

@@ -0,0 +1,89 @@
+// Filename: qpmodelNode.h
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 qpMODELNODE_H
+#define qpMODELNODE_H
+
+#include "pandabase.h"
+
+#include "pandaNode.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpModelNode
+// Description : This node is placed at key points within the scene
+//               graph to indicate the roots of "models": subtrees
+//               that are conceptually to be treated as a single unit,
+//               like a car or a room, for instance.  It doesn't
+//               affect rendering or any other operations; it's
+//               primarily useful as a high-level model indication.
+//
+//               qpModelNodes are created in response to a <Model> { 1 }
+//               flag within an egg file.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpModelNode : public PandaNode {
+PUBLISHED:
+  INLINE qpModelNode(const string &name);
+
+protected:
+  INLINE qpModelNode(const qpModelNode &copy);
+
+public:
+  virtual PandaNode *make_copy() const;
+
+  virtual bool safe_to_flatten() const;
+  virtual bool safe_to_transform() const;
+  virtual bool preserve_name() const;
+
+PUBLISHED:
+  INLINE void set_preserve_transform(bool preserve_transform);
+  INLINE bool get_preserve_transform() const;
+
+private:
+  bool _preserve_transform;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PandaNode::init_type();
+    register_type(_type_handle, "qpModelNode",
+                  PandaNode::get_class_type());
+  }
+  virtual TypeHandle get_type(void) const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qpmodelNode.I"
+
+#endif
+
+

+ 39 - 0
panda/src/pgraph/qpmodelRoot.I

@@ -0,0 +1,39 @@
+// Filename: qpmodelRoot.I
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: qpModelRoot::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpModelRoot::
+qpModelRoot(const string &name) :
+  qpModelNode(name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelRoot::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpModelRoot::
+qpModelRoot(const qpModelRoot &copy) :
+  qpModelNode(copy)
+{
+}

+ 89 - 0
panda/src/pgraph/qpmodelRoot.cxx

@@ -0,0 +1,89 @@
+// Filename: qpmodelRoot.cxx
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "qpmodelRoot.h"
+
+TypeHandle qpModelRoot::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelRoot::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated Node that is a shallow copy
+//               of this one.  It will be a different Node pointer,
+//               but its internal data may or may not be shared with
+//               that of the original Node.
+////////////////////////////////////////////////////////////////////
+PandaNode *qpModelRoot::
+make_copy() const {
+  return new qpModelRoot(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelRoot::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               qpModelRoot.
+////////////////////////////////////////////////////////////////////
+void qpModelRoot::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelRoot::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpModelRoot::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  qpModelNode::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelRoot::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type qpModelRoot is encountered
+//               in the Bam file.  It should create the qpModelRoot
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *qpModelRoot::
+make_from_bam(const FactoryParams &params) {
+  qpModelRoot *node = new qpModelRoot("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelRoot::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new qpModelRoot.
+////////////////////////////////////////////////////////////////////
+void qpModelRoot::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  qpModelNode::fillin(scan, manager);
+}

+ 75 - 0
panda/src/pgraph/qpmodelRoot.h

@@ -0,0 +1,75 @@
+// Filename: qpmodelRoot.h
+// Created by:  drose (16Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 qpMODELROOT_H
+#define qpMODELROOT_H
+
+#include "pandabase.h"
+
+#include "qpmodelNode.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpModelRoot
+// Description : A node of this type is created automatically at the
+//               root of each model file that is loaded.  It may
+//               eventually contain some information about the
+//               contents of the model; at the moment, it contains no
+//               special information, but can be used as a flag to
+//               indicate the presence of a loaded model file.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpModelRoot : public qpModelNode {
+PUBLISHED:
+  INLINE qpModelRoot(const string &name );
+
+protected:
+  INLINE qpModelRoot(const qpModelRoot &copy);
+
+public:
+  virtual PandaNode *make_copy() const;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpModelNode::init_type();
+    register_type(_type_handle, "qpModelRoot",
+                  qpModelNode::get_class_type());
+  }
+  virtual TypeHandle get_type(void) const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qpmodelRoot.I"
+
+#endif
+
+