Files
bullet3/Demos3/BasicGpuDemo/b3GpuDynamicsWorld.cpp
erwin coumans 69e5454d18 Add the old Bullet 2.x obsolete demos, and CMake buildsystem files, and gradually move them to newer Bullet 3.x structure
Use statically linked freeglut, instead of dynamic glut for the obsolete Bullet 2.x demos
Add the 'reset' method to b3GpuDynamicsWorld, and use it in the BasicGpuDemo (pretty slow in debug mode, use release mode)
Don't crash in btCollisionWorld, if there is no collision dispatcher
2013-12-19 12:40:59 -08:00

711 lines
21 KiB
C++

#include "b3GpuDynamicsWorld.h"
#include "BulletDynamics/Dynamics/btRigidBody.h"
//#include "../../../opencl/gpu_rigidbody_pipeline2/CLPhysicsDemo.h"
//#include "../../../opencl/gpu_rigidbody_pipeline/b3GpuNarrowPhaseAndSolver.h"
#include "BulletCollision/CollisionShapes/btPolyhedralConvexShape.h"
#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h"
#include "BulletCollision/CollisionShapes/btCompoundShape.h"
#include "BulletCollision/CollisionShapes/btSphereShape.h"
#include "BulletCollision/CollisionShapes/btStaticPlaneShape.h"
#include "BulletCollision/CollisionShapes/btConvexHullShape.h"
#include "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h"
#include "BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h"
#include "LinearMath/btQuickprof.h"
//#include "Bullet3Common/b3Logging.h"
#include "Bullet3OpenCL/BroadphaseCollision/b3GpuSapBroadphase.h"
#include "Bullet3OpenCL/RigidBody/b3GpuNarrowPhase.h"
#include "Bullet3OpenCL/RigidBody/b3GpuRigidBodyPipeline.h"
#include "Bullet3Dynamics/ConstraintSolver/b3Point2PointConstraint.h"
#include "Bullet3Dynamics/ConstraintSolver/b3Generic6DofConstraint.h"
#include "Bullet3Collision/NarrowPhaseCollision/b3RigidBodyCL.h"
#ifdef _WIN32
#include <windows.h>
#endif
//#if (BT_BULLET_VERSION >= 282)
//#define BT_USE_BODY_UPDATE_REVISION
//#endif
b3GpuDynamicsWorld::b3GpuDynamicsWorld(class b3GpuSapBroadphase* bp,class b3GpuNarrowPhase* np, class b3GpuRigidBodyPipeline* rigidBodyPipeline)
:btDynamicsWorld(0,0,0),
m_gravity(0,-10,0),
m_cpuGpuSync(true),
m_bp(bp),
m_np(np),
m_rigidBodyPipeline(rigidBodyPipeline),
m_localTime(0.f),
m_staticBody(0)
{
btConvexHullShape* nullShape = new btConvexHullShape();
m_staticBody = new btRigidBody(0,0,nullShape);
addRigidBody(m_staticBody,0,0);
}
b3GpuDynamicsWorld::~b3GpuDynamicsWorld()
{
}
#include <Windows.h>
int b3GpuDynamicsWorld::stepSimulation( btScalar timeStepUnused, int maxSubStepsUnused, btScalar fixedTimeStep)
{
///Don't use more than 1 simulation step, it destroys the performance having to copy the data between CPU and GPU multiple times per frame
///Please use the CPU version in btDiscreteDynamicsWorld if you don't like this
CProfileManager::Reset();
BT_PROFILE("stepSimulation");
{
BT_PROFILE("sync constraints CPU");
//todo: determine what data has changed, or perform copy on GPU?
for (int i=0;i<m_constraints.size();i++)
{
btTypedConstraint* constraint = m_constraints[i];
b3TypedConstraint* c = (b3TypedConstraint*) constraint->getUserConstraintPtr();
if (c)
{
switch (constraint->getConstraintType())
{
case POINT2POINT_CONSTRAINT_TYPE:
{
btPoint2PointConstraint* p2 = (btPoint2PointConstraint*) constraint;
b3Point2PointConstraint* p3 = (b3Point2PointConstraint*) c;
p3->setPivotA((const b3Vector3&)p2->getPivotInA());
p3->setPivotB((const b3Vector3&)p2->getPivotInB());
p3->m_setting.m_damping = p2->m_setting.m_damping;
p3->m_setting.m_impulseClamp = p2->m_setting.m_impulseClamp;
p3->m_setting.m_tau = p2->m_setting.m_tau;
break;
};
case D6_CONSTRAINT_TYPE:
{
btGeneric6DofConstraint* dof2 = (btGeneric6DofConstraint*) constraint;
b3Generic6DofConstraint* dof3 = (b3Generic6DofConstraint*) c;
const b3RigidBodyCL* bodiesCL = m_np->getBodiesCpu();
b3Transform frameInA = (b3Transform&) dof2->getFrameOffsetA();
b3Transform frameInB = (b3Transform&) dof2->getFrameOffsetB();
dof3->setFrames(frameInA,frameInB,bodiesCL);
break;
}
default:
{
}
};
}
}
}
// detect any change (very simple)
{
B3_PROFILE("body update revision detection (CPU)");
#ifdef BT_USE_BODY_UPDATE_REVISION
b3Assert(m_bodyUpdateRevisions.size() == m_collisionObjects.size());
b3Assert(m_np->getNumRigidBodies() == m_bodyUpdateRevisions.size());
#endif //BT_USE_BODY_UPDATE_REVISION
b3RigidBodyCL* bodiesCL = (b3RigidBodyCL*)m_np->getBodiesCpu();
for (int i=0;i<this->m_collisionObjects.size();i++)
{
if (i>=m_np->getNumRigidBodies())
{
b3Error("bodiesCL out-of-range\n");
continue;
}
#ifdef BT_USE_BODY_UPDATE_REVISION
if (m_bodyUpdateRevisions[i] != m_collisionObjects[i]->getUpdateRevisionInternal())
#endif//BT_USE_BODY_UPDATE_REVISION
{
m_cpuGpuSync = true;
#ifdef BT_USE_BODY_UPDATE_REVISION
m_bodyUpdateRevisions[i] = m_collisionObjects[i]->getUpdateRevisionInternal();
#endif
btRigidBody* body = btRigidBody::upcast(m_collisionObjects[i]);
if (body)
{
b3Vector3 pos = (const b3Vector3&)m_collisionObjects[i]->getWorldTransform().getOrigin();
btQuaternion orn2 = m_collisionObjects[i]->getWorldTransform().getRotation();
b3Quaternion orn(orn2[0],orn2[1],orn2[2],orn2[3]);
body->integrateVelocities(fixedTimeStep);
m_np->setObjectTransformCpu(&pos[0],&orn[0],i);
b3Vector3 linVel = (const b3Vector3&)body->getLinearVelocity();
b3Vector3 angVel = (const b3Vector3&)body->getAngularVelocity();
m_np->setObjectVelocityCpu(&linVel[0],&angVel[0],i);
}
}
}
}
if (m_cpuGpuSync)
{
BT_PROFILE("cpuGpuSync");
m_cpuGpuSync = false;
m_np->writeAllBodiesToGpu();
m_bp->writeAabbsToGpu();
m_rigidBodyPipeline->writeAllInstancesToGpu();
}
//internalSingleStepSimulation(fixedTimeStep);
// dispatch preTick callback
if(0 != m_internalPreTickCallback)
{
BT_PROFILE("internalPreTickCallback");
(*m_internalPreTickCallback)(this, fixedTimeStep);
}
{
BT_PROFILE("m_rigidBodyPipeline->stepSimulation");
m_rigidBodyPipeline->stepSimulation(fixedTimeStep);
}
{
BT_PROFILE("readbackBodiesToCpu");
//now copy info back to rigid bodies....
m_np->readbackAllBodiesToCpu();
}
{
BT_PROFILE("scatter transforms into rigidbody (CPU)");
const b3RigidBodyCL* bodiesCL = m_np->getBodiesCpu();
for (int i=0;i<this->m_collisionObjects.size();i++)
{
btVector3 pos;
btQuaternion orn;
if (m_np->getObjectTransformFromCpu(&pos[0],&orn[0],i))
{
btTransform newTrans;
newTrans.setOrigin(pos);
newTrans.setRotation(orn);
btCollisionObject* colObj = this->m_collisionObjects[i];
colObj->setWorldTransform(newTrans);
btRigidBody* body = btRigidBody::upcast(m_collisionObjects[i]);
if (body)
{
body->setLinearVelocity((btVector3&)bodiesCL[i].m_linVel);
body->setAngularVelocity((btVector3&)bodiesCL[i].m_angVel);
}
}
#ifdef BT_USE_BODY_UPDATE_REVISION
//ignore this revision update
m_bodyUpdateRevisions[i] = m_collisionObjects[i]->getUpdateRevisionInternal();
#endif //BT_USE_BODY_UPDATE_REVISION
}
{
BT_PROFILE("synchronizeMotionStates");
synchronizeMotionStates();
}
}
{
B3_PROFILE("clearForces");
clearForces();
}
#ifndef BT_NO_PROFILE
CProfileManager::Increment_Frame_Counter();
// CProfileManager::dumpAll();
#endif //BT_NO_PROFILE
return 1;
}
void b3GpuDynamicsWorld::clearForces()
{
///@todo: iterate over awake simulation islands!
for ( int i=0;i<m_collisionObjects.size();i++)
{
btRigidBody* body = btRigidBody::upcast(m_collisionObjects[i]);
//need to check if next line is ok
//it might break backward compatibility (people applying forces on sleeping objects get never cleared and accumulate on wake-up
if (body)
body->clearForces();
}
}
void b3GpuDynamicsWorld::setGravity(const btVector3& gravity)
{
m_gravity = gravity;
m_rigidBodyPipeline->setGravity(gravity);
}
int b3GpuDynamicsWorld::findOrRegisterCollisionShape(const btCollisionShape* colShape)
{
int index = m_uniqueShapes.findLinearSearch(colShape);
if (index==m_uniqueShapes.size())
{
if (colShape->isPolyhedral())
{
m_uniqueShapes.push_back(colShape);
btPolyhedralConvexShape* convex = (btPolyhedralConvexShape*)colShape;
int numVertices=convex->getNumVertices();
int strideInBytes=sizeof(btVector3);
btAlignedObjectArray<btVector3> tmpVertices;
tmpVertices.resize(numVertices);
for (int i=0;i<numVertices;i++)
convex->getVertex(i,tmpVertices[i]);
const float scaling[4]={1,1,1,1};
//bool noHeightField=true;
//int gpuShapeIndex = m_np->registerConvexHullShape(&tmpVertices[0].getX(), strideInBytes, numVertices, scaling);
const float* verts = numVertices? &tmpVertices[0].getX() : 0;
int gpuShapeIndex = m_np->registerConvexHullShape(verts,strideInBytes, numVertices, scaling);
m_uniqueShapeMapping.push_back(gpuShapeIndex);
} else
{
if (colShape->getShapeType()==TRIANGLE_MESH_SHAPE_PROXYTYPE)
{
m_uniqueShapes.push_back(colShape);
btBvhTriangleMeshShape* trimesh = (btBvhTriangleMeshShape*) colShape;
btStridingMeshInterface* meshInterface = trimesh->getMeshInterface();
b3AlignedObjectArray<b3Vector3> vertices;
b3AlignedObjectArray<int> indices;
btVector3 trimeshScaling(1,1,1);
for (int partId=0;partId<meshInterface->getNumSubParts();partId++)
{
const unsigned char *vertexbase = 0;
int numverts = 0;
PHY_ScalarType type = PHY_INTEGER;
int stride = 0;
const unsigned char *indexbase = 0;
int indexstride = 0;
int numfaces = 0;
PHY_ScalarType indicestype = PHY_INTEGER;
//PHY_ScalarType indexType=0;
b3Vector3 triangleVerts[3];
meshInterface->getLockedReadOnlyVertexIndexBase(&vertexbase,numverts, type,stride,&indexbase,indexstride,numfaces,indicestype,partId);
btVector3 aabbMin,aabbMax;
for (int triangleIndex = 0 ; triangleIndex < numfaces;triangleIndex++)
{
unsigned int* gfxbase = (unsigned int*)(indexbase+triangleIndex*indexstride);
for (int j=2;j>=0;j--)
{
int graphicsindex = indicestype==PHY_SHORT?((unsigned short*)gfxbase)[j]:gfxbase[j];
if (type == PHY_FLOAT)
{
float* graphicsbase = (float*)(vertexbase+graphicsindex*stride);
triangleVerts[j] = b3MakeVector3(
graphicsbase[0]*trimeshScaling.getX(),
graphicsbase[1]*trimeshScaling.getY(),
graphicsbase[2]*trimeshScaling.getZ());
}
else
{
double* graphicsbase = (double*)(vertexbase+graphicsindex*stride);
triangleVerts[j] = b3MakeVector3( btScalar(graphicsbase[0]*trimeshScaling.getX()),
btScalar(graphicsbase[1]*trimeshScaling.getY()),
btScalar(graphicsbase[2]*trimeshScaling.getZ()));
}
}
vertices.push_back(triangleVerts[0]);
vertices.push_back(triangleVerts[1]);
vertices.push_back(triangleVerts[2]);
indices.push_back(indices.size());
indices.push_back(indices.size());
indices.push_back(indices.size());
}
}
//GraphicsShape* gfxShape = 0;//b3BulletDataExtractor::createGraphicsShapeFromWavefrontObj(objData);
//GraphicsShape* gfxShape = b3BulletDataExtractor::createGraphicsShapeFromConvexHull(&sUnitSpherePoints[0],MY_UNITSPHERE_POINTS);
float meshScaling[4] = {1,1,1,1};
//int shapeIndex = renderer.registerShape(gfxShape->m_vertices,gfxShape->m_numvertices,gfxShape->m_indices,gfxShape->m_numIndices);
//float groundPos[4] = {0,0,0,0};
//renderer.registerGraphicsInstance(shapeIndex,groundPos,rotOrn,color,meshScaling);
if (vertices.size() && indices.size())
{
int gpuShapeIndex = m_np->registerConcaveMesh(&vertices,&indices, meshScaling);
m_uniqueShapeMapping.push_back(gpuShapeIndex);
} else
{
printf("Error: no vertices in mesh in b3GpuDynamicsWorld::addRigidBody\n");
index = -1;
b3Assert(0);
}
} else
{
if (colShape->getShapeType()==COMPOUND_SHAPE_PROXYTYPE)
{
btCompoundShape* compound = (btCompoundShape*) colShape;
b3AlignedObjectArray<b3GpuChildShape> childShapes;
for (int i=0;i<compound->getNumChildShapes();i++)
{
//for now, only support polyhedral child shapes
b3Assert(compound->getChildShape(i)->isPolyhedral());
b3GpuChildShape child;
child.m_shapeIndex = findOrRegisterCollisionShape(compound->getChildShape(i));
btVector3 pos = compound->getChildTransform(i).getOrigin();
btQuaternion orn = compound->getChildTransform(i).getRotation();
for (int v=0;v<4;v++)
{
child.m_childPosition[v] = pos[v];
child.m_childOrientation[v] = orn[v];
}
childShapes.push_back(child);
}
index = m_uniqueShapes.size();
m_uniqueShapes.push_back(colShape);
int gpuShapeIndex = m_np->registerCompoundShape(&childShapes);
m_uniqueShapeMapping.push_back(gpuShapeIndex);
/*printf("Error: unsupported compound type (%d) in b3GpuDynamicsWorld::addRigidBody\n",colShape->getShapeType());
index = -1;
b3Assert(0);
*/
} else
{
if (colShape->getShapeType()==SPHERE_SHAPE_PROXYTYPE)
{
m_uniqueShapes.push_back(colShape);
btSphereShape* sphere = (btSphereShape*)colShape;
int gpuShapeIndex = m_np->registerSphereShape(sphere->getRadius());
m_uniqueShapeMapping.push_back(gpuShapeIndex);
} else
{
if (colShape->getShapeType()==STATIC_PLANE_PROXYTYPE)
{
m_uniqueShapes.push_back(colShape);
btStaticPlaneShape* plane = (btStaticPlaneShape*)colShape;
int gpuShapeIndex = m_np->registerPlaneShape((b3Vector3&)plane->getPlaneNormal(),plane->getPlaneConstant());
m_uniqueShapeMapping.push_back(gpuShapeIndex);
} else
{
printf("Error: unsupported shape type (%d) in b3GpuDynamicsWorld::addRigidBody\n",colShape->getShapeType());
index = -1;
b3Assert(0);
}
}
}
}
}
}
return index;
}
void b3GpuDynamicsWorld::addRigidBody(btRigidBody* body)
{
m_cpuGpuSync = true;
//body->setMotionState(0);
int index = findOrRegisterCollisionShape(body->getCollisionShape());
if (index>=0)
{
int gpuShapeIndex= m_uniqueShapeMapping[index];
float mass = body->getInvMass() ? 1.f/body->getInvMass() : 0.f;
btVector3 pos = body->getWorldTransform().getOrigin();
btQuaternion orn = body->getWorldTransform().getRotation();
int bodyIndex = m_rigidBodyPipeline->registerPhysicsInstance(mass,&pos.getX(),&orn.getX(),gpuShapeIndex,m_collisionObjects.size(),false);
body->setUserIndex(bodyIndex);
m_collisionObjects.push_back(body);
m_bodyUpdateRevisions.push_back(-1);
}
}
void b3GpuDynamicsWorld::removeRigidBody(btRigidBody* colObj)
{
m_cpuGpuSync = true;
btDynamicsWorld::removeCollisionObject(colObj);
m_bodyUpdateRevisions.resize(this->m_collisionObjects.size());
for (int i=0;i<m_bodyUpdateRevisions.size();i++)
{
m_bodyUpdateRevisions[i] = -1;
}
int bodyIndex = colObj->getUserIndex();
if (getNumCollisionObjects()==0)
{
m_uniqueShapes.resize(0);
m_uniqueShapeMapping.resize(0);
m_np->reset();
m_bp->reset();
m_rigidBodyPipeline->reset();
#ifdef BT_USE_BODY_UPDATE_REVISION
m_bodyUpdateRevisions.resize(0);
#endif //BT_USE_BODY_UPDATE_REVISION
}
}
void b3GpuDynamicsWorld::reset()
{
m_staticBody = 0;
if (m_collisionObjects.size())
b3Warning("m_collisionObjects should be empty before calling b3GpuDynamicsWorld::reset");
m_collisionObjects.clear();
if (m_bodyUpdateRevisions.size())
b3Warning("world (m_bodyUpdateRevisions) should be empty before calling b3GpuDynamicsWorld::reset");
m_bodyUpdateRevisions.clear();
if (m_constraints.size())
b3Warning("m_constraints should be empty before calling b3GpuDynamicsWorld::reset");
m_constraints.clear();
m_uniqueShapes.resize(0);
m_uniqueShapeMapping.resize(0);
m_np->reset();
m_bp->reset();
m_rigidBodyPipeline->reset();
#ifdef BT_USE_BODY_UPDATE_REVISION
m_bodyUpdateRevisions.resize(0);
#endif
btConvexHullShape* nullShape = new btConvexHullShape();
m_staticBody = new btRigidBody(0,0,nullShape);
addRigidBody(m_staticBody,0,0);
}
void b3GpuDynamicsWorld::removeCollisionObject(btCollisionObject* colObj)
{
m_cpuGpuSync = true;
btDynamicsWorld::removeCollisionObject(colObj);
m_bodyUpdateRevisions.resize(this->m_collisionObjects.size());
for (int i=0;i<m_bodyUpdateRevisions.size();i++)
{
m_bodyUpdateRevisions[i] = -1;
}
if (getNumCollisionObjects()==0)
{
reset();
}
}
void b3GpuDynamicsWorld::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback) const
{
b3AlignedObjectArray<b3RayInfo> rays;
b3RayInfo ray;
ray.m_from = (const b3Vector3&)rayFromWorld;
ray.m_to = (const b3Vector3&)rayToWorld;
rays.push_back(ray);
b3AlignedObjectArray<b3RayHit> hitResults;
b3RayHit hit;
hit.m_hitFraction = 1.f;
hitResults.push_back(hit);
m_rigidBodyPipeline->castRays(rays,hitResults);
b3Printf("hit = %f\n", hitResults[0].m_hitFraction);
if (hitResults[0].m_hitFraction<1.f)
{
b3Assert(hitResults[0].m_hitBody >=0);
b3Assert(hitResults[0].m_hitBody < m_collisionObjects.size());
b3Vector3 hitNormalLocal = hitResults[0].m_hitNormal;
btCollisionObject* colObj = m_collisionObjects[hitResults[0].m_hitBody];
LocalRayResult rayResult(colObj,0,(btVector3&)hitNormalLocal,hitResults[0].m_hitFraction);
rayResult.m_hitFraction = hitResults[0].m_hitFraction;
resultCallback.addSingleResult(rayResult,true);
}
}
void b3GpuDynamicsWorld::synchronizeMotionStates()
{
//iterate over all collision objects
for ( int i=0;i<m_collisionObjects.size();i++)
{
btCollisionObject* colObj = m_collisionObjects[i];
btRigidBody* body = btRigidBody::upcast(colObj);
if (body)
synchronizeSingleMotionState(body);
}
}
void b3GpuDynamicsWorld::synchronizeSingleMotionState(btRigidBody* body)
{
btAssert(body);
if (body->getMotionState() && !body->isStaticOrKinematicObject())
{
//we need to call the update at least once, even for sleeping objects
//otherwise the 'graphics' transform never updates properly
const btTransform& centerOfMassWorldTrans = body->getWorldTransform();
body->getMotionState()->setWorldTransform(centerOfMassWorldTrans);
}
}
void b3GpuDynamicsWorld::debugDrawWorld()
{
BT_PROFILE("debugDrawWorld");
btCollisionWorld::debugDrawWorld();
}
void b3GpuDynamicsWorld::addConstraint(btTypedConstraint* constraint, bool disableCollisionsBetweenLinkedBodies)
{
constraint->setUserConstraintPtr(0);
m_constraints.push_back(constraint);
switch (constraint->getConstraintType())
{
case POINT2POINT_CONSTRAINT_TYPE:
{
btPoint2PointConstraint* p = (btPoint2PointConstraint*) constraint;
int rbA = p->getRigidBodyA().getUserIndex();
int rbB = p->getRigidBodyB().getUserIndex();
btVector3 pivotInB = p->getPivotInB();
if (rbB<=0)
{
pivotInB = p->getRigidBodyA().getWorldTransform()*p->getPivotInA();
rbB = m_staticBody->getUserIndex();
}
if (rbA>=0 && rbB>=0)
{
b3Point2PointConstraint* p2p = new b3Point2PointConstraint(rbA,rbB, (const b3Vector3&)p->getPivotInA(),(const b3Vector3&)pivotInB);
p2p->setBreakingImpulseThreshold(p->getBreakingImpulseThreshold());
constraint->setUserConstraintPtr(p2p);
m_rigidBodyPipeline->addConstraint(p2p);
} else
{
b3Error("invalid body index in addConstraint,b3Point2PointConstraint\n");
}
break;
}
case D6_CONSTRAINT_TYPE:
{
btGeneric6DofConstraint* dof2 = (btGeneric6DofConstraint*) constraint;
const b3RigidBodyCL* bodiesCL = m_np->getBodiesCpu();
int rbA = dof2->getRigidBodyA().getUserIndex();
int rbB = dof2->getRigidBodyB().getUserIndex();
btTransform frameInA = dof2->getFrameOffsetB();
btTransform frameInB = dof2->getFrameOffsetB();
if (rbA<=0)
{
frameInA = dof2->getRigidBodyB().getWorldTransform()*dof2->getFrameOffsetB();
rbA = m_staticBody->getUserIndex();
}
if (rbB<=0)
{
frameInB = dof2->getRigidBodyA().getWorldTransform()*dof2->getFrameOffsetA();
rbB = m_staticBody->getUserIndex();
}
if (rbA>=0 && rbB>=0)
{
b3Generic6DofConstraint* dof3 = new b3Generic6DofConstraint(rbA,rbB,(b3Transform&)frameInA,(b3Transform&)frameInB,false,bodiesCL);//(();//(rbA,rbB, (const b3Vector3&)p->getPivotInA(),(const b3Vector3&)pivotInB);
{
btVector3 limit(0,0,0);
dof2->getLinearLowerLimit(limit);
dof3->setLinearLowerLimit((const b3Vector3&)limit);
dof2->setLinearUpperLimit(limit);
dof3->setLinearUpperLimit((const b3Vector3&)limit);
dof2->setAngularLowerLimit(limit);
dof3->setAngularLowerLimit((const b3Vector3&)limit);
dof2->setAngularUpperLimit(limit);
dof3->setAngularUpperLimit((const b3Vector3&)limit);
/* for (int i=0;i<6;i++)
{
dof3->setParam(BT_CONSTRAINT_STOP_CFM,dof2->getParam(BT_CONSTRAINT_STOP_CFM,i),i);
dof3->setParam(BT_CONSTRAINT_STOP_ERP,dof2->getParam(BT_CONSTRAINT_STOP_ERP,i),i);
}
*/
dof3->setBreakingImpulseThreshold(dof2->getBreakingImpulseThreshold());
}
// p2p->setBreakingImpulseThreshold(p->getBreakingImpulseThreshold());
constraint->setUserConstraintPtr(dof3);
m_rigidBodyPipeline->addConstraint(dof3);
} else
{
b3Error("invalid body index in addConstraint, btGeneric6DofConstraint.\n");
}
// b3Generic6DofConstraint
break;
}
default:
b3Warning("Warning: b3GpuDynamicsWorld::addConstraint with unsupported constraint type\n");
};
}
void b3GpuDynamicsWorld::removeConstraint(btTypedConstraint* constraint)
{
b3TypedConstraint* c = (b3TypedConstraint*) constraint->getUserConstraintPtr();
if (c)
{
this->m_rigidBodyPipeline->removeConstraint(c);
delete c;
}
m_constraints.remove(constraint);
}