Browse Source

allow-collider-bitarray -> allow-collider-multiple

David Rose 18 years ago
parent
commit
6381866a56

+ 6 - 7
panda/src/collide/collisionLevelState.h

@@ -24,7 +24,7 @@
 #include "collisionLevelStateBase.h"
 #include "collisionLevelStateBase.h"
 #include "collisionNode.h"
 #include "collisionNode.h"
 #include "bitMask.h"
 #include "bitMask.h"
-#include "bitArray.h"
+#include "doubleBitMask.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : CollisionLevelState
 //       Class : CollisionLevelState
@@ -74,14 +74,13 @@ private:
 
 
 #include "collisionLevelState.I"
 #include "collisionLevelState.I"
 
 
-// Now instantiate a pair of implementations of CollisionLevelState:
+// Now instantiate a handful of implementations of CollisionLevelState:
 // one that uses a word-at-a-time bitmask to track the active
 // 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).
+// colliders, and a couple that use more words at a time.
 
 
-typedef CollisionLevelState<BitMaskNative> CollisionLevelStateWord;
-typedef CollisionLevelState<BitArray> CollisionLevelStateArray;
+typedef CollisionLevelState<BitMaskNative> CollisionLevelStateSingle;
+typedef CollisionLevelState<DoubleBitMaskNative> CollisionLevelStateDouble;
+typedef CollisionLevelState<QuadBitMaskNative> CollisionLevelStateQuad;
 
 
 #endif
 #endif
 
 

+ 348 - 76
panda/src/collide/collisionTraverser.cxx

@@ -51,10 +51,18 @@ PStatCollector CollisionTraverser::_geom_volume_pcollector("Collision Volumes:Ge
 // This function object class is used in prepare_colliders(), below.
 // This function object class is used in prepare_colliders(), below.
 class SortByColliderSort {
 class SortByColliderSort {
 public:
 public:
-  inline bool operator () (const CollisionTraverser::OrderedColliderDef &a,
-                           const CollisionTraverser::OrderedColliderDef &b) const {
-    return DCAST(CollisionNode, a._node_path.node())->get_collider_sort() < DCAST(CollisionNode, b._node_path.node())->get_collider_sort();
+  SortByColliderSort(const CollisionTraverser &trav) :
+    _trav(trav) 
+  {
   }
   }
+
+  inline bool operator () (int a, int b) const {
+    const CollisionTraverser::OrderedColliderDef &ocd_a = _trav._ordered_colliders[a];
+    const CollisionTraverser::OrderedColliderDef &ocd_b = _trav._ordered_colliders[b];
+    return DCAST(CollisionNode, ocd_a._node_path.node())->get_collider_sort() < DCAST(CollisionNode, ocd_b._node_path.node())->get_collider_sort();
+  }
+
+  const CollisionTraverser &_trav;
 };
 };
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -281,37 +289,59 @@ traverse(const NodePath &root) {
     (*hi).first->begin_group();
     (*hi).first->begin_group();
   }
   }
 
 
-  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);
+  bool traversal_done = false;
+  if (_colliders.size() <= CollisionLevelStateSingle::get_max_colliders() ||
+      !allow_collider_multiple) {
+    // Use the single-word-at-a-time traverser, which might need to make
+    // lots of passes.
+    LevelStatesSingle level_states;
+    prepare_colliders_single(level_states, root);
+
+    if (level_states.size() == 1 || !allow_collider_multiple) {
+      traversal_done = true;
 
 
-    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
       // Make a number of passes, one for each group of 32 Colliders (or
       // whatever number of bits we have available in CurrentMask).
       // whatever number of bits we have available in CurrentMask).
       for (size_t pass = 0; pass < level_states.size(); ++pass) {
       for (size_t pass = 0; pass < level_states.size(); ++pass) {
 #ifdef DO_PSTATS
 #ifdef DO_PSTATS
         PStatTimer pass_timer(get_pass_collector(pass));
         PStatTimer pass_timer(get_pass_collector(pass));
 #endif
 #endif
-        r_traverse_word(level_states[pass], pass);
+        r_traverse_single(level_states[pass], pass);
+      }
+    }
+  }
+
+  if (!traversal_done &&
+      _colliders.size() <= CollisionLevelStateDouble::get_max_colliders()) {
+    // Try the double-word-at-a-time traverser.
+    LevelStatesDouble level_states;
+    prepare_colliders_double(level_states, root);
+
+    if (level_states.size() == 1) {
+      traversal_done = true;
+
+      for (size_t pass = 0; pass < level_states.size(); ++pass) {
+#ifdef DO_PSTATS
+        PStatTimer pass_timer(get_pass_collector(pass));
+#endif
+        r_traverse_double(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);
+  if (!traversal_done) {
+    // OK, do the quad-word-at-a-time traverser.
+    LevelStatesQuad level_states;
+    prepare_colliders_quad(level_states, root);
 
 
+    traversal_done = true;
+
+    for (size_t pass = 0; pass < level_states.size(); ++pass) {
 #ifdef DO_PSTATS
 #ifdef DO_PSTATS
-    PStatTimer pass_timer(get_pass_collector(0));
+      PStatTimer pass_timer(get_pass_collector(pass));
 #endif
 #endif
-    r_traverse_array(level_state);
+      r_traverse_quad(level_states[pass], pass);
+    }
   }
   }
 
 
   hi = _handlers.begin();
   hi = _handlers.begin();
@@ -478,24 +508,238 @@ write(ostream &out, int indent_level) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionTraverser::prepare_colliders_single
+//       Access: Private
+//  Description: Fills up the set of LevelStates corresponding to the
+//               active colliders in use.
+//
+//               This flavor uses a CollisionLevelStateSingle, which is
+//               limited to a certain number of colliders per pass
+//               (typically 32).
+////////////////////////////////////////////////////////////////////
+void CollisionTraverser::
+prepare_colliders_single(CollisionTraverser::LevelStatesSingle &level_states, 
+                         const NodePath &root) {
+  int num_colliders = _colliders.size();
+  int max_colliders = CollisionLevelStateSingle::get_max_colliders();
+
+  CollisionLevelStateSingle 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
+  // reserve() call won't reserve enough, but the code is otherwise
+  // correct.
+  level_state.reserve(min(num_colliders, max_colliders));
+
+  // Create an indirect index array to walk through the colliders in
+  // sorted order, without affect the actual collider order.
+  int *indirect = (int *)alloca(sizeof(int) * num_colliders);
+  int i;
+  for (i = 0; i < num_colliders; ++i) {
+    indirect[i] = i;
+  }
+  sort(indirect, indirect + num_colliders, SortByColliderSort(*this));
+
+  int num_remaining_colliders = num_colliders;
+  for (i = 0; i < num_colliders; ++i) {
+    OrderedColliderDef &ocd = _ordered_colliders[indirect[i]];
+    NodePath cnode_path = ocd._node_path;
+
+    if (!cnode_path.is_same_graph(root)) {
+      if (ocd._in_graph) {
+        // Only report this warning once.
+        collide_cat.info()
+          << "Collider " << cnode_path
+          << " is not in scene graph.  Ignoring.\n";
+        ocd._in_graph = false;
+      }
+
+    } else {
+      ocd._in_graph = true;
+      CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
+      
+      CollisionLevelStateSingle::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);
+
+        if (level_state.get_num_colliders() == max_colliders) {
+          // That's the limit.  Save off this level state and make a
+          // new one.
+          level_states.push_back(level_state);
+          level_state.clear();
+          level_state.reserve(min(num_remaining_colliders, max_colliders));
+        }
+      }
+    }
+
+    --num_remaining_colliders;
+    nassertv(num_remaining_colliders >= 0);
+  }
+
+  if (level_state.get_num_colliders() != 0) {
+    level_states.push_back(level_state);
+  }
+  nassertv(num_remaining_colliders == 0);
+}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionTraverser::prepare_colliders_word
+//     Function: CollisionTraverser::r_traverse_single
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+void CollisionTraverser::
+r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
+  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[pass]);
+          #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[pass]);
+          #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()) {
+      CollisionLevelStateSingle next_state(level_state, node->get_child(index));
+      r_traverse_single(next_state, pass);
+    }
+
+  } 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) {
+      CollisionLevelStateSingle 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_single(next_state, pass);
+    }
+
+  } else {
+    // Otherwise, visit all the children.
+    int num_children = node->get_num_children();
+    for (int i = 0; i < num_children; ++i) {
+      CollisionLevelStateSingle next_state(level_state, node->get_child(i));
+      r_traverse_single(next_state, pass);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionTraverser::prepare_colliders_double
 //       Access: Private
 //       Access: Private
 //  Description: Fills up the set of LevelStates corresponding to the
 //  Description: Fills up the set of LevelStates corresponding to the
 //               active colliders in use.
 //               active colliders in use.
 //
 //
-//               This flavor uses a CollisionLevelStateWord, which is
+//               This flavor uses a CollisionLevelStateDouble, which is
 //               limited to a certain number of colliders per pass
 //               limited to a certain number of colliders per pass
 //               (typically 32).
 //               (typically 32).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CollisionTraverser::
 void CollisionTraverser::
-prepare_colliders_word(CollisionTraverser::LevelStatesWord &level_states, 
-                       const NodePath &root) {
+prepare_colliders_double(CollisionTraverser::LevelStatesDouble &level_states, 
+                         const NodePath &root) {
   int num_colliders = _colliders.size();
   int num_colliders = _colliders.size();
-  int max_colliders = CollisionLevelStateWord::get_max_colliders();
+  int max_colliders = CollisionLevelStateDouble::get_max_colliders();
 
 
-  CollisionLevelStateWord level_state(root);
+  CollisionLevelStateDouble level_state(root);
   // This reserve() call is only correct if there is exactly one solid
   // This reserve() call is only correct if there is exactly one solid
   // per collider added to the traverser, which is the normal case.
   // per collider added to the traverser, which is the normal case.
   // If there is more than one solid in any of the colliders, this
   // If there is more than one solid in any of the colliders, this
@@ -503,27 +747,34 @@ prepare_colliders_word(CollisionTraverser::LevelStatesWord &level_states,
   // correct.
   // correct.
   level_state.reserve(min(num_colliders, max_colliders));
   level_state.reserve(min(num_colliders, max_colliders));
 
 
-  OrderedColliders sorted = _ordered_colliders;
-  sort(sorted.begin(), sorted.end(), SortByColliderSort());
+  // Create an indirect index array to walk through the colliders in
+  // sorted order, without affect the actual collider order.
+  int *indirect = (int *)alloca(sizeof(int) * num_colliders);
+  int i;
+  for (i = 0; i < num_colliders; ++i) {
+    indirect[i] = i;
+  }
+  sort(indirect, indirect + num_colliders, SortByColliderSort(*this));
 
 
-  OrderedColliders::iterator oci;
-  for (oci = sorted.begin(); oci != sorted.end(); ++oci) {
-    NodePath cnode_path = (*oci)._node_path;
+  int num_remaining_colliders = num_colliders;
+  for (i = 0; i < num_colliders; ++i) {
+    OrderedColliderDef &ocd = _ordered_colliders[indirect[i]];
+    NodePath cnode_path = ocd._node_path;
 
 
     if (!cnode_path.is_same_graph(root)) {
     if (!cnode_path.is_same_graph(root)) {
-      if ((*oci)._in_graph) {
+      if (ocd._in_graph) {
         // Only report this warning once.
         // Only report this warning once.
         collide_cat.info()
         collide_cat.info()
           << "Collider " << cnode_path
           << "Collider " << cnode_path
           << " is not in scene graph.  Ignoring.\n";
           << " is not in scene graph.  Ignoring.\n";
-        (*oci)._in_graph = false;
+        ocd._in_graph = false;
       }
       }
 
 
     } else {
     } else {
-      (*oci)._in_graph = true;
+      ocd._in_graph = true;
       CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
       CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
       
       
-      CollisionLevelStateWord::ColliderDef def;
+      CollisionLevelStateDouble::ColliderDef def;
       def._node = cnode;
       def._node = cnode;
       def._node_path = cnode_path;
       def._node_path = cnode_path;
       
       
@@ -538,28 +789,28 @@ prepare_colliders_word(CollisionTraverser::LevelStatesWord &level_states,
           // new one.
           // new one.
           level_states.push_back(level_state);
           level_states.push_back(level_state);
           level_state.clear();
           level_state.clear();
-          level_state.reserve(min(num_colliders, max_colliders));
+          level_state.reserve(min(num_remaining_colliders, max_colliders));
         }
         }
       }
       }
     }
     }
 
 
-    --num_colliders;
-    nassertv(num_colliders >= 0);
+    --num_remaining_colliders;
+    nassertv(num_remaining_colliders >= 0);
   }
   }
 
 
   if (level_state.get_num_colliders() != 0) {
   if (level_state.get_num_colliders() != 0) {
     level_states.push_back(level_state);
     level_states.push_back(level_state);
   }
   }
-  nassertv(num_colliders == 0);
+  nassertv(num_remaining_colliders == 0);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionTraverser::r_traverse_word
+//     Function: CollisionTraverser::r_traverse_double
 //       Access: Private
 //       Access: Private
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CollisionTraverser::
 void CollisionTraverser::
-r_traverse_word(CollisionLevelStateWord &level_state, size_t pass) {
+r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
   if (!level_state.any_in_bounds()) {
   if (!level_state.any_in_bounds()) {
     return;
     return;
   }
   }
@@ -655,8 +906,8 @@ r_traverse_word(CollisionLevelStateWord &level_state, size_t pass) {
     // visible child.
     // visible child.
     int index = node->get_visible_child();
     int index = node->get_visible_child();
     if (index >= 0 && index < node->get_num_children()) {
     if (index >= 0 && index < node->get_num_children()) {
-      CollisionLevelStateWord next_state(level_state, node->get_child(index));
-      r_traverse_word(next_state, pass);
+      CollisionLevelStateDouble next_state(level_state, node->get_child(index));
+      r_traverse_double(next_state, pass);
     }
     }
 
 
   } else if (node->is_lod_node()) {
   } else if (node->is_lod_node()) {
@@ -669,66 +920,76 @@ r_traverse_word(CollisionLevelStateWord &level_state, size_t pass) {
     int index = DCAST(LODNode, node)->get_lowest_switch();
     int index = DCAST(LODNode, node)->get_lowest_switch();
     int num_children = node->get_num_children();
     int num_children = node->get_num_children();
     for (int i = 0; i < num_children; ++i) {
     for (int i = 0; i < num_children; ++i) {
-      CollisionLevelStateWord next_state(level_state, node->get_child(i));
+      CollisionLevelStateDouble next_state(level_state, node->get_child(i));
       if (i != index) {
       if (i != index) {
         next_state.set_include_mask(next_state.get_include_mask() &
         next_state.set_include_mask(next_state.get_include_mask() &
           ~GeomNode::get_default_collide_mask());
           ~GeomNode::get_default_collide_mask());
       }
       }
-      r_traverse_word(next_state, pass);
+      r_traverse_double(next_state, pass);
     }
     }
 
 
   } else {
   } else {
     // Otherwise, visit all the children.
     // Otherwise, visit all the children.
     int num_children = node->get_num_children();
     int num_children = node->get_num_children();
     for (int i = 0; i < num_children; ++i) {
     for (int i = 0; i < num_children; ++i) {
-      CollisionLevelStateWord next_state(level_state, node->get_child(i));
-      r_traverse_word(next_state, pass);
+      CollisionLevelStateDouble next_state(level_state, node->get_child(i));
+      r_traverse_double(next_state, pass);
     }
     }
   }
   }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionTraverser::prepare_colliders_array
+//     Function: CollisionTraverser::prepare_colliders_quad
 //       Access: Private
 //       Access: Private
 //  Description: Fills up the set of LevelStates corresponding to the
 //  Description: Fills up the set of LevelStates corresponding to the
 //               active colliders in use.
 //               active colliders in use.
 //
 //
-//               This flavor uses a CollisionLevelStateArray, which
-//               has no limit in the number of colliders it can handle
-//               in one pass.
+//               This flavor uses a CollisionLevelStateQuad, which is
+//               limited to a certain number of colliders per pass
+//               (typically 32).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CollisionTraverser::
 void CollisionTraverser::
-prepare_colliders_array(CollisionLevelStateArray &level_state, 
-                        const NodePath &root) {
+prepare_colliders_quad(CollisionTraverser::LevelStatesQuad &level_states, 
+                         const NodePath &root) {
   int num_colliders = _colliders.size();
   int num_colliders = _colliders.size();
+  int max_colliders = CollisionLevelStateQuad::get_max_colliders();
+
+  CollisionLevelStateQuad level_state(root);
   // This reserve() call is only correct if there is exactly one solid
   // This reserve() call is only correct if there is exactly one solid
   // per collider added to the traverser, which is the normal case.
   // per collider added to the traverser, which is the normal case.
   // If there is more than one solid in any of the colliders, this
   // If there is more than one solid in any of the colliders, this
   // reserve() call won't reserve enough, but the code is otherwise
   // reserve() call won't reserve enough, but the code is otherwise
   // correct.
   // correct.
-  level_state.reserve(num_colliders);
+  level_state.reserve(min(num_colliders, max_colliders));
 
 
-  OrderedColliders sorted = _ordered_colliders;
-  sort(sorted.begin(), sorted.end(), SortByColliderSort());
+  // Create an indirect index array to walk through the colliders in
+  // sorted order, without affect the actual collider order.
+  int *indirect = (int *)alloca(sizeof(int) * num_colliders);
+  int i;
+  for (i = 0; i < num_colliders; ++i) {
+    indirect[i] = i;
+  }
+  sort(indirect, indirect + num_colliders, SortByColliderSort(*this));
 
 
-  OrderedColliders::iterator oci;
-  for (oci = sorted.begin(); oci != sorted.end(); ++oci) {
-    NodePath cnode_path = (*oci)._node_path;
+  int num_remaining_colliders = num_colliders;
+  for (i = 0; i < num_colliders; ++i) {
+    OrderedColliderDef &ocd = _ordered_colliders[indirect[i]];
+    NodePath cnode_path = ocd._node_path;
 
 
     if (!cnode_path.is_same_graph(root)) {
     if (!cnode_path.is_same_graph(root)) {
-      if ((*oci)._in_graph) {
+      if (ocd._in_graph) {
         // Only report this warning once.
         // Only report this warning once.
         collide_cat.info()
         collide_cat.info()
           << "Collider " << cnode_path
           << "Collider " << cnode_path
           << " is not in scene graph.  Ignoring.\n";
           << " is not in scene graph.  Ignoring.\n";
-        (*oci)._in_graph = false;
+        ocd._in_graph = false;
       }
       }
 
 
     } else {
     } else {
-      (*oci)._in_graph = true;
+      ocd._in_graph = true;
       CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
       CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
       
       
-      CollisionLevelStateArray::ColliderDef def;
+      CollisionLevelStateQuad::ColliderDef def;
       def._node = cnode;
       def._node = cnode;
       def._node_path = cnode_path;
       def._node_path = cnode_path;
       
       
@@ -737,23 +998,34 @@ prepare_colliders_array(CollisionLevelStateArray &level_state,
         CollisionSolid *collider = cnode->get_solid(s);
         CollisionSolid *collider = cnode->get_solid(s);
         def._collider = collider;
         def._collider = collider;
         level_state.prepare_collider(def, root);
         level_state.prepare_collider(def, root);
+
+        if (level_state.get_num_colliders() == max_colliders) {
+          // That's the limit.  Save off this level state and make a
+          // new one.
+          level_states.push_back(level_state);
+          level_state.clear();
+          level_state.reserve(min(num_remaining_colliders, max_colliders));
+        }
       }
       }
     }
     }
 
 
-    --num_colliders;
-    nassertv(num_colliders >= 0);
+    --num_remaining_colliders;
+    nassertv(num_remaining_colliders >= 0);
   }
   }
 
 
-  nassertv(num_colliders == 0);
+  if (level_state.get_num_colliders() != 0) {
+    level_states.push_back(level_state);
+  }
+  nassertv(num_remaining_colliders == 0);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionTraverser::r_traverse_array
+//     Function: CollisionTraverser::r_traverse_quad
 //       Access: Private
 //       Access: Private
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CollisionTraverser::
 void CollisionTraverser::
-r_traverse_array(CollisionLevelStateArray &level_state) {
+r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
   if (!level_state.any_in_bounds()) {
   if (!level_state.any_in_bounds()) {
     return;
     return;
   }
   }
@@ -784,7 +1056,7 @@ r_traverse_array(CollisionLevelStateArray &level_state) {
         if ((entry._from_node->get_from_collide_mask() &
         if ((entry._from_node->get_from_collide_mask() &
              cnode->get_into_collide_mask()) != 0) {
              cnode->get_into_collide_mask()) != 0) {
           #ifdef DO_PSTATS
           #ifdef DO_PSTATS
-          PStatTimer collide_timer(_solid_collide_collectors[0]);
+          PStatTimer collide_timer(_solid_collide_collectors[pass]);
           #endif
           #endif
           entry._from_node_path = level_state.get_collider_node_path(c);
           entry._from_node_path = level_state.get_collider_node_path(c);
           entry._from = level_state.get_collider(c);
           entry._from = level_state.get_collider(c);
@@ -829,7 +1101,7 @@ r_traverse_array(CollisionLevelStateArray &level_state) {
         if ((entry._from_node->get_from_collide_mask() &
         if ((entry._from_node->get_from_collide_mask() &
              gnode->get_into_collide_mask()) != 0) {
              gnode->get_into_collide_mask()) != 0) {
           #ifdef DO_PSTATS
           #ifdef DO_PSTATS
-          PStatTimer collide_timer(_solid_collide_collectors[0]);
+          PStatTimer collide_timer(_solid_collide_collectors[pass]);
           #endif
           #endif
           entry._from_node_path = level_state.get_collider_node_path(c);
           entry._from_node_path = level_state.get_collider_node_path(c);
           entry._from = level_state.get_collider(c);
           entry._from = level_state.get_collider(c);
@@ -849,8 +1121,8 @@ r_traverse_array(CollisionLevelStateArray &level_state) {
     // visible child.
     // visible child.
     int index = node->get_visible_child();
     int index = node->get_visible_child();
     if (index >= 0 && index < node->get_num_children()) {
     if (index >= 0 && index < node->get_num_children()) {
-      CollisionLevelStateArray next_state(level_state, node->get_child(index));
-      r_traverse_array(next_state);
+      CollisionLevelStateQuad next_state(level_state, node->get_child(index));
+      r_traverse_quad(next_state, pass);
     }
     }
 
 
   } else if (node->is_lod_node()) {
   } else if (node->is_lod_node()) {
@@ -863,20 +1135,20 @@ r_traverse_array(CollisionLevelStateArray &level_state) {
     int index = DCAST(LODNode, node)->get_lowest_switch();
     int index = DCAST(LODNode, node)->get_lowest_switch();
     int num_children = node->get_num_children();
     int num_children = node->get_num_children();
     for (int i = 0; i < num_children; ++i) {
     for (int i = 0; i < num_children; ++i) {
-      CollisionLevelStateArray next_state(level_state, node->get_child(i));
+      CollisionLevelStateQuad next_state(level_state, node->get_child(i));
       if (i != index) {
       if (i != index) {
         next_state.set_include_mask(next_state.get_include_mask() &
         next_state.set_include_mask(next_state.get_include_mask() &
           ~GeomNode::get_default_collide_mask());
           ~GeomNode::get_default_collide_mask());
       }
       }
-      r_traverse_array(next_state);
+      r_traverse_quad(next_state, pass);
     }
     }
 
 
   } else {
   } else {
     // Otherwise, visit all the children.
     // Otherwise, visit all the children.
     int num_children = node->get_num_children();
     int num_children = node->get_num_children();
     for (int i = 0; i < num_children; ++i) {
     for (int i = 0; i < num_children; ++i) {
-      CollisionLevelStateArray next_state(level_state, node->get_child(i));
-      r_traverse_array(next_state);
+      CollisionLevelStateQuad next_state(level_state, node->get_child(i));
+      r_traverse_quad(next_state, pass);
     }
     }
   }
   }
 }
 }

+ 10 - 5
panda/src/collide/collisionTraverser.h

@@ -81,12 +81,17 @@ PUBLISHED:
   void write(ostream &out, int indent_level) const;
   void write(ostream &out, int indent_level) const;
 
 
 private:
 private:
-  typedef pvector<CollisionLevelStateWord> LevelStatesWord;
-  void prepare_colliders_word(LevelStatesWord &level_states, const NodePath &root);
-  void r_traverse_word(CollisionLevelStateWord &level_state, size_t pass);
+  typedef pvector<CollisionLevelStateSingle> LevelStatesSingle;
+  void prepare_colliders_single(LevelStatesSingle &level_states, const NodePath &root);
+  void r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass);
 
 
-  void prepare_colliders_array(CollisionLevelStateArray &level_state, const NodePath &root);
-  void r_traverse_array(CollisionLevelStateArray &level_state);
+  typedef pvector<CollisionLevelStateDouble> LevelStatesDouble;
+  void prepare_colliders_double(LevelStatesDouble &level_states, const NodePath &root);
+  void r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass);
+
+  typedef pvector<CollisionLevelStateQuad> LevelStatesQuad;
+  void prepare_colliders_quad(LevelStatesQuad &level_states, const NodePath &root);
+  void r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass);
 
 
   void compare_collider_to_node(CollisionEntry &entry,
   void compare_collider_to_node(CollisionEntry &entry,
                                 const GeometricBoundingVolume *from_parent_gbv,
                                 const GeometricBoundingVolume *from_parent_gbv,

+ 6 - 5
panda/src/collide/config_collide.cxx

@@ -63,12 +63,13 @@ ConfigVariableBool respect_effective_normal
           "collision solids (including polygons and planes) use their actual "
           "collision solids (including polygons and planes) use their actual "
           "normal for intersection and physics tests."));
           "normal for intersection and physics tests."));
 
 
-ConfigVariableBool allow_collider_bitarray
-("allow-collider-bitarray", false,
- PRC_DESC("Set this true to enable the use of a BitArray to manage many "
+ConfigVariableBool allow_collider_multiple
+("allow-collider-multiple", false,
+ PRC_DESC("Set this true to enable the use of a DoubleBitMask or QuadBitMask "
+          "to manage many "
           "colliders added to a single traverser in one pass.  If this is "
           "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."));
+          "false, a one-word BitMask is always used instead, which is faster "
+          "per pass, but may require more passes."));
 
 
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/collide/config_collide.h

@@ -27,7 +27,7 @@ NotifyCategoryDecl(collide, EXPCL_PANDA, EXPTP_PANDA);
 
 
 extern EXPCL_PANDA ConfigVariableBool respect_prev_transform;
 extern EXPCL_PANDA ConfigVariableBool respect_prev_transform;
 extern EXPCL_PANDA ConfigVariableBool respect_effective_normal;
 extern EXPCL_PANDA ConfigVariableBool respect_effective_normal;
-extern EXPCL_PANDA ConfigVariableBool allow_collider_bitarray;
+extern EXPCL_PANDA ConfigVariableBool allow_collider_multiple;
 
 
 extern EXPCL_PANDA void init_libcollide();
 extern EXPCL_PANDA void init_libcollide();