// Filename: collisionLevelState.I // Created by: drose (05Apr07) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// template INLINE CollisionLevelState:: CollisionLevelState(const NodePath &node_path) : CollisionLevelStateBase(node_path), _current(CurrentMask::all_off()) { } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::Constructor // Access: Public // Description: This constructor goes to the next child node in the // traversal. //////////////////////////////////////////////////////////////////// template INLINE CollisionLevelState:: CollisionLevelState(const CollisionLevelState &parent, PandaNode *child) : CollisionLevelStateBase(parent, child), _current(parent._current) { } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::Copy Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// template INLINE CollisionLevelState:: CollisionLevelState(const CollisionLevelState ©) : CollisionLevelStateBase(copy), _current(copy._current) { } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::Copy Assignment Operator // Access: Public // Description: //////////////////////////////////////////////////////////////////// template INLINE void CollisionLevelState:: operator = (const CollisionLevelState ©) { CollisionLevelStateBase::operator = (copy); _current = copy._current; } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::clear // Access: Public // Description: //////////////////////////////////////////////////////////////////// template INLINE void CollisionLevelState:: clear() { CollisionLevelStateBase::clear(); _current.clear(); } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // 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); } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // 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()) { // Also don't test a node with itself, or with any of its // descendants. if (node() == cnode) { #ifndef NDEBUG if (collide_cat.is_spam()) { indent(collide_cat.spam(false), indent_level) << "Not comparing " << c << " to " << _node_path << " (same node)\n"; } #endif // NDEBUG } else { // There are bits in common, and it's not the same // instance, so go ahead and try the bounding volume. const GeometricBoundingVolume *col_gbv = get_local_bound(c); is_in = true; // If there's no bounding volume, we're implicitly in. 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()) { 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"; if (num_colliders != 0) { collide_cat.spam(false) << " ("; for (int c = 0; c < num_colliders; c++) { if (has_collider(c)) { CollisionNode *cnode = get_collider_node(c); collide_cat.spam(false) << " " << c << ". " << cnode->get_name(); } } collide_cat.spam(false) << " )"; } collide_cat.spam(false) << "\n"; } #endif // NDEBUG return has_any_collider(); } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // 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. // // Returns true if the inverse transform is valid, or // false if it is not valid (e.g. the transform has a // scale to zero). If the inverse transform is not // valid, the caller should not visit this node. //////////////////////////////////////////////////////////////////// template bool CollisionLevelState:: apply_transform() { // The "parent" bounds list remembers the bounds list of the // previous node. _parent_bounds = _local_bounds; if (node()->is_final()) { // If this node has a "final" bounds, we blank out all of the from // bounding volumes, since we've already tested against this // node's into bounds, and there's no need to test any further // bounding volumes at this node level or below. BoundingVolumes new_bounds; int num_colliders = get_num_colliders(); new_bounds.reserve(num_colliders); for (int c = 0; c < num_colliders; c++) { new_bounds.push_back((GeometricBoundingVolume *)NULL); } _local_bounds = new_bounds; } else { // Otherwise, in the usual case, the bounds tests will continue. // 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()); if (!inv_transform->has_mat()) { // No inverse. return false; } 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; } } return true; } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // 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(); } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::get_max_colliders // Access: Public, Static // Description: Returns the maximum number of colliders that may be // added to the CollisionLevelStateBase at any one time. //////////////////////////////////////////////////////////////////// template INLINE int CollisionLevelState:: get_max_colliders() { return CurrentMask::get_max_num_bits(); } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::has_collider // Access: Public // Description: Returns true if the nth collider in the LevelState is // still part of the level. //////////////////////////////////////////////////////////////////// template INLINE bool CollisionLevelState:: has_collider(int n) const { nassertr(n >= 0 && n < (int)_colliders.size(), false); return (_current.get_bit(n)); } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::has_any_collider // Access: Public // Description: //////////////////////////////////////////////////////////////////// template INLINE bool CollisionLevelState:: has_any_collider() const { return !_current.is_zero(); } #endif // CPPPARSER #ifndef CPPPARSER //////////////////////////////////////////////////////////////////// // Function: CollisionLevelState::omit_collider // Access: Public // Description: //////////////////////////////////////////////////////////////////// template INLINE void CollisionLevelState:: omit_collider(int n) { nassertv(n >= 0 && n < (int)_colliders.size()); nassertv(has_collider(n)); _current.clear_bit(n); } #endif // CPPPARSER