pgraph bounding volumes

This commit is contained in:
David Rose 2002-03-01 22:15:00 +00:00
parent 5a7b991858
commit 168c647d05
8 changed files with 255 additions and 18 deletions

View File

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

View File

@ -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;

View File

@ -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));
}
}
////////////////////////////////////////////////////////////////////

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

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