From 168c647d0540c7f06ec2e0bb62611bb5d5ee72e6 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 1 Mar 2002 22:15:00 +0000 Subject: [PATCH] pgraph bounding volumes --- panda/src/graph/boundedObject.h | 2 + panda/src/mathutil/boundingVolume.h | 6 +- panda/src/pgraph/cullBinBackToFront.cxx | 23 ++++-- panda/src/pgraph/pandaNode.I | 66 +++++++++++++++ panda/src/pgraph/pandaNode.cxx | 105 ++++++++++++++++++++++-- panda/src/pgraph/pandaNode.h | 36 +++++++- panda/src/pgraph/qpgeomNode.cxx | 32 ++++++++ panda/src/pgraph/qpgeomNode.h | 3 + 8 files changed, 255 insertions(+), 18 deletions(-) diff --git a/panda/src/graph/boundedObject.h b/panda/src/graph/boundedObject.h index 295fbd4837..7d9965c53e 100644 --- a/panda/src/graph/boundedObject.h +++ b/panda/src/graph/boundedObject.h @@ -98,6 +98,8 @@ public: private: static TypeHandle _type_handle; + + friend class PandaNode; }; #ifndef DONT_INLINE_GRAPH diff --git a/panda/src/mathutil/boundingVolume.h b/panda/src/mathutil/boundingVolume.h index 974b22ee63..126c1aa830 100644 --- a/panda/src/mathutil/boundingVolume.h +++ b/panda/src/mathutil/boundingVolume.h @@ -19,10 +19,10 @@ #ifndef BOUNDINGVOLUME_H #define BOUNDINGVOLUME_H -#include +#include "pandabase.h" -#include -#include +#include "typedObject.h" +#include "typedReferenceCount.h" class BoundingSphere; class BoundingHexahedron; diff --git a/panda/src/pgraph/cullBinBackToFront.cxx b/panda/src/pgraph/cullBinBackToFront.cxx index 31c9575ac9..8b4f21dae8 100644 --- a/panda/src/pgraph/cullBinBackToFront.cxx +++ b/panda/src/pgraph/cullBinBackToFront.cxx @@ -18,6 +18,7 @@ #include "cullBinBackToFront.h" #include "graphicsStateGuardianBase.h" +#include "geometricBoundingVolume.h" #include @@ -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)); + } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index 387236b7fc..6c61c616d9 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/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 diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 225443befe..381ff5a875 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/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 ©) : _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 ©) : { // 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 ©) { 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 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 diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 945b5eaca6..41faeebb02 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/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 { diff --git a/panda/src/pgraph/qpgeomNode.cxx b/panda/src/pgraph/qpgeomNode.cxx index f076d3aba2..23134be45c 100644 --- a/panda/src/pgraph/qpgeomNode.cxx +++ b/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 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 diff --git a/panda/src/pgraph/qpgeomNode.h b/panda/src/pgraph/qpgeomNode.h index 829364da10..aa3bf39c6f 100644 --- a/panda/src/pgraph/qpgeomNode.h +++ b/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: