diff --git a/panda/src/collide/Sources.pp b/panda/src/collide/Sources.pp index 39ea7746d1..d70c8ad05f 100644 --- a/panda/src/collide/Sources.pp +++ b/panda/src/collide/Sources.pp @@ -22,6 +22,7 @@ collisionDSSolid.I collisionDSSolid.h \ collisionInvSphere.I collisionInvSphere.h \ collisionLine.I collisionLine.h \ + collisionLevelStateBase.I collisionLevelStateBase.h \ collisionLevelState.I collisionLevelState.h \ collisionNode.I collisionNode.h \ collisionPlane.I collisionPlane.h \ @@ -46,6 +47,7 @@ collisionHandlerPhysical.cxx \ collisionHandlerPusher.cxx \ collisionHandlerQueue.cxx \ + collisionLevelStateBase.cxx \ collisionLevelState.cxx \ collisionDSSolid.cxx \ collisionInvSphere.cxx \ @@ -75,6 +77,7 @@ collisionHandlerQueue.h \ collisionDSSolid.I collisionDSSolid.h \ collisionInvSphere.I collisionInvSphere.h \ + collisionLevelStateBase.I collisionLevelStateBase.h \ collisionLevelState.I collisionLevelState.h \ collisionLine.I collisionLine.h \ collisionNode.I collisionNode.h \ diff --git a/panda/src/collide/collide_composite1.cxx b/panda/src/collide/collide_composite1.cxx index 6395e76925..6021b7650c 100644 --- a/panda/src/collide/collide_composite1.cxx +++ b/panda/src/collide/collide_composite1.cxx @@ -10,6 +10,7 @@ #include "collisionHandlerQueue.cxx" #include "collisionDSSolid.cxx" #include "collisionInvSphere.cxx" +#include "collisionLevelStateBase.cxx" #include "collisionLevelState.cxx" #include "collisionLine.cxx" diff --git a/panda/src/collide/collisionLevelState.I b/panda/src/collide/collisionLevelState.I index 6e06498d75..fb72118e18 100644 --- a/panda/src/collide/collisionLevelState.I +++ b/panda/src/collide/collisionLevelState.I @@ -1,5 +1,5 @@ // Filename: collisionLevelState.I -// Created by: drose (16Mar02) +// Created by: drose (05Apr07) // //////////////////////////////////////////////////////////////////// // @@ -16,19 +16,20 @@ // //////////////////////////////////////////////////////////////////// - +#ifndef CPPPARSER // interrogate has trouble with these initializers. //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// -INLINE CollisionLevelState:: +template +INLINE CollisionLevelState:: CollisionLevelState(const NodePath &node_path) : - _node_path(node_path), - _current(CollisionLevelState::CurrentMask::all_off()), - _include_mask(CollideMask::all_on()) + CollisionLevelStateBase(node_path), + _current(CurrentMask::all_off()) { } +#endif // CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::Constructor @@ -36,13 +37,11 @@ CollisionLevelState(const NodePath &node_path) : // Description: This constructor goes to the next child node in the // traversal. //////////////////////////////////////////////////////////////////// -INLINE CollisionLevelState:: -CollisionLevelState(const CollisionLevelState &parent, PandaNode *child) : - _node_path(parent._node_path, child), - _colliders(parent._colliders), - _current(parent._current), - _include_mask(parent._include_mask), - _local_bounds(parent._local_bounds) +template +INLINE CollisionLevelState:: +CollisionLevelState(const CollisionLevelState &parent, PandaNode *child) : + CollisionLevelStateBase(parent, child), + _current(parent._current) { } @@ -51,14 +50,11 @@ CollisionLevelState(const CollisionLevelState &parent, PandaNode *child) : // Access: Public // Description: //////////////////////////////////////////////////////////////////// -INLINE CollisionLevelState:: -CollisionLevelState(const CollisionLevelState ©) : - _node_path(copy._node_path), - _colliders(copy._colliders), - _current(copy._current), - _include_mask(copy._include_mask), - _local_bounds(copy._local_bounds), - _parent_bounds(copy._parent_bounds) +template +INLINE CollisionLevelState:: +CollisionLevelState(const CollisionLevelState ©) : + CollisionLevelStateBase(copy), + _current(copy._current) { } @@ -67,66 +63,203 @@ CollisionLevelState(const CollisionLevelState ©) : // Access: Public // Description: //////////////////////////////////////////////////////////////////// -INLINE void CollisionLevelState:: -operator = (const CollisionLevelState ©) { - _node_path = copy._node_path; - _colliders = copy._colliders; +template +INLINE void CollisionLevelState:: +operator = (const CollisionLevelState ©) { + CollisionLevelStateBase::operator = (copy); _current = copy._current; - _include_mask = copy._include_mask; - _local_bounds = copy._local_bounds; - _parent_bounds = copy._parent_bounds; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::clear +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void CollisionLevelState:: +clear() { + CollisionLevelStateBase::clear(); + _current.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::prepare_collider +// Access: Public +// Description: Adds the indicated Collider to the set of Colliders +// in the current level state. +//////////////////////////////////////////////////////////////////// +template +INLINE void CollisionLevelState:: +prepare_collider(const ColliderDef &def, const NodePath &root) { + int index = (int)_colliders.size(); + nassertv(!CurrentMask::has_max_num_bits() || + index <= CurrentMask::get_max_num_bits()); + + CollisionLevelStateBase::prepare_collider(def, root); + _current.set_bit(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::any_in_bounds +// Access: Public +// Description: Checks the bounding volume of the current node +// against each of our colliders. Eliminates from the +// current collider list any that are outside of the +// bounding volume. Returns true if any colliders +// remain, false if all of them fall outside this node's +// bounding volume. +//////////////////////////////////////////////////////////////////// +template +bool CollisionLevelState:: +any_in_bounds() { +#ifndef NDEBUG + int indent_level = 0; + if (collide_cat.is_spam()) { + indent_level = _node_path.get_num_nodes() * 2; + collide_cat.spam(); + indent(collide_cat.spam(false), indent_level) + << "Considering " << _node_path.get_node_path() << "\n"; + } +#endif // NDEBUG + + CPT(BoundingVolume) node_bv = node()->get_bounds(); + if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { + const GeometricBoundingVolume *node_gbv; + DCAST_INTO_R(node_gbv, node_bv, false); + + int num_colliders = get_num_colliders(); + for (int c = 0; c < num_colliders; c++) { + if (has_collider(c)) { + CollisionNode *cnode = get_collider_node(c); + bool is_in = false; + + // Don't even bother testing the bounding volume if there are + // no collide bits in common between our collider and this + // node. + CollideMask from_mask = cnode->get_from_collide_mask() & _include_mask; + if (!(from_mask & node()->get_net_collide_mask()).is_zero()) { + // There are bits in common, so go ahead and try the + // bounding volume. + const GeometricBoundingVolume *col_gbv = + get_local_bound(c); + if (col_gbv != (GeometricBoundingVolume *)NULL) { + is_in = (node_gbv->contains(col_gbv) != 0); + _node_volume_pcollector.add_level(1); + +#ifndef NDEBUG + if (collide_cat.is_spam()) { + collide_cat.spam(); + indent(collide_cat.spam(false), indent_level) + << "Comparing " << c << ": " << *col_gbv + << " to " << *node_gbv << ", is_in = " << is_in << "\n"; + } +#endif // NDEBUG + } + } + + if (!is_in) { + // This collider cannot intersect with any geometry at + // this node or below. + omit_collider(c); + } + } + } + } + +#ifndef NDEBUG + if (collide_cat.is_spam()) { + int num_active_colliders = 0; + int num_colliders = get_num_colliders(); + for (int c = 0; c < num_colliders; c++) { + if (has_collider(c)) { + num_active_colliders++; + } + } + + collide_cat.spam(); + indent(collide_cat.spam(false), indent_level) + << _node_path.get_node_path() << " has " << num_active_colliders + << " interested colliders.\n"; + } +#endif // NDEBUG + return has_any_collider(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::apply_transform +// Access: Public +// Description: Applies the inverse transform from the current node, +// if any, onto all the colliders in the level state. +//////////////////////////////////////////////////////////////////// +template +void CollisionLevelState:: +apply_transform() { + // The "parent" bounds list remembers the bounds list of the + // previous node. + _parent_bounds = _local_bounds; + + // Recompute the bounds list of this node (if we have a transform). + const TransformState *node_transform = node()->get_transform(); + if (!node_transform->is_identity()) { + CPT(TransformState) inv_transform = + node_transform->invert_compose(TransformState::make_identity()); + const LMatrix4f &mat = inv_transform->get_mat(); + + // Now build the new bounding volumes list. + BoundingVolumes new_bounds; + + int num_colliders = get_num_colliders(); + new_bounds.reserve(num_colliders); + for (int c = 0; c < num_colliders; c++) { + if (!has_collider(c) || + get_local_bound(c) == (GeometricBoundingVolume *)NULL) { + new_bounds.push_back((GeometricBoundingVolume *)NULL); + } else { + const GeometricBoundingVolume *old_bound = get_local_bound(c); + GeometricBoundingVolume *new_bound = + DCAST(GeometricBoundingVolume, old_bound->make_copy()); + new_bound->xform(mat); + new_bounds.push_back(new_bound); + } + } + + _local_bounds = new_bounds; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::has_max_colliders +// Access: Public, Static +// Description: Returns true if there is any the maximum number of +// colliders that may be added to the +// CollisionLevelStateBase at any one time. +//////////////////////////////////////////////////////////////////// +template +INLINE bool CollisionLevelState:: +has_max_colliders() { + return CurrentMask::has_max_num_bits(); } //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::get_max_colliders // Access: Public, Static // Description: Returns the maximum number of colliders that may be -// added to the CollisionLevelState at any one time. +// added to the CollisionLevelStateBase at any one time. //////////////////////////////////////////////////////////////////// -INLINE int CollisionLevelState:: +template +INLINE int CollisionLevelState:: get_max_colliders() { return CurrentMask::get_max_num_bits(); } -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::get_node_path -// Access: Public -// Description: Returns the NodePath representing the node instance -// we have traversed to. -//////////////////////////////////////////////////////////////////// -INLINE NodePath CollisionLevelState:: -get_node_path() const { - return _node_path.get_node_path(); -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::node -// Access: Public -// Description: Returns the PandaNode pointer of the node we have -// traversed to. -//////////////////////////////////////////////////////////////////// -INLINE PandaNode *CollisionLevelState:: -node() const { - return _node_path.node(); -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::get_num_colliders -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE int CollisionLevelState:: -get_num_colliders() const { - return _colliders.size(); -} - //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::has_collider // Access: Public // Description: Returns true if the nth collider in the LevelState is // still part of the level. //////////////////////////////////////////////////////////////////// -INLINE bool CollisionLevelState:: +template +INLINE bool CollisionLevelState:: has_collider(int n) const { nassertr(n >= 0 && n < (int)_colliders.size(), false); return (_current.get_bit(n)); @@ -137,134 +270,22 @@ has_collider(int n) const { // Access: Public // Description: //////////////////////////////////////////////////////////////////// -INLINE bool CollisionLevelState:: +template +INLINE bool CollisionLevelState:: has_any_collider() const { return !_current.is_zero(); } -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::get_collider -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE CollisionSolid *CollisionLevelState:: -get_collider(int n) const { - nassertr(n >= 0 && n < (int)_colliders.size(), NULL); - nassertr(has_collider(n), NULL); - - return _colliders[n]._collider; -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::get_collider_node -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE CollisionNode *CollisionLevelState:: -get_collider_node(int n) const { - nassertr(n >= 0 && n < (int)_colliders.size(), NULL); - nassertr(has_collider(n), NULL); - - return _colliders[n]._node; -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::get_collider_node_path -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE NodePath CollisionLevelState:: -get_collider_node_path(int n) const { - nassertr(n >= 0 && n < (int)_colliders.size(), NodePath::fail()); - nassertr(has_collider(n), NodePath::fail()); - - return _colliders[n]._node_path; -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::get_local_bound -// Access: Public -// Description: Returns the bounding volume of the indicated -// collider, transformed into the current node's -// transform space. -//////////////////////////////////////////////////////////////////// -INLINE const GeometricBoundingVolume *CollisionLevelState:: -get_local_bound(int n) const { - nassertr(n >= 0 && n < (int)_colliders.size(), NULL); - nassertr(has_collider(n), NULL); - nassertr(n >= 0 && n < (int)_local_bounds.size(), NULL); - - // For whatever reason, the Intel compiler can't figure this line - // out. - //return _local_bounds[n]; - - // But it can figure out this equivalent line. - return *(_local_bounds + n); -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::get_parent_bound -// Access: Public -// Description: Returns the bounding volume of the indicated -// collider, transformed into the previous node's -// transform space, but not transformed by the current -// node's transform. This is appropriate for testing -// against the bounding volume of the current node -// (which does not have its own transform applied to -// it). -//////////////////////////////////////////////////////////////////// -INLINE const GeometricBoundingVolume *CollisionLevelState:: -get_parent_bound(int n) const { - nassertr(n >= 0 && n < (int)_colliders.size(), NULL); - nassertr(has_collider(n), NULL); - nassertr(n >= 0 && n < (int)_parent_bounds.size(), NULL); - - // But it can figure out this equivalent line. - return *(_parent_bounds + n); -} - //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::omit_collider // Access: Public // Description: //////////////////////////////////////////////////////////////////// -INLINE void CollisionLevelState:: +template +INLINE void CollisionLevelState:: omit_collider(int n) { nassertv(n >= 0 && n < (int)_colliders.size()); nassertv(has_collider(n)); _current.clear_bit(n); } - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::set_include_mask -// Access: Public -// Description: Specifies the mask that is applied to the into -// CollideMask of nodes in the scene graph before -// testing for bits in common with the from CollideMask -// of colliders. This is normally all bits on, but you -// may set it to some other mask to restrict certain -// bits from consideration. -// -// This is used by the CollisionTraverser to restrict -// collision with geometry except under the lowest level -// of LOD. -//////////////////////////////////////////////////////////////////// -INLINE void CollisionLevelState:: -set_include_mask(CollideMask include_mask) { - _include_mask = include_mask; -} - - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::get_include_mask -// Access: Public -// Description: Returns the mask that is applied to the into -// CollideMask of nodes in the scene graph before -// testing for bits in common with the from CollideMask -// of colliders. See set_include_mask(). -//////////////////////////////////////////////////////////////////// -INLINE CollideMask CollisionLevelState:: -get_include_mask() const { - return _include_mask; -} diff --git a/panda/src/collide/collisionLevelState.cxx b/panda/src/collide/collisionLevelState.cxx index 80f6b72dc8..3255514a42 100644 --- a/panda/src/collide/collisionLevelState.cxx +++ b/panda/src/collide/collisionLevelState.cxx @@ -1,5 +1,5 @@ // Filename: collisionLevelState.cxx -// Created by: drose (16Mar02) +// Created by: drose (05Apr07) // //////////////////////////////////////////////////////////////////// // @@ -17,211 +17,3 @@ //////////////////////////////////////////////////////////////////// #include "collisionLevelState.h" -#include "collisionSolid.h" -#include "collisionNode.h" -#include "dcast.h" - -PStatCollector CollisionLevelState::_node_volume_pcollector("Collision Volumes:PandaNode"); - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::clear -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -void CollisionLevelState:: -clear() { - _colliders.clear(); - _local_bounds.clear(); - _parent_bounds.clear(); - _current.clear(); -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::reserve -// Access: Public -// Description: Indicates an intention to add the indicated number of -// colliders to the level state. -//////////////////////////////////////////////////////////////////// -void CollisionLevelState:: -reserve(int num_colliders) { - nassertv(!CurrentMask::has_max_num_bits() || - num_colliders <= CurrentMask::get_max_num_bits()); - _colliders.reserve(num_colliders); - _local_bounds.reserve(num_colliders); -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::prepare_collider -// Access: Public -// Description: Adds the indicated Collider to the set of Colliders -// in the current level state. -//////////////////////////////////////////////////////////////////// -void CollisionLevelState:: -prepare_collider(const ColliderDef &def, const NodePath &root) { - int index = (int)_colliders.size(); - nassertv(!CurrentMask::has_max_num_bits() || - index <= CurrentMask::get_max_num_bits()); - _colliders.push_back(def); - - CollisionSolid *collider = def._collider; - CPT(BoundingVolume) bv = collider->get_bounds(); - if (!bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - _local_bounds.push_back((GeometricBoundingVolume *)NULL); - } else { - GeometricBoundingVolume *gbv; - DCAST_INTO_V(gbv, bv->make_copy()); - - // TODO: we need to make this logic work in the new relative - // world. The bounding volume should be extended by the object's - // motion relative to each object it is considering a collision - // with. That makes things complicated! - /* - if (def._delta != LVector3f::zero()) { - // If the node has a delta, we have to include the starting - // point in the volume as well. - - // Strictly speaking, we should actually transform gbv backward - // by the delta(), and extend by *that* volume, instead of - // just extending by the single point at -get_velocity(). - // However, assuming the solids within a moving CollisionNode - // tend to be near the origin, this will generally produce the - // same results, and is much easier to compute. - gbv->extend_by(LPoint3f(-def._delta)); - } - */ - - CPT(TransformState) rel_transform = def._node_path.get_transform(root.get_parent()); - gbv->xform(rel_transform->get_mat()); - _local_bounds.push_back(gbv); - } - - _current.set_bit(index); - - _parent_bounds = _local_bounds; -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::any_in_bounds -// Access: Public -// Description: Checks the bounding volume of the current node -// against each of our colliders. Eliminates from the -// current collider list any that are outside of the -// bounding volume. Returns true if any colliders -// remain, false if all of them fall outside this node's -// bounding volume. -//////////////////////////////////////////////////////////////////// -bool CollisionLevelState:: -any_in_bounds() { -#ifndef NDEBUG - int indent_level = 0; - if (collide_cat.is_spam()) { - indent_level = _node_path.get_num_nodes() * 2; - collide_cat.spam(); - indent(collide_cat.spam(false), indent_level) - << "Considering " << _node_path.get_node_path() << "\n"; - } -#endif // NDEBUG - - CPT(BoundingVolume) node_bv = node()->get_bounds(); - if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - const GeometricBoundingVolume *node_gbv; - DCAST_INTO_R(node_gbv, node_bv, false); - - int num_colliders = get_num_colliders(); - for (int c = 0; c < num_colliders; c++) { - if (has_collider(c)) { - CollisionNode *cnode = get_collider_node(c); - bool is_in = false; - - // Don't even bother testing the bounding volume if there are - // no collide bits in common between our collider and this - // node. - CollideMask from_mask = cnode->get_from_collide_mask() & _include_mask; - if (!(from_mask & node()->get_net_collide_mask()).is_zero()) { - // There are bits in common, so go ahead and try the - // bounding volume. - const GeometricBoundingVolume *col_gbv = - get_local_bound(c); - if (col_gbv != (GeometricBoundingVolume *)NULL) { - is_in = (node_gbv->contains(col_gbv) != 0); - _node_volume_pcollector.add_level(1); - -#ifndef NDEBUG - if (collide_cat.is_spam()) { - collide_cat.spam(); - indent(collide_cat.spam(false), indent_level) - << "Comparing " << c << ": " << *col_gbv - << " to " << *node_gbv << ", is_in = " << is_in << "\n"; - } -#endif // NDEBUG - } - } - - if (!is_in) { - // This collider cannot intersect with any geometry at - // this node or below. - omit_collider(c); - } - } - } - } - -#ifndef NDEBUG - if (collide_cat.is_spam()) { - int num_active_colliders = 0; - int num_colliders = get_num_colliders(); - for (int c = 0; c < num_colliders; c++) { - if (has_collider(c)) { - num_active_colliders++; - } - } - - collide_cat.spam(); - indent(collide_cat.spam(false), indent_level) - << _node_path.get_node_path() << " has " << num_active_colliders - << " interested colliders.\n"; - } -#endif // NDEBUG - return has_any_collider(); -} - -//////////////////////////////////////////////////////////////////// -// Function: CollisionLevelState::apply_transform -// Access: Public -// Description: Applies the inverse transform from the current node, -// if any, onto all the colliders in the level state. -//////////////////////////////////////////////////////////////////// -void CollisionLevelState:: -apply_transform() { - // The "parent" bounds list remembers the bounds list of the - // previous node. - _parent_bounds = _local_bounds; - - // Recompute the bounds list of this node (if we have a transform). - const TransformState *node_transform = node()->get_transform(); - if (!node_transform->is_identity()) { - CPT(TransformState) inv_transform = - node_transform->invert_compose(TransformState::make_identity()); - const LMatrix4f &mat = inv_transform->get_mat(); - - // Now build the new bounding volumes list. - BoundingVolumes new_bounds; - - int num_colliders = get_num_colliders(); - new_bounds.reserve(num_colliders); - for (int c = 0; c < num_colliders; c++) { - if (!has_collider(c) || - get_local_bound(c) == (GeometricBoundingVolume *)NULL) { - new_bounds.push_back((GeometricBoundingVolume *)NULL); - } else { - const GeometricBoundingVolume *old_bound = get_local_bound(c); - GeometricBoundingVolume *new_bound = - DCAST(GeometricBoundingVolume, old_bound->make_copy()); - new_bound->xform(mat); - new_bounds.push_back(new_bound); - } - } - - _local_bounds = new_bounds; - } -} diff --git a/panda/src/collide/collisionLevelState.h b/panda/src/collide/collisionLevelState.h index bb4a6fbb86..b3796e7d6f 100644 --- a/panda/src/collide/collisionLevelState.h +++ b/panda/src/collide/collisionLevelState.h @@ -21,93 +21,68 @@ #include "pandabase.h" -#include "luse.h" -#include "pointerToArray.h" -#include "geometricBoundingVolume.h" -#include "nodePath.h" -#include "workingNodePath.h" -#include "pointerTo.h" -#include "plist.h" -#include "pStatCollector.h" +#include "collisionLevelStateBase.h" +#include "collisionNode.h" #include "bitMask.h" - -class CollisionSolid; -class CollisionNode; +#include "bitArray.h" //////////////////////////////////////////////////////////////////// // Class : CollisionLevelState // Description : This is the state information the // CollisionTraverser retains for each level during // traversal. +// +// This is the template class that specifies the +// CurrentMask type: the type of bitmask that is used to +// keep track of the set of active colliders for each +// node. //////////////////////////////////////////////////////////////////// -class CollisionLevelState { +template +class CollisionLevelState : public CollisionLevelStateBase { public: - class ColliderDef { - public: - CollisionSolid *_collider; - CollisionNode *_node; - NodePath _node_path; - }; - INLINE CollisionLevelState(const NodePath &node_path); - INLINE CollisionLevelState(const CollisionLevelState &parent, + INLINE CollisionLevelState(const CollisionLevelState &parent, PandaNode *child); - INLINE CollisionLevelState(const CollisionLevelState ©); - INLINE void operator = (const CollisionLevelState ©); + INLINE CollisionLevelState(const CollisionLevelState ©); + INLINE void operator = (const CollisionLevelState ©); - void clear(); - void reserve(int num_colliders); - void prepare_collider(const ColliderDef &def, const NodePath &root); - - INLINE static int get_max_colliders(); + INLINE void clear(); + INLINE void prepare_collider(const ColliderDef &def, const NodePath &root); bool any_in_bounds(); void apply_transform(); - - INLINE NodePath get_node_path() const; - INLINE PandaNode *node() const; - INLINE int get_num_colliders() const; + INLINE static bool has_max_colliders(); + INLINE static int get_max_colliders(); + INLINE bool has_collider(int n) const; INLINE bool has_any_collider() const; - INLINE CollisionSolid *get_collider(int n) const; - INLINE CollisionNode *get_collider_node(int n) const; - INLINE NodePath get_collider_node_path(int n) const; - INLINE const GeometricBoundingVolume *get_local_bound(int n) const; - INLINE const GeometricBoundingVolume *get_parent_bound(int n) const; - INLINE void omit_collider(int n); - INLINE void set_include_mask(CollideMask include_mask); - INLINE CollideMask get_include_mask() const; - private: // CurrentMask here is a locally-defined value that simply serves // to keep track of the colliders that are still interested in the // current node. Don't confuse it with CollideMask, which is a set // of user-defined bits that specify which CollisionSolids may // possibly intersect with each other. - typedef BitMaskNative CurrentMask; - - WorkingNodePath _node_path; - - typedef PTA(ColliderDef) Colliders; - Colliders _colliders; + typedef MaskType CurrentMask; CurrentMask _current; - CollideMask _include_mask; - - typedef PTA(CPT(GeometricBoundingVolume)) BoundingVolumes; - BoundingVolumes _local_bounds; - BoundingVolumes _parent_bounds; - - static PStatCollector _node_volume_pcollector; friend class CollisionTraverser; }; #include "collisionLevelState.I" +// Now instantiate a pair of implementations of CollisionLevelState: +// one that uses a word-at-a-time bitmask to track the active +// colliders (and thus is limited to handling 32 or 64 colliders in a +// given pass), and another that uses an infinite BitArray to track +// these colliders (and thus has no particular limit). + +typedef CollisionLevelState CollisionLevelStateWord; +typedef CollisionLevelState CollisionLevelStateArray; + #endif diff --git a/panda/src/collide/collisionLevelStateBase.I b/panda/src/collide/collisionLevelStateBase.I new file mode 100644 index 0000000000..74f8d85ccb --- /dev/null +++ b/panda/src/collide/collisionLevelStateBase.I @@ -0,0 +1,215 @@ +// Filename: collisionLevelStateBase.I +// Created by: drose (16Mar02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionLevelStateBase:: +CollisionLevelStateBase(const NodePath &node_path) : + _node_path(node_path), + _include_mask(CollideMask::all_on()) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::Constructor +// Access: Public +// Description: This constructor goes to the next child node in the +// traversal. +//////////////////////////////////////////////////////////////////// +INLINE CollisionLevelStateBase:: +CollisionLevelStateBase(const CollisionLevelStateBase &parent, PandaNode *child) : + _node_path(parent._node_path, child), + _colliders(parent._colliders), + _include_mask(parent._include_mask), + _local_bounds(parent._local_bounds) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionLevelStateBase:: +CollisionLevelStateBase(const CollisionLevelStateBase ©) : + _node_path(copy._node_path), + _colliders(copy._colliders), + _include_mask(copy._include_mask), + _local_bounds(copy._local_bounds), + _parent_bounds(copy._parent_bounds) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionLevelStateBase:: +operator = (const CollisionLevelStateBase ©) { + _node_path = copy._node_path; + _colliders = copy._colliders; + _include_mask = copy._include_mask; + _local_bounds = copy._local_bounds; + _parent_bounds = copy._parent_bounds; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::get_node_path +// Access: Public +// Description: Returns the NodePath representing the node instance +// we have traversed to. +//////////////////////////////////////////////////////////////////// +INLINE NodePath CollisionLevelStateBase:: +get_node_path() const { + return _node_path.get_node_path(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::node +// Access: Public +// Description: Returns the PandaNode pointer of the node we have +// traversed to. +//////////////////////////////////////////////////////////////////// +INLINE PandaNode *CollisionLevelStateBase:: +node() const { + return _node_path.node(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::get_num_colliders +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int CollisionLevelStateBase:: +get_num_colliders() const { + return _colliders.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::get_collider +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSolid *CollisionLevelStateBase:: +get_collider(int n) const { + nassertr(n >= 0 && n < (int)_colliders.size(), NULL); + + return _colliders[n]._collider; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::get_collider_node +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionNode *CollisionLevelStateBase:: +get_collider_node(int n) const { + nassertr(n >= 0 && n < (int)_colliders.size(), NULL); + + return _colliders[n]._node; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::get_collider_node_path +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodePath CollisionLevelStateBase:: +get_collider_node_path(int n) const { + nassertr(n >= 0 && n < (int)_colliders.size(), NodePath::fail()); + + return _colliders[n]._node_path; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::get_local_bound +// Access: Public +// Description: Returns the bounding volume of the indicated +// collider, transformed into the current node's +// transform space. +//////////////////////////////////////////////////////////////////// +INLINE const GeometricBoundingVolume *CollisionLevelStateBase:: +get_local_bound(int n) const { + nassertr(n >= 0 && n < (int)_colliders.size(), NULL); + nassertr(n >= 0 && n < (int)_local_bounds.size(), NULL); + + // For whatever reason, the Intel compiler can't figure this line + // out. + //return _local_bounds[n]; + + // But it can figure out this equivalent line. + return *(_local_bounds + n); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::get_parent_bound +// Access: Public +// Description: Returns the bounding volume of the indicated +// collider, transformed into the previous node's +// transform space, but not transformed by the current +// node's transform. This is appropriate for testing +// against the bounding volume of the current node +// (which does not have its own transform applied to +// it). +//////////////////////////////////////////////////////////////////// +INLINE const GeometricBoundingVolume *CollisionLevelStateBase:: +get_parent_bound(int n) const { + nassertr(n >= 0 && n < (int)_colliders.size(), NULL); + nassertr(n >= 0 && n < (int)_parent_bounds.size(), NULL); + + // But it can figure out this equivalent line. + return *(_parent_bounds + n); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::set_include_mask +// Access: Public +// Description: Specifies the mask that is applied to the into +// CollideMask of nodes in the scene graph before +// testing for bits in common with the from CollideMask +// of colliders. This is normally all bits on, but you +// may set it to some other mask to restrict certain +// bits from consideration. +// +// This is used by the CollisionTraverser to restrict +// collision with geometry except under the lowest level +// of LOD. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionLevelStateBase:: +set_include_mask(CollideMask include_mask) { + _include_mask = include_mask; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::get_include_mask +// Access: Public +// Description: Returns the mask that is applied to the into +// CollideMask of nodes in the scene graph before +// testing for bits in common with the from CollideMask +// of colliders. See set_include_mask(). +//////////////////////////////////////////////////////////////////// +INLINE CollideMask CollisionLevelStateBase:: +get_include_mask() const { + return _include_mask; +} diff --git a/panda/src/collide/collisionLevelStateBase.cxx b/panda/src/collide/collisionLevelStateBase.cxx new file mode 100644 index 0000000000..bd93a6964e --- /dev/null +++ b/panda/src/collide/collisionLevelStateBase.cxx @@ -0,0 +1,93 @@ +// Filename: collisionLevelStateBase.cxx +// Created by: drose (16Mar02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "collisionLevelStateBase.h" +#include "collisionSolid.h" +#include "collisionNode.h" +#include "dcast.h" + +PStatCollector CollisionLevelStateBase::_node_volume_pcollector("Collision Volumes:PandaNode"); + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::clear +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionLevelStateBase:: +clear() { + _colliders.clear(); + _local_bounds.clear(); + _parent_bounds.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::reserve +// Access: Public +// Description: Indicates an intention to add the indicated number of +// colliders to the level state. +//////////////////////////////////////////////////////////////////// +void CollisionLevelStateBase:: +reserve(int num_colliders) { + _colliders.reserve(num_colliders); + _local_bounds.reserve(num_colliders); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelStateBase::prepare_collider +// Access: Public +// Description: Adds the indicated Collider to the set of Colliders +// in the current level state. +//////////////////////////////////////////////////////////////////// +void CollisionLevelStateBase:: +prepare_collider(const ColliderDef &def, const NodePath &root) { + _colliders.push_back(def); + + CollisionSolid *collider = def._collider; + CPT(BoundingVolume) bv = collider->get_bounds(); + if (!bv->is_of_type(GeometricBoundingVolume::get_class_type())) { + _local_bounds.push_back((GeometricBoundingVolume *)NULL); + } else { + GeometricBoundingVolume *gbv; + DCAST_INTO_V(gbv, bv->make_copy()); + + // TODO: we need to make this logic work in the new relative + // world. The bounding volume should be extended by the object's + // motion relative to each object it is considering a collision + // with. That makes things complicated! + /* + if (def._delta != LVector3f::zero()) { + // If the node has a delta, we have to include the starting + // point in the volume as well. + + // Strictly speaking, we should actually transform gbv backward + // by the delta(), and extend by *that* volume, instead of + // just extending by the single point at -get_velocity(). + // However, assuming the solids within a moving CollisionNode + // tend to be near the origin, this will generally produce the + // same results, and is much easier to compute. + gbv->extend_by(LPoint3f(-def._delta)); + } + */ + + CPT(TransformState) rel_transform = def._node_path.get_transform(root.get_parent()); + gbv->xform(rel_transform->get_mat()); + _local_bounds.push_back(gbv); + } + + _parent_bounds = _local_bounds; +} diff --git a/panda/src/collide/collisionLevelStateBase.h b/panda/src/collide/collisionLevelStateBase.h new file mode 100644 index 0000000000..f10a0fd946 --- /dev/null +++ b/panda/src/collide/collisionLevelStateBase.h @@ -0,0 +1,100 @@ +// Filename: collisionLevelStateBase.h +// Created by: drose (16Mar02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONLEVELSTATEBASE_H +#define COLLISIONLEVELSTATEBASE_H + +#include "pandabase.h" + +#include "luse.h" +#include "pointerToArray.h" +#include "geometricBoundingVolume.h" +#include "nodePath.h" +#include "workingNodePath.h" +#include "pointerTo.h" +#include "plist.h" +#include "pStatCollector.h" +#include "bitMask.h" + +class CollisionSolid; +class CollisionNode; + +//////////////////////////////////////////////////////////////////// +// Class : CollisionLevelStateBase +// Description : This is the state information the +// CollisionTraverser retains for each level during +// traversal. +// +// The CollisionLevelStateBase is the non-template base +// class. The template version further specifies this +// on CurrentMask type. +//////////////////////////////////////////////////////////////////// +class CollisionLevelStateBase { +public: + class ColliderDef { + public: + CollisionSolid *_collider; + CollisionNode *_node; + NodePath _node_path; + }; + + INLINE CollisionLevelStateBase(const NodePath &node_path); + INLINE CollisionLevelStateBase(const CollisionLevelStateBase &parent, + PandaNode *child); + INLINE CollisionLevelStateBase(const CollisionLevelStateBase ©); + INLINE void operator = (const CollisionLevelStateBase ©); + + void clear(); + void reserve(int num_colliders); + void prepare_collider(const ColliderDef &def, const NodePath &root); + + INLINE NodePath get_node_path() const; + INLINE PandaNode *node() const; + + INLINE int get_num_colliders() const; + + INLINE CollisionSolid *get_collider(int n) const; + INLINE CollisionNode *get_collider_node(int n) const; + INLINE NodePath get_collider_node_path(int n) const; + INLINE const GeometricBoundingVolume *get_local_bound(int n) const; + INLINE const GeometricBoundingVolume *get_parent_bound(int n) const; + + INLINE void set_include_mask(CollideMask include_mask); + INLINE CollideMask get_include_mask() const; + +protected: + WorkingNodePath _node_path; + + typedef PTA(ColliderDef) Colliders; + Colliders _colliders; + CollideMask _include_mask; + + typedef PTA(CPT(GeometricBoundingVolume)) BoundingVolumes; + BoundingVolumes _local_bounds; + BoundingVolumes _parent_bounds; + + static PStatCollector _node_volume_pcollector; + + friend class CollisionTraverser; +}; + +#include "collisionLevelStateBase.I" + +#endif + + diff --git a/panda/src/collide/collisionTraverser.cxx b/panda/src/collide/collisionTraverser.cxx index 5f7c6a1ba7..446574115c 100644 --- a/panda/src/collide/collisionTraverser.cxx +++ b/panda/src/collide/collisionTraverser.cxx @@ -275,22 +275,43 @@ traverse(const NodePath &root) { get_recorder()->begin_traversal(); } #endif // DO_COLLISION_RECORDING - - LevelStates level_states; - prepare_colliders(level_states, root); Handlers::iterator hi; for (hi = _handlers.begin(); hi != _handlers.end(); ++hi) { (*hi).first->begin_group(); } - // Make a number of passes, one for each group of 32 Colliders (or - // whatever number of bits we have available in CurrentMask). - for (size_t pass = 0; pass < level_states.size(); ++pass) { - #ifdef DO_PSTATS - PStatTimer pass_timer(get_pass_collector(pass)); - #endif - r_traverse(level_states[pass], pass); + bool do_array_pass = true; + if (_colliders.size() <= CollisionLevelStateWord::get_max_colliders() || + !allow_collider_bitarray) { + // Use the word-at-a-time traverser, which might need to make + // several passes. + LevelStatesWord level_states; + prepare_colliders_word(level_states, root); + + if (level_states.size() == 1 || !allow_collider_bitarray) { + do_array_pass = false; + // Make a number of passes, one for each group of 32 Colliders (or + // whatever number of bits we have available in CurrentMask). + for (size_t pass = 0; pass < level_states.size(); ++pass) { +#ifdef DO_PSTATS + PStatTimer pass_timer(get_pass_collector(pass)); +#endif + r_traverse_word(level_states[pass], pass); + } + } + } + + if (do_array_pass) { + // Use the array-at-a-time traverser, which can do the whole thing + // at once, even if there are many colliders. + CollisionLevelStateArray level_state(root); + prepare_colliders_array(level_state, root); + +#ifdef DO_PSTATS + PStatTimer pass_timer(get_pass_collector(0)); +#endif + r_traverse_array(level_state); } hi = _handlers.begin(); @@ -309,7 +330,7 @@ traverse(const NodePath &root) { } #endif // DO_COLLISION_RECORDING - CollisionLevelState::_node_volume_pcollector.flush_level(); + CollisionLevelStateBase::_node_volume_pcollector.flush_level(); _cnode_volume_pcollector.flush_level(); _gnode_volume_pcollector.flush_level(); _geom_volume_pcollector.flush_level(); @@ -459,17 +480,22 @@ write(ostream &out, int indent_level) const { //////////////////////////////////////////////////////////////////// -// Function: CollisionTraverser::prepare_colliders +// Function: CollisionTraverser::prepare_colliders_word // Access: Private -// Description: +// Description: Fills up the set of LevelStates corresponding to the +// active colliders in use. +// +// This flavor uses a CollisionLevelStateWord, which is +// limited to a certain number of colliders per pass +// (typically 32). //////////////////////////////////////////////////////////////////// void CollisionTraverser:: -prepare_colliders(CollisionTraverser::LevelStates &level_states, - const NodePath &root) { +prepare_colliders_word(CollisionTraverser::LevelStatesWord &level_states, + const NodePath &root) { int num_colliders = _colliders.size(); - int max_colliders = CollisionLevelState::get_max_colliders(); + int max_colliders = CollisionLevelStateWord::get_max_colliders(); - CollisionLevelState level_state(root); + CollisionLevelStateWord level_state(root); // This reserve() call is only correct if there is exactly one solid // per collider added to the traverser, which is the normal case. // If there is more than one solid in any of the colliders, this @@ -497,7 +523,7 @@ prepare_colliders(CollisionTraverser::LevelStates &level_states, (*oci)._in_graph = true; CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node()); - CollisionLevelState::ColliderDef def; + CollisionLevelStateWord::ColliderDef def; def._node = cnode; def._node_path = cnode_path; @@ -528,12 +554,12 @@ prepare_colliders(CollisionTraverser::LevelStates &level_states, } //////////////////////////////////////////////////////////////////// -// Function: CollisionTraverser::r_traverse +// Function: CollisionTraverser::r_traverse_word // Access: Private // Description: //////////////////////////////////////////////////////////////////// void CollisionTraverser:: -r_traverse(CollisionLevelState &level_state, size_t pass) { +r_traverse_word(CollisionLevelStateWord &level_state, size_t pass) { if (!level_state.any_in_bounds()) { return; } @@ -629,8 +655,8 @@ r_traverse(CollisionLevelState &level_state, size_t pass) { // visible child. int index = node->get_visible_child(); if (index >= 0 && index < node->get_num_children()) { - CollisionLevelState next_state(level_state, node->get_child(index)); - r_traverse(next_state, pass); + CollisionLevelStateWord next_state(level_state, node->get_child(index)); + r_traverse_word(next_state, pass); } } else if (node->is_lod_node()) { @@ -643,20 +669,214 @@ r_traverse(CollisionLevelState &level_state, size_t pass) { int index = DCAST(LODNode, node)->get_lowest_switch(); int num_children = node->get_num_children(); for (int i = 0; i < num_children; ++i) { - CollisionLevelState next_state(level_state, node->get_child(i)); + CollisionLevelStateWord next_state(level_state, node->get_child(i)); if (i != index) { next_state.set_include_mask(next_state.get_include_mask() & ~GeomNode::get_default_collide_mask()); } - r_traverse(next_state, pass); + r_traverse_word(next_state, pass); } } else { // Otherwise, visit all the children. int num_children = node->get_num_children(); for (int i = 0; i < num_children; ++i) { - CollisionLevelState next_state(level_state, node->get_child(i)); - r_traverse(next_state, pass); + CollisionLevelStateWord next_state(level_state, node->get_child(i)); + r_traverse_word(next_state, pass); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::prepare_colliders_array +// Access: Private +// Description: Fills up the set of LevelStates corresponding to the +// active colliders in use. +// +// This flavor uses a CollisionLevelStateArray, which +// has no limit in the number of colliders it can handle +// in one pass. +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +prepare_colliders_array(CollisionLevelStateArray &level_state, + const NodePath &root) { + int num_colliders = _colliders.size(); + // This reserve() call is only correct if there is exactly one solid + // per collider added to the traverser, which is the normal case. + // If there is more than one solid in any of the colliders, this + // reserve() call won't reserve enough, but the code is otherwise + // correct. + level_state.reserve(num_colliders); + + OrderedColliders sorted = _ordered_colliders; + sort(sorted.begin(), sorted.end(), SortByColliderSort()); + + OrderedColliders::iterator oci; + for (oci = sorted.begin(); oci != sorted.end(); ++oci) { + NodePath cnode_path = (*oci)._node_path; + + if (!cnode_path.is_same_graph(root)) { + if ((*oci)._in_graph) { + // Only report this warning once. + collide_cat.info() + << "Collider " << cnode_path + << " is not in scene graph. Ignoring.\n"; + (*oci)._in_graph = false; + } + + } else { + (*oci)._in_graph = true; + CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node()); + + CollisionLevelStateArray::ColliderDef def; + def._node = cnode; + def._node_path = cnode_path; + + 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, root); + } + } + + --num_colliders; + nassertv(num_colliders >= 0); + } + + nassertv(num_colliders == 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::r_traverse_array +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +r_traverse_array(CollisionLevelStateArray &level_state) { + if (!level_state.any_in_bounds()) { + return; + } + level_state.apply_transform(); + + PandaNode *node = level_state.node(); + if (node->is_exact_type(CollisionNode::get_class_type())) { + CollisionNode *cnode; + DCAST_INTO_V(cnode, node); + CPT(BoundingVolume) node_bv = cnode->get_bounds(); + const GeometricBoundingVolume *node_gbv = NULL; + if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { + DCAST_INTO_V(node_gbv, node_bv); + } + + CollisionEntry entry; + entry._into_node = cnode; + entry._into_node_path = level_state.get_node_path(); + if (_respect_prev_transform) { + entry._flags |= CollisionEntry::F_respect_prev_transform; + } + + 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_collider_node(c); + + if ((entry._from_node->get_from_collide_mask() & + cnode->get_into_collide_mask()) != 0) { + #ifdef DO_PSTATS + PStatTimer collide_timer(_solid_collide_collectors[0]); + #endif + entry._from_node_path = level_state.get_collider_node_path(c); + entry._from = level_state.get_collider(c); + + compare_collider_to_node( + entry, + level_state.get_parent_bound(c), + level_state.get_local_bound(c), + node_gbv); + } + } + } + + } else if (node->is_geom_node()) { + #ifndef NDEBUG + if (collide_cat.is_spam()) { + collide_cat.spam() + << "Reached " << *node << "\n"; + } + #endif + + GeomNode *gnode; + DCAST_INTO_V(gnode, node); + CPT(BoundingVolume) node_bv = gnode->get_bounds(); + const GeometricBoundingVolume *node_gbv = NULL; + if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { + DCAST_INTO_V(node_gbv, node_bv); + } + + CollisionEntry entry; + entry._into_node = gnode; + entry._into_node_path = level_state.get_node_path(); + if (_respect_prev_transform) { + entry._flags |= CollisionEntry::F_respect_prev_transform; + } + + 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_collider_node(c); + + if ((entry._from_node->get_from_collide_mask() & + gnode->get_into_collide_mask()) != 0) { + #ifdef DO_PSTATS + PStatTimer collide_timer(_solid_collide_collectors[0]); + #endif + entry._from_node_path = level_state.get_collider_node_path(c); + entry._from = level_state.get_collider(c); + + compare_collider_to_geom_node( + entry, + level_state.get_parent_bound(c), + level_state.get_local_bound(c), + node_gbv); + } + } + } + } + + if (node->has_single_child_visibility()) { + // If it's a switch node or sequence node, visit just the one + // visible child. + int index = node->get_visible_child(); + if (index >= 0 && index < node->get_num_children()) { + CollisionLevelStateArray next_state(level_state, node->get_child(index)); + r_traverse_array(next_state); + } + + } else if (node->is_lod_node()) { + // If it's an LODNode, visit the lowest level of detail with all + // bits, allowing collision with geometry under the lowest level + // of default; and visit all other levels without + // GeomNode::get_default_collide_mask(), allowing only collision + // with CollisionNodes and special geometry under higher levels of + // detail. + int index = DCAST(LODNode, node)->get_lowest_switch(); + int num_children = node->get_num_children(); + for (int i = 0; i < num_children; ++i) { + CollisionLevelStateArray next_state(level_state, node->get_child(i)); + if (i != index) { + next_state.set_include_mask(next_state.get_include_mask() & + ~GeomNode::get_default_collide_mask()); + } + r_traverse_array(next_state); + } + + } else { + // Otherwise, visit all the children. + int num_children = node->get_num_children(); + for (int i = 0; i < num_children; ++i) { + CollisionLevelStateArray next_state(level_state, node->get_child(i)); + r_traverse_array(next_state); } } } diff --git a/panda/src/collide/collisionTraverser.h b/panda/src/collide/collisionTraverser.h index 545d9aa918..cd329b41b8 100644 --- a/panda/src/collide/collisionTraverser.h +++ b/panda/src/collide/collisionTraverser.h @@ -81,10 +81,12 @@ PUBLISHED: void write(ostream &out, int indent_level) const; private: - typedef pvector LevelStates; - void prepare_colliders(LevelStates &level_states, const NodePath &root); + typedef pvector LevelStatesWord; + void prepare_colliders_word(LevelStatesWord &level_states, const NodePath &root); + void r_traverse_word(CollisionLevelStateWord &level_state, size_t pass); - void r_traverse(CollisionLevelState &level_state, size_t pass); + void prepare_colliders_array(CollisionLevelStateArray &level_state, const NodePath &root); + void r_traverse_array(CollisionLevelStateArray &level_state); void compare_collider_to_node(CollisionEntry &entry, const GeometricBoundingVolume *from_parent_gbv, diff --git a/panda/src/collide/config_collide.cxx b/panda/src/collide/config_collide.cxx index 8f0b5482d1..5f89a46b9f 100644 --- a/panda/src/collide/config_collide.cxx +++ b/panda/src/collide/config_collide.cxx @@ -63,6 +63,13 @@ ConfigVariableBool respect_effective_normal "collision solids (including polygons and planes) use their actual " "normal for intersection and physics tests.")); +ConfigVariableBool allow_collider_bitarray +("allow-collider-bitarray", true, + PRC_DESC("Set this true to enable the use of a BitArray to manage many " + "colliders added to a single traverser in one pass. If this is " + "false, a finite BitMask is always used instead, which is faster " + "per node visited, but may require multiple passes.")); + //////////////////////////////////////////////////////////////////// // Function: init_libcollide diff --git a/panda/src/collide/config_collide.h b/panda/src/collide/config_collide.h index e5fc709aae..2700735131 100644 --- a/panda/src/collide/config_collide.h +++ b/panda/src/collide/config_collide.h @@ -27,6 +27,7 @@ NotifyCategoryDecl(collide, EXPCL_PANDA, EXPTP_PANDA); extern EXPCL_PANDA ConfigVariableBool respect_prev_transform; extern EXPCL_PANDA ConfigVariableBool respect_effective_normal; +extern EXPCL_PANDA ConfigVariableBool allow_collider_bitarray; extern EXPCL_PANDA void init_libcollide();