00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "collisionTraverser.h"
00020 #include "collisionNode.h"
00021 #include "collisionEntry.h"
00022 #include "collisionPolygon.h"
00023 #include "collisionRecorder.h"
00024 #include "config_collide.h"
00025
00026 #include "transformState.h"
00027 #include "geomNode.h"
00028 #include "geom.h"
00029 #include "nodePath.h"
00030 #include "pStatTimer.h"
00031 #include "indent.h"
00032
00033 #ifndef CPPPARSER
00034 PStatCollector CollisionTraverser::_collisions_pcollector("App:Collisions");
00035 #endif
00036
00037
00038
00039
00040
00041
00042 CollisionTraverser::
00043 CollisionTraverser() {
00044 #ifdef DO_COLLISION_RECORDING
00045 _recorder = (CollisionRecorder *)NULL;
00046 #endif
00047 }
00048
00049
00050
00051
00052
00053
00054 CollisionTraverser::
00055 ~CollisionTraverser() {
00056 #ifdef DO_COLLISION_RECORDING
00057 clear_recorder();
00058 #endif
00059 }
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075 void CollisionTraverser::
00076 add_collider(CollisionNode *node, CollisionHandler *handler) {
00077 nassertv(_ordered_colliders.size() == _colliders.size());
00078 nassertv(node != (CollisionNode *)NULL);
00079 nassertv(handler != (CollisionHandler *)NULL);
00080
00081 Colliders::iterator ci = _colliders.find(node);
00082 if (ci != _colliders.end()) {
00083
00084 if ((*ci).second != handler) {
00085
00086 PT(CollisionHandler) old_handler = (*ci).second;
00087 (*ci).second = handler;
00088
00089
00090 Handlers::iterator hi = _handlers.find(old_handler);
00091 nassertv(hi != _handlers.end());
00092 (*hi).second--;
00093 nassertv((*hi).second >= 0);
00094 if ((*hi).second == 0) {
00095 _handlers.erase(hi);
00096 }
00097
00098 hi = _handlers.find(handler);
00099 if (hi == _handlers.end()) {
00100 _handlers.insert(Handlers::value_type(handler, 1));
00101 } else {
00102 (*hi).second++;
00103 }
00104 }
00105
00106 } else {
00107
00108 _colliders.insert(Colliders::value_type(node, handler));
00109 _ordered_colliders.push_back(node);
00110
00111 Handlers::iterator hi = _handlers.find(handler);
00112 if (hi == _handlers.end()) {
00113 _handlers.insert(Handlers::value_type(handler, 1));
00114 } else {
00115 (*hi).second++;
00116 }
00117 }
00118
00119 nassertv(_ordered_colliders.size() == _colliders.size());
00120 }
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131 bool CollisionTraverser::
00132 remove_collider(CollisionNode *node) {
00133 nassertr(_ordered_colliders.size() == _colliders.size(), false);
00134
00135 Colliders::iterator ci = _colliders.find(node);
00136 if (ci == _colliders.end()) {
00137
00138 return false;
00139 }
00140
00141 CollisionHandler *handler = (*ci).second;
00142
00143
00144 Handlers::iterator hi = _handlers.find(handler);
00145 nassertr(hi != _handlers.end(), false);
00146 (*hi).second--;
00147 nassertr((*hi).second >= 0, false);
00148 if ((*hi).second == 0) {
00149 _handlers.erase(hi);
00150 }
00151
00152 _colliders.erase(ci);
00153
00154 OrderedColliders::iterator oci =
00155 find(_ordered_colliders.begin(), _ordered_colliders.end(), node);
00156 nassertr(oci != _ordered_colliders.end(), false);
00157 _ordered_colliders.erase(oci);
00158
00159 nassertr(_ordered_colliders.size() == _colliders.size(), false);
00160 return true;
00161 }
00162
00163
00164
00165
00166
00167
00168
00169
00170 bool CollisionTraverser::
00171 has_collider(CollisionNode *node) const {
00172 Colliders::const_iterator ci = _colliders.find(node);
00173 return (ci != _colliders.end());
00174 }
00175
00176
00177
00178
00179
00180
00181
00182 int CollisionTraverser::
00183 get_num_colliders() const {
00184 nassertr(_ordered_colliders.size() == _colliders.size(), 0);
00185 return _ordered_colliders.size();
00186 }
00187
00188
00189
00190
00191
00192
00193
00194 CollisionNode *CollisionTraverser::
00195 get_collider(int n) const {
00196 nassertr(_ordered_colliders.size() == _colliders.size(), NULL);
00197 nassertr(n >= 0 && n < (int)_ordered_colliders.size(), NULL);
00198 return _ordered_colliders[n];
00199 }
00200
00201
00202
00203
00204
00205
00206
00207
00208 CollisionHandler *CollisionTraverser::
00209 get_handler(CollisionNode *node) const {
00210 Colliders::const_iterator ci = _colliders.find(node);
00211 if (ci != _colliders.end()) {
00212 return (*ci).second;
00213 };
00214 return NULL;
00215 }
00216
00217
00218
00219
00220
00221
00222
00223 void CollisionTraverser::
00224 clear_colliders() {
00225 _colliders.clear();
00226 _ordered_colliders.clear();
00227 }
00228
00229
00230
00231
00232
00233
00234
00235 void CollisionTraverser::
00236 traverse(const NodePath &root) {
00237 PStatTimer timer(_collisions_pcollector);
00238
00239 #ifdef DO_COLLISION_RECORDING
00240 if (has_recorder()) {
00241 get_recorder()->begin_traversal();
00242 }
00243 #endif // DO_COLLISION_RECORDING
00244
00245 CollisionLevelState level_state(root);
00246 prepare_colliders(level_state);
00247
00248 Handlers::iterator hi;
00249 for (hi = _handlers.begin(); hi != _handlers.end(); ++hi) {
00250 (*hi).first->begin_group();
00251 }
00252
00253 r_traverse(level_state);
00254
00255 hi = _handlers.begin();
00256 while (hi != _handlers.end()) {
00257 if (!(*hi).first->end_group()) {
00258
00259 Handlers::iterator hnext = hi;
00260 ++hnext;
00261 _handlers.erase(hi);
00262 hi = hnext;
00263 } else {
00264 ++hi;
00265 }
00266 }
00267
00268 if (auto_clear_velocity) {
00269
00270 OrderedColliders::iterator ci;
00271 for (ci = _ordered_colliders.begin();
00272 ci != _ordered_colliders.end();
00273 ++ci) {
00274 CollisionNode *node = (*ci);
00275 node->clear_velocity();
00276 }
00277 }
00278
00279 #ifdef DO_COLLISION_RECORDING
00280 if (has_recorder()) {
00281 get_recorder()->end_traversal();
00282 }
00283 #endif // DO_COLLISION_RECORDING
00284 }
00285
00286 #ifdef DO_COLLISION_RECORDING
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309 void CollisionTraverser::
00310 set_recorder(CollisionRecorder *recorder) {
00311 if (recorder != _recorder) {
00312
00313 if (_recorder != (CollisionRecorder *)NULL) {
00314 nassertv(_recorder->_trav == this);
00315 _recorder->_trav = (CollisionTraverser *)NULL;
00316 }
00317
00318 _recorder = recorder;
00319
00320
00321 if (_recorder != (CollisionRecorder *)NULL) {
00322 nassertv(_recorder->_trav != this);
00323 if (_recorder->_trav != (CollisionTraverser *)NULL) {
00324 _recorder->_trav->clear_recorder();
00325 }
00326 nassertv(_recorder->_trav == (CollisionTraverser *)NULL);
00327 _recorder->_trav = this;
00328 }
00329 }
00330 }
00331 #endif // DO_COLLISION_RECORDING
00332
00333
00334
00335
00336
00337
00338 void CollisionTraverser::
00339 output(ostream &out) const {
00340 out << "CollisionTraverser, " << _colliders.size()
00341 << " colliders and " << _handlers.size() << " handlers.\n";
00342 }
00343
00344
00345
00346
00347
00348
00349 void CollisionTraverser::
00350 write(ostream &out, int indent_level) const {
00351 indent(out, indent_level)
00352 << "CollisionTraverser, " << _colliders.size()
00353 << " colliders and " << _handlers.size() << " handlers:\n";
00354 Colliders::const_iterator ci;
00355 for (ci = _colliders.begin(); ci != _colliders.end(); ++ci) {
00356 CollisionNode *cnode = (*ci).first;
00357 CollisionHandler *handler = (*ci).second;
00358 nassertv(cnode != (CollisionNode *)NULL);
00359 nassertv(handler != (CollisionHandler *)NULL);
00360
00361 indent(out, indent_level + 2)
00362 << *cnode << " handled by " << handler->get_type() << "\n";
00363 int num_solids = cnode->get_num_solids();
00364 for (int i = 0; i < num_solids; i++) {
00365 cnode->get_solid(i)->write(out, indent_level + 4);
00366 }
00367 }
00368 }
00369
00370
00371
00372
00373
00374
00375
00376 void CollisionTraverser::
00377 prepare_colliders(CollisionLevelState &level_state) {
00378 level_state.clear();
00379 level_state.reserve(_colliders.size());
00380
00381 int i = 0;
00382 while (i < (int)_ordered_colliders.size()) {
00383 CollisionNode *cnode = _ordered_colliders[i];
00384
00385 CollisionLevelState::ColliderDef def;
00386 def._node = cnode;
00387 NodePath root;
00388 NodePath cnode_path(cnode);
00389 def._space = cnode_path.get_mat(root);
00390 def._inv_space = root.get_mat(cnode_path);
00391
00392 #ifndef NDEBUG
00393 if (def._space.is_nan()) {
00394 collide_cat.error()
00395 << "Collider " << *cnode
00396 << " has become NaN. Dropping from traverser.\n";
00397
00398
00399 remove_collider(cnode);
00400 } else
00401 #endif
00402 {
00403 int num_solids = cnode->get_num_solids();
00404 for (int s = 0; s < num_solids; s++) {
00405 CollisionSolid *collider = cnode->get_solid(s);
00406 def._collider = collider;
00407 level_state.prepare_collider(def);
00408 }
00409 i++;
00410 }
00411 }
00412 }
00413
00414
00415
00416
00417
00418
00419 void CollisionTraverser::
00420 r_traverse(CollisionLevelState &level_state) {
00421 if (!level_state.any_in_bounds()) {
00422 return;
00423 }
00424 level_state.apply_transform();
00425
00426 PandaNode *node = level_state.node();
00427 if (node->is_exact_type(CollisionNode::get_class_type())) {
00428 level_state.reached_collision_node();
00429
00430 CollisionNode *cnode;
00431 DCAST_INTO_V(cnode, node);
00432 const BoundingVolume &node_bv = cnode->get_bound();
00433 const GeometricBoundingVolume *node_gbv = NULL;
00434 if (node_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
00435 DCAST_INTO_V(node_gbv, &node_bv);
00436 }
00437
00438 CollisionEntry entry;
00439 entry._into_node = cnode;
00440 entry._into_node_path = level_state.get_node_path();
00441 entry._into_space = entry._into_node_path.get_mat(NodePath());
00442
00443 int num_colliders = level_state.get_num_colliders();
00444 for (int c = 0; c < num_colliders; c++) {
00445 if (level_state.has_collider(c)) {
00446 entry._from_node = level_state.get_node(c);
00447
00448 if ((entry._from_node->get_from_collide_mask() &
00449 cnode->get_into_collide_mask()) != 0) {
00450 entry._from = level_state.get_collider(c);
00451 entry._from_space = level_state.get_space(c);
00452
00453 if (entry._from_node->has_velocity()) {
00454 entry.set_from_velocity(entry._from_node->get_velocity());
00455 }
00456
00457 NodePath root;
00458 const LMatrix4f &into_space_inv =
00459 root.get_mat(entry._into_node_path);
00460 entry._wrt_space = entry._from_space * into_space_inv;
00461 entry._inv_wrt_space =
00462 entry._into_space * level_state.get_inv_space(c);
00463
00464 compare_collider_to_node(entry,
00465 level_state.get_parent_bound(c),
00466 level_state.get_local_bound(c),
00467 node_gbv);
00468 }
00469 }
00470 }
00471
00472 } else if (node->is_geom_node() && level_state.has_any_collide_geom()) {
00473 #ifndef NDEBUG
00474 if (collide_cat.is_spam()) {
00475 collide_cat.spam()
00476 << "Reached " << *node << "\n";
00477 }
00478 #endif
00479
00480 GeomNode *gnode;
00481 DCAST_INTO_V(gnode, node);
00482 const BoundingVolume &node_bv = gnode->get_bound();
00483 const GeometricBoundingVolume *node_gbv = NULL;
00484 if (node_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
00485 DCAST_INTO_V(node_gbv, &node_bv);
00486 }
00487
00488 CollisionEntry entry;
00489 entry._into_node = gnode;
00490 entry._into_node_path = level_state.get_node_path();
00491 entry._into_space = entry._into_node_path.get_mat(NodePath());
00492
00493 int num_colliders = level_state.get_num_colliders();
00494 for (int c = 0; c < num_colliders; c++) {
00495 if (level_state.has_collider_with_geom(c)) {
00496 entry._from_node = level_state.get_node(c);
00497
00498 entry._from = level_state.get_collider(c);
00499 entry._from_space = level_state.get_space(c);
00500
00501 NodePath root;
00502 const LMatrix4f &into_space_inv =
00503 root.get_mat(entry._into_node_path);
00504 entry._wrt_space = entry._from_space * into_space_inv;
00505 entry._inv_wrt_space =
00506 entry._into_space * level_state.get_inv_space(c);
00507
00508 compare_collider_to_geom_node(entry,
00509 level_state.get_parent_bound(c),
00510 level_state.get_local_bound(c),
00511 node_gbv);
00512 }
00513 }
00514 }
00515
00516 if (node->has_single_child_visibility()) {
00517
00518
00519 int index = node->get_visible_child();
00520 if (index >= 0 && index < node->get_num_children()) {
00521 CollisionLevelState next_state(level_state, node->get_child(index));
00522 r_traverse(next_state);
00523 }
00524
00525 } else {
00526
00527 int num_children = node->get_num_children();
00528 for (int i = 0; i < num_children; i++) {
00529 CollisionLevelState next_state(level_state, node->get_child(i));
00530 r_traverse(next_state);
00531 }
00532 }
00533 }
00534
00535
00536
00537
00538
00539
00540 void CollisionTraverser::
00541 compare_collider_to_node(CollisionEntry &entry,
00542 const GeometricBoundingVolume *from_parent_gbv,
00543 const GeometricBoundingVolume *from_node_gbv,
00544 const GeometricBoundingVolume *into_node_gbv) {
00545 bool within_node_bounds = true;
00546 if (from_parent_gbv != (GeometricBoundingVolume *)NULL &&
00547 into_node_gbv != (GeometricBoundingVolume *)NULL) {
00548 within_node_bounds = (into_node_gbv->contains(from_parent_gbv) != 0);
00549 }
00550
00551 if (within_node_bounds) {
00552 CollisionNode *cnode;
00553 DCAST_INTO_V(cnode, entry._into_node);
00554 int num_solids = cnode->get_num_solids();
00555 for (int s = 0; s < num_solids; s++) {
00556 entry._into = cnode->get_solid(s);
00557 if (entry._from != entry._into) {
00558 const BoundingVolume &solid_bv = entry._into->get_bound();
00559 const GeometricBoundingVolume *solid_gbv = NULL;
00560 if (num_solids > 1 &&
00561 solid_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
00562
00563
00564
00565
00566
00567 DCAST_INTO_V(solid_gbv, &solid_bv);
00568 }
00569
00570 compare_collider_to_solid(entry, from_node_gbv, solid_gbv);
00571 }
00572 }
00573 }
00574 }
00575
00576
00577
00578
00579
00580
00581 void CollisionTraverser::
00582 compare_collider_to_geom_node(CollisionEntry &entry,
00583 const GeometricBoundingVolume *from_parent_gbv,
00584 const GeometricBoundingVolume *from_node_gbv,
00585 const GeometricBoundingVolume *into_node_gbv) {
00586 bool within_node_bounds = true;
00587 if (from_parent_gbv != (GeometricBoundingVolume *)NULL &&
00588 into_node_gbv != (GeometricBoundingVolume *)NULL) {
00589 within_node_bounds = (into_node_gbv->contains(from_parent_gbv) != 0);
00590 }
00591
00592 if (within_node_bounds) {
00593 GeomNode *gnode;
00594 DCAST_INTO_V(gnode, entry._into_node);
00595 int num_geoms = gnode->get_num_geoms();
00596 for (int s = 0; s < num_geoms; s++) {
00597 entry._into = (CollisionSolid *)NULL;
00598 Geom *geom = DCAST(Geom, gnode->get_geom(s));
00599 if (geom != (Geom *)NULL) {
00600 const BoundingVolume &geom_bv = geom->get_bound();
00601 const GeometricBoundingVolume *geom_gbv = NULL;
00602 if (num_geoms > 1 &&
00603 geom_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
00604
00605
00606
00607
00608
00609 DCAST_INTO_V(geom_gbv, &geom_bv);
00610 }
00611
00612 compare_collider_to_geom(entry, geom, from_node_gbv, geom_gbv);
00613 }
00614 }
00615 }
00616 }
00617
00618
00619
00620
00621
00622
00623 void CollisionTraverser::
00624 compare_collider_to_solid(CollisionEntry &entry,
00625 const GeometricBoundingVolume *from_node_gbv,
00626 const GeometricBoundingVolume *solid_gbv) {
00627 bool within_solid_bounds = true;
00628 if (from_node_gbv != (GeometricBoundingVolume *)NULL &&
00629 solid_gbv != (GeometricBoundingVolume *)NULL) {
00630 within_solid_bounds = (solid_gbv->contains(from_node_gbv) != 0);
00631 }
00632 if (within_solid_bounds) {
00633 Colliders::const_iterator ci;
00634 ci = _colliders.find(entry.get_from_node());
00635 nassertv(ci != _colliders.end());
00636 entry.test_intersection((*ci).second, this);
00637 }
00638 }
00639
00640
00641
00642
00643
00644
00645 void CollisionTraverser::
00646 compare_collider_to_geom(CollisionEntry &entry, Geom *geom,
00647 const GeometricBoundingVolume *from_node_gbv,
00648 const GeometricBoundingVolume *geom_gbv) {
00649 bool within_geom_bounds = true;
00650 if (from_node_gbv != (GeometricBoundingVolume *)NULL &&
00651 geom_gbv != (GeometricBoundingVolume *)NULL) {
00652 within_geom_bounds = (geom_gbv->contains(from_node_gbv) != 0);
00653 }
00654 if (within_geom_bounds) {
00655 Colliders::const_iterator ci;
00656 ci = _colliders.find(entry.get_from_node());
00657 nassertv(ci != _colliders.end());
00658
00659 PTA_Vertexf coords;
00660 PTA_ushort vindex;
00661 geom->get_coords(coords, vindex);
00662 PTA_ushort tris = geom->get_tris();
00663
00664 for (int i = 0; i < (int)tris.size(); i += 3) {
00665 if (CollisionPolygon::verify_points(coords[tris[i]],
00666 coords[tris[i + 1]],
00667 coords[tris[i + 2]])) {
00668
00669
00670 entry._into =
00671 new CollisionPolygon(coords[tris[i]], coords[tris[i + 1]],
00672 coords[tris[i + 2]]);
00673 entry.test_intersection((*ci).second, this);
00674 }
00675 }
00676 }
00677 }