소스 검색

pgraph bounding volumes

David Rose 24 년 전
부모
커밋
168c647d05

+ 2 - 0
panda/src/graph/boundedObject.h

@@ -98,6 +98,8 @@ public:
 
 private:
   static TypeHandle _type_handle;
+
+  friend class PandaNode;
 };
 
 #ifndef DONT_INLINE_GRAPH

+ 3 - 3
panda/src/mathutil/boundingVolume.h

@@ -19,10 +19,10 @@
 #ifndef BOUNDINGVOLUME_H
 #define BOUNDINGVOLUME_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
-#include <typedObject.h>
-#include <typedReferenceCount.h>
+#include "typedObject.h"
+#include "typedReferenceCount.h"
 
 class BoundingSphere;
 class BoundingHexahedron;

+ 16 - 7
panda/src/pgraph/cullBinBackToFront.cxx

@@ -18,6 +18,7 @@
 
 #include "cullBinBackToFront.h"
 #include "graphicsStateGuardianBase.h"
+#include "geometricBoundingVolume.h"
 
 #include <algorithm>
 
@@ -33,14 +34,22 @@ TypeHandle CullBinBackToFront::_type_handle;
 void CullBinBackToFront::
 add_geom(Geom *geom, const TransformState *transform,
          const RenderState *state) {
-  // Since we don't have bounding volumes yet, for now we'll just use
-  // the origin of the node.  Only accurate for local transforms.
-  const LMatrix4f &mat = transform->get_mat();
-  const LVecBase3f &pos = mat.get_row3(3);
+  // Determine the center of the bounding volume.
+  const BoundingVolume &volume = geom->get_bound();
 
-  // Oops!  Don't have compute_distance_to() here either!
-  float dist = -pos[2];
-  _geoms.push_back(GeomData(geom, transform, state, dist));
+  if (!volume.is_empty() &&
+      volume.is_of_type(GeometricBoundingVolume::get_class_type())) {
+    const GeometricBoundingVolume *gbv;
+    DCAST_INTO_V(gbv, &volume);
+    
+    LPoint3f center = gbv->get_approx_center();
+    center = center * transform->get_mat();
+    
+    // Oops!  Don't have compute_distance_to() here yet!
+    //    float distance = gsg->compute_distance_to(center);
+    float distance = -center[2];
+    _geoms.push_back(GeomData(geom, transform, state, distance));
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 66 - 0
panda/src/pgraph/pandaNode.I

@@ -341,6 +341,7 @@ INLINE void PandaNode::
 set_transform(const TransformState *transform) {
   CDWriter cdata(_cycler);
   cdata->_transform = transform;
+  mark_bound_stale();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -391,6 +392,71 @@ ls(ostream &out, int indent_level) const {
   r_list_descendants(out, indent_level);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::set_bound
+//       Access: Published
+//  Description: Sets the type of the external bounding volume that is
+//               placed around this node and all of its children.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+set_bound(BoundingVolumeType type) {
+  BoundedObject::set_bound(type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::set_bound
+//       Access: Published
+//  Description: Resets the internal bounding volume so that it is the
+//               indicated volume.  The external bounding volume as
+//               returned by get_bound() (which includes all of the
+//               node's children) will be adjusted to include this
+//               internal volume.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+set_bound(const BoundingVolume &volume) {
+  _internal_bound.set_bound(volume);
+  changed_internal_bound();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_bound
+//       Access: Published
+//  Description: Returns the node's external bounding volume.  This is
+//               the bounding volume around the node and all of its
+//               children.
+////////////////////////////////////////////////////////////////////
+INLINE const BoundingVolume &PandaNode::
+get_bound() const {
+  return BoundedObject::get_bound();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_internal_bound
+//       Access: Published
+//  Description: Returns the node's internal bounding volume.  This is
+//               the bounding volume around the node alone, without
+//               including children.
+////////////////////////////////////////////////////////////////////
+INLINE const BoundingVolume &PandaNode::
+get_internal_bound() const {
+  if (_internal_bound.is_bound_stale()) {
+    ((PandaNode *)this)->recompute_internal_bound();
+  }
+  return _internal_bound.get_bound();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::changed_internal_bound
+//       Access: Protected
+//  Description: Should be called whenever you adjust the
+//               _internal_bound member, to force the external
+//               bounding volume to be recomputed.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+changed_internal_bound() {
+  BoundedObject::mark_bound_stale();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_children
 //       Access: Public

+ 99 - 6
panda/src/pgraph/pandaNode.cxx

@@ -22,6 +22,7 @@
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "indent.h"
+#include "geometricBoundingVolume.h"
 
 
 TypeHandle PandaNode::_type_handle;
@@ -37,8 +38,6 @@ CData(const PandaNode::CData &copy) :
   _down(copy._down),
   _up(copy._up),
   _chains(copy._chains),
-  _node_bounds(copy._node_bounds),
-  _subgraph_bounds(copy._subgraph_bounds),
   _state(copy._state),
   _transform(copy._transform)
 {
@@ -93,12 +92,11 @@ PandaNode(const PandaNode &copy) :
 {
   // Copying a node does not copy its children.
 
-  // Copy the other node's state and bounding volume.
+  // Copy the other node's state.
   CDReader copy_cdata(copy._cycler);
   CDWriter cdata(_cycler);
   cdata->_state = copy_cdata->_state;
   cdata->_transform = copy_cdata->_transform;
-  cdata->_node_bounds = copy_cdata->_node_bounds;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -112,12 +110,11 @@ operator = (const PandaNode &copy) {
   Namable::operator = (copy);
   ReferenceCount::operator = (copy);
 
-  // Copy the other node's state and bounding volume.
+  // Copy the other node's state.
   CDReader copy_cdata(copy._cycler);
   CDWriter cdata(_cycler);
   cdata->_state = copy_cdata->_state;
   cdata->_transform = copy_cdata->_transform;
-  cdata->_node_bounds = copy_cdata->_node_bounds;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -487,6 +484,102 @@ is_geom_node() const {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::propagate_stale_bound
+//       Access: Protected, Virtual
+//  Description: Called by BoundedObject::mark_bound_stale(), this
+//               should make sure that all bounding volumes that
+//               depend on this one are marked stale also.
+////////////////////////////////////////////////////////////////////
+void PandaNode::
+propagate_stale_bound() {
+  // Mark all of our parent nodes stale as well.
+  CDWriter cdata(_cycler);
+  Up::const_iterator ui;
+  for (ui = cdata->_up.begin(); ui != cdata->_up.end(); ++ui) {
+    PandaNode *parent = (*ui).get_parent();
+    parent->mark_bound_stale();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::recompute_bound
+//       Access: Protected, Virtual
+//  Description: Recomputes the dynamic bounding volume for this
+//               object.  The default behavior is the compute an empty
+//               bounding volume; this may be overridden to extend it
+//               to create a nonempty bounding volume.  However, after
+//               calling this function, it is guaranteed that the
+//               _bound pointer will not be shared with any other
+//               stage of the pipeline, and this new pointer is
+//               returned.
+////////////////////////////////////////////////////////////////////
+BoundingVolume *PandaNode::
+recompute_bound() {
+  // First, get ourselves a fresh, empty bounding volume.
+  BoundingVolume *bound = BoundedObject::recompute_bound();
+  nassertr(bound != (BoundingVolume*)NULL, bound);
+
+  // Now actually compute the bounding volume by putting it around all
+  // of our child bounding volumes.
+
+  pvector<const BoundingVolume *> child_volumes;
+
+  // It goes around this node's internal bounding volume . . .
+  child_volumes.push_back(&get_internal_bound());
+
+  CDReader cdata(_cycler);
+  Down::const_iterator di;
+  for (di = cdata->_down.begin(); di != cdata->_down.end(); ++di) {
+    // . . . plus each node's external bounding volume.
+    PandaNode *child = (*di).get_child();
+    const BoundingVolume &child_bound = child->get_bound();
+    child_volumes.push_back(&child_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);
+
+#ifndef NDEBUG
+  if (!success) {
+    pgraph_cat.error()
+      << "Unable to recompute bounding volume for " << *this << ":\n"
+      << "Cannot put " << bound->get_type() << " around:\n";
+    for (int i = 0; i < (int)child_volumes.size(); i++) {
+      pgraph_cat.error(false)
+        << "  " << *child_volumes[i] << "\n";
+    }
+  }
+#endif
+
+  // Now, if we have a transform, apply it to the bounding volume we
+  // just computed.
+  const TransformState *transform = get_transform();
+  if (!transform->is_identity()) {
+    GeometricBoundingVolume *gbv;
+    DCAST_INTO_R(gbv, bound, bound);
+    gbv->xform(transform->get_mat());
+  }
+
+  return bound;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::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 *PandaNode::
+recompute_internal_bound() {
+  return _internal_bound.recompute_bound();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::attach
 //       Access: Private, Static

+ 34 - 2
panda/src/pgraph/pandaNode.h

@@ -46,6 +46,7 @@ class NodeChainComponent;
 //               serves as a generic node with no special properties.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA PandaNode : public TypedWritable, public Namable,
+                              public BoundedObject,
                               virtual public ReferenceCount {
 PUBLISHED:
   PandaNode(const string &name);
@@ -106,9 +107,40 @@ PUBLISHED:
   INLINE void ls() const;
   INLINE void ls(ostream &out, int indent_level = 0) const;
 
+  // A node has two bounding volumes: the BoundedObject it inherits
+  // from is the "external" bound and represnts the node and all of
+  // its children, while the _internal_bound object is the "internal"
+  // bounds and represents only the node itself.
+
+  // We remap the inherited set_bound() and get_bound() functions so
+  // that set_bound() to a type sets the type of the external bound,
+  // while set_bound() to a specific bounding volume sets the volume
+  // of the *internal* bound.  At the same time, get_bound() returns
+  // the external bound.  Although it might seem strange and confusing
+  // to do this, this is actually the natural way the user thinks
+  // about nodes and bounding volumes.
+  INLINE void set_bound(BoundingVolumeType type);
+  INLINE void set_bound(const BoundingVolume &volume);
+  INLINE const BoundingVolume &get_bound() const;
+  INLINE const BoundingVolume &get_internal_bound() const;
+
 public:
   virtual bool is_geom_node() const;
 
+protected:
+  // Inherited from BoundedObject
+  virtual void propagate_stale_bound();
+  virtual BoundingVolume *recompute_bound();
+
+  // Local to PandaNode
+  virtual BoundingVolume *recompute_internal_bound();
+  INLINE void changed_internal_bound();
+
+  // This is the bounding volume around the contents of the node
+  // itself (without including all of the node's children).
+  // BoundedObject is itself cycled, so we don't need to protect it.
+  BoundedObject _internal_bound;
+
 private:
   // parent-child manipulation for NodeChain support.  Don't try to
   // call these directly.
@@ -173,8 +205,6 @@ private:
     Up _up;
     Chains _chains;
 
-    BoundedObject _node_bounds;
-    BoundedObject _subgraph_bounds;
     CPT(RenderState) _state;
     CPT(TransformState) _transform;
   };
@@ -216,9 +246,11 @@ public:
   }
   static void init_type() {
     TypedWritable::init_type();
+    BoundedObject::init_type();
     ReferenceCount::init_type();
     register_type(_type_handle, "PandaNode",
                   TypedWritable::get_class_type(),
+                  BoundedObject::get_class_type(),
                   ReferenceCount::get_class_type());
   }
   virtual TypeHandle get_type() const {

+ 32 - 0
panda/src/pgraph/qpgeomNode.cxx

@@ -160,6 +160,38 @@ is_geom_node() const {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::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 *qpGeomNode::
+recompute_internal_bound() {
+  // First, get ourselves a fresh, empty bounding volume.
+  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  nassertr(bound != (BoundingVolume *)NULL, bound);
+
+  // Now actually compute the bounding volume by putting it around all
+  // of our geoms' bounding volumes.
+  pvector<const BoundingVolume *> child_volumes;
+
+  CDReader cdata(_cycler);
+  Geoms::const_iterator gi;
+  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    const GeomEntry &entry = (*gi);
+    child_volumes.push_back(&entry._geom->get_bound());
+  }
+
+  const BoundingVolume **child_begin = &child_volumes[0];
+  const BoundingVolume **child_end = child_begin + child_volumes.size();
+
+  bound->around(child_begin, child_end);
+  return bound;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomNode::register_with_read_factory
 //       Access: Public, Static

+ 3 - 0
panda/src/pgraph/qpgeomNode.h

@@ -62,6 +62,9 @@ public:
 
   virtual bool is_geom_node() const;
 
+protected:
+  virtual BoundingVolume *recompute_internal_bound();
+
 private:
   class GeomEntry {
   public: