panda3d/panda/src/bullet/bulletWorld.cxx

1382 lines
32 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file bulletWorld.cxx
* @author enn0x
* @date 2010-01-23
*/
#include "bulletWorld.h"
#include "bulletPersistentManifold.h"
#include "bulletShape.h"
#include "bulletSoftBodyWorldInfo.h"
#include "collideMask.h"
#include "lightMutexHolder.h"
#define clamp(x, x_min, x_max) max(min(x, x_max), x_min)
TypeHandle BulletWorld::_type_handle;
PStatCollector BulletWorld::_pstat_physics("App:Bullet:DoPhysics");
PStatCollector BulletWorld::_pstat_simulation("App:Bullet:DoPhysics:Simulation");
PStatCollector BulletWorld::_pstat_p2b("App:Bullet:DoPhysics:SyncP2B");
PStatCollector BulletWorld::_pstat_b2p("App:Bullet:DoPhysics:SyncB2P");
PT(CallbackObject) bullet_contact_added_callback;
/**
*
*/
BulletWorld::
BulletWorld() {
// Init groups filter matrix
for (int i=0; i<32; i++) {
_filter_cb2._collide[i].clear();
_filter_cb2._collide[i].set_bit(i);
}
// Broadphase
btScalar dx(bullet_sap_extents);
btVector3 extents(dx, dx, dx);
switch (bullet_broadphase_algorithm) {
case BA_sweep_and_prune:
_broadphase = new btAxisSweep3(extents, extents, 1024);
break;
case BA_dynamic_aabb_tree:
_broadphase = new btDbvtBroadphase();
break;
default:
bullet_cat.error() << "no proper broadphase algorithm!" << endl;
}
nassertv(_broadphase);
// Configuration
_configuration = new btSoftBodyRigidBodyCollisionConfiguration();
nassertv(_configuration);
// Dispatcher
_dispatcher = new btCollisionDispatcher(_configuration);
nassertv(_dispatcher);
// Solver
_solver = new btSequentialImpulseConstraintSolver;
nassertv(_solver);
// World
_world = new btSoftRigidDynamicsWorld(_dispatcher, _broadphase, _solver, _configuration);
nassertv(_world);
nassertv(_world->getPairCache());
_world->setWorldUserInfo(this);
_world->setGravity(btVector3(0.0f, 0.0f, 0.0f));
// Ghost-pair callback
_world->getPairCache()->setInternalGhostPairCallback(&_ghost_cb);
// Filter callback
switch (bullet_filter_algorithm) {
case FA_mask:
_filter_cb = &_filter_cb1;
break;
case FA_groups_mask:
_filter_cb = &_filter_cb2;
break;
case FA_callback:
_filter_cb = &_filter_cb3;
break;
default:
bullet_cat.error() << "no proper filter algorithm!" << endl;
_filter_cb = NULL;
}
_world->getPairCache()->setOverlapFilterCallback(_filter_cb);
// Tick callback
_tick_callback_obj = NULL;
// SoftBodyWorldInfo
_info.m_dispatcher = _dispatcher;
_info.m_broadphase = _broadphase;
_info.m_gravity.setValue(0.0f, 0.0f, 0.0f);
_info.m_sparsesdf.Initialize();
// Register GIMPACT algorithm
btGImpactCollisionAlgorithm::registerAlgorithm(_dispatcher);
// Some prefered settings
_world->getDispatchInfo().m_enableSPU = true; // default: true
_world->getDispatchInfo().m_useContinuous = true; // default: true
_world->getSolverInfo().m_splitImpulse = false; // default: false
_world->getSolverInfo().m_numIterations = bullet_solver_iterations;
}
/**
*
*/
LightMutex &BulletWorld::
get_global_lock() {
static LightMutex lock;
return lock;
}
/**
*
*/
BulletSoftBodyWorldInfo BulletWorld::
get_world_info() {
return BulletSoftBodyWorldInfo(_info);
}
/**
*
*/
void BulletWorld::
set_debug_node(BulletDebugNode *node) {
LightMutexHolder holder(get_global_lock());
nassertv(node);
if (node != _debug) {
if (_debug != nullptr) {
_debug->_debug_stale = false;
_debug->_debug_world = nullptr;
}
_debug = node;
_world->setDebugDrawer(&(_debug->_drawer));
}
}
/**
* Removes a debug node that has been assigned to this BulletWorld.
*/
void BulletWorld::
clear_debug_node() {
LightMutexHolder holder(get_global_lock());
if (_debug != nullptr) {
_debug->_debug_stale = false;
_debug->_debug_world = nullptr;
_world->setDebugDrawer(nullptr);
_debug = nullptr;
}
}
/**
*
*/
void BulletWorld::
set_gravity(const LVector3 &gravity) {
LightMutexHolder holder(get_global_lock());
_world->setGravity(LVecBase3_to_btVector3(gravity));
_info.m_gravity.setValue(gravity.get_x(), gravity.get_y(), gravity.get_z());
}
/**
*
*/
void BulletWorld::
set_gravity(PN_stdfloat gx, PN_stdfloat gy, PN_stdfloat gz) {
LightMutexHolder holder(get_global_lock());
_world->setGravity(btVector3((btScalar)gx, (btScalar)gy, (btScalar)gz));
_info.m_gravity.setValue((btScalar)gx, (btScalar)gy, (btScalar)gz);
}
/**
*
*/
const LVector3 BulletWorld::
get_gravity() const {
LightMutexHolder holder(get_global_lock());
return btVector3_to_LVector3(_world->getGravity());
}
/**
*
*/
int BulletWorld::
do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) {
LightMutexHolder holder(get_global_lock());
_pstat_physics.start();
int num_substeps = clamp(int(dt / stepsize), 1, max_substeps);
// Synchronize Panda to Bullet
_pstat_p2b.start();
do_sync_p2b(dt, num_substeps);
_pstat_p2b.stop();
// Simulation
_pstat_simulation.start();
int n = _world->stepSimulation((btScalar)dt, max_substeps, (btScalar)stepsize);
_pstat_simulation.stop();
// Synchronize Bullet to Panda
_pstat_b2p.start();
do_sync_b2p();
_info.m_sparsesdf.GarbageCollect(bullet_gc_lifetime);
_pstat_b2p.stop();
// Render debug
if (_debug) {
_debug->do_sync_b2p(_world);
}
_pstat_physics.stop();
return n;
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_sync_p2b(PN_stdfloat dt, int num_substeps) {
for (int i=0; i < _bodies.size(); i++) {
_bodies[i]->do_sync_p2b();
}
for (int i=0; i < _softbodies.size(); i++) {
_softbodies[i]->do_sync_p2b();
}
for (int i=0; i < _ghosts.size(); i++) {
_ghosts[i]->do_sync_p2b();
}
for (int i=0; i < _characters.size(); i++) {
_characters[i]->do_sync_p2b(dt, num_substeps);
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_sync_b2p() {
for (int i=0; i < _vehicles.size(); i++) {
_vehicles[i]->do_sync_b2p();
}
for (int i=0; i < _bodies.size(); i++) {
_bodies[i]->do_sync_b2p();
}
for (int i=0; i < _softbodies.size(); i++) {
_softbodies[i]->do_sync_b2p();
}
for (int i=0; i < _ghosts.size(); i++) {
_ghosts[i]->do_sync_b2p();
}
for (int i=0; i < _characters.size(); i++) {
_characters[i]->do_sync_b2p();
}
}
/**
*
*/
void BulletWorld::
attach(TypedObject *object) {
LightMutexHolder holder(get_global_lock());
if (object->is_of_type(BulletGhostNode::get_class_type())) {
do_attach_ghost(DCAST(BulletGhostNode, object));
}
else if (object->is_of_type(BulletRigidBodyNode::get_class_type())) {
do_attach_rigid_body(DCAST(BulletRigidBodyNode, object));
}
else if (object->is_of_type(BulletSoftBodyNode::get_class_type())) {
do_attach_soft_body(DCAST(BulletSoftBodyNode, object));
}
else if (object->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
do_attach_character(DCAST(BulletBaseCharacterControllerNode, object));
}
else if (object->is_of_type(BulletVehicle::get_class_type())) {
do_attach_vehicle(DCAST(BulletVehicle, object));
}
else if (object->is_of_type(BulletConstraint::get_class_type())) {
do_attach_constraint(DCAST(BulletConstraint, object));
}
else {
bullet_cat->error() << "not a bullet world object!" << endl;
}
}
/**
*
*/
void BulletWorld::
remove(TypedObject *object) {
LightMutexHolder holder(get_global_lock());
if (object->is_of_type(BulletGhostNode::get_class_type())) {
do_remove_ghost(DCAST(BulletGhostNode, object));
}
else if (object->is_of_type(BulletRigidBodyNode::get_class_type())) {
do_remove_rigid_body(DCAST(BulletRigidBodyNode, object));
}
else if (object->is_of_type(BulletSoftBodyNode::get_class_type())) {
do_remove_soft_body(DCAST(BulletSoftBodyNode, object));
}
else if (object->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
do_remove_character(DCAST(BulletBaseCharacterControllerNode, object));
}
else if (object->is_of_type(BulletVehicle::get_class_type())) {
do_remove_vehicle(DCAST(BulletVehicle, object));
}
else if (object->is_of_type(BulletConstraint::get_class_type())) {
do_remove_constraint(DCAST(BulletConstraint, object));
}
else {
bullet_cat->error() << "not a bullet world object!" << endl;
}
}
/**
* Deprecated! Please use BulletWorld::attach
*/
void BulletWorld::
attach_rigid_body(BulletRigidBodyNode *node) {
LightMutexHolder holder(get_global_lock());
do_attach_rigid_body(node);
}
/**
* Deprecated.! Please use BulletWorld::remove
*/
void BulletWorld::
remove_rigid_body(BulletRigidBodyNode *node) {
LightMutexHolder holder(get_global_lock());
do_remove_rigid_body(node);
}
/**
* Deprecated! Please use BulletWorld::attach
*/
void BulletWorld::
attach_soft_body(BulletSoftBodyNode *node) {
LightMutexHolder holder(get_global_lock());
do_attach_soft_body(node);
}
/**
* Deprecated.! Please use BulletWorld::remove
*/
void BulletWorld::
remove_soft_body(BulletSoftBodyNode *node) {
LightMutexHolder holder(get_global_lock());
do_remove_soft_body(node);
}
/**
* Deprecated! Please use BulletWorld::attach
*/
void BulletWorld::
attach_ghost(BulletGhostNode *node) {
LightMutexHolder holder(get_global_lock());
do_attach_ghost(node);
}
/**
* Deprecated.! Please use BulletWorld::remove
*/
void BulletWorld::
remove_ghost(BulletGhostNode *node) {
LightMutexHolder holder(get_global_lock());
do_remove_ghost(node);
}
/**
* Deprecated! Please use BulletWorld::attach
*/
void BulletWorld::
attach_character(BulletBaseCharacterControllerNode *node) {
LightMutexHolder holder(get_global_lock());
do_attach_character(node);
}
/**
* Deprecated.! Please use BulletWorld::remove
*/
void BulletWorld::
remove_character(BulletBaseCharacterControllerNode *node) {
LightMutexHolder holder(get_global_lock());
do_remove_character(node);
}
/**
* Deprecated! Please use BulletWorld::attach
*/
void BulletWorld::
attach_vehicle(BulletVehicle *vehicle) {
LightMutexHolder holder(get_global_lock());
do_attach_vehicle(vehicle);
}
/**
* Deprecated.! Please use BulletWorld::remove
*/
void BulletWorld::
remove_vehicle(BulletVehicle *vehicle) {
LightMutexHolder holder(get_global_lock());
do_remove_vehicle(vehicle);
}
/**
* Attaches a single constraint to a world. Collision checks between the
* linked objects will be disabled if the second parameter is set to TRUE.
*/
void BulletWorld::
attach_constraint(BulletConstraint *constraint, bool linked_collision) {
LightMutexHolder holder(get_global_lock());
do_attach_constraint(constraint, linked_collision);
}
/**
* Deprecated.! Please use BulletWorld::remove
*/
void BulletWorld::
remove_constraint(BulletConstraint *constraint) {
LightMutexHolder holder(get_global_lock());
do_remove_constraint(constraint);
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_attach_rigid_body(BulletRigidBodyNode *node) {
nassertv(node);
btRigidBody *ptr = btRigidBody::upcast(node->get_object());
BulletRigidBodies::iterator found;
PT(BulletRigidBodyNode) ptnode = node;
found = find(_bodies.begin(), _bodies.end(), ptnode);
if (found == _bodies.end()) {
_bodies.push_back(node);
_world->addRigidBody(ptr);
}
else {
bullet_cat.warning() << "rigid body already attached" << endl;
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_remove_rigid_body(BulletRigidBodyNode *node) {
nassertv(node);
btRigidBody *ptr = btRigidBody::upcast(node->get_object());
BulletRigidBodies::iterator found;
PT(BulletRigidBodyNode) ptnode = node;
found = find(_bodies.begin(), _bodies.end(), ptnode);
if (found == _bodies.end()) {
bullet_cat.warning() << "rigid body not attached" << endl;
}
else {
_bodies.erase(found);
_world->removeRigidBody(ptr);
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_attach_soft_body(BulletSoftBodyNode *node) {
nassertv(node);
btSoftBody *ptr = btSoftBody::upcast(node->get_object());
// TODO: groupfilter settings (see ghost objects too)
short group = btBroadphaseProxy::DefaultFilter;
short mask = btBroadphaseProxy::AllFilter;
BulletSoftBodies::iterator found;
PT(BulletSoftBodyNode) ptnode = node;
found = find(_softbodies.begin(), _softbodies.end(), ptnode);
if (found == _softbodies.end()) {
_softbodies.push_back(node);
_world->addSoftBody(ptr, group, mask);
}
else {
bullet_cat.warning() << "soft body already attached" << endl;
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_remove_soft_body(BulletSoftBodyNode *node) {
nassertv(node);
btSoftBody *ptr = btSoftBody::upcast(node->get_object());
BulletSoftBodies::iterator found;
PT(BulletSoftBodyNode) ptnode = node;
found = find(_softbodies.begin(), _softbodies.end(), ptnode);
if (found == _softbodies.end()) {
bullet_cat.warning() << "soft body not attached" << endl;
}
else {
_softbodies.erase(found);
_world->removeSoftBody(ptr);
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_attach_ghost(BulletGhostNode *node) {
nassertv(node);
// TODO groupfilter settings...
/*
enum CollisionFilterGroups {
DefaultFilter = 1,
StaticFilter = 2,
KinematicFilter = 4,
DebrisFilter = 8,
SensorTrigger = 16,
CharacterFilter = 32,
AllFilter = -1
}
*/
short group = btBroadphaseProxy::SensorTrigger;
short mask = btBroadphaseProxy::AllFilter
& ~btBroadphaseProxy::StaticFilter
& ~btBroadphaseProxy::SensorTrigger;
btGhostObject *ptr = btGhostObject::upcast(node->get_object());
BulletGhosts::iterator found;
PT(BulletGhostNode) ptnode = node;
found = find(_ghosts.begin(), _ghosts.end(), ptnode);
if (found == _ghosts.end()) {
_ghosts.push_back(node);
_world->addCollisionObject(ptr, group, mask);
}
else {
bullet_cat.warning() << "ghost already attached" << endl;
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_remove_ghost(BulletGhostNode *node) {
nassertv(node);
btGhostObject *ptr = btGhostObject::upcast(node->get_object());
BulletGhosts::iterator found;
PT(BulletGhostNode) ptnode = node;
found = find(_ghosts.begin(), _ghosts.end(), ptnode);
if (found == _ghosts.end()) {
bullet_cat.warning() << "ghost not attached" << endl;
}
else {
_ghosts.erase(found);
_world->removeCollisionObject(ptr);
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_attach_character(BulletBaseCharacterControllerNode *node) {
nassertv(node);
BulletCharacterControllers::iterator found;
PT(BulletBaseCharacterControllerNode) ptnode = node;
found = find(_characters.begin(), _characters.end(), ptnode);
if (found == _characters.end()) {
_characters.push_back(node);
_world->addCollisionObject(node->get_ghost(),
btBroadphaseProxy::CharacterFilter,
btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);
_world->addCharacter(node->get_character());
}
else {
bullet_cat.warning() << "character already attached" << endl;
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_remove_character(BulletBaseCharacterControllerNode *node) {
nassertv(node);
BulletCharacterControllers::iterator found;
PT(BulletBaseCharacterControllerNode) ptnode = node;
found = find(_characters.begin(), _characters.end(), ptnode);
if (found == _characters.end()) {
bullet_cat.warning() << "character not attached" << endl;
}
else {
_characters.erase(found);
_world->removeCollisionObject(node->get_ghost());
_world->removeCharacter(node->get_character());
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_attach_vehicle(BulletVehicle *vehicle) {
nassertv(vehicle);
BulletVehicles::iterator found;
PT(BulletVehicle) ptvehicle = vehicle;
found = find(_vehicles.begin(), _vehicles.end(), ptvehicle);
if (found == _vehicles.end()) {
_vehicles.push_back(vehicle);
_world->addVehicle(vehicle->get_vehicle());
}
else {
bullet_cat.warning() << "vehicle already attached" << endl;
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_remove_vehicle(BulletVehicle *vehicle) {
nassertv(vehicle);
do_remove_rigid_body(vehicle->do_get_chassis());
BulletVehicles::iterator found;
PT(BulletVehicle) ptvehicle = vehicle;
found = find(_vehicles.begin(), _vehicles.end(), ptvehicle);
if (found == _vehicles.end()) {
bullet_cat.warning() << "vehicle not attached" << endl;
}
else {
_vehicles.erase(found);
_world->removeVehicle(vehicle->get_vehicle());
}
}
/**
* Attaches a single constraint to a world. Collision checks between the
* linked objects will be disabled if the second parameter is set to TRUE.
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_attach_constraint(BulletConstraint *constraint, bool linked_collision) {
nassertv(constraint);
BulletConstraints::iterator found;
PT(BulletConstraint) ptconstraint = constraint;
found = find(_constraints.begin(), _constraints.end(), ptconstraint);
if (found == _constraints.end()) {
_constraints.push_back(constraint);
_world->addConstraint(constraint->ptr(), linked_collision);
}
else {
bullet_cat.warning() << "constraint already attached" << endl;
}
}
/**
* Assumes the lock(bullet global lock) is held by the caller
*/
void BulletWorld::
do_remove_constraint(BulletConstraint *constraint) {
nassertv(constraint);
BulletConstraints::iterator found;
PT(BulletConstraint) ptconstraint = constraint;
found = find(_constraints.begin(), _constraints.end(), ptconstraint);
if (found == _constraints.end()) {
bullet_cat.warning() << "constraint not attached" << endl;
}
else {
_constraints.erase(found);
_world->removeConstraint(constraint->ptr());
}
}
/**
*
*/
int BulletWorld::
get_num_rigid_bodies() const {
LightMutexHolder holder(get_global_lock());
return _bodies.size();
}
/**
*
*/
BulletRigidBodyNode *BulletWorld::
get_rigid_body(int idx) const {
LightMutexHolder holder(get_global_lock());
nassertr(idx >= 0 && idx < (int)_bodies.size(), NULL);
return _bodies[idx];
}
/**
*
*/
int BulletWorld::
get_num_soft_bodies() const {
LightMutexHolder holder(get_global_lock());
return _softbodies.size();
}
/**
*
*/
BulletSoftBodyNode *BulletWorld::
get_soft_body(int idx) const {
LightMutexHolder holder(get_global_lock());
nassertr(idx >= 0 && idx < (int)_softbodies.size(), NULL);
return _softbodies[idx];
}
/**
*
*/
int BulletWorld::
get_num_ghosts() const {
LightMutexHolder holder(get_global_lock());
return _ghosts.size();
}
/**
*
*/
BulletGhostNode *BulletWorld::
get_ghost(int idx) const {
LightMutexHolder holder(get_global_lock());
nassertr(idx >= 0 && idx < (int)_ghosts.size(), NULL);
return _ghosts[idx];
}
/**
*
*/
int BulletWorld::
get_num_characters() const {
LightMutexHolder holder(get_global_lock());
return _characters.size();
}
/**
*
*/
BulletBaseCharacterControllerNode *BulletWorld::
get_character(int idx) const {
LightMutexHolder holder(get_global_lock());
nassertr(idx >= 0 && idx < (int)_characters.size(), NULL);
return _characters[idx];
}
/**
*
*/
int BulletWorld::
get_num_vehicles() const {
LightMutexHolder holder(get_global_lock());
return _vehicles.size();
}
/**
*
*/
BulletVehicle *BulletWorld::
get_vehicle(int idx) const {
LightMutexHolder holder(get_global_lock());
nassertr(idx >= 0 && idx < (int)_vehicles.size(), NULL);
return _vehicles[idx];
}
/**
*
*/
int BulletWorld::
get_num_constraints() const {
LightMutexHolder holder(get_global_lock());
return _constraints.size();
}
/**
*
*/
BulletConstraint *BulletWorld::
get_constraint(int idx) const {
LightMutexHolder holder(get_global_lock());
nassertr(idx >= 0 && idx < (int)_constraints.size(), NULL);
return _constraints[idx];
}
/**
*
*/
int BulletWorld::
get_num_manifolds() const {
LightMutexHolder holder(get_global_lock());
return _world->getDispatcher()->getNumManifolds();
}
/**
*
*/
BulletClosestHitRayResult BulletWorld::
ray_test_closest(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask &mask) const {
LightMutexHolder holder(get_global_lock());
nassertr(!from_pos.is_nan(), BulletClosestHitRayResult::empty());
nassertr(!to_pos.is_nan(), BulletClosestHitRayResult::empty());
const btVector3 from = LVecBase3_to_btVector3(from_pos);
const btVector3 to = LVecBase3_to_btVector3(to_pos);
BulletClosestHitRayResult cb(from, to, mask);
_world->rayTest(from, to, cb);
return cb;
}
/**
*
*/
BulletAllHitsRayResult BulletWorld::
ray_test_all(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask &mask) const {
LightMutexHolder holder(get_global_lock());
nassertr(!from_pos.is_nan(), BulletAllHitsRayResult::empty());
nassertr(!to_pos.is_nan(), BulletAllHitsRayResult::empty());
const btVector3 from = LVecBase3_to_btVector3(from_pos);
const btVector3 to = LVecBase3_to_btVector3(to_pos);
BulletAllHitsRayResult cb(from, to, mask);
_world->rayTest(from, to, cb);
return cb;
}
/**
*
*/
BulletClosestHitSweepResult BulletWorld::
sweep_test_closest(BulletShape *shape, const TransformState &from_ts, const TransformState &to_ts, const CollideMask &mask, PN_stdfloat penetration) const {
LightMutexHolder holder(get_global_lock());
nassertr(shape, BulletClosestHitSweepResult::empty());
const btConvexShape *convex = (const btConvexShape *) shape->ptr();
nassertr(convex->isConvex(), BulletClosestHitSweepResult::empty());
nassertr(!from_ts.is_invalid(), BulletClosestHitSweepResult::empty());
nassertr(!to_ts.is_invalid(), BulletClosestHitSweepResult::empty());
const btVector3 from_pos = LVecBase3_to_btVector3(from_ts.get_pos());
const btVector3 to_pos = LVecBase3_to_btVector3(to_ts.get_pos());
const btTransform from_trans = LMatrix4_to_btTrans(from_ts.get_mat());
const btTransform to_trans = LMatrix4_to_btTrans(to_ts.get_mat());
BulletClosestHitSweepResult cb(from_pos, to_pos, mask);
_world->convexSweepTest(convex, from_trans, to_trans, cb, penetration);
return cb;
}
/**
* Performs a test if two bodies should collide or not, based on the collision
* filter setting.
*/
bool BulletWorld::
filter_test(PandaNode *node0, PandaNode *node1) const {
LightMutexHolder holder(get_global_lock());
nassertr(node0, false);
nassertr(node1, false);
nassertr(_filter_cb, false);
btCollisionObject *obj0 = get_collision_object(node0);
btCollisionObject *obj1 = get_collision_object(node1);
nassertr(obj0, false);
nassertr(obj1, false);
btBroadphaseProxy *proxy0 = obj0->getBroadphaseHandle();
btBroadphaseProxy *proxy1 = obj1->getBroadphaseHandle();
nassertr(proxy0, false);
nassertr(proxy1, false);
return _filter_cb->needBroadphaseCollision(proxy0, proxy1);
}
/**
* Performas a test for all bodies which are currently in contact with the
* given body. The test returns a BulletContactResult object which may
* contain zero, one or more contacts.
*
* If the optional parameter use_filter is set to TRUE this test will consider
* filter settings. Otherwise all objects in contact are reported, no matter
* if they would collide or not.
*/
BulletContactResult BulletWorld::
contact_test(PandaNode *node, bool use_filter) const {
LightMutexHolder holder(get_global_lock());
btCollisionObject *obj = get_collision_object(node);
BulletContactResult cb;
if (obj) {
#if BT_BULLET_VERSION >= 281
if (use_filter) {
cb.use_filter(_filter_cb, obj->getBroadphaseHandle());
}
#endif
_world->contactTest(obj, cb);
}
return cb;
}
/**
* Performas a test if the two bodies given as parameters are in contact or
* not. The test returns a BulletContactResult object which may contain zero
* or one contacts.
*/
BulletContactResult BulletWorld::
contact_test_pair(PandaNode *node0, PandaNode *node1) const {
LightMutexHolder holder(get_global_lock());
btCollisionObject *obj0 = get_collision_object(node0);
btCollisionObject *obj1 = get_collision_object(node1);
BulletContactResult cb;
if (obj0 && obj1) {
_world->contactPairTest(obj0, obj1, cb);
}
return cb;
}
/**
*
*/
BulletPersistentManifold *BulletWorld::
get_manifold(int idx) const {
LightMutexHolder holder(get_global_lock());
nassertr(idx < get_num_manifolds(), NULL);
btPersistentManifold *ptr = _dispatcher->getManifoldByIndexInternal(idx);
return (ptr) ? new BulletPersistentManifold(ptr) : NULL;
}
/**
*
*/
btCollisionObject *BulletWorld::
get_collision_object(PandaNode *node) {
if (node->is_of_type(BulletRigidBodyNode::get_class_type())) {
return ((BulletRigidBodyNode *)node)->get_object();
}
else if (node->is_of_type(BulletGhostNode::get_class_type())) {
return ((BulletGhostNode *)node)->get_object();
}
else if (node->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) {
return ((BulletBaseCharacterControllerNode *)node)->get_ghost();
}
else if (node->is_of_type(BulletSoftBodyNode::get_class_type())) {
return ((BulletSoftBodyNode *)node)->get_object();
}
return NULL;
}
/**
*
*/
void BulletWorld::
set_group_collision_flag(unsigned int group1, unsigned int group2, bool enable) {
LightMutexHolder holder(get_global_lock());
if (bullet_filter_algorithm != FA_groups_mask) {
bullet_cat.warning() << "filter algorithm is not 'groups-mask'" << endl;
}
_filter_cb2._collide[group1].set_bit_to(group2, enable);
_filter_cb2._collide[group2].set_bit_to(group1, enable);
}
/**
*
*/
bool BulletWorld::
get_group_collision_flag(unsigned int group1, unsigned int group2) const {
LightMutexHolder holder(get_global_lock());
return _filter_cb2._collide[group1].get_bit(group2);
}
/**
*
*/
void BulletWorld::
set_force_update_all_aabbs(bool force) {
LightMutexHolder holder(get_global_lock());
_world->setForceUpdateAllAabbs(force);
}
/**
*
*/
bool BulletWorld::
get_force_update_all_aabbs() const {
LightMutexHolder holder(get_global_lock());
return _world->getForceUpdateAllAabbs();
}
/**
*
*/
void BulletWorld::
set_contact_added_callback(CallbackObject *obj) {
LightMutexHolder holder(get_global_lock());
_world->getSolverInfo().m_solverMode |= SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION;
_world->getSolverInfo().m_solverMode |= SOLVER_USE_2_FRICTION_DIRECTIONS;
_world->getSolverInfo().m_solverMode |= SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
bullet_contact_added_callback = obj;
}
/**
*
*/
void BulletWorld::
clear_contact_added_callback() {
LightMutexHolder holder(get_global_lock());
_world->getSolverInfo().m_solverMode &= ~SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION;
_world->getSolverInfo().m_solverMode &= ~SOLVER_USE_2_FRICTION_DIRECTIONS;
_world->getSolverInfo().m_solverMode &= ~SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
bullet_contact_added_callback = NULL;
}
/**
*
*/
void BulletWorld::
set_tick_callback(CallbackObject *obj, bool is_pretick) {
LightMutexHolder holder(get_global_lock());
nassertv(obj != NULL);
_tick_callback_obj = obj;
_world->setInternalTickCallback(&BulletWorld::tick_callback, this, is_pretick);
}
/**
*
*/
void BulletWorld::
clear_tick_callback() {
LightMutexHolder holder(get_global_lock());
_tick_callback_obj = NULL;
_world->setInternalTickCallback(NULL);
}
/**
*
*/
void BulletWorld::
tick_callback(btDynamicsWorld *world, btScalar timestep) {
nassertv(world->getWorldUserInfo());
BulletWorld *w = static_cast<BulletWorld *>(world->getWorldUserInfo());
CallbackObject *obj = w->_tick_callback_obj;
if (obj) {
BulletTickCallbackData cbdata(timestep);
obj->do_callback(&cbdata);
}
}
/**
*
*/
void BulletWorld::
set_filter_callback(CallbackObject *obj) {
LightMutexHolder holder(get_global_lock());
nassertv(obj != NULL);
if (bullet_filter_algorithm != FA_callback) {
bullet_cat.warning() << "filter algorithm is not 'callback'" << endl;
}
_filter_cb3._filter_callback_obj = obj;
}
/**
*
*/
void BulletWorld::
clear_filter_callback() {
LightMutexHolder holder(get_global_lock());
_filter_cb3._filter_callback_obj = NULL;
}
/**
*
*/
bool BulletWorld::btFilterCallback1::
needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const {
btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
nassertr(obj0, false);
nassertr(obj1, false);
PandaNode *node0 = (PandaNode *) obj0->getUserPointer();
PandaNode *node1 = (PandaNode *) obj1->getUserPointer();
nassertr(node0, false);
nassertr(node1, false);
CollideMask mask0 = node0->get_into_collide_mask();
CollideMask mask1 = node1->get_into_collide_mask();
return (mask0 & mask1) != 0;
}
/**
*
*/
bool BulletWorld::btFilterCallback2::
needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const {
btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
nassertr(obj0, false);
nassertr(obj1, false);
PandaNode *node0 = (PandaNode *) obj0->getUserPointer();
PandaNode *node1 = (PandaNode *) obj1->getUserPointer();
nassertr(node0, false);
nassertr(node1, false);
CollideMask mask0 = node0->get_into_collide_mask();
CollideMask mask1 = node1->get_into_collide_mask();
// cout << mask0 << " " << mask1 << endl;
for (int i=0; i<32; i++) {
if (mask0.get_bit(i)) {
if ((_collide[i] & mask1) != 0)
// cout << "collide: i=" << i << " _collide[i]" << _collide[i] << endl;
return true;
}
}
return false;
}
/**
*
*/
bool BulletWorld::btFilterCallback3::
needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const {
nassertr(_filter_callback_obj, false);
btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject;
btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject;
nassertr(obj0, false);
nassertr(obj1, false);
PandaNode *node0 = (PandaNode *) obj0->getUserPointer();
PandaNode *node1 = (PandaNode *) obj1->getUserPointer();
nassertr(node0, false);
nassertr(node1, false);
BulletFilterCallbackData cbdata(node0, node1);
_filter_callback_obj->do_callback(&cbdata);
return cbdata.get_collide();
}
/**
*
*/
ostream &
operator << (ostream &out, BulletWorld::BroadphaseAlgorithm algorithm) {
switch (algorithm) {
case BulletWorld::BA_sweep_and_prune:
return out << "sap";
case BulletWorld::BA_dynamic_aabb_tree:
return out << "aabb";
};
return out << "**invalid BulletWorld::BroadphaseAlgorithm(" << (int)algorithm << ")**";
}
/**
*
*/
istream &
operator >> (istream &in, BulletWorld::BroadphaseAlgorithm &algorithm) {
string word;
in >> word;
if (word == "sap") {
algorithm = BulletWorld::BA_sweep_and_prune;
}
else if (word == "aabb") {
algorithm = BulletWorld::BA_dynamic_aabb_tree;
}
else {
bullet_cat.error()
<< "Invalid BulletWorld::BroadphaseAlgorithm: " << word << "\n";
algorithm = BulletWorld::BA_dynamic_aabb_tree;
}
return in;
}
/**
*
*/
ostream &
operator << (ostream &out, BulletWorld::FilterAlgorithm algorithm) {
switch (algorithm) {
case BulletWorld::FA_mask:
return out << "mask";
case BulletWorld::FA_groups_mask:
return out << "groups-mask";
case BulletWorld::FA_callback:
return out << "callback";
};
return out << "**invalid BulletWorld::FilterAlgorithm(" << (int)algorithm << ")**";
}
/**
*
*/
istream &
operator >> (istream &in, BulletWorld::FilterAlgorithm &algorithm) {
string word;
in >> word;
if (word == "mask") {
algorithm = BulletWorld::FA_mask;
}
else if (word == "groups-mask") {
algorithm = BulletWorld::FA_groups_mask;
}
else if (word == "callback") {
algorithm = BulletWorld::FA_callback;
}
else {
bullet_cat.error()
<< "Invalid BulletWorld::FilterAlgorithm: " << word << "\n";
algorithm = BulletWorld::FA_mask;
}
return in;
}