Panda3D
collisionTraverser.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file collisionTraverser.cxx
10  * @author drose
11  * @date 2002-03-16
12  */
13 
14 #include "collisionTraverser.h"
15 #include "collisionNode.h"
16 #include "collisionEntry.h"
17 #include "collisionPolygon.h"
18 #include "collisionGeom.h"
19 #include "collisionRecorder.h"
20 #include "collisionVisualizer.h"
21 #include "collisionSphere.h"
22 #include "collisionBox.h"
23 #include "collisionCapsule.h"
24 #include "collisionPolygon.h"
25 #include "collisionPlane.h"
26 #include "config_collide.h"
27 #include "boundingSphere.h"
28 #include "transformState.h"
29 #include "geomNode.h"
30 #include "geom.h"
31 #include "geom.h"
32 #include "geomTriangles.h"
33 #include "geomVertexReader.h"
34 #include "lodNode.h"
35 #include "nodePath.h"
36 #include "pStatTimer.h"
37 #include "indent.h"
38 
39 #include <algorithm>
40 
41 using std::min;
42 
43 PStatCollector CollisionTraverser::_collisions_pcollector("App:Collisions");
44 
45 PStatCollector CollisionTraverser::_cnode_volume_pcollector("Collision Volumes:CollisionNode");
46 PStatCollector CollisionTraverser::_gnode_volume_pcollector("Collision Volumes:GeomNode");
47 PStatCollector CollisionTraverser::_geom_volume_pcollector("Collision Volumes:Geom");
48 
49 TypeHandle CollisionTraverser::_type_handle;
50 
51 // This function object class is used in prepare_colliders(), below.
52 class SortByColliderSort {
53 public:
54  SortByColliderSort(const CollisionTraverser &trav) :
55  _trav(trav)
56  {
57  }
58 
59  inline bool operator () (int a, int b) const {
60  const CollisionTraverser::OrderedColliderDef &ocd_a = _trav._ordered_colliders[a];
61  const CollisionTraverser::OrderedColliderDef &ocd_b = _trav._ordered_colliders[b];
62  return ((const CollisionNode *)ocd_a._node_path.node())->get_collider_sort() < ((const CollisionNode *)ocd_b._node_path.node())->get_collider_sort();
63  }
64 
65  const CollisionTraverser &_trav;
66 };
67 
68 /**
69  *
70  */
71 CollisionTraverser::
72 CollisionTraverser(const std::string &name) :
73  Namable(name),
74  _this_pcollector(_collisions_pcollector, name)
75 {
76  _respect_prev_transform = respect_prev_transform;
77  #ifdef DO_COLLISION_RECORDING
78  _recorder = nullptr;
79  #endif
80 }
81 
82 /**
83  *
84  */
85 CollisionTraverser::
86 ~CollisionTraverser() {
87  #ifdef DO_COLLISION_RECORDING
88  clear_recorder();
89  #endif
90 }
91 
92 /**
93  * Adds a new CollisionNode, representing an object that will be tested for
94  * collisions into other objects, along with the handler that will serve each
95  * detected collision. Each CollisionNode may be served by only one handler
96  * at a time, but a given handler may serve many CollisionNodes.
97  *
98  * The handler that serves a particular node may be changed from time to time
99  * by calling add_collider() again on the same node.
100  */
102 add_collider(const NodePath &collider, CollisionHandler *handler) {
103  nassertv(_ordered_colliders.size() == _colliders.size());
104  nassertv(!collider.is_empty() && collider.node()->is_collision_node());
105  nassertv(handler != nullptr);
106 
107  Colliders::iterator ci = _colliders.find(collider);
108  if (ci != _colliders.end()) {
109  // We already knew about this collider.
110  if ((*ci).second != handler) {
111  // Change the handler.
112  PT(CollisionHandler) old_handler = (*ci).second;
113  (*ci).second = handler;
114 
115  // Now update our own reference counts within our handler set.
116  Handlers::iterator hi = _handlers.find(old_handler);
117  nassertv(hi != _handlers.end());
118  (*hi).second--;
119  nassertv((*hi).second >= 0);
120  if ((*hi).second == 0) {
121  _handlers.erase(hi);
122  }
123 
124  hi = _handlers.find(handler);
125  if (hi == _handlers.end()) {
126  _handlers.insert(Handlers::value_type(handler, 1));
127  } else {
128  ++(*hi).second;
129  }
130  }
131 
132  } else {
133  // We hadn't already known about this collider.
134  _colliders.insert(Colliders::value_type(collider, handler));
135  OrderedColliderDef ocdef;
136  ocdef._node_path = collider;
137  ocdef._in_graph = true;
138  _ordered_colliders.push_back(ocdef);
139 
140  Handlers::iterator hi = _handlers.find(handler);
141  if (hi == _handlers.end()) {
142  _handlers.insert(Handlers::value_type(handler, 1));
143  } else {
144  ++(*hi).second;
145  }
146  }
147 
148  nassertv(_ordered_colliders.size() == _colliders.size());
149 }
150 
151 /**
152  * Removes the collider (and its associated handler) from the set of
153  * CollisionNodes that will be tested each frame for collisions into other
154  * objects. Returns true if the definition was found and removed, false if it
155  * wasn't present to begin with.
156  */
158 remove_collider(const NodePath &collider) {
159  nassertr(_ordered_colliders.size() == _colliders.size(), false);
160 
161  Colliders::iterator ci = _colliders.find(collider);
162  if (ci == _colliders.end()) {
163  // We didn't know about this node.
164  return false;
165  }
166 
167  CollisionHandler *handler = (*ci).second;
168 
169  // Update the set of handlers.
170  Handlers::iterator hi = _handlers.find(handler);
171  nassertr(hi != _handlers.end(), false);
172  (*hi).second--;
173  nassertr((*hi).second >= 0, false);
174  if ((*hi).second == 0) {
175  _handlers.erase(hi);
176  }
177 
178  _colliders.erase(ci);
179 
180  OrderedColliders::iterator oci;
181  oci = _ordered_colliders.begin();
182  while (oci != _ordered_colliders.end() &&
183  (*oci)._node_path != collider) {
184  ++oci;
185  }
186 
187  nassertr(oci != _ordered_colliders.end(), false);
188  _ordered_colliders.erase(oci);
189 
190  nassertr(_ordered_colliders.size() == _colliders.size(), false);
191  return true;
192 }
193 
194 /**
195  * Returns true if the indicated node is current in the set of nodes that will
196  * be tested each frame for collisions into other objects.
197  */
199 has_collider(const NodePath &collider) const {
200  Colliders::const_iterator ci = _colliders.find(collider);
201  return (ci != _colliders.end());
202 }
203 
204 /**
205  * Returns the number of CollisionNodes that have been added to the traverser
206  * via add_collider().
207  */
208 int CollisionTraverser::
209 get_num_colliders() const {
210  nassertr(_ordered_colliders.size() == _colliders.size(), 0);
211  return _ordered_colliders.size();
212 }
213 
214 /**
215  * Returns the nth CollisionNode that has been added to the traverser via
216  * add_collider().
217  */
219 get_collider(int n) const {
220  nassertr(_ordered_colliders.size() == _colliders.size(), NodePath());
221  nassertr(n >= 0 && n < (int)_ordered_colliders.size(), NodePath());
222  return _ordered_colliders[n]._node_path;
223 }
224 
225 /**
226  * Returns the handler that is currently assigned to serve the indicated
227  * collision node, or NULL if the node is not on the traverser's set of active
228  * nodes.
229  */
231 get_handler(const NodePath &collider) const {
232  Colliders::const_iterator ci = _colliders.find(collider);
233  if (ci != _colliders.end()) {
234  return (*ci).second;
235  }
236  return nullptr;
237 }
238 
239 /**
240  * Completely empties the set of collision nodes and their associated
241  * handlers.
242  */
245  _colliders.clear();
246  _ordered_colliders.clear();
247  _handlers.clear();
248 }
249 
250 /**
251  *
252  */
253 void CollisionTraverser::
254 traverse(const NodePath &root) {
255  PStatTimer timer(_this_pcollector);
256 
257  #ifdef DO_COLLISION_RECORDING
258  if (has_recorder()) {
259  get_recorder()->begin_traversal();
260  }
261  #endif // DO_COLLISION_RECORDING
262 
263  Handlers::iterator hi;
264  for (hi = _handlers.begin(); hi != _handlers.end(); ++hi) {
265  if ((*hi).first->wants_all_potential_collidees()) {
266  (*hi).first->set_root(root);
267  }
268  (*hi).first->begin_group();
269  }
270 
271  bool traversal_done = false;
272  if ((int)_colliders.size() <= CollisionLevelStateSingle::get_max_colliders() ||
273  !allow_collider_multiple) {
274  // Use the single-word-at-a-time traverser, which might need to make lots
275  // of passes.
276  LevelStatesSingle level_states;
277  prepare_colliders_single(level_states, root);
278 
279  if (level_states.size() == 1 || !allow_collider_multiple) {
280  traversal_done = true;
281 
282  // Make a number of passes, one for each group of 32 Colliders (or
283  // whatever number of bits we have available in CurrentMask).
284  for (size_t pass = 0; pass < level_states.size(); ++pass) {
285 #ifdef DO_PSTATS
286  PStatTimer pass_timer(get_pass_collector(pass));
287 #endif
288  r_traverse_single(level_states[pass], pass);
289  }
290  }
291  }
292 
293  if (!traversal_done &&
294  (int)_colliders.size() <= CollisionLevelStateDouble::get_max_colliders()) {
295  // Try the double-word-at-a-time traverser.
296  LevelStatesDouble level_states;
297  prepare_colliders_double(level_states, root);
298 
299  if (level_states.size() == 1) {
300  traversal_done = true;
301 
302  for (size_t pass = 0; pass < level_states.size(); ++pass) {
303 #ifdef DO_PSTATS
304  PStatTimer pass_timer(get_pass_collector(pass));
305 #endif
306  r_traverse_double(level_states[pass], pass);
307  }
308  }
309  }
310 
311  if (!traversal_done) {
312  // OK, do the quad-word-at-a-time traverser.
313  LevelStatesQuad level_states;
314  prepare_colliders_quad(level_states, root);
315 
316  traversal_done = true;
317 
318  for (size_t pass = 0; pass < level_states.size(); ++pass) {
319 #ifdef DO_PSTATS
320  PStatTimer pass_timer(get_pass_collector(pass));
321 #endif
322  r_traverse_quad(level_states[pass], pass);
323  }
324  }
325 
326  hi = _handlers.begin();
327  while (hi != _handlers.end()) {
328  if (!(*hi).first->end_group()) {
329  // This handler wants to remove itself from the traversal list.
330  hi = remove_handler(hi);
331  } else {
332  ++hi;
333  }
334  }
335 
336  #ifdef DO_COLLISION_RECORDING
337  if (has_recorder()) {
338  get_recorder()->end_traversal();
339  }
340  #endif // DO_COLLISION_RECORDING
341 
342  CollisionLevelStateBase::_node_volume_pcollector.flush_level();
343  _cnode_volume_pcollector.flush_level();
344  _gnode_volume_pcollector.flush_level();
345  _geom_volume_pcollector.flush_level();
346 
352 }
353 
354 #ifdef DO_COLLISION_RECORDING
355 /**
356  * Uses the indicated CollisionRecorder object to start recording the
357  * intersection tests made by each subsequent call to traverse() on this
358  * object. A particular CollisionRecorder object can only record one
359  * traverser at a time; if this object has already been assigned to another
360  * traverser, that assignment is broken.
361  *
362  * This is intended to be used in a debugging mode to try to determine what
363  * work is being performed by the collision traversal. Usually, attaching a
364  * recorder will impose significant runtime overhead.
365  *
366  * This does not transfer ownership of the CollisionRecorder pointer;
367  * maintenance of that remains the caller's responsibility. If the
368  * CollisionRecorder is destructed, it will cleanly remove itself from the
369  * traverser.
370  */
371 void CollisionTraverser::
372 set_recorder(CollisionRecorder *recorder) {
373  if (recorder != _recorder) {
374  // Remove the old recorder, if any.
375  if (_recorder != nullptr) {
376  nassertv(_recorder->_trav == this);
377  _recorder->_trav = nullptr;
378  }
379 
380  _recorder = recorder;
381 
382  // Tell the new recorder about his new owner.
383  if (_recorder != nullptr) {
384  nassertv(_recorder->_trav != this);
385  if (_recorder->_trav != nullptr) {
386  _recorder->_trav->clear_recorder();
387  }
388  nassertv(_recorder->_trav == nullptr);
389  _recorder->_trav = this;
390  }
391  }
392 }
393 
394 /**
395  * This is a high-level function to create a CollisionVisualizer object to
396  * render the collision tests performed by this traverser. The supplied root
397  * should be any node in the scene graph; typically, the top node (e.g.
398  * render). The CollisionVisualizer will be attached to this node.
399  */
400 CollisionVisualizer *CollisionTraverser::
401 show_collisions(const NodePath &root) {
402  hide_collisions();
403  CollisionVisualizer *viz = new CollisionVisualizer("show_collisions");
404  _collision_visualizer_np = root.attach_new_node(viz);
405  set_recorder(viz);
406  return viz;
407 }
408 
409 /**
410  * Undoes the effect of a previous call to show_collisions().
411  */
412 void CollisionTraverser::
413 hide_collisions() {
414  if (!_collision_visualizer_np.is_empty()) {
415  _collision_visualizer_np.remove_node();
416  }
417  clear_recorder();
418 }
419 
420 #endif // DO_COLLISION_RECORDING
421 
422 /**
423  *
424  */
425 void CollisionTraverser::
426 output(std::ostream &out) const {
427  out << "CollisionTraverser, " << _colliders.size()
428  << " colliders and " << _handlers.size() << " handlers.\n";
429 }
430 
431 /**
432  *
433  */
434 void CollisionTraverser::
435 write(std::ostream &out, int indent_level) const {
436  indent(out, indent_level)
437  << "CollisionTraverser, " << _colliders.size()
438  << " colliders and " << _handlers.size() << " handlers:\n";
439 
440  OrderedColliders::const_iterator oci;
441  for (oci = _ordered_colliders.begin();
442  oci != _ordered_colliders.end();
443  ++oci) {
444  NodePath cnode_path = (*oci)._node_path;
445  bool in_graph = (*oci)._in_graph;
446 
447  Colliders::const_iterator ci;
448  ci = _colliders.find(cnode_path);
449  nassertv(ci != _colliders.end());
450 
451  CollisionHandler *handler = (*ci).second;
452  nassertv(handler != nullptr);
453 
454  indent(out, indent_level + 2)
455  << cnode_path;
456  if (in_graph) {
457  out << " handled by " << handler->get_type() << "\n";
458  } else {
459  out << " ignored\n";
460  }
461 
462  if (!cnode_path.is_empty() && cnode_path.node()->is_collision_node()) {
463  CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
464 
465  int num_solids = cnode->get_num_solids();
466  for (int i = 0; i < num_solids; ++i) {
467  cnode->get_solid(i)->write(out, indent_level + 4);
468  }
469  }
470  }
471 }
472 
473 /**
474  * Fills up the set of LevelStates corresponding to the active colliders in
475  * use.
476  *
477  * This flavor uses a CollisionLevelStateSingle, which is limited to a certain
478  * number of colliders per pass (typically 32).
479  */
480 void CollisionTraverser::
481 prepare_colliders_single(CollisionTraverser::LevelStatesSingle &level_states,
482  const NodePath &root) {
483  int num_colliders = _colliders.size();
484  int max_colliders = CollisionLevelStateSingle::get_max_colliders();
485 
486  CollisionLevelStateSingle level_state(root);
487  // This reserve() call is only correct if there is exactly one solid per
488  // collider added to the traverser, which is the normal case. If there is
489  // more than one solid in any of the colliders, this reserve() call won't
490  // reserve enough, but the code is otherwise correct.
491  level_state.reserve(min(num_colliders, max_colliders));
492 
493  // Create an indirect index array to walk through the colliders in sorted
494  // order, without affect the actual collider order.
495  int *indirect = (int *)alloca(sizeof(int) * num_colliders);
496  int i;
497  for (i = 0; i < num_colliders; ++i) {
498  indirect[i] = i;
499  }
500  std::sort(indirect, indirect + num_colliders, SortByColliderSort(*this));
501 
502  int num_remaining_colliders = num_colliders;
503  for (i = 0; i < num_colliders; ++i) {
504  OrderedColliderDef &ocd = _ordered_colliders[indirect[i]];
505  NodePath cnode_path = ocd._node_path;
506 
507  if (!cnode_path.is_same_graph(root)) {
508  if (ocd._in_graph) {
509  // Only report this warning once.
510  collide_cat.info()
511  << "Collider " << cnode_path
512  << " is not in scene graph. Ignoring.\n";
513  ocd._in_graph = false;
514  }
515 
516  } else {
517  ocd._in_graph = true;
518  CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
519 
521  def._node = cnode;
522  def._node_path = cnode_path;
523 
524  int num_solids = cnode->get_num_solids();
525  for (int s = 0; s < num_solids; ++s) {
526  CPT(CollisionSolid) collider = cnode->get_solid(s);
527  def._collider = collider;
528  level_state.prepare_collider(def, root);
529 
530  if (level_state.get_num_colliders() == max_colliders) {
531  // That's the limit. Save off this level state and make a new one.
532  level_states.push_back(level_state);
533  level_state.clear();
534  level_state.reserve(min(num_remaining_colliders, max_colliders));
535  }
536  }
537  }
538 
539  --num_remaining_colliders;
540  nassertv(num_remaining_colliders >= 0);
541  }
542 
543  if (level_state.get_num_colliders() != 0) {
544  level_states.push_back(level_state);
545  }
546  nassertv(num_remaining_colliders == 0);
547 }
548 
549 /**
550  *
551  */
552 void CollisionTraverser::
553 r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
554  if (!level_state.any_in_bounds()) {
555  return;
556  }
557  if (!level_state.apply_transform()) {
558  return;
559  }
560 
561  PandaNode *node = level_state.node();
562  if (node->is_collision_node()) {
563  CollisionNode *cnode;
564  DCAST_INTO_V(cnode, node);
565  CPT(BoundingVolume) node_bv = cnode->get_bounds();
566  const GeometricBoundingVolume *node_gbv = nullptr;
567  if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
568  DCAST_INTO_V(node_gbv, node_bv);
569  }
570 
571  CollisionEntry entry;
572  entry._into_node = cnode;
573  entry._into_node_path = level_state.get_node_path();
574  if (_respect_prev_transform) {
575  entry._flags |= CollisionEntry::F_respect_prev_transform;
576  }
577 
578  int num_colliders = level_state.get_num_colliders();
579  for (int c = 0; c < num_colliders; ++c) {
580  if (level_state.has_collider(c)) {
581  entry._from_node = level_state.get_collider_node(c);
582 
583  if ((entry._from_node->get_from_collide_mask() &
584  cnode->get_into_collide_mask()) != 0) {
585  #ifdef DO_PSTATS
586  // PStatTimer collide_timer(_solid_collide_collectors[pass]);
587  #endif
588  entry._from_node_path = level_state.get_collider_node_path(c);
589  entry._from = level_state.get_collider(c);
590 
591  compare_collider_to_node(
592  entry,
593  level_state.get_parent_bound(c),
594  level_state.get_local_bound(c),
595  node_gbv);
596  }
597  }
598  }
599 
600  } else if (node->is_geom_node()) {
601  #ifndef NDEBUG
602  if (collide_cat.is_spam()) {
603  collide_cat.spam()
604  << "Reached " << *node << "\n";
605  }
606  #endif
607 
608  GeomNode *gnode;
609  DCAST_INTO_V(gnode, node);
610  CPT(BoundingVolume) node_bv = gnode->get_bounds();
611  const GeometricBoundingVolume *node_gbv = nullptr;
612  if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
613  DCAST_INTO_V(node_gbv, node_bv);
614  }
615 
616  CollisionEntry entry;
617  entry._into_node = gnode;
618  entry._into_node_path = level_state.get_node_path();
619  if (_respect_prev_transform) {
620  entry._flags |= CollisionEntry::F_respect_prev_transform;
621  }
622 
623  int num_colliders = level_state.get_num_colliders();
624  for (int c = 0; c < num_colliders; ++c) {
625  if (level_state.has_collider(c)) {
626  entry._from_node = level_state.get_collider_node(c);
627 
628  if ((entry._from_node->get_from_collide_mask() &
629  gnode->get_into_collide_mask()) != 0) {
630  #ifdef DO_PSTATS
631  // PStatTimer collide_timer(_solid_collide_collectors[pass]);
632  #endif
633  entry._from_node_path = level_state.get_collider_node_path(c);
634  entry._from = level_state.get_collider(c);
635 
636  compare_collider_to_geom_node(
637  entry,
638  level_state.get_parent_bound(c),
639  level_state.get_local_bound(c),
640  node_gbv);
641  }
642  }
643  }
644  }
645 
646  if (node->has_single_child_visibility()) {
647  // If it's a switch node or sequence node, visit just the one visible
648  // child.
649  int index = node->get_visible_child();
650  if (index >= 0 && index < node->get_num_children()) {
651  CollisionLevelStateSingle next_state(level_state, node->get_child(index));
652  r_traverse_single(next_state, pass);
653  }
654 
655  } else if (node->is_lod_node()) {
656  // If it's an LODNode, visit the lowest level of detail with all bits,
657  // allowing collision with geometry under the lowest level of default; and
658  // visit all other levels without GeomNode::get_default_collide_mask(),
659  // allowing only collision with CollisionNodes and special geometry under
660  // higher levels of detail.
661  int index = DCAST(LODNode, node)->get_lowest_switch();
662  PandaNode::Children children = node->get_children();
663  int num_children = children.get_num_children();
664  for (int i = 0; i < num_children; ++i) {
665  CollisionLevelStateSingle next_state(level_state, children.get_child(i));
666  if (i != index) {
667  next_state.set_include_mask(next_state.get_include_mask() &
668  ~GeomNode::get_default_collide_mask());
669  }
670  r_traverse_single(next_state, pass);
671  }
672 
673  } else {
674  // Otherwise, visit all the children.
675  PandaNode::Children children = node->get_children();
676  int num_children = children.get_num_children();
677  for (int i = 0; i < num_children; ++i) {
678  CollisionLevelStateSingle next_state(level_state, children.get_child(i));
679  r_traverse_single(next_state, pass);
680  }
681  }
682 }
683 
684 /**
685  * Fills up the set of LevelStates corresponding to the active colliders in
686  * use.
687  *
688  * This flavor uses a CollisionLevelStateDouble, which is limited to a certain
689  * number of colliders per pass (typically 32).
690  */
691 void CollisionTraverser::
692 prepare_colliders_double(CollisionTraverser::LevelStatesDouble &level_states,
693  const NodePath &root) {
694  int num_colliders = _colliders.size();
695  int max_colliders = CollisionLevelStateDouble::get_max_colliders();
696 
697  CollisionLevelStateDouble level_state(root);
698  // This reserve() call is only correct if there is exactly one solid per
699  // collider added to the traverser, which is the normal case. If there is
700  // more than one solid in any of the colliders, this reserve() call won't
701  // reserve enough, but the code is otherwise correct.
702  level_state.reserve(min(num_colliders, max_colliders));
703 
704  // Create an indirect index array to walk through the colliders in sorted
705  // order, without affect the actual collider order.
706  int *indirect = (int *)alloca(sizeof(int) * num_colliders);
707  int i;
708  for (i = 0; i < num_colliders; ++i) {
709  indirect[i] = i;
710  }
711  std::sort(indirect, indirect + num_colliders, SortByColliderSort(*this));
712 
713  int num_remaining_colliders = num_colliders;
714  for (i = 0; i < num_colliders; ++i) {
715  OrderedColliderDef &ocd = _ordered_colliders[indirect[i]];
716  NodePath cnode_path = ocd._node_path;
717 
718  if (!cnode_path.is_same_graph(root)) {
719  if (ocd._in_graph) {
720  // Only report this warning once.
721  collide_cat.info()
722  << "Collider " << cnode_path
723  << " is not in scene graph. Ignoring.\n";
724  ocd._in_graph = false;
725  }
726 
727  } else {
728  ocd._in_graph = true;
729  CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
730 
732  def._node = cnode;
733  def._node_path = cnode_path;
734 
735  int num_solids = cnode->get_num_solids();
736  for (int s = 0; s < num_solids; ++s) {
737  CPT(CollisionSolid) collider = cnode->get_solid(s);
738  def._collider = collider;
739  level_state.prepare_collider(def, root);
740 
741  if (level_state.get_num_colliders() == max_colliders) {
742  // That's the limit. Save off this level state and make a new one.
743  level_states.push_back(level_state);
744  level_state.clear();
745  level_state.reserve(min(num_remaining_colliders, max_colliders));
746  }
747  }
748  }
749 
750  --num_remaining_colliders;
751  nassertv(num_remaining_colliders >= 0);
752  }
753 
754  if (level_state.get_num_colliders() != 0) {
755  level_states.push_back(level_state);
756  }
757  nassertv(num_remaining_colliders == 0);
758 }
759 
760 /**
761  *
762  */
763 void CollisionTraverser::
764 r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
765  if (!level_state.any_in_bounds()) {
766  return;
767  }
768  if (!level_state.apply_transform()) {
769  return;
770  }
771 
772  PandaNode *node = level_state.node();
773  if (node->is_collision_node()) {
774  CollisionNode *cnode;
775  DCAST_INTO_V(cnode, node);
776  CPT(BoundingVolume) node_bv = cnode->get_bounds();
777  const GeometricBoundingVolume *node_gbv = nullptr;
778  if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
779  DCAST_INTO_V(node_gbv, node_bv);
780  }
781 
782  CollisionEntry entry;
783  entry._into_node = cnode;
784  entry._into_node_path = level_state.get_node_path();
785  if (_respect_prev_transform) {
786  entry._flags |= CollisionEntry::F_respect_prev_transform;
787  }
788 
789  int num_colliders = level_state.get_num_colliders();
790  for (int c = 0; c < num_colliders; ++c) {
791  if (level_state.has_collider(c)) {
792  entry._from_node = level_state.get_collider_node(c);
793 
794  if ((entry._from_node->get_from_collide_mask() &
795  cnode->get_into_collide_mask()) != 0) {
796  #ifdef DO_PSTATS
797  // PStatTimer collide_timer(_solid_collide_collectors[pass]);
798  #endif
799  entry._from_node_path = level_state.get_collider_node_path(c);
800  entry._from = level_state.get_collider(c);
801 
802  compare_collider_to_node(
803  entry,
804  level_state.get_parent_bound(c),
805  level_state.get_local_bound(c),
806  node_gbv);
807  }
808  }
809  }
810 
811  } else if (node->is_geom_node()) {
812  #ifndef NDEBUG
813  if (collide_cat.is_spam()) {
814  collide_cat.spam()
815  << "Reached " << *node << "\n";
816  }
817  #endif
818 
819  GeomNode *gnode;
820  DCAST_INTO_V(gnode, node);
821  CPT(BoundingVolume) node_bv = gnode->get_bounds();
822  const GeometricBoundingVolume *node_gbv = nullptr;
823  if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
824  DCAST_INTO_V(node_gbv, node_bv);
825  }
826 
827  CollisionEntry entry;
828  entry._into_node = gnode;
829  entry._into_node_path = level_state.get_node_path();
830  if (_respect_prev_transform) {
831  entry._flags |= CollisionEntry::F_respect_prev_transform;
832  }
833 
834  int num_colliders = level_state.get_num_colliders();
835  for (int c = 0; c < num_colliders; ++c) {
836  if (level_state.has_collider(c)) {
837  entry._from_node = level_state.get_collider_node(c);
838 
839  if ((entry._from_node->get_from_collide_mask() &
840  gnode->get_into_collide_mask()) != 0) {
841  #ifdef DO_PSTATS
842  // PStatTimer collide_timer(_solid_collide_collectors[pass]);
843  #endif
844  entry._from_node_path = level_state.get_collider_node_path(c);
845  entry._from = level_state.get_collider(c);
846 
847  compare_collider_to_geom_node(
848  entry,
849  level_state.get_parent_bound(c),
850  level_state.get_local_bound(c),
851  node_gbv);
852  }
853  }
854  }
855  }
856 
857  if (node->has_single_child_visibility()) {
858  // If it's a switch node or sequence node, visit just the one visible
859  // child.
860  int index = node->get_visible_child();
861  if (index >= 0 && index < node->get_num_children()) {
862  CollisionLevelStateDouble next_state(level_state, node->get_child(index));
863  r_traverse_double(next_state, pass);
864  }
865 
866  } else if (node->is_lod_node()) {
867  // If it's an LODNode, visit the lowest level of detail with all bits,
868  // allowing collision with geometry under the lowest level of default; and
869  // visit all other levels without GeomNode::get_default_collide_mask(),
870  // allowing only collision with CollisionNodes and special geometry under
871  // higher levels of detail.
872  int index = DCAST(LODNode, node)->get_lowest_switch();
873  PandaNode::Children children = node->get_children();
874  int num_children = children.get_num_children();
875  for (int i = 0; i < num_children; ++i) {
876  CollisionLevelStateDouble next_state(level_state, children.get_child(i));
877  if (i != index) {
878  next_state.set_include_mask(next_state.get_include_mask() &
879  ~GeomNode::get_default_collide_mask());
880  }
881  r_traverse_double(next_state, pass);
882  }
883 
884  } else {
885  // Otherwise, visit all the children.
886  PandaNode::Children children = node->get_children();
887  int num_children = children.get_num_children();
888  for (int i = 0; i < num_children; ++i) {
889  CollisionLevelStateDouble next_state(level_state, children.get_child(i));
890  r_traverse_double(next_state, pass);
891  }
892  }
893 }
894 
895 /**
896  * Fills up the set of LevelStates corresponding to the active colliders in
897  * use.
898  *
899  * This flavor uses a CollisionLevelStateQuad, which is limited to a certain
900  * number of colliders per pass (typically 32).
901  */
902 void CollisionTraverser::
903 prepare_colliders_quad(CollisionTraverser::LevelStatesQuad &level_states,
904  const NodePath &root) {
905  int num_colliders = _colliders.size();
906  int max_colliders = CollisionLevelStateQuad::get_max_colliders();
907 
908  CollisionLevelStateQuad level_state(root);
909  // This reserve() call is only correct if there is exactly one solid per
910  // collider added to the traverser, which is the normal case. If there is
911  // more than one solid in any of the colliders, this reserve() call won't
912  // reserve enough, but the code is otherwise correct.
913  level_state.reserve(min(num_colliders, max_colliders));
914 
915  // Create an indirect index array to walk through the colliders in sorted
916  // order, without affect the actual collider order.
917  int *indirect = (int *)alloca(sizeof(int) * num_colliders);
918  int i;
919  for (i = 0; i < num_colliders; ++i) {
920  indirect[i] = i;
921  }
922  std::sort(indirect, indirect + num_colliders, SortByColliderSort(*this));
923 
924  int num_remaining_colliders = num_colliders;
925  for (i = 0; i < num_colliders; ++i) {
926  OrderedColliderDef &ocd = _ordered_colliders[indirect[i]];
927  NodePath cnode_path = ocd._node_path;
928 
929  if (!cnode_path.is_same_graph(root)) {
930  if (ocd._in_graph) {
931  // Only report this warning once.
932  collide_cat.info()
933  << "Collider " << cnode_path
934  << " is not in scene graph. Ignoring.\n";
935  ocd._in_graph = false;
936  }
937 
938  } else {
939  ocd._in_graph = true;
940  CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
941 
943  def._node = cnode;
944  def._node_path = cnode_path;
945 
946  int num_solids = cnode->get_num_solids();
947  for (int s = 0; s < num_solids; ++s) {
948  CPT(CollisionSolid) collider = cnode->get_solid(s);
949  def._collider = collider;
950  level_state.prepare_collider(def, root);
951 
952  if (level_state.get_num_colliders() == max_colliders) {
953  // That's the limit. Save off this level state and make a new one.
954  level_states.push_back(level_state);
955  level_state.clear();
956  level_state.reserve(min(num_remaining_colliders, max_colliders));
957  }
958  }
959  }
960 
961  --num_remaining_colliders;
962  nassertv(num_remaining_colliders >= 0);
963  }
964 
965  if (level_state.get_num_colliders() != 0) {
966  level_states.push_back(level_state);
967  }
968  nassertv(num_remaining_colliders == 0);
969 }
970 
971 /**
972  *
973  */
974 void CollisionTraverser::
975 r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
976  if (!level_state.any_in_bounds()) {
977  return;
978  }
979  if (!level_state.apply_transform()) {
980  return;
981  }
982 
983  PandaNode *node = level_state.node();
984  if (node->is_collision_node()) {
985  CollisionNode *cnode;
986  DCAST_INTO_V(cnode, node);
987  CPT(BoundingVolume) node_bv = cnode->get_bounds();
988  const GeometricBoundingVolume *node_gbv = nullptr;
989  if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
990  DCAST_INTO_V(node_gbv, node_bv);
991  }
992 
993  CollisionEntry entry;
994  entry._into_node = cnode;
995  entry._into_node_path = level_state.get_node_path();
996  if (_respect_prev_transform) {
997  entry._flags |= CollisionEntry::F_respect_prev_transform;
998  }
999 
1000  int num_colliders = level_state.get_num_colliders();
1001  for (int c = 0; c < num_colliders; ++c) {
1002  if (level_state.has_collider(c)) {
1003  entry._from_node = level_state.get_collider_node(c);
1004 
1005  if ((entry._from_node->get_from_collide_mask() &
1006  cnode->get_into_collide_mask()) != 0) {
1007  #ifdef DO_PSTATS
1008  // PStatTimer collide_timer(_solid_collide_collectors[pass]);
1009  #endif
1010  entry._from_node_path = level_state.get_collider_node_path(c);
1011  entry._from = level_state.get_collider(c);
1012 
1013  compare_collider_to_node(
1014  entry,
1015  level_state.get_parent_bound(c),
1016  level_state.get_local_bound(c),
1017  node_gbv);
1018  }
1019  }
1020  }
1021 
1022  } else if (node->is_geom_node()) {
1023  #ifndef NDEBUG
1024  if (collide_cat.is_spam()) {
1025  collide_cat.spam()
1026  << "Reached " << *node << "\n";
1027  }
1028  #endif
1029 
1030  GeomNode *gnode;
1031  DCAST_INTO_V(gnode, node);
1032  CPT(BoundingVolume) node_bv = gnode->get_bounds();
1033  const GeometricBoundingVolume *node_gbv = nullptr;
1034  if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
1035  DCAST_INTO_V(node_gbv, node_bv);
1036  }
1037 
1038  CollisionEntry entry;
1039  entry._into_node = gnode;
1040  entry._into_node_path = level_state.get_node_path();
1041  if (_respect_prev_transform) {
1042  entry._flags |= CollisionEntry::F_respect_prev_transform;
1043  }
1044 
1045  int num_colliders = level_state.get_num_colliders();
1046  for (int c = 0; c < num_colliders; ++c) {
1047  if (level_state.has_collider(c)) {
1048  entry._from_node = level_state.get_collider_node(c);
1049 
1050  if ((entry._from_node->get_from_collide_mask() &
1051  gnode->get_into_collide_mask()) != 0) {
1052  #ifdef DO_PSTATS
1053  // PStatTimer collide_timer(_solid_collide_collectors[pass]);
1054  #endif
1055  entry._from_node_path = level_state.get_collider_node_path(c);
1056  entry._from = level_state.get_collider(c);
1057 
1058  compare_collider_to_geom_node(
1059  entry,
1060  level_state.get_parent_bound(c),
1061  level_state.get_local_bound(c),
1062  node_gbv);
1063  }
1064  }
1065  }
1066  }
1067 
1068  if (node->has_single_child_visibility()) {
1069  // If it's a switch node or sequence node, visit just the one visible
1070  // child.
1071  int index = node->get_visible_child();
1072  if (index >= 0 && index < node->get_num_children()) {
1073  CollisionLevelStateQuad next_state(level_state, node->get_child(index));
1074  r_traverse_quad(next_state, pass);
1075  }
1076 
1077  } else if (node->is_lod_node()) {
1078  // If it's an LODNode, visit the lowest level of detail with all bits,
1079  // allowing collision with geometry under the lowest level of default; and
1080  // visit all other levels without GeomNode::get_default_collide_mask(),
1081  // allowing only collision with CollisionNodes and special geometry under
1082  // higher levels of detail.
1083  int index = DCAST(LODNode, node)->get_lowest_switch();
1084  PandaNode::Children children = node->get_children();
1085  int num_children = children.get_num_children();
1086  for (int i = 0; i < num_children; ++i) {
1087  CollisionLevelStateQuad next_state(level_state, children.get_child(i));
1088  if (i != index) {
1089  next_state.set_include_mask(next_state.get_include_mask() &
1090  ~GeomNode::get_default_collide_mask());
1091  }
1092  r_traverse_quad(next_state, pass);
1093  }
1094 
1095  } else {
1096  // Otherwise, visit all the children.
1097  PandaNode::Children children = node->get_children();
1098  int num_children = children.get_num_children();
1099  for (int i = 0; i < num_children; ++i) {
1100  CollisionLevelStateQuad next_state(level_state, children.get_child(i));
1101  r_traverse_quad(next_state, pass);
1102  }
1103  }
1104 }
1105 
1106 /**
1107  *
1108  */
1109 void CollisionTraverser::
1110 compare_collider_to_node(CollisionEntry &entry,
1111  const GeometricBoundingVolume *from_parent_gbv,
1112  const GeometricBoundingVolume *from_node_gbv,
1113  const GeometricBoundingVolume *into_node_gbv) {
1114  bool within_node_bounds = true;
1115  if (from_parent_gbv != nullptr &&
1116  into_node_gbv != nullptr) {
1117  within_node_bounds = (into_node_gbv->contains(from_parent_gbv) != 0);
1118  _cnode_volume_pcollector.add_level(1);
1119  }
1120 
1121  if (within_node_bounds) {
1122  Thread *current_thread = Thread::get_current_thread();
1123 
1124  CollisionNode *cnode;
1125  DCAST_INTO_V(cnode, entry._into_node);
1126 
1127  int num_solids = cnode->get_num_solids();
1128  if (collide_cat.is_spam()) {
1129  collide_cat.spam()
1130  << "Colliding against CollisionNode " << entry._into_node
1131  << " which has " << num_solids << " collision solids.\n";
1132  }
1133 
1134  // Only bother to test against each solid's bounding volume if we have
1135  // more than one solid in the node, as a slight optimization. (If the
1136  // node contains just one solid, then the node's bounding volume, which
1137  // we just tested, is the same as the solid's bounding volume.)
1138  if (num_solids == 1) {
1139  entry._into = cnode->_solids[0].get_read_pointer(current_thread);
1140  Colliders::const_iterator ci;
1141  ci = _colliders.find(entry.get_from_node_path());
1142  nassertv(ci != _colliders.end());
1143  entry.test_intersection((*ci).second, this);
1144  } else {
1145  CollisionNode::Solids::const_iterator si;
1146  for (si = cnode->_solids.begin(); si != cnode->_solids.end(); ++si) {
1147  entry._into = (*si).get_read_pointer(current_thread);
1148 
1149  // We should allow a collision test for solid into itself, because the
1150  // solid might be simply instanced into multiple different
1151  // CollisionNodes. We are already filtering out tests for a
1152  // CollisionNode into itself.
1153  CPT(BoundingVolume) solid_bv = entry._into->get_bounds();
1154  const GeometricBoundingVolume *solid_gbv = nullptr;
1155  if (solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
1156  solid_gbv = (const GeometricBoundingVolume *)solid_bv.p();
1157  }
1158 
1159  compare_collider_to_solid(entry, from_node_gbv, solid_gbv);
1160  }
1161  }
1162  }
1163 }
1164 
1165 /**
1166  *
1167  */
1168 void CollisionTraverser::
1169 compare_collider_to_geom_node(CollisionEntry &entry,
1170  const GeometricBoundingVolume *from_parent_gbv,
1171  const GeometricBoundingVolume *from_node_gbv,
1172  const GeometricBoundingVolume *into_node_gbv) {
1173  bool within_node_bounds = true;
1174  if (from_parent_gbv != nullptr &&
1175  into_node_gbv != nullptr) {
1176  within_node_bounds = (into_node_gbv->contains(from_parent_gbv) != 0);
1177  _gnode_volume_pcollector.add_level(1);
1178  }
1179 
1180  if (within_node_bounds) {
1181  GeomNode *gnode;
1182  DCAST_INTO_V(gnode, entry._into_node);
1183  int num_geoms = gnode->get_num_geoms();
1184  for (int s = 0; s < num_geoms; ++s) {
1185  entry._into = nullptr;
1186  const Geom *geom = DCAST(Geom, gnode->get_geom(s));
1187  if (geom != nullptr) {
1188  CPT(BoundingVolume) geom_bv = geom->get_bounds();
1189  const GeometricBoundingVolume *geom_gbv = nullptr;
1190  if (num_geoms > 1 &&
1191  geom_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
1192  // Only bother to test against each geom's bounding volume if we
1193  // have more than one geom in the node, as a slight optimization.
1194  // (If the node contains just one geom, then the node's bounding
1195  // volume, which we just tested, is the same as the geom's bounding
1196  // volume.)
1197  DCAST_INTO_V(geom_gbv, geom_bv);
1198  }
1199 
1200  compare_collider_to_geom(entry, geom, from_node_gbv, geom_gbv);
1201  }
1202  }
1203  }
1204 }
1205 
1206 /**
1207  *
1208  */
1209 void CollisionTraverser::
1210 compare_collider_to_solid(CollisionEntry &entry,
1211  const GeometricBoundingVolume *from_node_gbv,
1212  const GeometricBoundingVolume *solid_gbv) {
1213  bool within_solid_bounds = true;
1214  if (from_node_gbv != nullptr &&
1215  solid_gbv != nullptr) {
1216  within_solid_bounds = (solid_gbv->contains(from_node_gbv) != 0);
1217  #ifdef DO_PSTATS
1218  ((CollisionSolid *)entry.get_into())->get_volume_pcollector().add_level(1);
1219  #endif // DO_PSTATS
1220 #ifndef NDEBUG
1221  if (collide_cat.is_spam()) {
1222  collide_cat.spam(false)
1223  << "Comparing to solid: " << *from_node_gbv
1224  << " to " << *solid_gbv << ", within_solid_bounds = "
1225  << within_solid_bounds << "\n";
1226  }
1227 #endif // NDEBUG
1228  }
1229  if (within_solid_bounds) {
1230  Colliders::const_iterator ci;
1231  ci = _colliders.find(entry.get_from_node_path());
1232  nassertv(ci != _colliders.end());
1233  entry.test_intersection((*ci).second, this);
1234  }
1235 }
1236 
1237 /**
1238  *
1239  */
1240 void CollisionTraverser::
1241 compare_collider_to_geom(CollisionEntry &entry, const Geom *geom,
1242  const GeometricBoundingVolume *from_node_gbv,
1243  const GeometricBoundingVolume *geom_gbv) {
1244  bool within_geom_bounds = true;
1245  if (from_node_gbv != nullptr &&
1246  geom_gbv != nullptr) {
1247  within_geom_bounds = (geom_gbv->contains(from_node_gbv) != 0);
1248  _geom_volume_pcollector.add_level(1);
1249  }
1250  if (within_geom_bounds) {
1251  Colliders::const_iterator ci;
1252  ci = _colliders.find(entry.get_from_node_path());
1253  nassertv(ci != _colliders.end());
1254 
1255  if (geom->get_primitive_type() == Geom::PT_polygons) {
1256  Thread *current_thread = Thread::get_current_thread();
1257  CPT(GeomVertexData) data = geom->get_animated_vertex_data(true, current_thread);
1258  GeomVertexReader vertex(data, InternalName::get_vertex());
1259 
1260  int num_primitives = geom->get_num_primitives();
1261  for (int i = 0; i < num_primitives; ++i) {
1262  const GeomPrimitive *primitive = geom->get_primitive(i);
1263  CPT(GeomPrimitive) tris = primitive->decompose();
1264  nassertv(tris->is_of_type(GeomTriangles::get_class_type()));
1265 
1266  if (tris->is_indexed()) {
1267  // Indexed case.
1268  GeomVertexReader index(tris->get_vertices(), 0);
1269  while (!index.is_at_end()) {
1270  LPoint3 v[3];
1271 
1272  vertex.set_row_unsafe(index.get_data1i());
1273  v[0] = vertex.get_data3();
1274  vertex.set_row_unsafe(index.get_data1i());
1275  v[1] = vertex.get_data3();
1276  vertex.set_row_unsafe(index.get_data1i());
1277  v[2] = vertex.get_data3();
1278 
1279  // Generate a temporary CollisionGeom on the fly for each triangle
1280  // in the Geom.
1281  if (CollisionPolygon::verify_points(v[0], v[1], v[2])) {
1282  bool within_solid_bounds = true;
1283  if (from_node_gbv != nullptr) {
1284  PT(BoundingSphere) sphere = new BoundingSphere;
1285  sphere->around(v, v + 3);
1286  within_solid_bounds = (sphere->contains(from_node_gbv) != 0);
1287 #ifdef DO_PSTATS
1288  CollisionGeom::_volume_pcollector.add_level(1);
1289 #endif // DO_PSTATS
1290  }
1291  if (within_solid_bounds) {
1292  PT(CollisionGeom) cgeom = new CollisionGeom(LVecBase3(v[0]), LVecBase3(v[1]), LVecBase3(v[2]));
1293  entry._into = cgeom;
1294  entry.test_intersection((*ci).second, this);
1295  }
1296  }
1297  }
1298  } else {
1299  // Non-indexed case.
1300  vertex.set_row_unsafe(primitive->get_first_vertex());
1301  int num_vertices = primitive->get_num_vertices();
1302  for (int i = 0; i < num_vertices; i += 3) {
1303  LPoint3 v[3];
1304 
1305  v[0] = vertex.get_data3();
1306  v[1] = vertex.get_data3();
1307  v[2] = vertex.get_data3();
1308 
1309  // Generate a temporary CollisionGeom on the fly for each triangle
1310  // in the Geom.
1311  if (CollisionPolygon::verify_points(v[0], v[1], v[2])) {
1312  bool within_solid_bounds = true;
1313  if (from_node_gbv != nullptr) {
1314  PT(BoundingSphere) sphere = new BoundingSphere;
1315  sphere->around(v, v + 3);
1316  within_solid_bounds = (sphere->contains(from_node_gbv) != 0);
1317 #ifdef DO_PSTATS
1318  CollisionGeom::_volume_pcollector.add_level(1);
1319 #endif // DO_PSTATS
1320  }
1321  if (within_solid_bounds) {
1322  PT(CollisionGeom) cgeom = new CollisionGeom(LVecBase3(v[0]), LVecBase3(v[1]), LVecBase3(v[2]));
1323  entry._into = cgeom;
1324  entry.test_intersection((*ci).second, this);
1325  }
1326  }
1327  }
1328  }
1329  }
1330  }
1331  }
1332 }
1333 
1334 /**
1335  * Removes the indicated CollisionHandler from the list of handlers to be
1336  * processed, and returns the iterator to the next handler in the list. This
1337  * is designed to be called safely from within a traversal of the handler
1338  * list.
1339  *
1340  * This also removes any colliders that depend on this handler, to keep
1341  * internal structures intact.
1342  */
1343 CollisionTraverser::Handlers::iterator CollisionTraverser::
1344 remove_handler(CollisionTraverser::Handlers::iterator hi) {
1345  nassertr(hi != _handlers.end(), hi);
1346 
1347  CollisionHandler *handler = (*hi).first;
1348  Handlers::iterator hnext = hi;
1349  ++hnext;
1350  _handlers.erase(hi);
1351  hi = hnext;
1352 
1353  // Now scan for colliders that reference this handler.
1354  Colliders::iterator ci;
1355  ci = _colliders.begin();
1356  while (ci != _colliders.end()) {
1357  if ((*ci).second == handler) {
1358  // This collider references this handler; remove it.
1359  NodePath node_path = (*ci).first;
1360 
1361  Colliders::iterator cnext = ci;
1362  ++cnext;
1363  _colliders.erase(ci);
1364  ci = cnext;
1365 
1366  // Also remove it from the ordered list.
1367  OrderedColliders::iterator oci;
1368  oci = _ordered_colliders.begin();
1369  while (oci != _ordered_colliders.end() &&
1370  (*oci)._node_path != node_path) {
1371  ++oci;
1372  }
1373  nassertr(oci != _ordered_colliders.end(), hi);
1374  _ordered_colliders.erase(oci);
1375 
1376  nassertr(_ordered_colliders.size() == _colliders.size(), hi);
1377 
1378  } else {
1379  // This collider references some other handler; keep it.
1380  ++ci;
1381  }
1382  }
1383 
1384  return hi;
1385 }
1386 
1387 /**
1388  * Returns the PStatCollector suitable for timing the nth pass.
1389  */
1390 PStatCollector &CollisionTraverser::
1391 get_pass_collector(int pass) {
1392  nassertr(pass >= 0, _this_pcollector);
1393  while ((int)_pass_collectors.size() <= pass) {
1394  std::ostringstream name;
1395  name << "pass" << (_pass_collectors.size() + 1);
1396  PStatCollector col(_this_pcollector, name.str());
1397  _pass_collectors.push_back(col);
1398  PStatCollector sc_col(col, "solid_collide");
1399  _solid_collide_collectors.push_back(sc_col);
1400  }
1401 
1402  return _pass_collectors[pass];
1403 }
virtual int get_visible_child() const
Returns the index number of the currently visible child of this node.
Definition: pandaNode.cxx:457
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
get_from_node_path
Returns the NodePath that represents the CollisionNode that contains the CollisionSolid that triggere...
The abstract interface to a number of classes that decide what to do when a collision is detected.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int contains(const GeometricBoundingVolume *vol) const
Returns the appropriate set of IntersectionFlags to indicate the amount of intersection with the indi...
NodePath get_node_path() const
Returns the NodePath representing the node instance we have traversed to.
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
PandaNode * get_child(size_t n) const
Returns the nth child of the node.
Definition: pandaNode.I:962
This is the state information the CollisionTraverser retains for each level during traversal.
The abstract base class for all things that can collide with other things in the world,...
This defines a bounding sphere, consisting of a center and a radius.
bool has_collider(int n) const
Returns true if the nth collider in the LevelState is still part of the level.
void set_include_mask(CollideMask include_mask)
Specifies the mask that is applied to the into CollideMask of nodes in the scene graph before testing...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_num_vertices
Returns the number of indices used by all the primitives in this object.
Definition: geomPrimitive.h:99
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_collider(const NodePath &collider, CollisionHandler *handler)
Adds a new CollisionNode, representing an object that will be tested for collisions into other object...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_first_vertex() const
Returns the first vertex number referenced by the primitive.
Definition: geomPrimitive.I:98
void clear_colliders()
Completely empties the set of collision nodes and their associated handlers.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
A lightweight class that represents a single element that may be timed and/or counted via stats.
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
A base class for all things which can have a name.
Definition: namable.h:26
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static bool verify_points(const LPoint3 &a, const LPoint3 &b, const LPoint3 &c)
Verifies that the indicated set of points will define a valid CollisionPolygon: that is,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool any_in_bounds()
Checks the bounding volume of the current node against each of our colliders.
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:563
A Level-of-Detail node.
Definition: lodNode.h:28
size_t get_num_children() const
Returns the number of children of the node.
Definition: pandaNode.I:953
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
virtual bool is_collision_node() const
A simple downcast check.
Definition: pandaNode.cxx:2092
Defines a single collision event.
const GeometricBoundingVolume * get_local_bound(int n) const
Returns the bounding volume of the indicated collider, transformed into the current node's transform ...
get_into_collide_mask
Returns the current "into" CollideMask.
Definition: collisionNode.h:61
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
void prepare_collider(const ColliderDef &def, const NodePath &root)
Adds the indicated Collider to the set of Colliders in the current level state.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
Definition: geom.h:54
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition: pandaNode.h:784
A special CollisionPolygon created just for the purpose of detecting collision against geometry.
Definition: collisionGeom.h:29
const GeometricBoundingVolume * get_parent_bound(int n) const
Returns the bounding volume of the indicated collider, transformed into the previous node's transform...
void reserve(int num_colliders)
Indicates an intention to add the indicated number of colliders to the level state.
get_collider
Returns the nth CollisionNode that has been added to the traverser via add_collider().
virtual bool is_lod_node() const
A simple downcast check.
Definition: pandaNode.cxx:2080
bool has_collider(const NodePath &collider) const
Returns true if the indicated node is current in the set of nodes that will be tested each frame for ...
static void flush_level()
Flushes the PStatCollectors used during traversal.
Definition: collisionBox.I:83
bool is_same_graph(const NodePath &other, Thread *current_thread=Thread::get_current_thread()) const
Returns true if the node represented by this NodePath is parented within the same graph as that of th...
Definition: nodePath.I:275
get_primitive_type
Returns the fundamental primitive type that is common to all GeomPrimitives added within the Geom.
Definition: geom.h:74
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CollisionHandler * get_handler(const NodePath &collider) const
Returns the handler that is currently assigned to serve the indicated collision node,...
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
get_into
Returns the CollisionSolid pointer for the particular solid was collided into.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_num_geoms
Returns the number of geoms in the node.
Definition: geomNode.h:71
static void flush_level()
Flushes the PStatCollectors used during traversal.
A thread; that is, a lightweight process.
Definition: thread.h:46
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
static void flush_level()
Flushes the PStatCollectors used during traversal.
A node in the scene graph that can hold any number of CollisionSolids.
Definition: collisionNode.h:30
virtual bool has_single_child_visibility() const
Should be overridden by derived classes to return true if this kind of node has the special property ...
Definition: pandaNode.cxx:448
bool remove_collider(const NodePath &collider)
Removes the collider (and its associated handler) from the set of CollisionNodes that will be tested ...
void set_row_unsafe(int row)
Sets the start row to the indicated value, without internal checks.
This class manages the traversal through the scene graph to detect collisions.
get_child
Returns the nth child node of this node.
Definition: pandaNode.h:124
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
static void flush_level()
Flushes the PStatCollectors used during traversal.
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2068
bool apply_transform()
Applies the inverse transform from the current node, if any, onto all the colliders in the level stat...
bool around(const GeometricBoundingVolume **first, const GeometricBoundingVolume **last)
Resets the volume to enclose only the volumes indicated.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
get_into_collide_mask
Returns the "into" collide mask for this node.
Definition: pandaNode.h:264
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PandaNode * node() const
Returns the PandaNode pointer of the node we have traversed to.
static void flush_level()
Flushes the PStatCollectors used during traversal.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static int get_max_colliders()
Returns the maximum number of colliders that may be added to the CollisionLevelStateBase at any one t...