// Filename: collisionLevelState.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 "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 = 0; } //////////////////////////////////////////////////////////////////// // 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(num_colliders <= get_max_colliders()); _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(index < get_max_colliders()); _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); gbv->xform(rel_transform->get_mat()); _local_bounds.push_back(gbv); } CurrentMask mask = get_mask(index); _current |= mask; _parent_bounds = _local_bounds; nassertv(mask != 0); } //////////////////////////////////////////////////////////////////// // 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 (cnode->get_collide_geom() || (from_mask & node()->get_net_collide_mask()) != 0) { // 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; } }