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::
175 clear_debug_node() {
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  */
368 void BulletWorld::
370  LightMutexHolder holder(get_global_lock());
371 
372  do_attach_rigid_body(node);
373 }
374 
375 /**
376  * Deprecated.! Please use BulletWorld::remove
377  */
378 void BulletWorld::
380  LightMutexHolder holder(get_global_lock());
381 
382  do_remove_rigid_body(node);
383 }
384 
385 /**
386  * Deprecated! Please use BulletWorld::attach
387  */
388 void BulletWorld::
390  LightMutexHolder holder(get_global_lock());
391 
392  do_attach_soft_body(node);
393 }
394 
395 /**
396  * Deprecated.! Please use BulletWorld::remove
397  */
398 void BulletWorld::
400  LightMutexHolder holder(get_global_lock());
401 
402  do_remove_soft_body(node);
403 }
404 
405 /**
406  * Deprecated! Please use BulletWorld::attach
407  */
408 void BulletWorld::
410  LightMutexHolder holder(get_global_lock());
411 
412  do_attach_ghost(node);
413 }
414 
415 /**
416  * Deprecated.! Please use BulletWorld::remove
417  */
418 void BulletWorld::
420  LightMutexHolder holder(get_global_lock());
421 
422  do_remove_ghost(node);
423 }
424 
425 /**
426  * Deprecated! Please use BulletWorld::attach
427  */
428 void BulletWorld::
430  LightMutexHolder holder(get_global_lock());
431 
432  do_attach_character(node);
433 }
434 
435 /**
436  * Deprecated.! Please use BulletWorld::remove
437  */
438 void BulletWorld::
440  LightMutexHolder holder(get_global_lock());
441 
442  do_remove_character(node);
443 }
444 
445 /**
446  * Deprecated! Please use BulletWorld::attach
447  */
448 void BulletWorld::
450  LightMutexHolder holder(get_global_lock());
451 
452  do_attach_vehicle(vehicle);
453 }
454 
455 /**
456  * Deprecated.! Please use BulletWorld::remove
457  */
458 void BulletWorld::
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  */
469 void BulletWorld::
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  */
479 void BulletWorld::
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  */
985 bool BulletWorld::
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.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void remove_soft_body(BulletSoftBodyNode *node)
Deprecated.
Indicates a coordinate-system transform on vertices.
void attach_rigid_body(BulletRigidBodyNode *node)
Deprecated! Please use BulletWorld::attach.
void attach_vehicle(BulletVehicle *vehicle)
Deprecated! Please use BulletWorld::attach.
BulletContactResult contact_test_pair(PandaNode *node0, PandaNode *node1) const
Performas a test if the two bodies given as parameters are in contact or not.
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.
static BulletAllHitsRayResult empty()
Named constructor intended to be used for asserts with have to return a concrete value.
void remove_vehicle(BulletVehicle *vehicle)
Deprecated.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract class that all classes which use TypeHandle, and also provide virtual functions t...
Definition: typedObject.h:88
void remove_ghost(BulletGhostNode *node)
Deprecated.
void attach_soft_body(BulletSoftBodyNode *node)
Deprecated! Please use BulletWorld::attach.
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 remove_rigid_body(BulletRigidBodyNode *node)
Deprecated.
Simulates a raycast vehicle which casts a ray per wheel at the ground as a cheap replacement for comp...
Definition: bulletVehicle.h:68
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static BulletClosestHitRayResult empty()
Named constructor intended to be used for asserts with have to return a concrete value.
get_mat
Returns the matrix that describes the transform.
bool get_bit(int index) const
Returns true if the nth bit is set, false if it is cleared.
Definition: bitMask.I:109
A lightweight class that represents a single element that may be timed and/or counted via stats.
Similar to MutexHolder, but for a light mutex.
void acquire() const
Grabs the lightMutex if it is available.
get_pos
Returns the pos component of the transform.
void release() const
Releases the lightMutex.
void attach_ghost(BulletGhostNode *node)
Deprecated! Please use BulletWorld::attach.
BulletRigidBodyNode * do_get_chassis()
Returns the chassis of this vehicle.
static BulletClosestHitSweepResult empty()
Named constructor intended to be used for asserts with have to return a concrete value.
This is a generic object that can be assigned to a callback at various points in the rendering proces...
bool is_invalid() const
Returns true if the transform represents an invalid matrix, for instance the result of inverting a si...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool filter_test(PandaNode *node0, PandaNode *node1) const
Performs a test if two bodies should collide or not, based on the collision filter setting.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void remove_character(BulletBaseCharacterControllerNode *node)
Deprecated.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void attach_constraint(BulletConstraint *constraint, bool linked_collision=false)
Attaches a single constraint to a world.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
void attach_character(BulletBaseCharacterControllerNode *node)
Deprecated! Please use BulletWorld::attach.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
This is a standard, non-reentrant mutex, similar to the Mutex class.
Definition: lightMutex.h:39
void remove_constraint(BulletConstraint *constraint)
Deprecated.
get_into_collide_mask
Returns the "into" collide mask for this node.
Definition: pandaNode.h:264
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.