Panda3D
bulletWorld.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 bulletWorld.cxx
10  * @author enn0x
11  * @date 2010-01-23
12  */
13 
14 #include "bulletWorld.h"
15 
16 #include "config_bullet.h"
17 
20 #include "bulletShape.h"
22 #include "bulletTickCallbackData.h"
23 
24 #include "collideMask.h"
25 #include "lightMutexHolder.h"
26 
27 #define clamp(x, x_min, x_max) std::max(std::min(x, x_max), x_min)
28 
29 using std::endl;
30 using std::istream;
31 using std::ostream;
32 using std::string;
33 
34 TypeHandle BulletWorld::_type_handle;
35 
36 PStatCollector BulletWorld::_pstat_physics("App:Bullet:DoPhysics");
37 PStatCollector BulletWorld::_pstat_simulation("App:Bullet:DoPhysics:Simulation");
38 PStatCollector BulletWorld::_pstat_p2b("App:Bullet:DoPhysics:SyncP2B");
39 PStatCollector BulletWorld::_pstat_b2p("App:Bullet:DoPhysics:SyncB2P");
40 
41 PT(CallbackObject) bullet_contact_added_callback;
42 
43 /**
44  *
45  */
46 BulletWorld::
47 BulletWorld() {
48 
49  // Init groups filter matrix
50  for (size_t i = 0; i < 32; ++i) {
51  _filter_cb2._collide[i].clear();
52  _filter_cb2._collide[i].set_bit(i);
53  }
54 
55  // Broadphase
56  btScalar dx(bullet_sap_extents);
57  btVector3 extents(dx, dx, dx);
58 
59  switch (bullet_broadphase_algorithm) {
60  case BA_sweep_and_prune:
61  _broadphase = new btAxisSweep3(extents, extents, 1024);
62  break;
63  case BA_dynamic_aabb_tree:
64  _broadphase = new btDbvtBroadphase();
65  break;
66  default:
67  bullet_cat.error() << "no proper broadphase algorithm!" << endl;
68  }
69  nassertv(_broadphase);
70 
71  // Configuration
72  _configuration = new btSoftBodyRigidBodyCollisionConfiguration();
73  nassertv(_configuration);
74 
75  // Dispatcher
76  _dispatcher = new btCollisionDispatcher(_configuration);
77  nassertv(_dispatcher);
78 
79  // Solver
80  _solver = new btSequentialImpulseConstraintSolver;
81  nassertv(_solver);
82 
83  // World
84  _world = new btSoftRigidDynamicsWorld(_dispatcher, _broadphase, _solver, _configuration);
85  nassertv(_world);
86  nassertv(_world->getPairCache());
87 
88  _world->setWorldUserInfo(this);
89  _world->setGravity(btVector3(0.0f, 0.0f, 0.0f));
90 
91  // Ghost-pair callback
92  _world->getPairCache()->setInternalGhostPairCallback(&_ghost_cb);
93 
94  // Filter callback
95  _filter_algorithm = bullet_filter_algorithm;
96  switch (_filter_algorithm) {
97  case FA_mask:
98  _filter_cb = &_filter_cb1;
99  break;
100  case FA_groups_mask:
101  _filter_cb = &_filter_cb2;
102  break;
103  case FA_callback:
104  _filter_cb = &_filter_cb3;
105  break;
106  default:
107  bullet_cat.error() << "no proper filter algorithm!" << endl;
108  _filter_cb = nullptr;
109  }
110 
111  _world->getPairCache()->setOverlapFilterCallback(_filter_cb);
112 
113  // Tick callback
114  _tick_callback_obj = nullptr;
115 
116  // SoftBodyWorldInfo
117  _info.m_dispatcher = _dispatcher;
118  _info.m_broadphase = _broadphase;
119  _info.m_gravity.setValue(0.0f, 0.0f, 0.0f);
120  _info.m_sparsesdf.Initialize();
121 
122  // Register GIMPACT algorithm
123  btGImpactCollisionAlgorithm::registerAlgorithm(_dispatcher);
124 
125  // Some prefered settings
126  _world->getDispatchInfo().m_enableSPU = true; // default: true
127  _world->getDispatchInfo().m_useContinuous = true; // default: true
128  _world->getSolverInfo().m_splitImpulse = false; // default: false
129  _world->getSolverInfo().m_numIterations = bullet_solver_iterations;
130 }
131 
132 /**
133  *
134  */
135 LightMutex &BulletWorld::
136 get_global_lock() {
137 
138  static LightMutex lock;
139 
140  return lock;
141 }
142 
143 /**
144  *
145  */
146 BulletSoftBodyWorldInfo BulletWorld::
147 get_world_info() {
148 
149  return BulletSoftBodyWorldInfo(_info);
150 }
151 
152 /**
153  *
154  */
155 void BulletWorld::
156 set_debug_node(BulletDebugNode *node) {
157  LightMutexHolder holder(get_global_lock());
158 
159  nassertv(node);
160  if (node != _debug) {
161  if (_debug != nullptr) {
162  _debug->_debug_stale = false;
163  _debug->_debug_world = nullptr;
164  }
165 
166  _debug = node;
167  _world->setDebugDrawer(&(_debug->_drawer));
168  }
169 }
170 
171 /**
172  * Removes a debug node that has been assigned to this BulletWorld.
173  */
174 void BulletWorld::
176  LightMutexHolder holder(get_global_lock());
177 
178  if (_debug != nullptr) {
179  _debug->_debug_stale = false;
180  _debug->_debug_world = nullptr;
181  _world->setDebugDrawer(nullptr);
182  _debug = nullptr;
183  }
184 }
185 
186 /**
187  *
188  */
189 void BulletWorld::
190 set_gravity(const LVector3 &gravity) {
191  LightMutexHolder holder(get_global_lock());
192 
193  _world->setGravity(LVecBase3_to_btVector3(gravity));
194  _info.m_gravity.setValue(gravity.get_x(), gravity.get_y(), gravity.get_z());
195 }
196 
197 /**
198  *
199  */
200 void BulletWorld::
201 set_gravity(PN_stdfloat gx, PN_stdfloat gy, PN_stdfloat gz) {
202  LightMutexHolder holder(get_global_lock());
203 
204  _world->setGravity(btVector3((btScalar)gx, (btScalar)gy, (btScalar)gz));
205  _info.m_gravity.setValue((btScalar)gx, (btScalar)gy, (btScalar)gz);
206 }
207 
208 /**
209  *
210  */
211 const LVector3 BulletWorld::
212 get_gravity() const {
213  LightMutexHolder holder(get_global_lock());
214 
215  return btVector3_to_LVector3(_world->getGravity());
216 }
217 
218 /**
219  *
220  */
221 int BulletWorld::
222 do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) {
223  LightMutexHolder holder(get_global_lock());
224 
225  _pstat_physics.start();
226 
227  int num_substeps = clamp(int(dt / stepsize), 1, max_substeps);
228 
229  // Synchronize Panda to Bullet
230  _pstat_p2b.start();
231  do_sync_p2b(dt, num_substeps);
232  _pstat_p2b.stop();
233 
234  // Simulation
235  _pstat_simulation.start();
236  int n = _world->stepSimulation((btScalar)dt, max_substeps, (btScalar)stepsize);
237  _pstat_simulation.stop();
238 
239  // Synchronize Bullet to Panda
240  _pstat_b2p.start();
241  do_sync_b2p();
242  _info.m_sparsesdf.GarbageCollect(bullet_gc_lifetime);
243  _pstat_b2p.stop();
244 
245  // Render debug
246  if (_debug) {
247  _debug->do_sync_b2p(_world);
248  }
249 
250  _pstat_physics.stop();
251 
252  return n;
253 }
254 
255 /**
256  * Assumes the lock(bullet global lock) is held by the caller
257  */
258 void BulletWorld::
259 do_sync_p2b(PN_stdfloat dt, int num_substeps) {
260 
261  for (BulletRigidBodyNode *body : _bodies) {
262  body->do_sync_p2b();
263  }
264 
265  for (BulletSoftBodyNode *softbody : _softbodies) {
266  softbody->do_sync_p2b();
267  }
268 
269  for (BulletGhostNode *ghost : _ghosts) {
270  ghost->do_sync_p2b();
271  }
272 
273  for (BulletBaseCharacterControllerNode *character : _characters) {
274  character->do_sync_p2b(dt, num_substeps);
275  }
276 }
277 
278 /**
279  * Assumes the lock(bullet global lock) is held by the caller
280  */
281 void BulletWorld::
282 do_sync_b2p() {
283 
284  for (BulletRigidBodyNode *body : _bodies) {
285  body->do_sync_b2p();
286  }
287 
288  for (BulletSoftBodyNode *softbody : _softbodies) {
289  softbody->do_sync_b2p();
290  }
291 
292  for (BulletGhostNode *ghost : _ghosts) {
293  ghost->do_sync_b2p();
294  }
295 
296  for (BulletBaseCharacterControllerNode *character : _characters) {
297  character->do_sync_b2p();
298  }
299 
300  for (BulletVehicle *vehicle : _vehicles) {
301  vehicle->do_sync_b2p();
302  }
303 }
304 
305 /**
306  *
307  */
308 void BulletWorld::
309 attach(TypedObject *object) {
310  LightMutexHolder holder(get_global_lock());
311 
312  if (object->is_of_type(BulletGhostNode::get_class_type())) {
313  do_attach_ghost(DCAST(BulletGhostNode, object));
314  }
315  else if (object->is_of_type(BulletRigidBodyNode::get_class_type())) {
316  do_attach_rigid_body(DCAST(BulletRigidBodyNode, object));
317  }
318  else if (object->is_of_type(BulletSoftBodyNode::get_class_type())) {
319  do_attach_soft_body(DCAST(BulletSoftBodyNode, object));
320  }
321  else if (object->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
322  do_attach_character(DCAST(BulletBaseCharacterControllerNode, object));
323  }
324  else if (object->is_of_type(BulletVehicle::get_class_type())) {
325  do_attach_vehicle(DCAST(BulletVehicle, object));
326  }
327  else if (object->is_of_type(BulletConstraint::get_class_type())) {
328  do_attach_constraint(DCAST(BulletConstraint, object));
329  }
330  else {
331  bullet_cat->error() << "not a bullet world object!" << endl;
332  }
333 }
334 
335 /**
336  *
337  */
338 void BulletWorld::
339 remove(TypedObject *object) {
340  LightMutexHolder holder(get_global_lock());
341 
342  if (object->is_of_type(BulletGhostNode::get_class_type())) {
343  do_remove_ghost(DCAST(BulletGhostNode, object));
344  }
345  else if (object->is_of_type(BulletRigidBodyNode::get_class_type())) {
346  do_remove_rigid_body(DCAST(BulletRigidBodyNode, object));
347  }
348  else if (object->is_of_type(BulletSoftBodyNode::get_class_type())) {
349  do_remove_soft_body(DCAST(BulletSoftBodyNode, object));
350  }
351  else if (object->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
352  do_remove_character(DCAST(BulletBaseCharacterControllerNode, object));
353  }
354  else if (object->is_of_type(BulletVehicle::get_class_type())) {
355  do_remove_vehicle(DCAST(BulletVehicle, object));
356  }
357  else if (object->is_of_type(BulletConstraint::get_class_type())) {
358  do_remove_constraint(DCAST(BulletConstraint, object));
359  }
360  else {
361  bullet_cat->error() << "not a bullet world object!" << endl;
362  }
363 }
364 
365 /**
366  * @deprecated Please use BulletWorld::attach
367  */
370  LightMutexHolder holder(get_global_lock());
371 
372  do_attach_rigid_body(node);
373 }
374 
375 /**
376  * @deprecated Please use BulletWorld::remove
377  */
380  LightMutexHolder holder(get_global_lock());
381 
382  do_remove_rigid_body(node);
383 }
384 
385 /**
386  * @deprecated Please use BulletWorld::attach
387  */
390  LightMutexHolder holder(get_global_lock());
391 
392  do_attach_soft_body(node);
393 }
394 
395 /**
396  * @deprecated Please use BulletWorld::remove
397  */
400  LightMutexHolder holder(get_global_lock());
401 
402  do_remove_soft_body(node);
403 }
404 
405 /**
406  * @deprecated Please use BulletWorld::attach
407  */
410  LightMutexHolder holder(get_global_lock());
411 
412  do_attach_ghost(node);
413 }
414 
415 /**
416  * @deprecated Please use BulletWorld::remove
417  */
420  LightMutexHolder holder(get_global_lock());
421 
422  do_remove_ghost(node);
423 }
424 
425 /**
426  * @deprecated Please use BulletWorld::attach
427  */
430  LightMutexHolder holder(get_global_lock());
431 
432  do_attach_character(node);
433 }
434 
435 /**
436  * @deprecated Please use BulletWorld::remove
437  */
440  LightMutexHolder holder(get_global_lock());
441 
442  do_remove_character(node);
443 }
444 
445 /**
446  * @deprecated Please use BulletWorld::attach
447  */
449 attach_vehicle(BulletVehicle *vehicle) {
450  LightMutexHolder holder(get_global_lock());
451 
452  do_attach_vehicle(vehicle);
453 }
454 
455 /**
456  * @deprecated Please use BulletWorld::remove
457  */
459 remove_vehicle(BulletVehicle *vehicle) {
460  LightMutexHolder holder(get_global_lock());
461 
462  do_remove_vehicle(vehicle);
463 }
464 
465 /**
466  * Attaches a single constraint to a world. Collision checks between the
467  * linked objects will be disabled if the second parameter is set to TRUE.
468  */
470 attach_constraint(BulletConstraint *constraint, bool linked_collision) {
471  LightMutexHolder holder(get_global_lock());
472 
473  do_attach_constraint(constraint, linked_collision);
474 }
475 
476 /**
477  * @deprecated Please use BulletWorld::remove
478  */
480 remove_constraint(BulletConstraint *constraint) {
481  LightMutexHolder holder(get_global_lock());
482 
483  do_remove_constraint(constraint);
484 }
485 
486 /**
487  * Assumes the lock(bullet global lock) is held by the caller
488  */
489 void BulletWorld::
490 do_attach_rigid_body(BulletRigidBodyNode *node) {
491 
492  nassertv(node);
493 
494  btRigidBody *ptr = btRigidBody::upcast(node->get_object());
495 
496  BulletRigidBodies::iterator found;
497  PT(BulletRigidBodyNode) ptnode = node;
498  found = find(_bodies.begin(), _bodies.end(), ptnode);
499 
500  if (found == _bodies.end()) {
501  _bodies.push_back(node);
502  _world->addRigidBody(ptr);
503  }
504  else {
505  bullet_cat.warning() << "rigid body already attached" << endl;
506  }
507 }
508 
509 /**
510  * Assumes the lock(bullet global lock) is held by the caller
511  */
512 void BulletWorld::
513 do_remove_rigid_body(BulletRigidBodyNode *node) {
514 
515  nassertv(node);
516 
517  btRigidBody *ptr = btRigidBody::upcast(node->get_object());
518 
519  BulletRigidBodies::iterator found;
520  PT(BulletRigidBodyNode) ptnode = node;
521  found = find(_bodies.begin(), _bodies.end(), ptnode);
522 
523  if (found == _bodies.end()) {
524  bullet_cat.warning() << "rigid body not attached" << endl;
525  }
526  else {
527  _bodies.erase(found);
528  _world->removeRigidBody(ptr);
529  }
530 }
531 
532 /**
533  * Assumes the lock(bullet global lock) is held by the caller
534  */
535 void BulletWorld::
536 do_attach_soft_body(BulletSoftBodyNode *node) {
537 
538  nassertv(node);
539 
540  btSoftBody *ptr = btSoftBody::upcast(node->get_object());
541 
542  // TODO: groupfilter settings (see ghost objects too)
543  short group = btBroadphaseProxy::DefaultFilter;
544  short mask = btBroadphaseProxy::AllFilter;
545 
546  BulletSoftBodies::iterator found;
547  PT(BulletSoftBodyNode) ptnode = node;
548  found = find(_softbodies.begin(), _softbodies.end(), ptnode);
549 
550  if (found == _softbodies.end()) {
551  _softbodies.push_back(node);
552  _world->addSoftBody(ptr, group, mask);
553  }
554  else {
555  bullet_cat.warning() << "soft body already attached" << endl;
556  }
557 }
558 
559 /**
560  * Assumes the lock(bullet global lock) is held by the caller
561  */
562 void BulletWorld::
563 do_remove_soft_body(BulletSoftBodyNode *node) {
564 
565  nassertv(node);
566 
567  btSoftBody *ptr = btSoftBody::upcast(node->get_object());
568 
569  BulletSoftBodies::iterator found;
570  PT(BulletSoftBodyNode) ptnode = node;
571  found = find(_softbodies.begin(), _softbodies.end(), ptnode);
572 
573  if (found == _softbodies.end()) {
574  bullet_cat.warning() << "soft body not attached" << endl;
575  }
576  else {
577  _softbodies.erase(found);
578  _world->removeSoftBody(ptr);
579  }
580 }
581 
582 /**
583  * Assumes the lock(bullet global lock) is held by the caller
584  */
585 void BulletWorld::
586 do_attach_ghost(BulletGhostNode *node) {
587 
588  nassertv(node);
589 
590  // TODO groupfilter settings...
591 /*
592 enum CollisionFilterGroups {
593  DefaultFilter = 1,
594  StaticFilter = 2,
595  KinematicFilter = 4,
596  DebrisFilter = 8,
597  SensorTrigger = 16,
598  CharacterFilter = 32,
599  AllFilter = -1
600 }
601 */
602 
603  short group = btBroadphaseProxy::SensorTrigger;
604  short mask = btBroadphaseProxy::AllFilter
605  & ~btBroadphaseProxy::StaticFilter
606  & ~btBroadphaseProxy::SensorTrigger;
607 
608  btGhostObject *ptr = btGhostObject::upcast(node->get_object());
609 
610  BulletGhosts::iterator found;
611  PT(BulletGhostNode) ptnode = node;
612  found = find(_ghosts.begin(), _ghosts.end(), ptnode);
613 
614  if (found == _ghosts.end()) {
615  _ghosts.push_back(node);
616  _world->addCollisionObject(ptr, group, mask);
617  }
618  else {
619  bullet_cat.warning() << "ghost already attached" << endl;
620  }
621 }
622 
623 /**
624  * Assumes the lock(bullet global lock) is held by the caller
625  */
626 void BulletWorld::
627 do_remove_ghost(BulletGhostNode *node) {
628 
629  nassertv(node);
630 
631  btGhostObject *ptr = btGhostObject::upcast(node->get_object());
632 
633  BulletGhosts::iterator found;
634  PT(BulletGhostNode) ptnode = node;
635  found = find(_ghosts.begin(), _ghosts.end(), ptnode);
636 
637  if (found == _ghosts.end()) {
638  bullet_cat.warning() << "ghost not attached" << endl;
639  }
640  else {
641  _ghosts.erase(found);
642  _world->removeCollisionObject(ptr);
643  }
644 }
645 
646 /**
647  * Assumes the lock(bullet global lock) is held by the caller
648  */
649 void BulletWorld::
650 do_attach_character(BulletBaseCharacterControllerNode *node) {
651 
652  nassertv(node);
653 
654  BulletCharacterControllers::iterator found;
655  PT(BulletBaseCharacterControllerNode) ptnode = node;
656  found = find(_characters.begin(), _characters.end(), ptnode);
657 
658  if (found == _characters.end()) {
659  _characters.push_back(node);
660 
661  _world->addCollisionObject(node->get_ghost(),
662  btBroadphaseProxy::CharacterFilter,
663  btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);
664 
665  _world->addCharacter(node->get_character());
666  }
667  else {
668  bullet_cat.warning() << "character already attached" << endl;
669  }
670 }
671 
672 /**
673  * Assumes the lock(bullet global lock) is held by the caller
674  */
675 void BulletWorld::
676 do_remove_character(BulletBaseCharacterControllerNode *node) {
677 
678  nassertv(node);
679 
680  BulletCharacterControllers::iterator found;
681  PT(BulletBaseCharacterControllerNode) ptnode = node;
682  found = find(_characters.begin(), _characters.end(), ptnode);
683 
684  if (found == _characters.end()) {
685  bullet_cat.warning() << "character not attached" << endl;
686  }
687  else {
688  _characters.erase(found);
689  _world->removeCollisionObject(node->get_ghost());
690  _world->removeCharacter(node->get_character());
691  }
692 }
693 
694 /**
695  * Assumes the lock(bullet global lock) is held by the caller
696  */
697 void BulletWorld::
698 do_attach_vehicle(BulletVehicle *vehicle) {
699 
700  nassertv(vehicle);
701 
702  BulletVehicles::iterator found;
703  PT(BulletVehicle) ptvehicle = vehicle;
704  found = find(_vehicles.begin(), _vehicles.end(), ptvehicle);
705 
706  if (found == _vehicles.end()) {
707  _vehicles.push_back(vehicle);
708  _world->addVehicle(vehicle->get_vehicle());
709  }
710  else {
711  bullet_cat.warning() << "vehicle already attached" << endl;
712  }
713 }
714 
715 /**
716  * Assumes the lock(bullet global lock) is held by the caller
717  */
718 void BulletWorld::
719 do_remove_vehicle(BulletVehicle *vehicle) {
720 
721  nassertv(vehicle);
722 
723  do_remove_rigid_body(vehicle->do_get_chassis());
724 
725  BulletVehicles::iterator found;
726  PT(BulletVehicle) ptvehicle = vehicle;
727  found = find(_vehicles.begin(), _vehicles.end(), ptvehicle);
728 
729  if (found == _vehicles.end()) {
730  bullet_cat.warning() << "vehicle not attached" << endl;
731  }
732  else {
733  _vehicles.erase(found);
734  _world->removeVehicle(vehicle->get_vehicle());
735  }
736 }
737 
738 /**
739  * Attaches a single constraint to a world. Collision checks between the
740  * linked objects will be disabled if the second parameter is set to TRUE.
741  * Assumes the lock(bullet global lock) is held by the caller
742  */
743 void BulletWorld::
744 do_attach_constraint(BulletConstraint *constraint, bool linked_collision) {
745 
746  nassertv(constraint);
747 
748  BulletConstraints::iterator found;
749  PT(BulletConstraint) ptconstraint = constraint;
750  found = find(_constraints.begin(), _constraints.end(), ptconstraint);
751 
752  if (found == _constraints.end()) {
753  _constraints.push_back(constraint);
754  _world->addConstraint(constraint->ptr(), linked_collision);
755  }
756  else {
757  bullet_cat.warning() << "constraint already attached" << endl;
758  }
759 }
760 
761 /**
762  * Assumes the lock(bullet global lock) is held by the caller
763  */
764 void BulletWorld::
765 do_remove_constraint(BulletConstraint *constraint) {
766 
767  nassertv(constraint);
768 
769  BulletConstraints::iterator found;
770  PT(BulletConstraint) ptconstraint = constraint;
771  found = find(_constraints.begin(), _constraints.end(), ptconstraint);
772 
773  if (found == _constraints.end()) {
774  bullet_cat.warning() << "constraint not attached" << endl;
775  }
776  else {
777  _constraints.erase(found);
778  _world->removeConstraint(constraint->ptr());
779  }
780 }
781 
782 /**
783  *
784  */
785 int BulletWorld::
786 get_num_rigid_bodies() const {
787  LightMutexHolder holder(get_global_lock());
788 
789  return _bodies.size();
790 }
791 
792 /**
793  *
794  */
795 BulletRigidBodyNode *BulletWorld::
796 get_rigid_body(int idx) const {
797  LightMutexHolder holder(get_global_lock());
798 
799  nassertr(idx >= 0 && idx < (int)_bodies.size(), nullptr);
800  return _bodies[idx];
801 }
802 
803 /**
804  *
805  */
806 int BulletWorld::
807 get_num_soft_bodies() const {
808  LightMutexHolder holder(get_global_lock());
809 
810  return _softbodies.size();
811 }
812 
813 /**
814  *
815  */
816 BulletSoftBodyNode *BulletWorld::
817 get_soft_body(int idx) const {
818  LightMutexHolder holder(get_global_lock());
819 
820  nassertr(idx >= 0 && idx < (int)_softbodies.size(), nullptr);
821  return _softbodies[idx];
822 }
823 
824 /**
825  *
826  */
827 int BulletWorld::
828 get_num_ghosts() const {
829  LightMutexHolder holder(get_global_lock());
830 
831  return _ghosts.size();
832 }
833 
834 /**
835  *
836  */
837 BulletGhostNode *BulletWorld::
838 get_ghost(int idx) const {
839  LightMutexHolder holder(get_global_lock());
840 
841  nassertr(idx >= 0 && idx < (int)_ghosts.size(), nullptr);
842  return _ghosts[idx];
843 }
844 
845 /**
846  *
847  */
848 int BulletWorld::
849 get_num_characters() const {
850  LightMutexHolder holder(get_global_lock());
851 
852  return _characters.size();
853 }
854 
855 /**
856  *
857  */
859 get_character(int idx) const {
860  LightMutexHolder holder(get_global_lock());
861 
862  nassertr(idx >= 0 && idx < (int)_characters.size(), nullptr);
863  return _characters[idx];
864 }
865 
866 /**
867  *
868  */
869 int BulletWorld::
870 get_num_vehicles() const {
871  LightMutexHolder holder(get_global_lock());
872 
873  return _vehicles.size();
874 }
875 
876 /**
877  *
878  */
879 BulletVehicle *BulletWorld::
880 get_vehicle(int idx) const {
881  LightMutexHolder holder(get_global_lock());
882 
883  nassertr(idx >= 0 && idx < (int)_vehicles.size(), nullptr);
884  return _vehicles[idx];
885 }
886 
887 /**
888  *
889  */
890 int BulletWorld::
891 get_num_constraints() const {
892  LightMutexHolder holder(get_global_lock());
893 
894  return _constraints.size();
895 }
896 
897 /**
898  *
899  */
900 BulletConstraint *BulletWorld::
901 get_constraint(int idx) const {
902  LightMutexHolder holder(get_global_lock());
903 
904  nassertr(idx >= 0 && idx < (int)_constraints.size(), nullptr);
905  return _constraints[idx];
906 }
907 
908 /**
909  *
910  */
911 int BulletWorld::
912 get_num_manifolds() const {
913  LightMutexHolder holder(get_global_lock());
914 
915  return _world->getDispatcher()->getNumManifolds();
916 }
917 
918 /**
919  *
920  */
921 BulletClosestHitRayResult BulletWorld::
922 ray_test_closest(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask &mask) const {
923  LightMutexHolder holder(get_global_lock());
924 
925  nassertr(!from_pos.is_nan(), BulletClosestHitRayResult::empty());
926  nassertr(!to_pos.is_nan(), BulletClosestHitRayResult::empty());
927 
928  const btVector3 from = LVecBase3_to_btVector3(from_pos);
929  const btVector3 to = LVecBase3_to_btVector3(to_pos);
930 
931  BulletClosestHitRayResult cb(from, to, mask);
932  _world->rayTest(from, to, cb);
933  return cb;
934 }
935 
936 /**
937  *
938  */
939 BulletAllHitsRayResult BulletWorld::
940 ray_test_all(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask &mask) const {
941  LightMutexHolder holder(get_global_lock());
942 
943  nassertr(!from_pos.is_nan(), BulletAllHitsRayResult::empty());
944  nassertr(!to_pos.is_nan(), BulletAllHitsRayResult::empty());
945 
946  const btVector3 from = LVecBase3_to_btVector3(from_pos);
947  const btVector3 to = LVecBase3_to_btVector3(to_pos);
948 
949  BulletAllHitsRayResult cb(from, to, mask);
950  _world->rayTest(from, to, cb);
951  return cb;
952 }
953 
954 /**
955  * Performs a sweep test against all other shapes that match the given group
956  * mask. The provided shape must be a convex shape; it is an error to invoke
957  * this method using a non-convex shape.
958  */
960 sweep_test_closest(BulletShape *shape, const TransformState &from_ts, const TransformState &to_ts, const CollideMask &mask, PN_stdfloat penetration) const {
961  LightMutexHolder holder(get_global_lock());
962 
963  nassertr(shape, BulletClosestHitSweepResult::empty());
964 
965  const btConvexShape *convex = (const btConvexShape *) shape->ptr();
966  nassertr(convex->isConvex(), BulletClosestHitSweepResult::empty());
967 
968  nassertr(!from_ts.is_invalid(), BulletClosestHitSweepResult::empty());
969  nassertr(!to_ts.is_invalid(), BulletClosestHitSweepResult::empty());
970 
971  const btVector3 from_pos = LVecBase3_to_btVector3(from_ts.get_pos());
972  const btVector3 to_pos = LVecBase3_to_btVector3(to_ts.get_pos());
973  const btTransform from_trans = LMatrix4_to_btTrans(from_ts.get_mat());
974  const btTransform to_trans = LMatrix4_to_btTrans(to_ts.get_mat());
975 
976  BulletClosestHitSweepResult cb(from_pos, to_pos, mask);
977  _world->convexSweepTest(convex, from_trans, to_trans, cb, penetration);
978  return cb;
979 }
980 
981 /**
982  * Performs a test if two bodies should collide or not, based on the collision
983  * filter setting.
984  */
986 filter_test(PandaNode *node0, PandaNode *node1) const {
987  LightMutexHolder holder(get_global_lock());
988 
989  nassertr(node0, false);
990  nassertr(node1, false);
991  nassertr(_filter_cb, false);
992 
993  btCollisionObject *obj0 = get_collision_object(node0);
994  btCollisionObject *obj1 = get_collision_object(node1);
995 
996  nassertr(obj0, false);
997  nassertr(obj1, false);
998 
999  btBroadphaseProxy *proxy0 = obj0->getBroadphaseHandle();
1000  btBroadphaseProxy *proxy1 = obj1->getBroadphaseHandle();
1001 
1002  nassertr(proxy0, false);
1003  nassertr(proxy1, false);
1004 
1005  return _filter_cb->needBroadphaseCollision(proxy0, proxy1);
1006 }
1007 
1008 /**
1009  * Performas a test for all bodies which are currently in contact with the
1010  * given body. The test returns a BulletContactResult object which may
1011  * contain zero, one or more contacts.
1012  *
1013  * If the optional parameter use_filter is set to TRUE this test will consider
1014  * filter settings. Otherwise all objects in contact are reported, no matter
1015  * if they would collide or not.
1016  */
1018 contact_test(PandaNode *node, bool use_filter) const {
1019  LightMutexHolder holder(get_global_lock());
1020 
1021  btCollisionObject *obj = get_collision_object(node);
1022 
1024 
1025  if (obj) {
1026 #if BT_BULLET_VERSION >= 281
1027  if (use_filter) {
1028  cb.use_filter(_filter_cb, obj->getBroadphaseHandle());
1029  }
1030 #endif
1031 
1032  _world->contactTest(obj, cb);
1033  }
1034 
1035  return cb;
1036 }
1037 
1038 /**
1039  * Performas a test if the two bodies given as parameters are in contact or
1040  * not. The test returns a BulletContactResult object which may contain zero
1041  * or one contacts.
1042  */
1044 contact_test_pair(PandaNode *node0, PandaNode *node1) const {
1045  LightMutexHolder holder(get_global_lock());
1046 
1047  btCollisionObject *obj0 = get_collision_object(node0);
1048  btCollisionObject *obj1 = get_collision_object(node1);
1049 
1051 
1052  if (obj0 && obj1) {
1053 
1054  _world->contactPairTest(obj0, obj1, cb);
1055  }
1056 
1057  return cb;
1058 }
1059 
1060 /**
1061  *
1062  */
1063 BulletPersistentManifold *BulletWorld::
1064 get_manifold(int idx) const {
1065  LightMutexHolder holder(get_global_lock());
1066 
1067  nassertr(idx < _dispatcher->getNumManifolds(), nullptr);
1068 
1069  btPersistentManifold *ptr = _dispatcher->getManifoldByIndexInternal(idx);
1070  return (ptr) ? new BulletPersistentManifold(ptr) : nullptr;
1071 }
1072 
1073 /**
1074  *
1075  */
1076 btCollisionObject *BulletWorld::
1077 get_collision_object(PandaNode *node) {
1078 
1079  if (node->is_of_type(BulletRigidBodyNode::get_class_type())) {
1080  return ((BulletRigidBodyNode *)node)->get_object();
1081  }
1082  else if (node->is_of_type(BulletGhostNode::get_class_type())) {
1083  return ((BulletGhostNode *)node)->get_object();
1084  }
1085  else if (node->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
1086  return ((BulletBaseCharacterControllerNode *)node)->get_ghost();
1087  }
1088  else if (node->is_of_type(BulletSoftBodyNode::get_class_type())) {
1089  return ((BulletSoftBodyNode *)node)->get_object();
1090  }
1091 
1092  return nullptr;
1093 }
1094 
1095 /**
1096  *
1097  */
1098 void BulletWorld::
1099 set_group_collision_flag(unsigned int group1, unsigned int group2, bool enable) {
1100  LightMutexHolder holder(get_global_lock());
1101 
1102  if (_filter_algorithm != FA_groups_mask) {
1103  bullet_cat.warning() << "filter algorithm is not 'groups-mask'" << endl;
1104  }
1105 
1106  _filter_cb2._collide[group1].set_bit_to(group2, enable);
1107  _filter_cb2._collide[group2].set_bit_to(group1, enable);
1108 }
1109 
1110 /**
1111  *
1112  */
1113 bool BulletWorld::
1114 get_group_collision_flag(unsigned int group1, unsigned int group2) const {
1115  LightMutexHolder holder(get_global_lock());
1116 
1117  return _filter_cb2._collide[group1].get_bit(group2);
1118 }
1119 
1120 /**
1121  *
1122  */
1123 void BulletWorld::
1124 set_force_update_all_aabbs(bool force) {
1125  LightMutexHolder holder(get_global_lock());
1126  _world->setForceUpdateAllAabbs(force);
1127 }
1128 
1129 /**
1130  *
1131  */
1132 bool BulletWorld::
1133 get_force_update_all_aabbs() const {
1134  LightMutexHolder holder(get_global_lock());
1135  return _world->getForceUpdateAllAabbs();
1136 }
1137 
1138 /**
1139  *
1140  */
1141 void BulletWorld::
1142 set_contact_added_callback(CallbackObject *obj) {
1143  LightMutexHolder holder(get_global_lock());
1144 
1145  _world->getSolverInfo().m_solverMode |= SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION;
1146  _world->getSolverInfo().m_solverMode |= SOLVER_USE_2_FRICTION_DIRECTIONS;
1147  _world->getSolverInfo().m_solverMode |= SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
1148 
1149  bullet_contact_added_callback = obj;
1150 }
1151 
1152 /**
1153  *
1154  */
1155 void BulletWorld::
1156 clear_contact_added_callback() {
1157  LightMutexHolder holder(get_global_lock());
1158 
1159  _world->getSolverInfo().m_solverMode &= ~SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION;
1160  _world->getSolverInfo().m_solverMode &= ~SOLVER_USE_2_FRICTION_DIRECTIONS;
1161  _world->getSolverInfo().m_solverMode &= ~SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
1162 
1163  bullet_contact_added_callback = nullptr;
1164 }
1165 
1166 /**
1167  *
1168  */
1169 void BulletWorld::
1170 set_tick_callback(CallbackObject *obj, bool is_pretick) {
1171  LightMutexHolder holder(get_global_lock());
1172 
1173  nassertv(obj != nullptr);
1174  _tick_callback_obj = obj;
1175  _world->setInternalTickCallback(&BulletWorld::tick_callback, this, is_pretick);
1176 }
1177 
1178 /**
1179  *
1180  */
1181 void BulletWorld::
1182 clear_tick_callback() {
1183  LightMutexHolder holder(get_global_lock());
1184 
1185  _tick_callback_obj = nullptr;
1186  _world->setInternalTickCallback(nullptr);
1187 }
1188 
1189 /**
1190  *
1191  */
1192 void BulletWorld::
1193 tick_callback(btDynamicsWorld *world, btScalar timestep) {
1194 
1195  nassertv(world->getWorldUserInfo());
1196 
1197  BulletWorld *w = static_cast<BulletWorld *>(world->getWorldUserInfo());
1198  CallbackObject *obj = w->_tick_callback_obj;
1199  if (obj) {
1200  BulletTickCallbackData cbdata(timestep);
1201  // Release the global lock that we are holding during the tick callback
1202  // and allow interactions with bullet world in the user callback
1203  get_global_lock().release();
1204  obj->do_callback(&cbdata);
1205  // Acquire the global lock again and protect the execution
1206  get_global_lock().acquire();
1207  }
1208 }
1209 
1210 /**
1211  *
1212  */
1213 void BulletWorld::
1214 set_filter_callback(CallbackObject *obj) {
1215  LightMutexHolder holder(get_global_lock());
1216 
1217  nassertv(obj != nullptr);
1218 
1219  if (_filter_algorithm != FA_callback) {
1220  bullet_cat.warning() << "filter algorithm is not 'callback'" << endl;
1221  }
1222 
1223  _filter_cb3._filter_callback_obj = obj;
1224 }
1225 
1226 /**
1227  *
1228  */
1229 void BulletWorld::
1230 clear_filter_callback() {
1231  LightMutexHolder holder(get_global_lock());
1232 
1233  _filter_cb3._filter_callback_obj = nullptr;
1234 }
1235 
1236 /**
1237  *
1238  */
1239 bool BulletWorld::btFilterCallback1::
1240 needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const {
1241 
1242  btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
1243  btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
1244 
1245  nassertr(obj0, false);
1246  nassertr(obj1, false);
1247 
1248  PandaNode *node0 = (PandaNode *) obj0->getUserPointer();
1249  PandaNode *node1 = (PandaNode *) obj1->getUserPointer();
1250 
1251  nassertr(node0, false);
1252  nassertr(node1, false);
1253 
1254  CollideMask mask0 = node0->get_into_collide_mask();
1255  CollideMask mask1 = node1->get_into_collide_mask();
1256 
1257  return (mask0 & mask1) != 0;
1258 }
1259 
1260 /**
1261  *
1262  */
1263 bool BulletWorld::btFilterCallback2::
1264 needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const {
1265 
1266  btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
1267  btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
1268 
1269  nassertr(obj0, false);
1270  nassertr(obj1, false);
1271 
1272  PandaNode *node0 = (PandaNode *) obj0->getUserPointer();
1273  PandaNode *node1 = (PandaNode *) obj1->getUserPointer();
1274 
1275  nassertr(node0, false);
1276  nassertr(node1, false);
1277 
1278  CollideMask mask0 = node0->get_into_collide_mask();
1279  CollideMask mask1 = node1->get_into_collide_mask();
1280 
1281 // cout << mask0 << " " << mask1 << endl;
1282 
1283  for (size_t i = 0; i < 32; ++i) {
1284  if (mask0.get_bit(i)) {
1285  if ((_collide[i] & mask1) != 0)
1286 // cout << "collide: i=" << i << " _collide[i]" << _collide[i] << endl;
1287  return true;
1288  }
1289  }
1290 
1291  return false;
1292 }
1293 
1294 /**
1295  *
1296  */
1297 bool BulletWorld::btFilterCallback3::
1298 needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const {
1299 
1300  nassertr(_filter_callback_obj, false);
1301 
1302  btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
1303  btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
1304 
1305  nassertr(obj0, false);
1306  nassertr(obj1, false);
1307 
1308  PandaNode *node0 = (PandaNode *) obj0->getUserPointer();
1309  PandaNode *node1 = (PandaNode *) obj1->getUserPointer();
1310 
1311  nassertr(node0, false);
1312  nassertr(node1, false);
1313 
1314  BulletFilterCallbackData cbdata(node0, node1);
1315  _filter_callback_obj->do_callback(&cbdata);
1316  return cbdata.get_collide();
1317 }
1318 
1319 /**
1320  *
1321  */
1322 ostream &
1323 operator << (ostream &out, BulletWorld::BroadphaseAlgorithm algorithm) {
1324 
1325  switch (algorithm) {
1326  case BulletWorld::BA_sweep_and_prune:
1327  return out << "sap";
1328 
1329  case BulletWorld::BA_dynamic_aabb_tree:
1330  return out << "aabb";
1331  };
1332 
1333  return out << "**invalid BulletWorld::BroadphaseAlgorithm(" << (int)algorithm << ")**";
1334 }
1335 
1336 /**
1337  *
1338  */
1339 istream &
1340 operator >> (istream &in, BulletWorld::BroadphaseAlgorithm &algorithm) {
1341  string word;
1342  in >> word;
1343 
1344  if (word == "sap") {
1345  algorithm = BulletWorld::BA_sweep_and_prune;
1346  }
1347  else if (word == "aabb") {
1348  algorithm = BulletWorld::BA_dynamic_aabb_tree;
1349  }
1350  else {
1351  bullet_cat.error()
1352  << "Invalid BulletWorld::BroadphaseAlgorithm: " << word << "\n";
1353  algorithm = BulletWorld::BA_dynamic_aabb_tree;
1354  }
1355 
1356  return in;
1357 }
1358 
1359 /**
1360  *
1361  */
1362 ostream &
1363 operator << (ostream &out, BulletWorld::FilterAlgorithm algorithm) {
1364 
1365  switch (algorithm) {
1366  case BulletWorld::FA_mask:
1367  return out << "mask";
1368  case BulletWorld::FA_groups_mask:
1369  return out << "groups-mask";
1370  case BulletWorld::FA_callback:
1371  return out << "callback";
1372  };
1373  return out << "**invalid BulletWorld::FilterAlgorithm(" << (int)algorithm << ")**";
1374 }
1375 
1376 /**
1377  *
1378  */
1379 istream &
1380 operator >> (istream &in, BulletWorld::FilterAlgorithm &algorithm) {
1381  string word;
1382  in >> word;
1383 
1384  if (word == "mask") {
1385  algorithm = BulletWorld::FA_mask;
1386  }
1387  else if (word == "groups-mask") {
1388  algorithm = BulletWorld::FA_groups_mask;
1389  }
1390  else if (word == "callback") {
1391  algorithm = BulletWorld::FA_callback;
1392  }
1393  else {
1394  bullet_cat.error()
1395  << "Invalid BulletWorld::FilterAlgorithm: " << word << "\n";
1396  algorithm = BulletWorld::FA_mask;
1397  }
1398  return in;
1399 }
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool get_bit(int index) const
Returns true if the nth bit is set, false if it is cleared.
Definition: bitMask.I:109
Simulates a raycast vehicle which casts a ray per wheel at the ground as a cheap replacement for comp...
Definition: bulletVehicle.h:68
BulletRigidBodyNode * do_get_chassis()
Returns the chassis of this vehicle.
void attach_ghost(BulletGhostNode *node)
void remove_soft_body(BulletSoftBodyNode *node)
void attach_character(BulletBaseCharacterControllerNode *node)
void remove_character(BulletBaseCharacterControllerNode *node)
clear_debug_node
Removes a debug node that has been assigned to this BulletWorld.
Definition: bulletWorld.h:164
BulletContactResult contact_test_pair(PandaNode *node0, PandaNode *node1) const
Performas a test if the two bodies given as parameters are in contact or not.
void remove_ghost(BulletGhostNode *node)
void remove_vehicle(BulletVehicle *vehicle)
BulletClosestHitSweepResult sweep_test_closest(BulletShape *shape, const TransformState &from_ts, const TransformState &to_ts, const CollideMask &mask=CollideMask::all_on(), PN_stdfloat penetration=0.0f) const
Performs a sweep test against all other shapes that match the given group mask.
void attach_vehicle(BulletVehicle *vehicle)
bool filter_test(PandaNode *node0, PandaNode *node1) const
Performs a test if two bodies should collide or not, based on the collision filter setting.
void attach_constraint(BulletConstraint *constraint, bool linked_collision=false)
Attaches a single constraint to a world.
void attach_rigid_body(BulletRigidBodyNode *node)
void attach_soft_body(BulletSoftBodyNode *node)
BulletContactResult contact_test(PandaNode *node, bool use_filter=false) const
Performas a test for all bodies which are currently in contact with the given body.
void remove_rigid_body(BulletRigidBodyNode *node)
void remove_constraint(BulletConstraint *constraint)
This is a generic object that can be assigned to a callback at various points in the rendering proces...
virtual void do_callback(CallbackData *cbdata)
This method called when the callback is triggered; it *replaces* the original function.
void release() const
Releases the lightMutex.
void acquire() const
Grabs the lightMutex if it is available.
Similar to MutexHolder, but for a light mutex.
This is a standard, non-reentrant mutex, similar to the Mutex class.
Definition: lightMutex.h:41
A lightweight class that represents a single element that may be timed and/or counted via stats.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
get_into_collide_mask
Returns the "into" collide mask for this node.
Definition: pandaNode.h:264
Indicates a coordinate-system transform on vertices.
bool is_invalid() const
Returns true if the transform represents an invalid matrix, for instance the result of inverting a si...
get_mat
Returns the matrix that describes the transform.
get_pos
Returns the pos component of the transform.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
This is an abstract class that all classes which use TypeHandle, and also provide virtual functions t...
Definition: typedObject.h:88
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static BulletAllHitsRayResult empty()
Named constructor intended to be used for asserts with have to return a concrete value.
static BulletClosestHitRayResult empty()
Named constructor intended to be used for asserts with have to return a concrete value.
static BulletClosestHitSweepResult empty()
Named constructor intended to be used for asserts with have to return a concrete value.