Panda3D
Loading...
Searching...
No Matches
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"
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
29using std::endl;
30using std::istream;
31using std::ostream;
32using std::string;
33
34TypeHandle BulletWorld::_type_handle;
35
36PStatCollector BulletWorld::_pstat_physics("App:Bullet:DoPhysics");
37PStatCollector BulletWorld::_pstat_simulation("App:Bullet:DoPhysics:Simulation");
38PStatCollector BulletWorld::_pstat_p2b("App:Bullet:DoPhysics:SyncP2B");
39PStatCollector BulletWorld::_pstat_b2p("App:Bullet:DoPhysics:SyncB2P");
40
41PT(CallbackObject) bullet_contact_added_callback;
42
43/**
44 *
45 */
46BulletWorld::
47BulletWorld() {
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 = bullet_split_impulse;
129 _world->getSolverInfo().m_numIterations = bullet_solver_iterations;
130}
131
132/**
133 *
134 */
135LightMutex &BulletWorld::
136get_global_lock() {
137
138 static LightMutex lock;
139
140 return lock;
141}
142
143/**
144 *
145 */
146BulletSoftBodyWorldInfo BulletWorld::
147get_world_info() {
148
149 return BulletSoftBodyWorldInfo(_info);
150}
151
152/**
153 *
154 */
155void BulletWorld::
156set_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 */
174void 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 */
189void BulletWorld::
190set_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 */
200void BulletWorld::
201set_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 */
211const LVector3 BulletWorld::
212get_gravity() const {
213 LightMutexHolder holder(get_global_lock());
214
215 return btVector3_to_LVector3(_world->getGravity());
216}
217
218/**
219 *
220 */
221int BulletWorld::
222do_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 */
258void BulletWorld::
259do_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 */
281void BulletWorld::
282do_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 */
308void BulletWorld::
309attach(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 */
338void BulletWorld::
339remove(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 */
450 LightMutexHolder holder(get_global_lock());
451
452 do_attach_vehicle(vehicle);
453}
454
455/**
456 * @deprecated Please use BulletWorld::remove
457 */
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 */
470attach_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 */
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 */
489void BulletWorld::
490do_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 */
512void BulletWorld::
513do_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 */
535void BulletWorld::
536do_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 */
562void BulletWorld::
563do_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 */
585void BulletWorld::
586do_attach_ghost(BulletGhostNode *node) {
587
588 nassertv(node);
589
590 // TODO groupfilter settings...
591/*
592enum 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 */
626void BulletWorld::
627do_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 */
649void BulletWorld::
650do_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 */
675void BulletWorld::
676do_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 */
697void BulletWorld::
698do_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 */
718void BulletWorld::
719do_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 */
743void BulletWorld::
744do_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 */
764void BulletWorld::
765do_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 */
785int BulletWorld::
786get_num_rigid_bodies() const {
787 LightMutexHolder holder(get_global_lock());
788
789 return _bodies.size();
790}
791
792/**
793 *
794 */
795BulletRigidBodyNode *BulletWorld::
796get_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 */
806int BulletWorld::
807get_num_soft_bodies() const {
808 LightMutexHolder holder(get_global_lock());
809
810 return _softbodies.size();
811}
812
813/**
814 *
815 */
816BulletSoftBodyNode *BulletWorld::
817get_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 */
827int BulletWorld::
828get_num_ghosts() const {
829 LightMutexHolder holder(get_global_lock());
830
831 return _ghosts.size();
832}
833
834/**
835 *
836 */
837BulletGhostNode *BulletWorld::
838get_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 */
848int BulletWorld::
849get_num_characters() const {
850 LightMutexHolder holder(get_global_lock());
851
852 return _characters.size();
853}
854
855/**
856 *
857 */
859get_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 */
869int BulletWorld::
870get_num_vehicles() const {
871 LightMutexHolder holder(get_global_lock());
872
873 return _vehicles.size();
874}
875
876/**
877 *
878 */
879BulletVehicle *BulletWorld::
880get_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 */
890int BulletWorld::
891get_num_constraints() const {
892 LightMutexHolder holder(get_global_lock());
893
894 return _constraints.size();
895}
896
897/**
898 *
899 */
900BulletConstraint *BulletWorld::
901get_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 */
911int BulletWorld::
912get_num_manifolds() const {
913 LightMutexHolder holder(get_global_lock());
914
915 return _world->getDispatcher()->getNumManifolds();
916}
917
918/**
919 *
920 */
921BulletClosestHitRayResult BulletWorld::
922ray_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 */
939BulletAllHitsRayResult BulletWorld::
940ray_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 */
960sweep_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());
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 */
986filter_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 */
1018contact_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 */
1044contact_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 */
1063BulletPersistentManifold *BulletWorld::
1064get_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 */
1076BulletPersistentManifold BulletWorld::
1077__get_manifold(int idx) const {
1078 LightMutexHolder holder(get_global_lock());
1079
1080 nassertr(idx < _dispatcher->getNumManifolds(), BulletPersistentManifold(nullptr));
1081
1082 btPersistentManifold *ptr = _dispatcher->getManifoldByIndexInternal(idx);
1083 return BulletPersistentManifold(ptr);
1084}
1085
1086/**
1087 *
1088 */
1089btCollisionObject *BulletWorld::
1090get_collision_object(PandaNode *node) {
1091
1092 if (node->is_of_type(BulletRigidBodyNode::get_class_type())) {
1093 return ((BulletRigidBodyNode *)node)->get_object();
1094 }
1095 else if (node->is_of_type(BulletGhostNode::get_class_type())) {
1096 return ((BulletGhostNode *)node)->get_object();
1097 }
1098 else if (node->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
1099 return ((BulletBaseCharacterControllerNode *)node)->get_ghost();
1100 }
1101 else if (node->is_of_type(BulletSoftBodyNode::get_class_type())) {
1102 return ((BulletSoftBodyNode *)node)->get_object();
1103 }
1104
1105 return nullptr;
1106}
1107
1108/**
1109 *
1110 */
1111void BulletWorld::
1112set_group_collision_flag(unsigned int group1, unsigned int group2, bool enable) {
1113 LightMutexHolder holder(get_global_lock());
1114
1115 if (_filter_algorithm != FA_groups_mask) {
1116 bullet_cat.warning() << "filter algorithm is not 'groups-mask'" << endl;
1117 }
1118
1119 _filter_cb2._collide[group1].set_bit_to(group2, enable);
1120 _filter_cb2._collide[group2].set_bit_to(group1, enable);
1121}
1122
1123/**
1124 *
1125 */
1126bool BulletWorld::
1127get_group_collision_flag(unsigned int group1, unsigned int group2) const {
1128 LightMutexHolder holder(get_global_lock());
1129
1130 return _filter_cb2._collide[group1].get_bit(group2);
1131}
1132
1133/**
1134 *
1135 */
1136void BulletWorld::
1137set_force_update_all_aabbs(bool force) {
1138 LightMutexHolder holder(get_global_lock());
1139 _world->setForceUpdateAllAabbs(force);
1140}
1141
1142/**
1143 *
1144 */
1145bool BulletWorld::
1146get_force_update_all_aabbs() const {
1147 LightMutexHolder holder(get_global_lock());
1148 return _world->getForceUpdateAllAabbs();
1149}
1150
1151/**
1152 *
1153 */
1154void BulletWorld::
1155set_contact_added_callback(CallbackObject *obj) {
1156 LightMutexHolder holder(get_global_lock());
1157
1158 _world->getSolverInfo().m_solverMode |= SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION;
1159 _world->getSolverInfo().m_solverMode |= SOLVER_USE_2_FRICTION_DIRECTIONS;
1160 _world->getSolverInfo().m_solverMode |= SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
1161
1162 bullet_contact_added_callback = obj;
1163}
1164
1165/**
1166 *
1167 */
1168void BulletWorld::
1169clear_contact_added_callback() {
1170 LightMutexHolder holder(get_global_lock());
1171
1172 _world->getSolverInfo().m_solverMode &= ~SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION;
1173 _world->getSolverInfo().m_solverMode &= ~SOLVER_USE_2_FRICTION_DIRECTIONS;
1174 _world->getSolverInfo().m_solverMode &= ~SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
1175
1176 bullet_contact_added_callback = nullptr;
1177}
1178
1179/**
1180 *
1181 */
1182void BulletWorld::
1183set_tick_callback(CallbackObject *obj, bool is_pretick) {
1184 LightMutexHolder holder(get_global_lock());
1185
1186 nassertv(obj != nullptr);
1187 _tick_callback_obj = obj;
1188 _world->setInternalTickCallback(&BulletWorld::tick_callback, this, is_pretick);
1189}
1190
1191/**
1192 *
1193 */
1194void BulletWorld::
1195clear_tick_callback() {
1196 LightMutexHolder holder(get_global_lock());
1197
1198 _tick_callback_obj = nullptr;
1199 _world->setInternalTickCallback(nullptr);
1200}
1201
1202/**
1203 *
1204 */
1205void BulletWorld::
1206tick_callback(btDynamicsWorld *world, btScalar timestep) {
1207
1208 nassertv(world->getWorldUserInfo());
1209
1210 BulletWorld *w = static_cast<BulletWorld *>(world->getWorldUserInfo());
1211 CallbackObject *obj = w->_tick_callback_obj;
1212 if (obj) {
1213 BulletTickCallbackData cbdata(timestep);
1214 // Release the global lock that we are holding during the tick callback
1215 // and allow interactions with bullet world in the user callback
1216 get_global_lock().release();
1217 obj->do_callback(&cbdata);
1218 // Acquire the global lock again and protect the execution
1219 get_global_lock().acquire();
1220 }
1221}
1222
1223/**
1224 *
1225 */
1226void BulletWorld::
1227set_filter_callback(CallbackObject *obj) {
1228 LightMutexHolder holder(get_global_lock());
1229
1230 nassertv(obj != nullptr);
1231
1232 if (_filter_algorithm != FA_callback) {
1233 bullet_cat.warning() << "filter algorithm is not 'callback'" << endl;
1234 }
1235
1236 _filter_cb3._filter_callback_obj = obj;
1237}
1238
1239/**
1240 *
1241 */
1242void BulletWorld::
1243clear_filter_callback() {
1244 LightMutexHolder holder(get_global_lock());
1245
1246 _filter_cb3._filter_callback_obj = nullptr;
1247}
1248
1249/**
1250 *
1251 */
1252bool BulletWorld::btFilterCallback1::
1253needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const {
1254
1255 btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
1256 btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
1257
1258 nassertr(obj0, false);
1259 nassertr(obj1, false);
1260
1261 PandaNode *node0 = (PandaNode *) obj0->getUserPointer();
1262 PandaNode *node1 = (PandaNode *) obj1->getUserPointer();
1263
1264 nassertr(node0, false);
1265 nassertr(node1, false);
1266
1267 CollideMask mask0 = node0->get_into_collide_mask();
1268 CollideMask mask1 = node1->get_into_collide_mask();
1269
1270 return (mask0 & mask1) != 0;
1271}
1272
1273/**
1274 *
1275 */
1276bool BulletWorld::btFilterCallback2::
1277needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const {
1278
1279 btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
1280 btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
1281
1282 nassertr(obj0, false);
1283 nassertr(obj1, false);
1284
1285 PandaNode *node0 = (PandaNode *) obj0->getUserPointer();
1286 PandaNode *node1 = (PandaNode *) obj1->getUserPointer();
1287
1288 nassertr(node0, false);
1289 nassertr(node1, false);
1290
1291 CollideMask mask0 = node0->get_into_collide_mask();
1292 CollideMask mask1 = node1->get_into_collide_mask();
1293
1294// cout << mask0 << " " << mask1 << endl;
1295
1296 for (size_t i = 0; i < 32; ++i) {
1297 if (mask0.get_bit(i)) {
1298 if ((_collide[i] & mask1) != 0)
1299// cout << "collide: i=" << i << " _collide[i]" << _collide[i] << endl;
1300 return true;
1301 }
1302 }
1303
1304 return false;
1305}
1306
1307/**
1308 *
1309 */
1310bool BulletWorld::btFilterCallback3::
1311needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const {
1312
1313 nassertr(_filter_callback_obj, false);
1314
1315 btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
1316 btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
1317
1318 nassertr(obj0, false);
1319 nassertr(obj1, false);
1320
1321 PandaNode *node0 = (PandaNode *) obj0->getUserPointer();
1322 PandaNode *node1 = (PandaNode *) obj1->getUserPointer();
1323
1324 nassertr(node0, false);
1325 nassertr(node1, false);
1326
1327 BulletFilterCallbackData cbdata(node0, node1);
1328 _filter_callback_obj->do_callback(&cbdata);
1329 return cbdata.get_collide();
1330}
1331
1332/**
1333 *
1334 */
1335ostream &
1336operator << (ostream &out, BulletWorld::BroadphaseAlgorithm algorithm) {
1337
1338 switch (algorithm) {
1339 case BulletWorld::BA_sweep_and_prune:
1340 return out << "sap";
1341
1342 case BulletWorld::BA_dynamic_aabb_tree:
1343 return out << "aabb";
1344 };
1345
1346 return out << "**invalid BulletWorld::BroadphaseAlgorithm(" << (int)algorithm << ")**";
1347}
1348
1349/**
1350 *
1351 */
1352istream &
1353operator >> (istream &in, BulletWorld::BroadphaseAlgorithm &algorithm) {
1354 string word;
1355 in >> word;
1356
1357 if (word == "sap") {
1358 algorithm = BulletWorld::BA_sweep_and_prune;
1359 }
1360 else if (word == "aabb") {
1361 algorithm = BulletWorld::BA_dynamic_aabb_tree;
1362 }
1363 else {
1364 bullet_cat.error()
1365 << "Invalid BulletWorld::BroadphaseAlgorithm: " << word << "\n";
1366 algorithm = BulletWorld::BA_dynamic_aabb_tree;
1367 }
1368
1369 return in;
1370}
1371
1372/**
1373 *
1374 */
1375ostream &
1376operator << (ostream &out, BulletWorld::FilterAlgorithm algorithm) {
1377
1378 switch (algorithm) {
1379 case BulletWorld::FA_mask:
1380 return out << "mask";
1381 case BulletWorld::FA_groups_mask:
1382 return out << "groups-mask";
1383 case BulletWorld::FA_callback:
1384 return out << "callback";
1385 };
1386 return out << "**invalid BulletWorld::FilterAlgorithm(" << (int)algorithm << ")**";
1387}
1388
1389/**
1390 *
1391 */
1392istream &
1393operator >> (istream &in, BulletWorld::FilterAlgorithm &algorithm) {
1394 string word;
1395 in >> word;
1396
1397 if (word == "mask") {
1398 algorithm = BulletWorld::FA_mask;
1399 }
1400 else if (word == "groups-mask") {
1401 algorithm = BulletWorld::FA_groups_mask;
1402 }
1403 else if (word == "callback") {
1404 algorithm = BulletWorld::FA_callback;
1405 }
1406 else {
1407 bullet_cat.error()
1408 << "Invalid BulletWorld::FilterAlgorithm: " << word << "\n";
1409 algorithm = BulletWorld::FA_mask;
1410 }
1411 return in;
1412}
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.
void set_bit_to(int index, bool value)
Sets the nth bit either on or off, according to the indicated bool value.
Definition bitMask.I:140
void set_bit(int index)
Sets the nth bit on.
Definition bitMask.I:119
bool get_bit(int index) const
Returns true if the nth bit is set, false if it is cleared.
Definition bitMask.I:109
void clear()
Sets all the bits in the BitMask off.
Definition bitMask.I:395
Simulates a raycast vehicle which casts a ray per wheel at the ground as a cheap replacement for comp...
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.
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.