27 #define clamp(x, x_min, x_max) std::max(std::min(x, x_max), x_min)
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");
50 for (
size_t i = 0; i < 32; ++i) {
51 _filter_cb2._collide[i].clear();
52 _filter_cb2._collide[i].set_bit(i);
56 btScalar dx(bullet_sap_extents);
57 btVector3 extents(dx, dx, dx);
59 switch (bullet_broadphase_algorithm) {
60 case BA_sweep_and_prune:
61 _broadphase =
new btAxisSweep3(extents, extents, 1024);
63 case BA_dynamic_aabb_tree:
64 _broadphase =
new btDbvtBroadphase();
67 bullet_cat.error() <<
"no proper broadphase algorithm!" << endl;
69 nassertv(_broadphase);
72 _configuration =
new btSoftBodyRigidBodyCollisionConfiguration();
73 nassertv(_configuration);
76 _dispatcher =
new btCollisionDispatcher(_configuration);
77 nassertv(_dispatcher);
80 _solver =
new btSequentialImpulseConstraintSolver;
84 _world =
new btSoftRigidDynamicsWorld(_dispatcher, _broadphase, _solver, _configuration);
86 nassertv(_world->getPairCache());
88 _world->setWorldUserInfo(
this);
89 _world->setGravity(btVector3(0.0f, 0.0f, 0.0f));
92 _world->getPairCache()->setInternalGhostPairCallback(&_ghost_cb);
95 _filter_algorithm = bullet_filter_algorithm;
96 switch (_filter_algorithm) {
98 _filter_cb = &_filter_cb1;
101 _filter_cb = &_filter_cb2;
104 _filter_cb = &_filter_cb3;
107 bullet_cat.error() <<
"no proper filter algorithm!" << endl;
108 _filter_cb =
nullptr;
111 _world->getPairCache()->setOverlapFilterCallback(_filter_cb);
114 _tick_callback_obj =
nullptr;
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();
123 btGImpactCollisionAlgorithm::registerAlgorithm(_dispatcher);
126 _world->getDispatchInfo().m_enableSPU =
true;
127 _world->getDispatchInfo().m_useContinuous =
true;
128 _world->getSolverInfo().m_splitImpulse =
false;
129 _world->getSolverInfo().m_numIterations = bullet_solver_iterations;
160 if (node != _debug) {
161 if (_debug !=
nullptr) {
162 _debug->_debug_stale =
false;
163 _debug->_debug_world =
nullptr;
167 _world->setDebugDrawer(&(_debug->_drawer));
178 if (_debug !=
nullptr) {
179 _debug->_debug_stale =
false;
180 _debug->_debug_world =
nullptr;
181 _world->setDebugDrawer(
nullptr);
190 set_gravity(
const LVector3 &gravity) {
193 _world->setGravity(LVecBase3_to_btVector3(gravity));
194 _info.m_gravity.setValue(gravity.get_x(), gravity.get_y(), gravity.get_z());
201 set_gravity(PN_stdfloat gx, PN_stdfloat gy, PN_stdfloat gz) {
204 _world->setGravity(btVector3((btScalar)gx, (btScalar)gy, (btScalar)gz));
205 _info.m_gravity.setValue((btScalar)gx, (btScalar)gy, (btScalar)gz);
211 const LVector3 BulletWorld::
212 get_gravity()
const {
215 return btVector3_to_LVector3(_world->getGravity());
222 do_physics(PN_stdfloat dt,
int max_substeps, PN_stdfloat stepsize) {
225 _pstat_physics.start();
227 int num_substeps = clamp(
int(dt / stepsize), 1, max_substeps);
231 do_sync_p2b(dt, num_substeps);
235 _pstat_simulation.start();
236 int n = _world->stepSimulation((btScalar)dt, max_substeps, (btScalar)stepsize);
237 _pstat_simulation.stop();
242 _info.m_sparsesdf.GarbageCollect(bullet_gc_lifetime);
247 _debug->do_sync_b2p(_world);
250 _pstat_physics.stop();
259 do_sync_p2b(PN_stdfloat dt,
int num_substeps) {
266 softbody->do_sync_p2b();
270 ghost->do_sync_p2b();
274 character->do_sync_p2b(dt, num_substeps);
289 softbody->do_sync_b2p();
293 ghost->do_sync_b2p();
297 character->do_sync_b2p();
301 vehicle->do_sync_b2p();
312 if (object->
is_of_type(BulletGhostNode::get_class_type())) {
315 else if (object->
is_of_type(BulletRigidBodyNode::get_class_type())) {
318 else if (object->
is_of_type(BulletSoftBodyNode::get_class_type())) {
321 else if (object->
is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
324 else if (object->
is_of_type(BulletVehicle::get_class_type())) {
327 else if (object->
is_of_type(BulletConstraint::get_class_type())) {
331 bullet_cat->error() <<
"not a bullet world object!" << endl;
342 if (object->
is_of_type(BulletGhostNode::get_class_type())) {
345 else if (object->
is_of_type(BulletRigidBodyNode::get_class_type())) {
348 else if (object->
is_of_type(BulletSoftBodyNode::get_class_type())) {
351 else if (object->
is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
354 else if (object->
is_of_type(BulletVehicle::get_class_type())) {
357 else if (object->
is_of_type(BulletConstraint::get_class_type())) {
361 bullet_cat->error() <<
"not a bullet world object!" << endl;
372 do_attach_rigid_body(node);
382 do_remove_rigid_body(node);
392 do_attach_soft_body(node);
402 do_remove_soft_body(node);
412 do_attach_ghost(node);
422 do_remove_ghost(node);
432 do_attach_character(node);
442 do_remove_character(node);
452 do_attach_vehicle(vehicle);
462 do_remove_vehicle(vehicle);
473 do_attach_constraint(constraint, linked_collision);
483 do_remove_constraint(constraint);
494 btRigidBody *ptr = btRigidBody::upcast(node->get_object());
496 BulletRigidBodies::iterator found;
498 found = find(_bodies.begin(), _bodies.end(), ptnode);
500 if (found == _bodies.end()) {
501 _bodies.push_back(node);
502 _world->addRigidBody(ptr);
505 bullet_cat.warning() <<
"rigid body already attached" << endl;
517 btRigidBody *ptr = btRigidBody::upcast(node->get_object());
519 BulletRigidBodies::iterator found;
521 found = find(_bodies.begin(), _bodies.end(), ptnode);
523 if (found == _bodies.end()) {
524 bullet_cat.warning() <<
"rigid body not attached" << endl;
527 _bodies.erase(found);
528 _world->removeRigidBody(ptr);
540 btSoftBody *ptr = btSoftBody::upcast(node->get_object());
543 short group = btBroadphaseProxy::DefaultFilter;
544 short mask = btBroadphaseProxy::AllFilter;
546 BulletSoftBodies::iterator found;
548 found = find(_softbodies.begin(), _softbodies.end(), ptnode);
550 if (found == _softbodies.end()) {
551 _softbodies.push_back(node);
552 _world->addSoftBody(ptr, group, mask);
555 bullet_cat.warning() <<
"soft body already attached" << endl;
567 btSoftBody *ptr = btSoftBody::upcast(node->get_object());
569 BulletSoftBodies::iterator found;
571 found = find(_softbodies.begin(), _softbodies.end(), ptnode);
573 if (found == _softbodies.end()) {
574 bullet_cat.warning() <<
"soft body not attached" << endl;
577 _softbodies.erase(found);
578 _world->removeSoftBody(ptr);
603 short group = btBroadphaseProxy::SensorTrigger;
604 short mask = btBroadphaseProxy::AllFilter
605 & ~btBroadphaseProxy::StaticFilter
606 & ~btBroadphaseProxy::SensorTrigger;
608 btGhostObject *ptr = btGhostObject::upcast(node->get_object());
610 BulletGhosts::iterator found;
612 found = find(_ghosts.begin(), _ghosts.end(), ptnode);
614 if (found == _ghosts.end()) {
615 _ghosts.push_back(node);
616 _world->addCollisionObject(ptr, group, mask);
619 bullet_cat.warning() <<
"ghost already attached" << endl;
631 btGhostObject *ptr = btGhostObject::upcast(node->get_object());
633 BulletGhosts::iterator found;
635 found = find(_ghosts.begin(), _ghosts.end(), ptnode);
637 if (found == _ghosts.end()) {
638 bullet_cat.warning() <<
"ghost not attached" << endl;
641 _ghosts.erase(found);
642 _world->removeCollisionObject(ptr);
654 BulletCharacterControllers::iterator found;
656 found = find(_characters.begin(), _characters.end(), ptnode);
658 if (found == _characters.end()) {
659 _characters.push_back(node);
661 _world->addCollisionObject(node->get_ghost(),
662 btBroadphaseProxy::CharacterFilter,
663 btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);
665 _world->addCharacter(node->get_character());
668 bullet_cat.warning() <<
"character already attached" << endl;
680 BulletCharacterControllers::iterator found;
682 found = find(_characters.begin(), _characters.end(), ptnode);
684 if (found == _characters.end()) {
685 bullet_cat.warning() <<
"character not attached" << endl;
688 _characters.erase(found);
689 _world->removeCollisionObject(node->get_ghost());
690 _world->removeCharacter(node->get_character());
702 BulletVehicles::iterator found;
704 found = find(_vehicles.begin(), _vehicles.end(), ptvehicle);
706 if (found == _vehicles.end()) {
707 _vehicles.push_back(vehicle);
708 _world->addVehicle(vehicle->get_vehicle());
711 bullet_cat.warning() <<
"vehicle already attached" << endl;
725 BulletVehicles::iterator found;
727 found = find(_vehicles.begin(), _vehicles.end(), ptvehicle);
729 if (found == _vehicles.end()) {
730 bullet_cat.warning() <<
"vehicle not attached" << endl;
733 _vehicles.erase(found);
734 _world->removeVehicle(vehicle->get_vehicle());
746 nassertv(constraint);
748 BulletConstraints::iterator found;
750 found = find(_constraints.begin(), _constraints.end(), ptconstraint);
752 if (found == _constraints.end()) {
753 _constraints.push_back(constraint);
754 _world->addConstraint(constraint->ptr(), linked_collision);
757 bullet_cat.warning() <<
"constraint already attached" << endl;
767 nassertv(constraint);
769 BulletConstraints::iterator found;
771 found = find(_constraints.begin(), _constraints.end(), ptconstraint);
773 if (found == _constraints.end()) {
774 bullet_cat.warning() <<
"constraint not attached" << endl;
777 _constraints.erase(found);
778 _world->removeConstraint(constraint->ptr());
786 get_num_rigid_bodies()
const {
789 return _bodies.size();
796 get_rigid_body(
int idx)
const {
799 nassertr(idx >= 0 && idx < (
int)_bodies.size(),
nullptr);
807 get_num_soft_bodies()
const {
810 return _softbodies.size();
817 get_soft_body(
int idx)
const {
820 nassertr(idx >= 0 && idx < (
int)_softbodies.size(),
nullptr);
821 return _softbodies[idx];
828 get_num_ghosts()
const {
831 return _ghosts.size();
838 get_ghost(
int idx)
const {
841 nassertr(idx >= 0 && idx < (
int)_ghosts.size(),
nullptr);
849 get_num_characters()
const {
852 return _characters.size();
859 get_character(
int idx)
const {
862 nassertr(idx >= 0 && idx < (
int)_characters.size(),
nullptr);
863 return _characters[idx];
870 get_num_vehicles()
const {
873 return _vehicles.size();
880 get_vehicle(
int idx)
const {
883 nassertr(idx >= 0 && idx < (
int)_vehicles.size(),
nullptr);
884 return _vehicles[idx];
891 get_num_constraints()
const {
894 return _constraints.size();
901 get_constraint(
int idx)
const {
904 nassertr(idx >= 0 && idx < (
int)_constraints.size(),
nullptr);
905 return _constraints[idx];
912 get_num_manifolds()
const {
915 return _world->getDispatcher()->getNumManifolds();
922 ray_test_closest(
const LPoint3 &from_pos,
const LPoint3 &to_pos,
const CollideMask &mask)
const {
928 const btVector3 from = LVecBase3_to_btVector3(from_pos);
929 const btVector3 to = LVecBase3_to_btVector3(to_pos);
932 _world->rayTest(from, to, cb);
940 ray_test_all(
const LPoint3 &from_pos,
const LPoint3 &to_pos,
const CollideMask &mask)
const {
946 const btVector3 from = LVecBase3_to_btVector3(from_pos);
947 const btVector3 to = LVecBase3_to_btVector3(to_pos);
950 _world->rayTest(from, to, cb);
965 const btConvexShape *convex = (
const btConvexShape *) shape->ptr();
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());
977 _world->convexSweepTest(convex, from_trans, to_trans, cb, penetration);
989 nassertr(node0,
false);
990 nassertr(node1,
false);
991 nassertr(_filter_cb,
false);
993 btCollisionObject *obj0 = get_collision_object(node0);
994 btCollisionObject *obj1 = get_collision_object(node1);
996 nassertr(obj0,
false);
997 nassertr(obj1,
false);
999 btBroadphaseProxy *proxy0 = obj0->getBroadphaseHandle();
1000 btBroadphaseProxy *proxy1 = obj1->getBroadphaseHandle();
1002 nassertr(proxy0,
false);
1003 nassertr(proxy1,
false);
1005 return _filter_cb->needBroadphaseCollision(proxy0, proxy1);
1021 btCollisionObject *obj = get_collision_object(node);
1026 #if BT_BULLET_VERSION >= 281
1028 cb.use_filter(_filter_cb, obj->getBroadphaseHandle());
1032 _world->contactTest(obj, cb);
1047 btCollisionObject *obj0 = get_collision_object(node0);
1048 btCollisionObject *obj1 = get_collision_object(node1);
1054 _world->contactPairTest(obj0, obj1, cb);
1064 get_manifold(
int idx)
const {
1067 nassertr(idx < _dispatcher->getNumManifolds(),
nullptr);
1069 btPersistentManifold *ptr = _dispatcher->getManifoldByIndexInternal(idx);
1076 btCollisionObject *BulletWorld::
1079 if (node->
is_of_type(BulletRigidBodyNode::get_class_type())) {
1082 else if (node->
is_of_type(BulletGhostNode::get_class_type())) {
1085 else if (node->
is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
1088 else if (node->
is_of_type(BulletSoftBodyNode::get_class_type())) {
1099 set_group_collision_flag(
unsigned int group1,
unsigned int group2,
bool enable) {
1102 if (_filter_algorithm != FA_groups_mask) {
1103 bullet_cat.warning() <<
"filter algorithm is not 'groups-mask'" << endl;
1106 _filter_cb2._collide[group1].set_bit_to(group2, enable);
1107 _filter_cb2._collide[group2].set_bit_to(group1, enable);
1114 get_group_collision_flag(
unsigned int group1,
unsigned int group2)
const {
1117 return _filter_cb2._collide[group1].get_bit(group2);
1124 set_force_update_all_aabbs(
bool force) {
1126 _world->setForceUpdateAllAabbs(force);
1133 get_force_update_all_aabbs()
const {
1135 return _world->getForceUpdateAllAabbs();
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;
1149 bullet_contact_added_callback = obj;
1156 clear_contact_added_callback() {
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;
1163 bullet_contact_added_callback =
nullptr;
1173 nassertv(obj !=
nullptr);
1174 _tick_callback_obj = obj;
1175 _world->setInternalTickCallback(&BulletWorld::tick_callback,
this, is_pretick);
1182 clear_tick_callback() {
1185 _tick_callback_obj =
nullptr;
1186 _world->setInternalTickCallback(
nullptr);
1193 tick_callback(btDynamicsWorld *world, btScalar timestep) {
1195 nassertv(world->getWorldUserInfo());
1217 nassertv(obj !=
nullptr);
1219 if (_filter_algorithm != FA_callback) {
1220 bullet_cat.warning() <<
"filter algorithm is not 'callback'" << endl;
1223 _filter_cb3._filter_callback_obj = obj;
1230 clear_filter_callback() {
1233 _filter_cb3._filter_callback_obj =
nullptr;
1239 bool BulletWorld::btFilterCallback1::
1240 needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1)
const {
1242 btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
1243 btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
1245 nassertr(obj0,
false);
1246 nassertr(obj1,
false);
1251 nassertr(node0,
false);
1252 nassertr(node1,
false);
1257 return (mask0 & mask1) != 0;
1263 bool BulletWorld::btFilterCallback2::
1264 needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1)
const {
1266 btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
1267 btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
1269 nassertr(obj0,
false);
1270 nassertr(obj1,
false);
1275 nassertr(node0,
false);
1276 nassertr(node1,
false);
1283 for (
size_t i = 0; i < 32; ++i) {
1285 if ((_collide[i] & mask1) != 0)
1297 bool BulletWorld::btFilterCallback3::
1298 needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1)
const {
1300 nassertr(_filter_callback_obj,
false);
1302 btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
1303 btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
1305 nassertr(obj0,
false);
1306 nassertr(obj1,
false);
1311 nassertr(node0,
false);
1312 nassertr(node1,
false);
1315 _filter_callback_obj->do_callback(&cbdata);
1316 return cbdata.get_collide();
1323 operator << (ostream &out, BulletWorld::BroadphaseAlgorithm algorithm) {
1325 switch (algorithm) {
1326 case BulletWorld::BA_sweep_and_prune:
1327 return out <<
"sap";
1329 case BulletWorld::BA_dynamic_aabb_tree:
1330 return out <<
"aabb";
1333 return out <<
"**invalid BulletWorld::BroadphaseAlgorithm(" << (int)algorithm <<
")**";
1340 operator >> (istream &in, BulletWorld::BroadphaseAlgorithm &algorithm) {
1344 if (word ==
"sap") {
1345 algorithm = BulletWorld::BA_sweep_and_prune;
1347 else if (word ==
"aabb") {
1348 algorithm = BulletWorld::BA_dynamic_aabb_tree;
1352 <<
"Invalid BulletWorld::BroadphaseAlgorithm: " << word <<
"\n";
1353 algorithm = BulletWorld::BA_dynamic_aabb_tree;
1363 operator << (ostream &out, BulletWorld::FilterAlgorithm algorithm) {
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";
1373 return out <<
"**invalid BulletWorld::FilterAlgorithm(" << (int)algorithm <<
")**";
1380 operator >> (istream &in, BulletWorld::FilterAlgorithm &algorithm) {
1384 if (word ==
"mask") {
1385 algorithm = BulletWorld::FA_mask;
1387 else if (word ==
"groups-mask") {
1388 algorithm = BulletWorld::FA_groups_mask;
1390 else if (word ==
"callback") {
1391 algorithm = BulletWorld::FA_callback;
1395 <<
"Invalid BulletWorld::FilterAlgorithm: " << word <<
"\n";
1396 algorithm = BulletWorld::FA_mask;
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.
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.
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.
get_into_collide_mask
Returns the "into" collide mask for this node.
TypeHandle is the identifier used to differentiate C++ class types.
This is an abstract class that all classes which use TypeHandle, and also provide virtual functions t...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
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.