BGE bug #17491 fixed: BGE, Dupli instance with different scale, massive slowdown.

The root cause of this bug is the fact that Bullet shapes 
are shared between duplicated game objects. As the physics
object scale is stored in the shape, all duplicas must 
have the same scale otherwise the physics representation
is incorrect.
This fix introduces a mechanism to duplicate shapes at
runtime so that Bullet shapes are not shared anymore.
The drawback is an increased memory consuption. 
A reference count mechanism will be introduced in a 
later revision to keep Bullet shape shared between
duplicas that have the same scale.
This commit is contained in:
Benoit Bolsee 2008-08-21 15:19:54 +00:00
parent 4b9f5b2710
commit e912ca9331
8 changed files with 420 additions and 330 deletions

@ -151,7 +151,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\..\..\..\..\build\msvc_7\intern\moto\include;..\..\..\..\..\..\build\msvc_7\extern\bullet\include;..\..\..\..\..\source\gameengine\Physics\common;..\..\..\..\..\source\gameengine\Physics\Bullet"
AdditionalIncludeDirectories="..\..\..\..\..\..\build\msvc_7\intern\moto\include;..\..\..\..\..\..\build\msvc_7\intern\string\include;..\..\..\..\..\..\build\msvc_7\extern\bullet\include;..\..\..\..\..\source\gameengine\Physics\common;..\..\..\..\..\source\gameengine\Physics\Bullet;..\..\..\..\..\source\gameengine\Rasterizer;..\..\..\..\..\source\kernel\gen_system"
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
MinimalRebuild="FALSE"
BasicRuntimeChecks="3"

@ -680,188 +680,6 @@ void KX_ConvertODEEngineObject(KX_GameObject* gameobj,
// forward declarations
static btCollisionShape* CreateBulletShapeFromMesh(RAS_MeshObject* meshobj, bool polytope)
{
if (!meshobj)
return 0;
btCollisionShape* collisionMeshShape = 0;
btConvexHullShape* convexHullShape = 0;
btTriangleMeshShape* concaveShape = 0;
btTriangleMesh* collisionMeshData = 0;
//see if there is any polygons, if not, bail out.
int numPoints = 0;
btVector3* points = 0;
// Mesh has no polygons!
int numpolys = meshobj->NumPolygons();
if (!numpolys)
{
return NULL;
}
// Count the number of collision polygons and check they all come from the same
// vertex array
int numvalidpolys = 0;
int vtxarray = -1;
RAS_IPolyMaterial *poly_material = NULL;
bool reinstance = true;
for (int p=0; p<numpolys; p++)
{
RAS_Polygon* poly = meshobj->GetPolygon(p);
// only add polygons that have the collisionflag set
if (poly->IsCollider())
{
// check polygon is from the same vertex array
if (poly->GetVertexIndexBase().m_vtxarray != vtxarray)
{
if (vtxarray < 0)
vtxarray = poly->GetVertexIndexBase().m_vtxarray;
else
{
reinstance = false;
vtxarray = -1;
}
}
// check poly is from the same material
if (poly->GetMaterial()->GetPolyMaterial() != poly_material)
{
if (poly_material)
{
reinstance = false;
poly_material = NULL;
}
else
poly_material = poly->GetMaterial()->GetPolyMaterial();
}
// count the number of collision polys
numvalidpolys++;
// We have one collision poly, and we can't reinstance, so we
// might as well break here.
if (!reinstance)
break;
}
}
// No collision polygons
if (numvalidpolys < 1)
return NULL;
if (polytope)
{
convexHullShape = new btConvexHullShape(&points[0].getX(),numPoints);
collisionMeshShape = convexHullShape;
} else
{
collisionMeshData = new btTriangleMesh();
// concaveShape = new btTriangleMeshShape(collisionMeshData);
//collisionMeshShape = concaveShape;
}
numvalidpolys = 0;
for (int p2=0; p2<numpolys; p2++)
{
RAS_Polygon* poly = meshobj->GetPolygon(p2);
// only add polygons that have the collisionflag set
if (poly->IsCollider())
{
//Bullet can raycast any shape, so
if (polytope)
{
for (int i=0;i<poly->VertexCount();i++)
{
const float* vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[i],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 point(vtx[0],vtx[1],vtx[2]);
convexHullShape->addPoint(point);
}
if (poly->VertexCount())
numvalidpolys++;
} else
{
{
const float* vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[2],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex0(vtx[0],vtx[1],vtx[2]);
vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[1],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex1(vtx[0],vtx[1],vtx[2]);
vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[0],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex2(vtx[0],vtx[1],vtx[2]);
collisionMeshData->addTriangle(vertex0,vertex1,vertex2);
numvalidpolys++;
}
if (poly->VertexCount() == 4)
{
const float* vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[3],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex0(vtx[0],vtx[1],vtx[2]);
vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[2],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex1(vtx[0],vtx[1],vtx[2]);
vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[0],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex2(vtx[0],vtx[1],vtx[2]);
collisionMeshData->addTriangle(vertex0,vertex1,vertex2);
numvalidpolys++;
}
}
}
}
if (numvalidpolys > 0)
{
if (!polytope)
{
bool useQuantization = true;
concaveShape = new btBvhTriangleMeshShape( collisionMeshData, useQuantization );
//concaveShape = new btTriangleMeshShape( collisionMeshData );
concaveShape->recalcLocalAabb();
if (collisionMeshShape)
delete collisionMeshShape;
collisionMeshShape = concaveShape;
}
return collisionMeshShape;
}
if (collisionMeshShape)
delete collisionMeshShape;
if (collisionMeshData)
delete collisionMeshData;
return NULL;
}
void KX_ConvertBulletObject( class KX_GameObject* gameobj,
class RAS_MeshObject* meshobj,
@ -878,6 +696,7 @@ void KX_ConvertBulletObject( class KX_GameObject* gameobj,
bool isbulletdyna = false;
CcdConstructionInfo ci;
class PHY_IMotionState* motionstate = new KX_MotionState(gameobj->GetSGNode());
class CcdShapeConstructionInfo *shapeInfo = new CcdShapeConstructionInfo();
@ -894,120 +713,80 @@ void KX_ConvertBulletObject( class KX_GameObject* gameobj,
ci.m_gravity = btVector3(0,0,0);
ci.m_localInertiaTensor =btVector3(0,0,0);
ci.m_mass = objprop->m_dyna ? shapeprops->m_mass : 0.f;
shapeInfo->m_radius = objprop->m_radius;
isbulletdyna = objprop->m_dyna;
ci.m_localInertiaTensor = btVector3(ci.m_mass/3.f,ci.m_mass/3.f,ci.m_mass/3.f);
btTransform trans;
trans.setIdentity();
btCollisionShape* bm = 0;
switch (objprop->m_boundclass)
{
case KX_BOUNDSPHERE:
{
float radius = objprop->m_radius;
btVector3 inertiaHalfExtents (
radius,
radius,
radius);
//float radius = objprop->m_radius;
//btVector3 inertiaHalfExtents (
// radius,
// radius,
// radius);
//blender doesn't support multisphere, but for testing:
//bm = new MultiSphereShape(inertiaHalfExtents,,&trans.getOrigin(),&radius,1);
bm = new btSphereShape(objprop->m_radius);
bm->calculateLocalInertia(ci.m_mass,ci.m_localInertiaTensor);
shapeInfo->m_shapeType = PHY_SHAPE_SPHERE;
bm = shapeInfo->CreateBulletShape();
break;
};
case KX_BOUNDBOX:
{
MT_Vector3 halfExtents (
shapeInfo->m_halfExtend.setValue(
objprop->m_boundobject.box.m_extends[0],
objprop->m_boundobject.box.m_extends[1],
objprop->m_boundobject.box.m_extends[2]);
objprop->m_boundobject.box.m_extends[1],
objprop->m_boundobject.box.m_extends[2]);
halfExtents /= 2.f;
//btVector3 he (halfExtents[0]-CONVEX_DISTANCE_MARGIN ,halfExtents[1]-CONVEX_DISTANCE_MARGIN ,halfExtents[2]-CONVEX_DISTANCE_MARGIN );
//he = he.absolute();
btVector3 he (halfExtents[0],halfExtents[1],halfExtents[2]);
he = he.absolute();
bm = new btBoxShape(he);
bm->calculateLocalInertia(ci.m_mass,ci.m_localInertiaTensor);
shapeInfo->m_halfExtend /= 2.0;
shapeInfo->m_halfExtend = shapeInfo->m_halfExtend.absolute();
shapeInfo->m_shapeType = PHY_SHAPE_BOX;
bm = shapeInfo->CreateBulletShape();
break;
};
case KX_BOUNDCYLINDER:
{
btVector3 halfExtents (
shapeInfo->m_halfExtend.setValue(
objprop->m_boundobject.c.m_radius,
objprop->m_boundobject.c.m_radius,
objprop->m_boundobject.c.m_height * 0.5f
);
bm = new btCylinderShapeZ(halfExtents);
bm->calculateLocalInertia(ci.m_mass,ci.m_localInertiaTensor);
shapeInfo->m_shapeType = PHY_SHAPE_CYLINDER;
bm = shapeInfo->CreateBulletShape();
break;
}
case KX_BOUNDCONE:
case KX_BOUNDCONE:
{
btVector3 halfExtents (objprop->m_boundobject.box.m_extends[0],
objprop->m_boundobject.box.m_extends[1],
objprop->m_boundobject.box.m_extends[2]);
halfExtents /= 2.f;
bm = new btConeShapeZ(objprop->m_boundobject.c.m_radius,objprop->m_boundobject.c.m_height);
bm->calculateLocalInertia(ci.m_mass,ci.m_localInertiaTensor);
shapeInfo->m_radius = objprop->m_boundobject.c.m_radius;
shapeInfo->m_height = objprop->m_boundobject.c.m_height;
shapeInfo->m_shapeType = PHY_SHAPE_CONE;
bm = shapeInfo->CreateBulletShape();
break;
}
case KX_BOUNDPOLYTOPE:
{
bm = CreateBulletShapeFromMesh(meshobj,true);
if (bm)
{
bm->calculateLocalInertia(ci.m_mass,ci.m_localInertiaTensor);
}
break;
}
case KX_BOUNDMESH:
{
if (!ci.m_mass)
{
bm = CreateBulletShapeFromMesh(meshobj,false);
ci.m_localInertiaTensor.setValue(0.f,0.f,0.f);
//no moving concave meshes, so don't bother calculating inertia
//bm->calculateLocalInertia(ci.m_mass,ci.m_localInertiaTensor);
}
break;
}
default:
//interpret the shape as a concave triangle-mesh
case KX_BOUNDPOLYTOPE:
{
if (meshobj)
{
bm = CreateBulletShapeFromMesh(meshobj,false);
ci.m_localInertiaTensor.setValue(0.f,0.f,0.f);
// assert(0);
/*
meshobj->ScheduleCollisionPolygons();
KX_DeformableMesh* gfxmesh = new KX_DeformableMesh(meshobj);
gfxmesh->sendFixedMapping();
//trianglemesh
bm = new TriangleMeshInterface(gfxmesh,trans);
*/
shapeInfo->SetMesh(meshobj, true);
bm = shapeInfo->CreateBulletShape();
break;
}
case KX_BOUNDMESH:
{
if (!ci.m_mass)
{
shapeInfo->SetMesh(meshobj, false);
bm = shapeInfo->CreateBulletShape();
//no moving concave meshes, so don't bother calculating inertia
//bm->calculateLocalInertia(ci.m_mass,ci.m_localInertiaTensor);
}
break;
}
}
@ -1017,10 +796,13 @@ void KX_ConvertBulletObject( class KX_GameObject* gameobj,
if (!bm)
{
delete motionstate;
delete shapeInfo;
return;
}
bm->setMargin(0.06);
if (objprop->m_dyna)
bm->calculateLocalInertia(ci.m_mass,ci.m_localInertiaTensor);
@ -1030,31 +812,28 @@ void KX_ConvertBulletObject( class KX_GameObject* gameobj,
//take relative transform into account!
KX_BulletPhysicsController* parentCtrl = (KX_BulletPhysicsController*)objprop->m_dynamic_parent->GetPhysicsController();
assert(parentCtrl);
CcdShapeConstructionInfo* parentShapeInfo = parentCtrl->GetShapeInfo();
btRigidBody* rigidbody = parentCtrl->GetRigidBody();
btCollisionShape* colShape = rigidbody->getCollisionShape();
assert(colShape->isCompound());
btCompoundShape* compoundShape = (btCompoundShape*)colShape;
btTransform childTrans;
childTrans.setIdentity();
NodeList& children = objprop->m_dynamic_parent->GetSGNode()->GetSGChildren();
MT_Point3 childPos = gameobj->GetSGNode()->GetLocalPosition();
MT_Matrix3x3 childRot = gameobj->GetSGNode()->GetLocalOrientation();
MT_Vector3 childScale = gameobj->GetSGNode()->GetLocalScale();
bm->setLocalScaling(btVector3(childScale.x(),childScale.y(),childScale.z()));
childTrans.setOrigin(btVector3(childPos.x(),childPos.y(),childPos.z()));
shapeInfo->m_childTrans.setOrigin(btVector3(childPos.x(),childPos.y(),childPos.z()));
float rotval[12];
childRot.getValue(rotval);
btMatrix3x3 newRot;
newRot.setValue(rotval[0],rotval[1],rotval[2],rotval[4],rotval[5],rotval[6],rotval[8],rotval[9],rotval[10]);
newRot = newRot.transpose();
childTrans.setBasis(newRot);
compoundShape->addChildShape(childTrans,bm);
kxscene->AddShape(bm);
shapeInfo->m_childTrans.setBasis(newRot);
parentShapeInfo->AddShape(shapeInfo);
compoundShape->addChildShape(shapeInfo->m_childTrans,bm);
//do some recalc?
//recalc inertia for rigidbody
if (!rigidbody->isStaticOrKinematicObject())
@ -1069,15 +848,16 @@ void KX_ConvertBulletObject( class KX_GameObject* gameobj,
if (objprop->m_hasCompoundChildren)
{
//replace shape by compoundShape
// create a compound shape info
CcdShapeConstructionInfo *compoundShapeInfo = new CcdShapeConstructionInfo();
compoundShapeInfo->m_shapeType = PHY_SHAPE_COMPOUND;
compoundShapeInfo->AddShape(shapeInfo);
// create the compound shape manually as we already have the child shape
btCompoundShape* compoundShape = new btCompoundShape();
btTransform identTrans;
identTrans.setIdentity();
compoundShape->addChildShape(identTrans,bm);
//note abount compoundShape: Bullet does not delete the child shapes when
//the compound shape is deleted, so insert also the child shapes
kxscene->AddShape(bm);
compoundShape->addChildShape(shapeInfo->m_childTrans,bm);
// now replace the shape
bm = compoundShape;
shapeInfo = compoundShapeInfo;
}
@ -1113,6 +893,7 @@ void KX_ConvertBulletObject( class KX_GameObject* gameobj,
ci.m_collisionShape = bm;
ci.m_shapeInfo = shapeInfo;
ci.m_friction = smmaterial->m_friction;//tweak the friction a bit, so the default 0.5 works nice
ci.m_restitution = smmaterial->m_restitution;
ci.m_physicsEnv = env;
@ -1125,8 +906,9 @@ void KX_ConvertBulletObject( class KX_GameObject* gameobj,
ci.m_collisionFilterMask = (isbulletdyna) ? short(CcdConstructionInfo::AllFilter) : short(CcdConstructionInfo::AllFilter ^ CcdConstructionInfo::StaticFilter);
ci.m_bRigid = objprop->m_dyna && objprop->m_angular_rigidbody;
KX_BulletPhysicsController* physicscontroller = new KX_BulletPhysicsController(ci,isbulletdyna);
//remember that we created a shape so that we can delete it when the scene is removed (bullet will not delete it)
kxscene->AddShape(bm);
// shapeInfo is reference counted, decrement now as we don't use it anymore
if (shapeInfo)
shapeInfo->Release();
if (objprop->m_in_active_layer)
{

@ -234,40 +234,9 @@ KX_Scene::~KX_Scene()
{
delete m_bucketmanager;
}
#ifdef USE_BULLET
// This is a fix for memory leaks in bullet: the collision shapes is not destroyed
// when the physical controllers are destroyed. The reason is that shapes are shared
// between replicas of an object. There is no reference count in Bullet so the
// only workaround that does not involve changes in Bullet is to save in this array
// the list of shapes that are created when the scene is created (see KX_ConvertPhysicsObjects.cpp)
class btCollisionShape* shape;
class btTriangleMeshShape* meshShape;
vector<class btCollisionShape*>::iterator it = m_shapes.begin();
while (it != m_shapes.end()) {
shape = *it;
if (shape->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE)
{
meshShape = static_cast<btTriangleMeshShape*>(shape);
// shapes based on meshes use an interface that contains the vertices.
// Again the idea is to be able to share the interface between shapes but
// this is not used in Blender: each base object will have its own interface
btStridingMeshInterface* meshInterface = meshShape->getMeshInterface();
if (meshInterface)
delete meshInterface;
}
delete shape;
it++;
}
#endif
//Py_DECREF(m_attrlist);
}
void KX_Scene::AddShape(class btCollisionShape*shape)
{
m_shapes.push_back(shape);
}
void KX_Scene::SetProjectionMatrix(MT_CmMatrix4x4& pmat)
{
m_projectionmat = pmat;

@ -121,11 +121,6 @@ protected:
* The set of cameras for this scene
*/
list<class KX_Camera*> m_cameras;
/**
* The set of bullet shapes that must be deleted at the end of the scene
* to avoid memory leak (not deleted by bullet because shape are shared between replicas)
*/
vector<class btCollisionShape*> m_shapes;
/**
* Various SCA managers used by the scene
*/
@ -322,7 +317,6 @@ public:
int NewRemoveObject(CValue* gameobj);
void ReplaceMesh(CValue* gameobj,
void* meshobj);
void AddShape(class btCollisionShape* shape);
/**
* @section Logic stuff
* Initiate an update of the logic system.

@ -18,6 +18,7 @@ subject to the following restrictions:
#include "PHY_IMotionState.h"
#include "CcdPhysicsEnvironment.h"
#include "RAS_MeshObject.h"
class BP_Proxy;
@ -44,7 +45,14 @@ CcdPhysicsController::CcdPhysicsController (const CcdConstructionInfo& ci)
m_newClientInfo = 0;
m_registerCount = 0;
// copy pointers locally to allow smart release
m_MotionState = ci.m_MotionState;
m_collisionShape = ci.m_collisionShape;
// shape info is shared, increment ref count
m_shapeInfo = ci.m_shapeInfo;
if (m_shapeInfo)
m_shapeInfo->AddRef();
m_bulletMotionState = 0;
@ -116,7 +124,7 @@ void CcdPhysicsController::CreateRigidbody()
m_body = new btRigidBody(m_cci.m_mass,
m_bulletMotionState,
m_cci.m_collisionShape,
m_collisionShape,
m_cci.m_localInertiaTensor * m_cci.m_inertiaFactor,
m_cci.m_linearDamping,m_cci.m_angularDamping,
m_cci.m_friction,m_cci.m_restitution);
@ -144,6 +152,19 @@ void CcdPhysicsController::CreateRigidbody()
}
}
static void DeleteBulletShape(btCollisionShape* shape)
{
if (shape->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE)
{
// shapes based on meshes use an interface that contains the vertices.
btTriangleMeshShape* meshShape = static_cast<btTriangleMeshShape*>(shape);
btStridingMeshInterface* meshInterface = meshShape->getMeshInterface();
if (meshInterface)
delete meshInterface;
}
delete shape;
}
CcdPhysicsController::~CcdPhysicsController()
{
//will be reference counted, due to sharing
@ -155,6 +176,27 @@ CcdPhysicsController::~CcdPhysicsController()
if (m_bulletMotionState)
delete m_bulletMotionState;
delete m_body;
if (m_collisionShape)
{
// collision shape is always unique to the controller, can delete it here
if (m_collisionShape->isCompound())
{
// bullet does not delete the child shape, must do it here
btCompoundShape* compoundShape = (btCompoundShape*)m_collisionShape;
int numChild = compoundShape->getNumChildShapes();
for (int i=numChild-1 ; i >= 0; i--)
{
btCollisionShape* childShape = compoundShape->getChildShape(i);
DeleteBulletShape(childShape);
}
}
DeleteBulletShape(m_collisionShape);
}
if (m_shapeInfo)
{
m_shapeInfo->Release();
}
}
@ -219,11 +261,33 @@ void CcdPhysicsController::PostProcessReplica(class PHY_IMotionState* motionsta
{
m_MotionState = motionstate;
m_registerCount = 0;
m_collisionShape = NULL;
// always create a new shape to avoid scaling bug
if (m_shapeInfo)
{
m_shapeInfo->AddRef();
m_collisionShape = m_shapeInfo->CreateBulletShape();
if (m_collisionShape)
{
// new shape has no scaling, apply initial scaling
m_collisionShape->setLocalScaling(m_cci.m_scaling);
if (m_cci.m_mass)
m_collisionShape->calculateLocalInertia(m_cci.m_mass, m_cci.m_localInertiaTensor);
}
}
m_body = 0;
CreateRigidbody();
if (m_body)
{
if (m_cci.m_mass)
{
m_body->setMassProps(m_cci.m_mass, m_cci.m_localInertiaTensor * m_cci.m_inertiaFactor);
}
}
m_cci.m_physicsEnv->addCcdPhysicsController(this);
@ -597,29 +661,32 @@ bool CcdPhysicsController::wantsSleeping()
PHY_IPhysicsController* CcdPhysicsController::GetReplica()
{
//very experimental, shape sharing is not implemented yet.
//just support btSphereShape/ConeShape for now
// This is used only to replicate Near and Radar sensor controllers
// The replication of object physics controller is done in KX_BulletPhysicsController::GetReplica()
CcdConstructionInfo cinfo = m_cci;
if (cinfo.m_collisionShape)
if (m_shapeInfo)
{
switch (cinfo.m_collisionShape->getShapeType())
// This situation does not normally happen
cinfo.m_collisionShape = m_shapeInfo->CreateBulletShape();
}
else if (m_collisionShape)
{
switch (m_collisionShape->getShapeType())
{
case SPHERE_SHAPE_PROXYTYPE:
{
btSphereShape* orgShape = (btSphereShape*)cinfo.m_collisionShape;
btSphereShape* orgShape = (btSphereShape*)m_collisionShape;
cinfo.m_collisionShape = new btSphereShape(*orgShape);
break;
}
case CONE_SHAPE_PROXYTYPE:
case CONE_SHAPE_PROXYTYPE:
{
btConeShape* orgShape = (btConeShape*)cinfo.m_collisionShape;
btConeShape* orgShape = (btConeShape*)m_collisionShape;
cinfo.m_collisionShape = new btConeShape(*orgShape);
break;
}
default:
{
return 0;
@ -628,6 +695,7 @@ PHY_IPhysicsController* CcdPhysicsController::GetReplica()
}
cinfo.m_MotionState = new DefaultMotionState();
cinfo.m_shapeInfo = m_shapeInfo;
CcdPhysicsController* replica = new CcdPhysicsController(cinfo);
return replica;
@ -689,3 +757,198 @@ void DefaultMotionState::calculateWorldTransformations()
}
// Shape constructor
bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, bool polytope)
{
// assume no shape information
m_shapeType = PHY_SHAPE_NONE;
m_vertexArray.clear();
if (!meshobj)
return false;
// Mesh has no polygons!
int numpolys = meshobj->NumPolygons();
if (!numpolys)
{
return false;
}
// check that we have at least one colliding polygon
int numvalidpolys = 0;
for (int p=0; p<numpolys; p++)
{
RAS_Polygon* poly = meshobj->GetPolygon(p);
// only add polygons that have the collisionflag set
if (poly->IsCollider())
{
numvalidpolys++;
break;
}
}
// No collision polygons
if (numvalidpolys < 1)
return false;
m_shapeType = (polytope) ? PHY_SHAPE_POLYTOPE : PHY_SHAPE_MESH;
numvalidpolys = 0;
for (int p2=0; p2<numpolys; p2++)
{
RAS_Polygon* poly = meshobj->GetPolygon(p2);
// only add polygons that have the collisionflag set
if (poly->IsCollider())
{
//Bullet can raycast any shape, so
if (polytope)
{
for (int i=0;i<poly->VertexCount();i++)
{
const float* vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[i],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 point(vtx[0],vtx[1],vtx[2]);
m_vertexArray.push_back(point);
numvalidpolys++;
}
} else
{
{
const float* vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[2],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex0(vtx[0],vtx[1],vtx[2]);
vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[1],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex1(vtx[0],vtx[1],vtx[2]);
vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[0],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex2(vtx[0],vtx[1],vtx[2]);
m_vertexArray.push_back(vertex0);
m_vertexArray.push_back(vertex1);
m_vertexArray.push_back(vertex2);
numvalidpolys++;
}
if (poly->VertexCount() == 4)
{
const float* vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[3],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex0(vtx[0],vtx[1],vtx[2]);
vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[2],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex1(vtx[0],vtx[1],vtx[2]);
vtx = meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray,
poly->GetVertexIndexBase().m_indexarray[0],
poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ();
btPoint3 vertex2(vtx[0],vtx[1],vtx[2]);
m_vertexArray.push_back(vertex0);
m_vertexArray.push_back(vertex1);
m_vertexArray.push_back(vertex2);
numvalidpolys++;
}
}
}
}
if (!numvalidpolys)
{
// should not happen
m_shapeType = PHY_SHAPE_NONE;
return false;
}
return true;
}
btCollisionShape* CcdShapeConstructionInfo::CreateBulletShape()
{
btCollisionShape* collisionShape = 0;
btTriangleMeshShape* concaveShape = 0;
btTriangleMesh* collisionMeshData = 0;
btCompoundShape* compoundShape = 0;
CcdShapeConstructionInfo* nextShapeInfo;
switch (m_shapeType)
{
case PHY_SHAPE_BOX:
collisionShape = new btBoxShape(m_halfExtend);
break;
case PHY_SHAPE_SPHERE:
collisionShape = new btSphereShape(m_radius);
break;
case PHY_SHAPE_CYLINDER:
collisionShape = new btCylinderShapeZ(m_halfExtend);
break;
case PHY_SHAPE_CONE:
collisionShape = new btConeShapeZ(m_radius, m_height);
break;
case PHY_SHAPE_POLYTOPE:
collisionShape = new btConvexHullShape(&m_vertexArray.begin()->getX(), m_vertexArray.size());
break;
case PHY_SHAPE_MESH:
collisionMeshData = new btTriangleMesh();
// m_vertexArray is necessarily a multiple of 3
for (std::vector<btPoint3>::iterator it=m_vertexArray.begin(); it != m_vertexArray.end(); )
{
collisionMeshData->addTriangle(*it++,*it++,*it++);
}
concaveShape = new btBvhTriangleMeshShape( collisionMeshData, true );
concaveShape->recalcLocalAabb();
collisionShape = concaveShape;
break;
case PHY_SHAPE_COMPOUND:
if (m_nextShape)
{
compoundShape = new btCompoundShape();
for (nextShapeInfo=m_nextShape; nextShapeInfo; nextShapeInfo = nextShapeInfo->m_nextShape)
{
collisionShape = nextShapeInfo->CreateBulletShape();
if (collisionShape)
{
compoundShape->addChildShape(nextShapeInfo->m_childTrans, collisionShape);
}
}
collisionShape = compoundShape;
}
}
return collisionShape;
}
void CcdShapeConstructionInfo::AddShape(CcdShapeConstructionInfo* shapeInfo)
{
CcdShapeConstructionInfo* nextShape = this;
while (nextShape->m_nextShape != NULL)
nextShape = nextShape->m_nextShape;
nextShape->m_nextShape = shapeInfo;
}
CcdShapeConstructionInfo::~CcdShapeConstructionInfo()
{
CcdShapeConstructionInfo* childShape = m_nextShape;
while (childShape)
{
CcdShapeConstructionInfo* nextShape = childShape->m_nextShape;
childShape->m_nextShape = NULL;
childShape->Release();
childShape = nextShape;
}
m_vertexArray.clear();
}

@ -17,11 +17,14 @@ subject to the following restrictions:
#ifndef BULLET2_PHYSICSCONTROLLER_H
#define BULLET2_PHYSICSCONTROLLER_H
#include <vector>
#include "PHY_IPhysicsController.h"
/// PHY_IPhysicsController is the abstract simplified Interface to a physical object.
/// It contains the IMotionState and IDeformableMesh Interfaces.
#include "btBulletDynamicsCommon.h"
#include "LinearMath/btTransform.h"
#include "PHY_IMotionState.h"
@ -31,8 +34,66 @@ extern float gAngularSleepingTreshold;
extern bool gDisableDeactivation;
class CcdPhysicsEnvironment;
class btMotionState;
class RAS_MeshObject;
class btCollisionShape;
// Shape contructor
// It contains all the information needed to create a simple bullet shape at runtime
class CcdShapeConstructionInfo
{
public:
CcdShapeConstructionInfo() :
m_shapeType(PHY_SHAPE_NONE),
m_radius(1.0),
m_height(1.0),
m_halfExtend(0.f,0.f,0.f),
m_nextShape(NULL),
m_refCount(1)
{
m_childTrans.setIdentity();
}
~CcdShapeConstructionInfo();
CcdShapeConstructionInfo* AddRef()
{
m_refCount++;
return this;
}
int Release()
{
if (--m_refCount > 0)
return m_refCount;
delete this;
return 0;
}
void AddShape(CcdShapeConstructionInfo* shapeInfo);
CcdShapeConstructionInfo* GetNextShape()
{
return m_nextShape;
}
bool SetMesh(RAS_MeshObject* mesh, bool polytope);
btCollisionShape* CreateBulletShape();
// member variables
PHY_ShapeType m_shapeType;
btScalar m_radius;
btScalar m_height;
btVector3 m_halfExtend;
btTransform m_childTrans;
std::vector<btPoint3> m_vertexArray; // Contains both vertex array for polytope shape and
// triangle array for concave mesh shape.
// In this case a triangle is made of 3 consecutive points
protected:
CcdShapeConstructionInfo* m_nextShape; // for compound shape
int m_refCount; // this class is shared between replicas
// keep track of users so that we can release it
};
struct CcdConstructionInfo
{
@ -65,6 +126,7 @@ struct CcdConstructionInfo
m_collisionFilterMask(AllFilter),
m_collisionShape(0),
m_MotionState(0),
m_shapeInfo(0),
m_physicsEnv(0),
m_inertiaFactor(1.f)
{
@ -89,8 +151,11 @@ struct CcdConstructionInfo
short int m_collisionFilterGroup;
short int m_collisionFilterMask;
///these pointers are used as argument passing for the CcdPhysicsController constructor
///and not anymore after that
class btCollisionShape* m_collisionShape;
class PHY_IMotionState* m_MotionState;
class CcdShapeConstructionInfo* m_shapeInfo;
CcdPhysicsEnvironment* m_physicsEnv; //needed for self-replication
float m_inertiaFactor;//tweak the inertia (hooked up to Blender 'formfactor'
@ -106,6 +171,9 @@ class CcdPhysicsController : public PHY_IPhysicsController
btRigidBody* m_body;
class PHY_IMotionState* m_MotionState;
btMotionState* m_bulletMotionState;
class btCollisionShape* m_collisionShape;
class CcdShapeConstructionInfo* m_shapeInfo;
friend class CcdPhysicsEnvironment; // needed when updating the controller
@ -137,6 +205,7 @@ class CcdPhysicsController : public PHY_IPhysicsController
btRigidBody* GetRigidBody() { return m_body;}
CcdShapeConstructionInfo* GetShapeInfo() { return m_shapeInfo; }
btCollisionShape* GetCollisionShape() {
return m_body->getCollisionShape();

@ -1370,8 +1370,9 @@ int CcdPhysicsEnvironment::createConstraint(class PHY_IPhysicsController* ctrl
PHY_IPhysicsController* CcdPhysicsEnvironment::CreateConeController(float coneradius,float coneheight)
{
CcdConstructionInfo cinfo;
//This is a memory leak: Bullet does not delete the shape and it cannot be added to
//the KX_Scene.m_shapes list -- too bad but that's not a lot of data
// we don't need a CcdShapeConstructionInfo for this shape:
// it is simple enough for the standard copy constructor (see CcdPhysicsController::GetReplica)
cinfo.m_collisionShape = new btConeShape(coneradius,coneheight);
cinfo.m_MotionState = 0;
cinfo.m_physicsEnv = this;

@ -87,6 +87,18 @@ typedef enum PHY_ConstraintType {
} PHY_ConstraintType;
typedef enum PHY_ShapeType {
PHY_SHAPE_NONE,
PHY_SHAPE_BOX,
PHY_SHAPE_SPHERE,
PHY_SHAPE_CYLINDER,
PHY_SHAPE_CONE,
PHY_SHAPE_MESH,
PHY_SHAPE_POLYTOPE,
PHY_SHAPE_COMPOUND
} PHY_ShapeType;
typedef float PHY_Vector3[3];
#endif //__PHY_DYNAMIC_TYPES