diff options
author | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2009-04-20 19:06:46 +0400 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2009-04-20 19:06:46 +0400 |
commit | 874c29cea8e6f9bc411fccf2d6f4cb07e94328d0 (patch) | |
tree | 5971e577cf7c02e05a1e37b5ad058c71a6744877 /source/gameengine/Physics/Bullet | |
parent | 7555bfa793a2b0fc187c6211c56986f35b2d7b09 (diff) | |
parent | c5bc4e4fb1a33eda8c31f2ea02e91f32f74c8fa5 (diff) |
2.50: svn merge https://svn.blender.org/svnroot/bf-blender/trunk/blender -r19323:HEAD
Notes:
* blenderbuttons and ICON_SNAP_PEEL_OBJECT were not merged.
Diffstat (limited to 'source/gameengine/Physics/Bullet')
-rw-r--r-- | source/gameengine/Physics/Bullet/CMakeLists.txt | 10 | ||||
-rw-r--r-- | source/gameengine/Physics/Bullet/CcdGraphicController.cpp | 112 | ||||
-rw-r--r-- | source/gameengine/Physics/Bullet/CcdGraphicController.h | 74 | ||||
-rw-r--r-- | source/gameengine/Physics/Bullet/CcdPhysicsController.cpp | 338 | ||||
-rw-r--r-- | source/gameengine/Physics/Bullet/CcdPhysicsController.h | 47 | ||||
-rw-r--r-- | source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp | 655 | ||||
-rw-r--r-- | source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h | 15 | ||||
-rw-r--r-- | source/gameengine/Physics/Bullet/Makefile | 8 | ||||
-rw-r--r-- | source/gameengine/Physics/Bullet/SConscript | 16 |
9 files changed, 1148 insertions, 127 deletions
diff --git a/source/gameengine/Physics/Bullet/CMakeLists.txt b/source/gameengine/Physics/Bullet/CMakeLists.txt index 6c733786caf..ec2cdede683 100644 --- a/source/gameengine/Physics/Bullet/CMakeLists.txt +++ b/source/gameengine/Physics/Bullet/CMakeLists.txt @@ -24,16 +24,24 @@ # # ***** END GPL LICENSE BLOCK ***** -SET(SRC CcdPhysicsEnvironment.cpp CcdPhysicsController.cpp) +SET(SRC CcdPhysicsEnvironment.cpp CcdPhysicsController.cpp CcdGraphicController.cpp) SET(INC . ../common ../../../../extern/bullet2/src + ../../../../extern/glew/include ../../../../intern/moto/include ../../../kernel/gen_system ../../../../intern/string + ../../../../intern/SoundSystem ../../Rasterizer + ../../Ketsji + ../../Expressions + ../../GameLogic + ../../SceneGraph + ../../../../source/blender/makesdna + ${PYTHON_INC} ) BLENDERLIB(bf_bullet "${SRC}" "${INC}") diff --git a/source/gameengine/Physics/Bullet/CcdGraphicController.cpp b/source/gameengine/Physics/Bullet/CcdGraphicController.cpp new file mode 100644 index 00000000000..caf18fd28ba --- /dev/null +++ b/source/gameengine/Physics/Bullet/CcdGraphicController.cpp @@ -0,0 +1,112 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include "CcdPhysicsEnvironment.h" +#include "CcdGraphicController.h" +#include "btBulletDynamicsCommon.h" +#include "MT_Point3.h" + + +CcdGraphicController::CcdGraphicController (CcdPhysicsEnvironment* phyEnv, PHY_IMotionState* motionState) : + m_localAabbMin(0.f, 0.f, 0.f), + m_localAabbMax(0.f, 0.f, 0.f), + m_motionState(motionState), + m_phyEnv(phyEnv), + m_handle(NULL), + m_newClientInfo(NULL) +{ +} + +CcdGraphicController::~CcdGraphicController() +{ + if (m_phyEnv) + m_phyEnv->removeCcdGraphicController(this); + + if (m_motionState) + delete m_motionState; +} + +void CcdGraphicController::setLocalAabb(const btVector3& aabbMin,const btVector3& aabbMax) +{ + m_localAabbMin = aabbMin; + m_localAabbMax = aabbMax; + SetGraphicTransform(); +} + +void CcdGraphicController::setLocalAabb(const MT_Point3& aabbMin,const MT_Point3& aabbMax) +{ + m_localAabbMin = btVector3(aabbMin[0],aabbMin[1],aabbMin[2]); + m_localAabbMax = btVector3(aabbMax[0],aabbMax[1],aabbMax[2]); + SetGraphicTransform(); +} + + +void CcdGraphicController::getAabb(btVector3& aabbMin, btVector3& aabbMax) +{ + btVector3 pos; + btVector3 scale; + float ori[12]; + m_motionState->getWorldPosition(pos.m_floats[0],pos.m_floats[1],pos.m_floats[2]); + m_motionState->getWorldScaling(scale.m_floats[0],scale.m_floats[1],scale.m_floats[2]); + m_motionState->getWorldOrientation(ori); + btMatrix3x3 rot(ori[0], ori[4], ori[8], + ori[1], ori[5], ori[9], + ori[2], ori[6], ori[10]); + + btVector3 localAabbMin = m_localAabbMin; + btVector3 localAabbMax = m_localAabbMax; + btVector3 tmpAabbMin = m_localAabbMin * scale; + btVector3 tmpAabbMax = m_localAabbMax * scale; + + localAabbMin[0] = (scale.getX() >= 0.) ? tmpAabbMin[0] : tmpAabbMax[0]; + localAabbMin[1] = (scale.getY() >= 0.) ? tmpAabbMin[1] : tmpAabbMax[1]; + localAabbMin[2] = (scale.getZ() >= 0.) ? tmpAabbMin[2] : tmpAabbMax[2]; + localAabbMax[0] = (scale.getX() <= 0.) ? tmpAabbMin[0] : tmpAabbMax[0]; + localAabbMax[1] = (scale.getY() <= 0.) ? tmpAabbMin[1] : tmpAabbMax[1]; + localAabbMax[2] = (scale.getZ() <= 0.) ? tmpAabbMin[2] : tmpAabbMax[2]; + + btVector3 localHalfExtents = btScalar(0.5)*(localAabbMax-localAabbMin); + btVector3 localCenter = btScalar(0.5)*(localAabbMax+localAabbMin); + + btMatrix3x3 abs_b = rot.absolute(); + btVector3 center = rot*localCenter + pos; + btVector3 extent = abs_b*localHalfExtents; + aabbMin = center - extent; + aabbMax = center + extent; +} + +bool CcdGraphicController::SetGraphicTransform() +{ + if (!m_handle) + return false; + btVector3 aabbMin; + btVector3 aabbMax; + getAabb(aabbMin, aabbMax); + // update Aabb in broadphase + m_phyEnv->getCullingTree()->setAabb(m_handle,aabbMin,aabbMax,NULL); + return true; +} + +PHY_IGraphicController* CcdGraphicController::GetReplica(class PHY_IMotionState* motionState) +{ + CcdGraphicController* replica = new CcdGraphicController(*this); + replica->m_motionState = motionState; + replica->m_newClientInfo = NULL; + replica->m_handle = NULL; + m_phyEnv->addCcdGraphicController(replica); + return replica; +} + + diff --git a/source/gameengine/Physics/Bullet/CcdGraphicController.h b/source/gameengine/Physics/Bullet/CcdGraphicController.h new file mode 100644 index 00000000000..8faa0944313 --- /dev/null +++ b/source/gameengine/Physics/Bullet/CcdGraphicController.h @@ -0,0 +1,74 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + +#ifndef BULLET2_GRAPHICCONTROLLER_H +#define BULLET2_GRAPHICCONTROLLER_H + +#include "PHY_IGraphicController.h" + +#include "btBulletDynamicsCommon.h" +#include "LinearMath/btTransform.h" + +#include "PHY_IMotionState.h" +#include "MT_Point3.h" + +class CcdPhysicsEnvironment; +class btCollisionObject; + +///CcdGraphicController is a graphic object that supports view frustrum culling and occlusion +class CcdGraphicController : public PHY_IGraphicController +{ +public: + CcdGraphicController(CcdPhysicsEnvironment* phyEnv, PHY_IMotionState* motionState); + + virtual ~CcdGraphicController(); + + void setLocalAabb(const btVector3& aabbMin,const btVector3& aabbMax); + void setLocalAabb(const MT_Point3& aabbMin,const MT_Point3& aabbMax); + + PHY_IMotionState* GetMotionState() { return m_motionState; } + void getAabb(btVector3& aabbMin, btVector3& aabbMax); + + virtual void setBroadphaseHandle(btBroadphaseProxy* handle) { m_handle = handle; } + virtual btBroadphaseProxy* getBroadphaseHandle() { return m_handle; } + + //////////////////////////////////// + // PHY_IGraphicController interface + //////////////////////////////////// + + /** + * Updates the Aabb based on the motion state + */ + virtual bool SetGraphicTransform(); + + // client info for culling + virtual void* getNewClientInfo() { return m_newClientInfo; } + virtual void setNewClientInfo(void* clientinfo) { m_newClientInfo = clientinfo; } + virtual PHY_IGraphicController* GetReplica(class PHY_IMotionState* motionstate); + +private: + // unscaled aabb corner + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + + PHY_IMotionState* m_motionState; + CcdPhysicsEnvironment* m_phyEnv; + btBroadphaseProxy* m_handle; + void* m_newClientInfo; + +}; + +#endif //BULLET2_PHYSICSCONTROLLER_H diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp index eecdea55349..0b9da8f46d3 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp @@ -16,6 +16,9 @@ subject to the following restrictions: #include "CcdPhysicsController.h" #include "btBulletDynamicsCommon.h" #include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h" + +#include "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h" + #include "PHY_IMotionState.h" #include "CcdPhysicsEnvironment.h" #include "RAS_MeshObject.h" @@ -24,10 +27,14 @@ subject to the following restrictions: #include "BulletSoftBody/btSoftBodyHelpers.h" #include "LinearMath/btConvexHull.h" #include "BulletCollision/Gimpact/btGImpactShape.h" +#include "BulletCollision/Gimpact/btGImpactShape.h" #include "BulletSoftBody/btSoftRigidDynamicsWorld.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + class BP_Proxy; ///todo: fill all the empty CcdPhysicsController methods, hook them up to the btRigidBody class @@ -577,7 +584,19 @@ bool CcdPhysicsController::SynchronizeMotionStates(float time) if (body && !body->isStaticObject()) { - + + if ((m_cci.m_clamp_vel_max>0.0) || (m_cci.m_clamp_vel_min>0.0)) + { + const btVector3& linvel = body->getLinearVelocity(); + float len= linvel.length(); + + if((m_cci.m_clamp_vel_max>0.0) && (len > m_cci.m_clamp_vel_max)) + body->setLinearVelocity(linvel * (m_cci.m_clamp_vel_max / len)); + + else if ((m_cci.m_clamp_vel_min>0.0) && btFuzzyZero(len)==0 && (len < m_cci.m_clamp_vel_min)) + body->setLinearVelocity(linvel * (m_cci.m_clamp_vel_min / len)); + } + const btVector3& worldPos = body->getCenterOfMassPosition(); m_MotionState->setWorldPosition(worldPos[0],worldPos[1],worldPos[2]); @@ -1238,6 +1257,22 @@ void DefaultMotionState::getWorldOrientation(float& quatIma0,float& quatIma1,flo quatReal = m_worldTransform.getRotation()[3]; } +void DefaultMotionState::getWorldOrientation(float* ori) +{ + *ori++ = m_worldTransform.getBasis()[0].x(); + *ori++ = m_worldTransform.getBasis()[1].x(); + *ori++ = m_worldTransform.getBasis()[1].x(); + *ori++ = 0.f; + *ori++ = m_worldTransform.getBasis()[0].y(); + *ori++ = m_worldTransform.getBasis()[1].y(); + *ori++ = m_worldTransform.getBasis()[1].y(); + *ori++ = 0.f; + *ori++ = m_worldTransform.getBasis()[0].z(); + *ori++ = m_worldTransform.getBasis()[1].z(); + *ori++ = m_worldTransform.getBasis()[1].z(); + *ori++ = 0.f; +} + void DefaultMotionState::setWorldPosition(float posX,float posY,float posZ) { btVector3 pos(posX,posY,posZ); @@ -1272,123 +1307,212 @@ CcdShapeConstructionInfo* CcdShapeConstructionInfo::FindMesh(RAS_MeshObject* mes bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, bool polytope,bool useGimpact) { + int numpolys; + m_useGimpact = useGimpact; // assume no shape information // no support for dynamic change of shape yet assert(IsUnused()); m_shapeType = PHY_SHAPE_NONE; - m_vertexArray.clear(); - m_polygonIndexArray.clear(); m_meshObject = NULL; - if (!meshobj) - return false; - - // Mesh has no polygons! - int numpolys = meshobj->NumPolygons(); - if (!numpolys) - { + // No mesh object or mesh has no polys + if (!meshobj || meshobj->HasColliderPolygon()==false) { + m_vertexArray.clear(); + m_polygonIndexArray.clear(); + m_triFaceArray.clear(); return false; } - // check that we have at least one colliding polygon - int numvalidpolys = 0; + numpolys = meshobj->NumPolygons(); - for (int p=0; p<numpolys; p++) - { - RAS_Polygon* poly = meshobj->GetPolygon(p); + m_shapeType = (polytope) ? PHY_SHAPE_POLYTOPE : PHY_SHAPE_MESH; - // only add polygons that have the collisionflag set - if (poly->IsCollider()) + /* Convert blender geometry into bullet mesh, need these vars for mapping */ + vector<bool> vert_tag_array(meshobj->GetMesh()->totvert, false); + unsigned int tot_bt_verts= 0; + unsigned int orig_index; + int i; + + if (polytope) + { + // Tag verts we're using + for (int p2=0; p2<numpolys; p2++) { - numvalidpolys++; - break; - } - } + RAS_Polygon* poly= meshobj->GetPolygon(p2); - // No collision polygons - if (numvalidpolys < 1) - return false; + // only add polygons that have the collision flag set + if (poly->IsCollider()) + { + for(i=0; i<poly->VertexCount(); i++) { + orig_index= poly->GetVertex(i)->getOrigIndex(); + if (vert_tag_array[orig_index]==false) + { + vert_tag_array[orig_index]= true; + tot_bt_verts++; + } + } + } + } - m_shapeType = (polytope) ? PHY_SHAPE_POLYTOPE : PHY_SHAPE_MESH; + m_vertexArray.resize(tot_bt_verts); - numvalidpolys = 0; + btVector3 *bt= &m_vertexArray[0]; - for (int p2=0; p2<numpolys; p2++) - { - RAS_Polygon* poly = meshobj->GetPolygon(p2); + 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) + // only add polygons that have the collisionflag set + if (poly->IsCollider()) { - for (int i=0;i<poly->VertexCount();i++) - { - const float* vtx = poly->GetVertex(i)->getXYZ(); - btVector3 point(vtx[0],vtx[1],vtx[2]); - //avoid duplicates (could better directly use vertex offsets, rather than a vertex compare) - bool found = false; - for (int j=0;j<m_vertexArray.size();j++) + for(i=0; i<poly->VertexCount(); i++) { + RAS_TexVert *v= poly->GetVertex(i); + orig_index= v->getOrigIndex(); + + if (vert_tag_array[orig_index]==true) { - if (m_vertexArray[j]==point) - { - found = true; - break; - } - } - if (!found) - m_vertexArray.push_back(point); + const float* vtx = v->getXYZ(); + vert_tag_array[orig_index]= false; - numvalidpolys++; + bt->setX(vtx[0]); bt->setY(vtx[1]); bt->setZ(vtx[2]); + bt++; + } } - } else + } + } + } + else { + unsigned int tot_bt_tris= 0; + vector<int> vert_remap_array(meshobj->GetMesh()->totvert, 0); + + // Tag verts we're using + for (int p2=0; p2<numpolys; p2++) + { + RAS_Polygon* poly= meshobj->GetPolygon(p2); + + // only add polygons that have the collision flag set + if (poly->IsCollider()) { - { - const float* vtx = poly->GetVertex(2)->getXYZ(); - btVector3 vertex0(vtx[0],vtx[1],vtx[2]); + for(i=0; i<poly->VertexCount(); i++) { + orig_index= poly->GetVertex(i)->getOrigIndex(); + if (vert_tag_array[orig_index]==false) + { + vert_tag_array[orig_index]= true; + vert_remap_array[orig_index]= tot_bt_verts; + tot_bt_verts++; + } + } + + tot_bt_tris += (i==4 ? 2:1); /* a quad or a tri */ + } + } - vtx = poly->GetVertex(1)->getXYZ(); - btVector3 vertex1(vtx[0],vtx[1],vtx[2]); + m_vertexArray.resize(tot_bt_verts); + m_polygonIndexArray.resize(tot_bt_tris); + m_triFaceArray.resize(tot_bt_tris*3); - vtx = poly->GetVertex(0)->getXYZ(); - btVector3 vertex2(vtx[0],vtx[1],vtx[2]); + btVector3 *bt= &m_vertexArray[0]; + int *poly_index_pt= &m_polygonIndexArray[0]; + int *tri_pt= &m_triFaceArray[0]; - m_vertexArray.push_back(vertex0); - m_vertexArray.push_back(vertex1); - m_vertexArray.push_back(vertex2); - m_polygonIndexArray.push_back(p2); - numvalidpolys++; + + for (int p2=0; p2<numpolys; p2++) + { + RAS_Polygon* poly= meshobj->GetPolygon(p2); + + // only add polygons that have the collisionflag set + if (poly->IsCollider()) + { + RAS_TexVert *v1= poly->GetVertex(0); + RAS_TexVert *v2= poly->GetVertex(1); + RAS_TexVert *v3= poly->GetVertex(2); + int i1= v1->getOrigIndex(); + int i2= v2->getOrigIndex(); + int i3= v3->getOrigIndex(); + const float* vtx; + + // the face indicies + tri_pt[0]= vert_remap_array[i1]; + tri_pt[1]= vert_remap_array[i2]; + tri_pt[2]= vert_remap_array[i3]; + tri_pt= tri_pt+3; + + // m_polygonIndexArray + *poly_index_pt= p2; + poly_index_pt++; + + // the vertex location + if (vert_tag_array[i1]==true) { /* *** v1 *** */ + vert_tag_array[i1]= false; + vtx = v1->getXYZ(); + bt->setX(vtx[0]); bt->setY( vtx[1]); bt->setZ(vtx[2]); + bt++; + } + if (vert_tag_array[i2]==true) { /* *** v2 *** */ + vert_tag_array[i2]= false; + vtx = v2->getXYZ(); + bt->setX(vtx[0]); bt->setY(vtx[1]); bt->setZ(vtx[2]); + bt++; } - if (poly->VertexCount() == 4) + if (vert_tag_array[i3]==true) { /* *** v3 *** */ + vert_tag_array[i3]= false; + vtx = v3->getXYZ(); + bt->setX(vtx[0]); bt->setY(vtx[1]); bt->setZ(vtx[2]); + bt++; + } + + if (poly->VertexCount()==4) { - const float* vtx = poly->GetVertex(3)->getXYZ(); - btVector3 vertex0(vtx[0],vtx[1],vtx[2]); + RAS_TexVert *v4= poly->GetVertex(3); + int i4= v4->getOrigIndex(); + + tri_pt[0]= vert_remap_array[i1]; + tri_pt[1]= vert_remap_array[i3]; + tri_pt[2]= vert_remap_array[i4]; + tri_pt= tri_pt+3; + + // m_polygonIndexArray + *poly_index_pt= p2; + poly_index_pt++; + + // the vertex location + if (vert_tag_array[i4]==true) { /* *** v4 *** */ + vert_tag_array[i4]= false; + vtx = v4->getXYZ(); + bt->setX(vtx[0]); bt->setY(vtx[1]); bt->setZ(vtx[2]); + bt++; + } + } + } + } - vtx = poly->GetVertex(2)->getXYZ(); - btVector3 vertex1(vtx[0],vtx[1],vtx[2]); - vtx = poly->GetVertex(0)->getXYZ(); - btVector3 vertex2(vtx[0],vtx[1],vtx[2]); + /* If this ever gets confusing, print out an OBJ file for debugging */ +#if 0 + printf("# vert count %d\n", m_vertexArray.size()); + for(i=0; i<m_vertexArray.size(); i+=1) { + printf("v %.6f %.6f %.6f\n", m_vertexArray[i].x(), m_vertexArray[i].y(), m_vertexArray[i].z()); + } - m_vertexArray.push_back(vertex0); - m_vertexArray.push_back(vertex1); - m_vertexArray.push_back(vertex2); - m_polygonIndexArray.push_back(p2); - numvalidpolys++; - } - } + printf("# face count %d\n", m_triFaceArray.size()); + for(i=0; i<m_triFaceArray.size(); i+=3) { + printf("f %d %d %d\n", m_triFaceArray[i]+1, m_triFaceArray[i+1]+1, m_triFaceArray[i+2]+1); } +#endif + } - if (!numvalidpolys) +#if 0 + if (validpolys==false) { // should not happen m_shapeType = PHY_SHAPE_NONE; return false; } +#endif + m_meshObject = meshobj; if (!polytope) { @@ -1413,7 +1537,6 @@ btCollisionShape* CcdShapeConstructionInfo::CreateBulletShape() { btCollisionShape* collisionShape = 0; btTriangleMeshShape* concaveShape = 0; - btTriangleMesh* collisionMeshData = 0; btCompoundShape* compoundShape = 0; CcdShapeConstructionInfo* nextShapeInfo; @@ -1454,18 +1577,17 @@ btCollisionShape* CcdShapeConstructionInfo::CreateBulletShape() // One possible optimization is to use directly the btBvhTriangleMeshShape when the scale is 1,1,1 // and btScaledBvhTriangleMeshShape otherwise. if (m_useGimpact) - { - collisionMeshData = new btTriangleMesh(); - - bool removeDuplicateVertices=true; - - // m_vertexArray is necessarily a multiple of 3 - for (int i=0;i<m_vertexArray.size(); i+=3 ) - { - collisionMeshData->addTriangle(m_vertexArray[i+2],m_vertexArray[i+1],m_vertexArray[i],removeDuplicateVertices); - } + { + btTriangleIndexVertexArray* indexVertexArrays = new btTriangleIndexVertexArray( + m_polygonIndexArray.size(), + &m_triFaceArray[0], + 3*sizeof(int), + m_vertexArray.size(), + (btScalar*) &m_vertexArray[0].x(), + sizeof(btVector3) + ); - btGImpactMeshShape* gimpactShape = new btGImpactMeshShape(collisionMeshData); + btGImpactMeshShape* gimpactShape = new btGImpactMeshShape(indexVertexArrays); collisionShape = gimpactShape; gimpactShape->updateBound(); @@ -1474,17 +1596,39 @@ btCollisionShape* CcdShapeConstructionInfo::CreateBulletShape() { if (!m_unscaledShape) { - collisionMeshData = new btTriangleMesh(true,false); - collisionMeshData->m_weldingThreshold = m_weldingThreshold; + + btTriangleIndexVertexArray* indexVertexArrays = 0; - bool removeDuplicateVertices=true; - // m_vertexArray is necessarily a multiple of 3 - for (int i=0;i<m_vertexArray.size(); i+=3 ) + ///enable welding, only for the objects that need it (such as soft bodies) + if (0.f != m_weldingThreshold1) { - collisionMeshData->addTriangle(m_vertexArray[i+2],m_vertexArray[i+1],m_vertexArray[i],removeDuplicateVertices); + btTriangleMesh* collisionMeshData = new btTriangleMesh(true,false); + collisionMeshData->m_weldingThreshold = m_weldingThreshold1; + bool removeDuplicateVertices=true; + // m_vertexArray not in multiple of 3 anymore, use m_triFaceArray + for(int i=0; i<m_triFaceArray.size(); i+=3) { + collisionMeshData->addTriangle( + m_vertexArray[m_triFaceArray[i]], + m_vertexArray[m_triFaceArray[i+1]], + m_vertexArray[m_triFaceArray[i+2]], + removeDuplicateVertices + ); + } + indexVertexArrays = collisionMeshData; + + } else + { + indexVertexArrays = new btTriangleIndexVertexArray( + m_polygonIndexArray.size(), + &m_triFaceArray[0], + 3*sizeof(int), + m_vertexArray.size(), + (btScalar*) &m_vertexArray[0].x(), + sizeof(btVector3)); } + // this shape will be shared and not deleted until shapeInfo is deleted - m_unscaledShape = new btBvhTriangleMeshShape( collisionMeshData, true ); + m_unscaledShape = new btBvhTriangleMeshShape( indexVertexArrays, true ); m_unscaledShape->recalcLocalAabb(); } collisionShape = new btScaledBvhTriangleMeshShape(m_unscaledShape, btVector3(1.0f,1.0f,1.0f)); diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.h b/source/gameengine/Physics/Bullet/CcdPhysicsController.h index deb3c0880e9..4510bbddf65 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.h +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.h @@ -72,7 +72,7 @@ public: m_meshObject(NULL), m_unscaledShape(NULL), m_useGimpact(false), - m_weldingThreshold(0.f), + m_weldingThreshold1(0.f), m_shapeProxy(NULL) { m_childTrans.setIdentity(); @@ -107,7 +107,7 @@ public: } CcdShapeConstructionInfo* GetChildShape(int i) { - if (i < 0 || i >= m_shapeArray.size()) + if (i < 0 || i >= (int)m_shapeArray.size()) return NULL; return m_shapeArray.at(i); @@ -116,7 +116,7 @@ public: { if (shapeInfo == NULL) return -1; - for (int i=0; i<m_shapeArray.size(); i++) + for (int i=0; i<(int)m_shapeArray.size(); i++) { CcdShapeConstructionInfo* childInfo = m_shapeArray.at(i); if ((userData == NULL || userData == childInfo->m_userData) && @@ -130,10 +130,10 @@ public: bool RemoveChildShape(int i) { - if (i < 0 || i >= m_shapeArray.size()) + if (i < 0 || i >= (int)m_shapeArray.size()) return false; m_shapeArray.at(i)->Release(); - if (i < m_shapeArray.size()-1) + if (i < (int)m_shapeArray.size()-1) m_shapeArray[i] = m_shapeArray.back(); m_shapeArray.pop_back(); return true; @@ -167,14 +167,17 @@ public: std::vector<int> m_polygonIndexArray; // Contains the array of polygon index in the // original mesh that correspond to shape triangles. // only set for concave mesh shape. + + std::vector<int> m_triFaceArray; // Contains an array of triplets of face indicies + // quads turn into 2 tris - void setVertexWeldingThreshold(float threshold) + void setVertexWeldingThreshold1(float threshold) { - m_weldingThreshold = threshold; + m_weldingThreshold1 = threshold; } - float getVertexWeldingThreshold() const + float getVertexWeldingThreshold1() const { - return m_weldingThreshold; + return m_weldingThreshold1; } protected: static std::map<RAS_MeshObject*, CcdShapeConstructionInfo*> m_meshShapeMap; @@ -185,7 +188,7 @@ protected: // the actual shape is of type btScaledBvhTriangleMeshShape std::vector<CcdShapeConstructionInfo*> m_shapeArray; // for compound shapes bool m_useGimpact; //use gimpact for concave dynamic/moving collision detection - float m_weldingThreshold; //welding closeby vertices together can improve softbody stability etc. + float m_weldingThreshold1; //welding closeby vertices together can improve softbody stability etc. CcdShapeConstructionInfo* m_shapeProxy; // only used for PHY_SHAPE_PROXY, pointer to actual shape info }; @@ -211,6 +214,8 @@ struct CcdConstructionInfo m_gravity(0,0,0), m_scaling(1.f,1.f,1.f), m_mass(0.f), + m_clamp_vel_min(-1.f), + m_clamp_vel_max(-1.f), m_restitution(0.1f), m_friction(0.5f), m_linearDamping(0.1f), @@ -236,6 +241,8 @@ struct CcdConstructionInfo btVector3 m_gravity; btVector3 m_scaling; btScalar m_mass; + btScalar m_clamp_vel_min; + btScalar m_clamp_vel_max; btScalar m_restitution; btScalar m_friction; btScalar m_linearDamping; @@ -476,7 +483,24 @@ class CcdPhysicsController : public PHY_IPhysicsController } m_cci.m_radius = margin; } - + + // velocity clamping + virtual void SetLinVelocityMin(float val) + { + m_cci.m_clamp_vel_min= val; + } + virtual float GetLinVelocityMin() const + { + return m_cci.m_clamp_vel_min; + } + virtual void SetLinVelocityMax(float val) + { + m_cci.m_clamp_vel_max= val; + } + virtual float GetLinVelocityMax() const + { + return m_cci.m_clamp_vel_max; + } bool wantsSleeping(); @@ -541,6 +565,7 @@ class DefaultMotionState : public PHY_IMotionState virtual void setWorldPosition(float posX,float posY,float posZ); virtual void setWorldOrientation(float quatIma0,float quatIma1,float quatIma2,float quatReal); + virtual void getWorldOrientation(float* ori); virtual void calculateWorldTransformations(); diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp index dd21e58bd68..3e1e0294321 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp @@ -18,6 +18,7 @@ subject to the following restrictions: #include "CcdPhysicsEnvironment.h" #include "CcdPhysicsController.h" +#include "CcdGraphicController.h" #include <algorithm> #include "btBulletDynamicsCommon.h" @@ -32,6 +33,10 @@ subject to the following restrictions: #include "PHY_IMotionState.h" +#include "KX_GameObject.h" +#include "RAS_MeshObject.h" +#include "RAS_Polygon.h" +#include "RAS_TexVert.h" #define CCD_CONSTRAINT_DISABLE_LINKED_COLLISION 0x80 @@ -46,7 +51,9 @@ btRaycastVehicle::btVehicleTuning gTuning; #endif //NEW_BULLET_VEHICLE_SUPPORT #include "LinearMath/btAabbUtil2.h" - +#include "MT_Matrix4x4.h" +#include "MT_Vector3.h" +#include "GL/glew.h" #ifdef WIN32 void DrawRasterizerLine(const float* from,const float* to,int color); @@ -316,8 +323,10 @@ static void DrawAabb(btIDebugDraw* debugDrawer,const btVector3& from,const btVec -CcdPhysicsEnvironment::CcdPhysicsEnvironment(btDispatcher* dispatcher,btOverlappingPairCache* pairCache) -:m_numIterations(10), +CcdPhysicsEnvironment::CcdPhysicsEnvironment(bool useDbvtCulling,btDispatcher* dispatcher,btOverlappingPairCache* pairCache) +:m_cullingCache(NULL), +m_cullingTree(NULL), +m_numIterations(10), m_scalingPropagated(false), m_numTimeSubSteps(1), m_ccdMode(0), @@ -350,6 +359,11 @@ m_ownDispatcher(NULL) //m_broadphase = new btAxisSweep3(btVector3(-1000,-1000,-1000),btVector3(1000,1000,1000)); //m_broadphase = new btSimpleBroadphase(); m_broadphase = new btDbvtBroadphase(); + // avoid any collision in the culling tree + if (useDbvtCulling) { + m_cullingCache = new btNullPairCache(); + m_cullingTree = new btDbvtBroadphase(m_cullingCache); + } m_filterCallback = new CcdOverlapFilterCallBack(this); m_broadphase->getOverlappingPairCache()->setOverlapFilterCallback(m_filterCallback); @@ -364,7 +378,6 @@ m_ownDispatcher(NULL) m_gravity = btVector3(0.f,-10.f,0.f); m_dynamicsWorld->setGravity(m_gravity); - } void CcdPhysicsEnvironment::addCcdPhysicsController(CcdPhysicsController* ctrl) @@ -558,6 +571,41 @@ void CcdPhysicsEnvironment::refreshCcdPhysicsController(CcdPhysicsController* ct } } +void CcdPhysicsEnvironment::addCcdGraphicController(CcdGraphicController* ctrl) +{ + if (m_cullingTree) + { + btVector3 minAabb; + btVector3 maxAabb; + ctrl->getAabb(minAabb, maxAabb); + + ctrl->setBroadphaseHandle(m_cullingTree->createProxy( + minAabb, + maxAabb, + INVALID_SHAPE_PROXYTYPE, // this parameter is not used + ctrl, + 0, // this object does not collision with anything + 0, + NULL, // dispatcher => this parameter is not used + 0)); + + assert(ctrl->getBroadphaseHandle()); + } +} + +void CcdPhysicsEnvironment::removeCcdGraphicController(CcdGraphicController* ctrl) +{ + if (m_cullingTree) + { + btBroadphaseProxy* bp = ctrl->getBroadphaseHandle(); + if (bp) + { + m_cullingTree->destroyProxy(bp,NULL); + ctrl->setBroadphaseHandle(0); + } + } +} + void CcdPhysicsEnvironment::beginFrame() { @@ -593,10 +641,10 @@ bool CcdPhysicsEnvironment::proceedDeltaTime(double curTime,float timeStep) (*it)->SynchronizeMotionStates(timeStep); } - for (it=m_controllers.begin(); it!=m_controllers.end(); it++) - { - (*it)->SynchronizeMotionStates(timeStep); - } + //for (it=m_controllers.begin(); it!=m_controllers.end(); it++) + //{ + // (*it)->SynchronizeMotionStates(timeStep); + //} for (i=0;i<m_wrapperVehicles.size();i++) { @@ -618,9 +666,10 @@ class ClosestRayResultCallbackNotMe : public btCollisionWorld::ClosestRayResultC public: ClosestRayResultCallbackNotMe(const btVector3& rayFromWorld,const btVector3& rayToWorld,btCollisionObject* owner,btCollisionObject* parent) :btCollisionWorld::ClosestRayResultCallback(rayFromWorld,rayToWorld), - m_owner(owner) + m_owner(owner), + m_parent(parent) { - + } virtual bool needsCollision(btBroadphaseProxy* proxy0) const @@ -668,7 +717,7 @@ void CcdPhysicsEnvironment::processFhSprings(double curTime,float timeStep) //btVector3 rayToWorld = rayFromWorld + body->getCenterOfMassTransform().getBasis() * rayDirLocal; //ray always points down the z axis in world space... btVector3 rayToWorld = rayFromWorld + rayDirLocal; - + ClosestRayResultCallbackNotMe resultCallback(rayFromWorld,rayToWorld,body,parentBody); m_dynamicsWorld->rayTest(rayFromWorld,rayToWorld,resultCallback); @@ -1146,7 +1195,578 @@ PHY_IPhysicsController* CcdPhysicsEnvironment::rayTest(PHY_IRayCastFilterCallbac return result.m_controller; } +// Handles occlusion culling. +// The implementation is based on the CDTestFramework +struct OcclusionBuffer +{ + struct WriteOCL + { + static inline bool Process(btScalar& q,btScalar v) { if(q<v) q=v;return(false); } + static inline void Occlusion(bool& flag) { flag = true; } + }; + struct QueryOCL + { + static inline bool Process(btScalar& q,btScalar v) { return(q<=v); } + static inline void Occlusion(bool& flag) { } + }; + btScalar* m_buffer; + size_t m_bufferSize; + bool m_initialized; + bool m_occlusion; + int m_sizes[2]; + btScalar m_scales[2]; + btScalar m_offsets[2]; + btScalar m_wtc[16]; // world to clip transform + btScalar m_mtc[16]; // model to clip transform + // constructor: size=largest dimension of the buffer. + // Buffer size depends on aspect ratio + OcclusionBuffer() + { + m_initialized=false; + m_occlusion = false; + m_buffer == NULL; + m_bufferSize = 0; + } + // multiplication of column major matrices: m=m1*m2 + template<typename T1, typename T2> + void CMmat4mul(btScalar* m, const T1* m1, const T2* m2) + { + m[ 0] = btScalar(m1[ 0]*m2[ 0]+m1[ 4]*m2[ 1]+m1[ 8]*m2[ 2]+m1[12]*m2[ 3]); + m[ 1] = btScalar(m1[ 1]*m2[ 0]+m1[ 5]*m2[ 1]+m1[ 9]*m2[ 2]+m1[13]*m2[ 3]); + m[ 2] = btScalar(m1[ 2]*m2[ 0]+m1[ 6]*m2[ 1]+m1[10]*m2[ 2]+m1[14]*m2[ 3]); + m[ 3] = btScalar(m1[ 3]*m2[ 0]+m1[ 7]*m2[ 1]+m1[11]*m2[ 2]+m1[15]*m2[ 3]); + + m[ 4] = btScalar(m1[ 0]*m2[ 4]+m1[ 4]*m2[ 5]+m1[ 8]*m2[ 6]+m1[12]*m2[ 7]); + m[ 5] = btScalar(m1[ 1]*m2[ 4]+m1[ 5]*m2[ 5]+m1[ 9]*m2[ 6]+m1[13]*m2[ 7]); + m[ 6] = btScalar(m1[ 2]*m2[ 4]+m1[ 6]*m2[ 5]+m1[10]*m2[ 6]+m1[14]*m2[ 7]); + m[ 7] = btScalar(m1[ 3]*m2[ 4]+m1[ 7]*m2[ 5]+m1[11]*m2[ 6]+m1[15]*m2[ 7]); + + m[ 8] = btScalar(m1[ 0]*m2[ 8]+m1[ 4]*m2[ 9]+m1[ 8]*m2[10]+m1[12]*m2[11]); + m[ 9] = btScalar(m1[ 1]*m2[ 8]+m1[ 5]*m2[ 9]+m1[ 9]*m2[10]+m1[13]*m2[11]); + m[10] = btScalar(m1[ 2]*m2[ 8]+m1[ 6]*m2[ 9]+m1[10]*m2[10]+m1[14]*m2[11]); + m[11] = btScalar(m1[ 3]*m2[ 8]+m1[ 7]*m2[ 9]+m1[11]*m2[10]+m1[15]*m2[11]); + + m[12] = btScalar(m1[ 0]*m2[12]+m1[ 4]*m2[13]+m1[ 8]*m2[14]+m1[12]*m2[15]); + m[13] = btScalar(m1[ 1]*m2[12]+m1[ 5]*m2[13]+m1[ 9]*m2[14]+m1[13]*m2[15]); + m[14] = btScalar(m1[ 2]*m2[12]+m1[ 6]*m2[13]+m1[10]*m2[14]+m1[14]*m2[15]); + m[15] = btScalar(m1[ 3]*m2[12]+m1[ 7]*m2[13]+m1[11]*m2[14]+m1[15]*m2[15]); + } + void setup(int size) + { + m_initialized=false; + m_occlusion=false; + // compute the size of the buffer + GLint v[4]; + GLdouble m[16],p[16]; + int maxsize; + double ratio; + glGetIntegerv(GL_VIEWPORT,v); + maxsize = (v[2] > v[3]) ? v[2] : v[3]; + assert(maxsize > 0); + ratio = 1.0/(2*maxsize); + // ensure even number + m_sizes[0] = 2*((int)(size*v[2]*ratio+0.5)); + m_sizes[1] = 2*((int)(size*v[3]*ratio+0.5)); + m_scales[0]=btScalar(m_sizes[0]/2); + m_scales[1]=btScalar(m_sizes[1]/2); + m_offsets[0]=m_scales[0]+0.5f; + m_offsets[1]=m_scales[1]+0.5f; + // prepare matrix + // at this time of the rendering, the modelview matrix is the + // world to camera transformation and the projection matrix is + // camera to clip transformation. combine both so that + glGetDoublev(GL_MODELVIEW_MATRIX,m); + glGetDoublev(GL_PROJECTION_MATRIX,p); + CMmat4mul(m_wtc,p,m); + } + void initialize() + { + size_t newsize = (m_sizes[0]*m_sizes[1])*sizeof(btScalar); + if (m_buffer) + { + // see if we can reuse + if (newsize > m_bufferSize) + { + free(m_buffer); + m_buffer = NULL; + m_bufferSize = 0; + } + } + if (!m_buffer) + { + m_buffer = (btScalar*)calloc(1, newsize); + m_bufferSize = newsize; + } else + { + // buffer exists already, just clears it + memset(m_buffer, 0, newsize); + } + // memory allocate must succeed + assert(m_buffer != NULL); + m_initialized = true; + m_occlusion = false; + } + void SetModelMatrix(double *fl) + { + CMmat4mul(m_mtc,m_wtc,fl); + if (!m_initialized) + initialize(); + } + + // transform a segment in world coordinate to clip coordinate + void transformW(const btVector3& x, btVector4& t) + { + t[0] = x[0]*m_wtc[0]+x[1]*m_wtc[4]+x[2]*m_wtc[8]+m_wtc[12]; + t[1] = x[0]*m_wtc[1]+x[1]*m_wtc[5]+x[2]*m_wtc[9]+m_wtc[13]; + t[2] = x[0]*m_wtc[2]+x[1]*m_wtc[6]+x[2]*m_wtc[10]+m_wtc[14]; + t[3] = x[0]*m_wtc[3]+x[1]*m_wtc[7]+x[2]*m_wtc[11]+m_wtc[15]; + } + void transformM(const float* x, btVector4& t) + { + t[0] = x[0]*m_mtc[0]+x[1]*m_mtc[4]+x[2]*m_mtc[8]+m_mtc[12]; + t[1] = x[0]*m_mtc[1]+x[1]*m_mtc[5]+x[2]*m_mtc[9]+m_mtc[13]; + t[2] = x[0]*m_mtc[2]+x[1]*m_mtc[6]+x[2]*m_mtc[10]+m_mtc[14]; + t[3] = x[0]*m_mtc[3]+x[1]*m_mtc[7]+x[2]*m_mtc[11]+m_mtc[15]; + } + // convert polygon to device coordinates + static bool project(btVector4* p,int n) + { + for(int i=0;i<n;++i) + { + const btScalar iw=1/p[i][3]; + p[i][2]=1/p[i][3]; + p[i][0]*=p[i][2]; + p[i][1]*=p[i][2]; + } + return(true); + } + // pi: closed polygon in clip coordinate, NP = number of segments + // po: same polygon with clipped segments removed + template <const int NP> + static int clip(const btVector4* pi,btVector4* po) + { + btScalar s[2*NP]; + btVector4 pn[2*NP]; + int i, j, m, n, ni; + // deal with near clipping + for(i=0, m=0;i<NP;++i) + { + s[i]=pi[i][2]+pi[i][3]; + if(s[i]<0) m+=1<<i; + } + if(m==((1<<NP)-1)) + return(0); + if(m!=0) + { + for(i=NP-1,j=0,n=0;j<NP;i=j++) + { + const btVector4& a=pi[i]; + const btVector4& b=pi[j]; + const btScalar t=s[i]/(a[3]+a[2]-b[3]-b[2]); + if((t>0)&&(t<1)) + { + pn[n][0] = a[0]+(b[0]-a[0])*t; + pn[n][1] = a[1]+(b[1]-a[1])*t; + pn[n][2] = a[2]+(b[2]-a[2])*t; + pn[n][3] = a[3]+(b[3]-a[3])*t; + ++n; + } + if(s[j]>0) pn[n++]=b; + } + // ready to test far clipping, start from the modified polygon + pi = pn; + ni = n; + } else + { + // no clipping on the near plane, keep same vector + ni = NP; + } + // now deal with far clipping + for(i=0, m=0;i<ni;++i) + { + s[i]=pi[i][2]-pi[i][3]; + if(s[i]>0) m+=1<<i; + } + if(m==((1<<ni)-1)) + return(0); + if(m!=0) + { + for(i=ni-1,j=0,n=0;j<ni;i=j++) + { + const btVector4& a=pi[i]; + const btVector4& b=pi[j]; + const btScalar t=s[i]/(a[2]-a[3]-b[2]+b[3]); + if((t>0)&&(t<1)) + { + po[n][0] = a[0]+(b[0]-a[0])*t; + po[n][1] = a[1]+(b[1]-a[1])*t; + po[n][2] = a[2]+(b[2]-a[2])*t; + po[n][3] = a[3]+(b[3]-a[3])*t; + ++n; + } + if(s[j]<0) po[n++]=b; + } + return(n); + } + for(int i=0;i<ni;++i) po[i]=pi[i]; + return(ni); + } + // write or check a triangle to buffer. a,b,c in device coordinates (-1,+1) + template <typename POLICY> + inline bool draw( const btVector4& a, + const btVector4& b, + const btVector4& c, + const float face, + const btScalar minarea) + { + const btScalar a2=cross(b-a,c-a)[2]; + if((face*a2)<0.f || btFabs(a2)<minarea) + return false; + // further down we are normally going to write to the Zbuffer, mark it so + POLICY::Occlusion(m_occlusion); + + int x[3], y[3], ib=1, ic=2; + btScalar z[3]; + x[0]=(int)(a.x()*m_scales[0]+m_offsets[0]); + y[0]=(int)(a.y()*m_scales[1]+m_offsets[1]); + z[0]=a.z(); + if (a2 < 0.f) + { + // negative aire is possible with double face => must + // change the order of b and c otherwise the algorithm doesn't work + ib=2; + ic=1; + } + x[ib]=(int)(b.x()*m_scales[0]+m_offsets[0]); + x[ic]=(int)(c.x()*m_scales[0]+m_offsets[0]); + y[ib]=(int)(b.y()*m_scales[1]+m_offsets[1]); + y[ic]=(int)(c.y()*m_scales[1]+m_offsets[1]); + z[ib]=b.z(); + z[ic]=c.z(); + const int mix=btMax(0,btMin(x[0],btMin(x[1],x[2]))); + const int mxx=btMin(m_sizes[0],1+btMax(x[0],btMax(x[1],x[2]))); + const int miy=btMax(0,btMin(y[0],btMin(y[1],y[2]))); + const int mxy=btMin(m_sizes[1],1+btMax(y[0],btMax(y[1],y[2]))); + const int width=mxx-mix; + const int height=mxy-miy; + if ((width*height) <= 1) + { + // degenerated in at most one single pixel + btScalar* scan=&m_buffer[miy*m_sizes[0]+mix]; + // use for loop to detect the case where width or height == 0 + for(int iy=miy;iy<mxy;++iy) + { + for(int ix=mix;ix<mxx;++ix) + { + if(POLICY::Process(*scan,z[0])) + return(true); + if(POLICY::Process(*scan,z[1])) + return(true); + if(POLICY::Process(*scan,z[2])) + return(true); + } + } + } else if (width == 1) + { + // Degenerated in at least 2 vertical lines + // The algorithm below doesn't work when face has a single pixel width + // We cannot use general formulas because the plane is degenerated. + // We have to interpolate along the 3 edges that overlaps and process each pixel. + // sort the y coord to make formula simpler + int ytmp; + btScalar ztmp; + if (y[0] > y[1]) { ytmp=y[1];y[1]=y[0];y[0]=ytmp;ztmp=z[1];z[1]=z[0];z[0]=ztmp; } + if (y[0] > y[2]) { ytmp=y[2];y[2]=y[0];y[0]=ytmp;ztmp=z[2];z[2]=z[0];z[0]=ztmp; } + if (y[1] > y[2]) { ytmp=y[2];y[2]=y[1];y[1]=ytmp;ztmp=z[2];z[2]=z[1];z[1]=ztmp; } + int dy[]={ y[0]-y[1], + y[1]-y[2], + y[2]-y[0]}; + btScalar dzy[3]; + dzy[0] = (dy[0]) ? (z[0]-z[1])/dy[0] : btScalar(0.f); + dzy[1] = (dy[1]) ? (z[1]-z[2])/dy[1] : btScalar(0.f); + dzy[2] = (dy[2]) ? (z[2]-z[0])/dy[2] : btScalar(0.f); + btScalar v[3] = { dzy[0]*(miy-y[0])+z[0], + dzy[1]*(miy-y[1])+z[1], + dzy[2]*(miy-y[2])+z[2] }; + dy[0] = y[1]-y[0]; + dy[1] = y[0]-y[1]; + dy[2] = y[2]-y[0]; + btScalar* scan=&m_buffer[miy*m_sizes[0]+mix]; + for(int iy=miy;iy<mxy;++iy) + { + if(dy[0] >= 0 && POLICY::Process(*scan,v[0])) + return(true); + if(dy[1] >= 0 && POLICY::Process(*scan,v[1])) + return(true); + if(dy[2] >= 0 && POLICY::Process(*scan,v[2])) + return(true); + scan+=m_sizes[0]; + v[0] += dzy[0]; v[1] += dzy[1]; v[2] += dzy[2]; + dy[0]--; dy[1]++, dy[2]--; + } + } else if (height == 1) + { + // Degenerated in at least 2 horizontal lines + // The algorithm below doesn't work when face has a single pixel width + // We cannot use general formulas because the plane is degenerated. + // We have to interpolate along the 3 edges that overlaps and process each pixel. + int xtmp; + btScalar ztmp; + if (x[0] > x[1]) { xtmp=x[1];x[1]=x[0];x[0]=xtmp;ztmp=z[1];z[1]=z[0];z[0]=ztmp; } + if (x[0] > x[2]) { xtmp=x[2];x[2]=x[0];x[0]=xtmp;ztmp=z[2];z[2]=z[0];z[0]=ztmp; } + if (x[1] > x[2]) { xtmp=x[2];x[2]=x[1];x[1]=xtmp;ztmp=z[2];z[2]=z[1];z[1]=ztmp; } + int dx[]={ x[0]-x[1], + x[1]-x[2], + x[2]-x[0]}; + btScalar dzx[3]; + dzx[0] = (dx[0]) ? (z[0]-z[1])/dx[0] : btScalar(0.f); + dzx[1] = (dx[1]) ? (z[1]-z[2])/dx[1] : btScalar(0.f); + dzx[2] = (dx[2]) ? (z[2]-z[0])/dx[2] : btScalar(0.f); + btScalar v[3] = { dzx[0]*(mix-x[0])+z[0], + dzx[1]*(mix-x[1])+z[1], + dzx[2]*(mix-x[2])+z[2] }; + dx[0] = x[1]-x[0]; + dx[1] = x[0]-x[1]; + dx[2] = x[2]-x[0]; + btScalar* scan=&m_buffer[miy*m_sizes[0]+mix]; + for(int ix=mix;ix<mxx;++ix) + { + if(dx[0] >= 0 && POLICY::Process(*scan,v[0])) + return(true); + if(dx[1] >= 0 && POLICY::Process(*scan,v[1])) + return(true); + if(dx[2] >= 0 && POLICY::Process(*scan,v[2])) + return(true); + scan++; + v[0] += dzx[0]; v[1] += dzx[1]; v[2] += dzx[2]; + dx[0]--; dx[1]++, dx[2]--; + } + } else + { + // general case + const int dx[]={ y[0]-y[1], + y[1]-y[2], + y[2]-y[0]}; + const int dy[]={ x[1]-x[0]-dx[0]*width, + x[2]-x[1]-dx[1]*width, + x[0]-x[2]-dx[2]*width}; + const int a=x[2]*y[0]+x[0]*y[1]-x[2]*y[1]-x[0]*y[2]+x[1]*y[2]-x[1]*y[0]; + const btScalar ia=1/(btScalar)a; + const btScalar dzx=ia*(y[2]*(z[1]-z[0])+y[1]*(z[0]-z[2])+y[0]*(z[2]-z[1])); + const btScalar dzy=ia*(x[2]*(z[0]-z[1])+x[0]*(z[1]-z[2])+x[1]*(z[2]-z[0]))-(dzx*width); + int c[]={ miy*x[1]+mix*y[0]-x[1]*y[0]-mix*y[1]+x[0]*y[1]-miy*x[0], + miy*x[2]+mix*y[1]-x[2]*y[1]-mix*y[2]+x[1]*y[2]-miy*x[1], + miy*x[0]+mix*y[2]-x[0]*y[2]-mix*y[0]+x[2]*y[0]-miy*x[2]}; + btScalar v=ia*((z[2]*c[0])+(z[0]*c[1])+(z[1]*c[2])); + btScalar* scan=&m_buffer[miy*m_sizes[0]]; + for(int iy=miy;iy<mxy;++iy) + { + for(int ix=mix;ix<mxx;++ix) + { + if((c[0]>=0)&&(c[1]>=0)&&(c[2]>=0)) + { + if(POLICY::Process(scan[ix],v)) + return(true); + } + c[0]+=dx[0];c[1]+=dx[1];c[2]+=dx[2];v+=dzx; + } + c[0]+=dy[0];c[1]+=dy[1];c[2]+=dy[2];v+=dzy; + scan+=m_sizes[0]; + } + } + return(false); + } + // clip than write or check a polygon + template <const int NP,typename POLICY> + inline bool clipDraw( const btVector4* p, + const float face, + btScalar minarea) + { + btVector4 o[NP*2]; + int n=clip<NP>(p,o); + bool earlyexit=false; + if (n) + { + project(o,n); + for(int i=2;i<n && !earlyexit;++i) + { + earlyexit|=draw<POLICY>(o[0],o[i-1],o[i],face,minarea); + } + } + return(earlyexit); + } + // add a triangle (in model coordinate) + // face = 0.f if face is double side, + // = 1.f if face is single sided and scale is positive + // = -1.f if face is single sided and scale is negative + void appendOccluderM(const float* a, + const float* b, + const float* c, + const float face) + { + btVector4 p[3]; + transformM(a,p[0]); + transformM(b,p[1]); + transformM(c,p[2]); + clipDraw<3,WriteOCL>(p,face,btScalar(0.f)); + } + // add a quad (in model coordinate) + void appendOccluderM(const float* a, + const float* b, + const float* c, + const float* d, + const float face) + { + btVector4 p[4]; + transformM(a,p[0]); + transformM(b,p[1]); + transformM(c,p[2]); + transformM(d,p[3]); + clipDraw<4,WriteOCL>(p,face,btScalar(0.f)); + } + // query occluder for a box (c=center, e=extend) in world coordinate + inline bool queryOccluderW( const btVector3& c, + const btVector3& e) + { + if (!m_occlusion) + // no occlusion yet, no need to check + return true; + btVector4 x[8]; + transformW(btVector3(c[0]-e[0],c[1]-e[1],c[2]-e[2]),x[0]); + transformW(btVector3(c[0]+e[0],c[1]-e[1],c[2]-e[2]),x[1]); + transformW(btVector3(c[0]+e[0],c[1]+e[1],c[2]-e[2]),x[2]); + transformW(btVector3(c[0]-e[0],c[1]+e[1],c[2]-e[2]),x[3]); + transformW(btVector3(c[0]-e[0],c[1]-e[1],c[2]+e[2]),x[4]); + transformW(btVector3(c[0]+e[0],c[1]-e[1],c[2]+e[2]),x[5]); + transformW(btVector3(c[0]+e[0],c[1]+e[1],c[2]+e[2]),x[6]); + transformW(btVector3(c[0]-e[0],c[1]+e[1],c[2]+e[2]),x[7]); + for(int i=0;i<8;++i) + { + // the box is clipped, it's probably a large box, don't waste our time to check + if((x[i][2]+x[i][3])<=0) return(true); + } + static const int d[]={ 1,0,3,2, + 4,5,6,7, + 4,7,3,0, + 6,5,1,2, + 7,6,2,3, + 5,4,0,1}; + for(int i=0;i<(sizeof(d)/sizeof(d[0]));) + { + const btVector4 p[]={ x[d[i++]], + x[d[i++]], + x[d[i++]], + x[d[i++]]}; + if(clipDraw<4,QueryOCL>(p,1.f,0.f)) + return(true); + } + return(false); + } +}; + + +struct DbvtCullingCallback : btDbvt::ICollide +{ + PHY_CullingCallback m_clientCallback; + void* m_userData; + OcclusionBuffer *m_ocb; + + DbvtCullingCallback(PHY_CullingCallback clientCallback, void* userData) + { + m_clientCallback = clientCallback; + m_userData = userData; + m_ocb = NULL; + } + bool Descent(const btDbvtNode* node) + { + return(m_ocb->queryOccluderW(node->volume.Center(),node->volume.Extents())); + } + void Process(const btDbvtNode* node,btScalar depth) + { + Process(node); + } + void Process(const btDbvtNode* leaf) + { + btBroadphaseProxy* proxy=(btBroadphaseProxy*)leaf->data; + // the client object is a graphic controller + CcdGraphicController* ctrl = static_cast<CcdGraphicController*>(proxy->m_clientObject); + KX_ClientObjectInfo* info = (KX_ClientObjectInfo*)ctrl->getNewClientInfo(); + if (m_ocb) + { + // means we are doing occlusion culling. Check if this object is an occluders + KX_GameObject* gameobj = KX_GameObject::GetClientObject(info); + if (gameobj && gameobj->GetOccluder()) + { + double* fl = gameobj->GetOpenGLMatrixPtr()->getPointer(); + // this will create the occlusion buffer if not already done + // and compute the transformation from model local space to clip space + m_ocb->SetModelMatrix(fl); + float face = (gameobj->IsNegativeScaling()) ? -1.0f : 1.0f; + // walk through the meshes and for each add to buffer + for (int i=0; i<gameobj->GetMeshCount(); i++) + { + RAS_MeshObject* meshobj = gameobj->GetMesh(i); + const float *v1, *v2, *v3, *v4; + int polycount = meshobj->NumPolygons(); + for (int j=0; j<polycount; j++) + { + RAS_Polygon* poly = meshobj->GetPolygon(j); + switch (poly->VertexCount()) + { + case 3: + v1 = poly->GetVertex(0)->getXYZ(); + v2 = poly->GetVertex(1)->getXYZ(); + v3 = poly->GetVertex(2)->getXYZ(); + m_ocb->appendOccluderM(v1,v2,v3,((poly->IsTwoside())?0.f:face)); + break; + case 4: + v1 = poly->GetVertex(0)->getXYZ(); + v2 = poly->GetVertex(1)->getXYZ(); + v3 = poly->GetVertex(2)->getXYZ(); + v4 = poly->GetVertex(3)->getXYZ(); + m_ocb->appendOccluderM(v1,v2,v3,v4,((poly->IsTwoside())?0.f:face)); + break; + } + } + } + } + } + if (info) + (*m_clientCallback)(info, m_userData); + } +}; + +static OcclusionBuffer gOcb; +bool CcdPhysicsEnvironment::cullingTest(PHY_CullingCallback callback, void* userData, PHY__Vector4 *planes, int nplanes, int occlusionRes) +{ + if (!m_cullingTree) + return false; + DbvtCullingCallback dispatcher(callback, userData); + btVector3 planes_n[6]; + btScalar planes_o[6]; + if (nplanes > 6) + nplanes = 6; + for (int i=0; i<nplanes; i++) + { + planes_n[i].setValue(planes[i][0], planes[i][1], planes[i][2]); + planes_o[i] = planes[i][3]; + } + // if occlusionRes != 0 => occlusion culling + if (occlusionRes) + { + gOcb.setup(occlusionRes); + dispatcher.m_ocb = &gOcb; + // occlusion culling, the direction of the view is taken from the first plan which MUST be the near plane + btDbvt::collideOCL(m_cullingTree->m_sets[1].m_root,planes_n,planes_o,planes_n[0],nplanes,dispatcher); + btDbvt::collideOCL(m_cullingTree->m_sets[0].m_root,planes_n,planes_o,planes_n[0],nplanes,dispatcher); + }else + { + btDbvt::collideKDOP(m_cullingTree->m_sets[1].m_root,planes_n,planes_o,nplanes,dispatcher); + btDbvt::collideKDOP(m_cullingTree->m_sets[0].m_root,planes_n,planes_o,nplanes,dispatcher); + } + return true; +} int CcdPhysicsEnvironment::getNumContactPoints() { @@ -1211,6 +1831,13 @@ CcdPhysicsEnvironment::~CcdPhysicsEnvironment() if (NULL != m_broadphase) delete m_broadphase; + + if (NULL != m_cullingTree) + delete m_cullingTree; + + if (NULL != m_cullingCache) + delete m_cullingCache; + } @@ -1465,8 +2092,8 @@ PHY_IPhysicsController* CcdPhysicsEnvironment::CreateSphereController(float radi { CcdConstructionInfo cinfo; - // memory leak! The shape is not deleted by Bullet and we cannot add it to the KX_Scene.m_shapes list - cinfo.m_collisionShape = new btSphereShape(radius); + memset(&cinfo, 0, sizeof(cinfo)); /* avoid uninitialized values */ + cinfo.m_collisionShape = new btSphereShape(radius); // memory leak! The shape is not deleted by Bullet and we cannot add it to the KX_Scene.m_shapes list cinfo.m_MotionState = 0; cinfo.m_physicsEnv = this; // declare this object as Dyamic rather then static!! @@ -1925,7 +2552,7 @@ int CcdPhysicsEnvironment::createConstraint(class PHY_IPhysicsController* ctrl PHY_IPhysicsController* CcdPhysicsEnvironment::CreateConeController(float coneradius,float coneheight) { CcdConstructionInfo cinfo; - + memset(&cinfo, 0, sizeof(cinfo)); /* avoid uninitialized values */ // 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); diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h index 2f1f0bb254b..f861621ae37 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h @@ -20,6 +20,7 @@ subject to the following restrictions: #include <vector> #include <set> class CcdPhysicsController; +class CcdGraphicController; #include "LinearMath/btVector3.h" #include "LinearMath/btTransform.h" @@ -40,6 +41,7 @@ class btDispatcher; class WrapperVehicle; class btPersistentManifold; class btBroadphaseInterface; +struct btDbvtBroadphase; class btOverlappingPairCache; class btIDebugDraw; class PHY_IVehicle; @@ -58,7 +60,10 @@ protected: btIDebugDraw* m_debugDrawer; class btDefaultCollisionConfiguration* m_collisionConfiguration; - class btBroadphaseInterface* m_broadphase; + class btBroadphaseInterface* m_broadphase; // broadphase for dynamic world + // for culling only + btOverlappingPairCache* m_cullingCache; + struct btDbvtBroadphase* m_cullingTree; // broadphase for culling //solver iterations int m_numIterations; @@ -77,7 +82,7 @@ protected: void processFhSprings(double curTime,float timeStep); public: - CcdPhysicsEnvironment(btDispatcher* dispatcher=0, btOverlappingPairCache* pairCache=0); + CcdPhysicsEnvironment(bool useDbvtCulling, btDispatcher* dispatcher=0, btOverlappingPairCache* pairCache=0); virtual ~CcdPhysicsEnvironment(); @@ -167,6 +172,7 @@ protected: btTypedConstraint* getConstraintById(int constraintId); virtual PHY_IPhysicsController* rayTest(PHY_IRayCastFilterCallback &filterCallback, float fromX,float fromY,float fromZ, float toX,float toY,float toZ); + virtual bool cullingTest(PHY_CullingCallback callback, void* userData, PHY__Vector4* planes, int nplanes, int occlusionRes); //Methods for gamelogic collision/physics callbacks @@ -200,7 +206,12 @@ protected: void refreshCcdPhysicsController(CcdPhysicsController* ctrl); + void addCcdGraphicController(CcdGraphicController* ctrl); + + void removeCcdGraphicController(CcdGraphicController* ctrl); + btBroadphaseInterface* getBroadphase(); + btDbvtBroadphase* getCullingTree() { return m_cullingTree; } btDispatcher* getDispatcher(); diff --git a/source/gameengine/Physics/Bullet/Makefile b/source/gameengine/Physics/Bullet/Makefile index d5570e75833..48e537bb6a3 100644 --- a/source/gameengine/Physics/Bullet/Makefile +++ b/source/gameengine/Physics/Bullet/Makefile @@ -39,8 +39,16 @@ CPPFLAGS += -I$(NAN_BULLET2)/include CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include CPPFLAGS += -I$(NAN_STRING)/include CPPFLAGS += -I$(NAN_MOTO)/include +CPPFLAGS += -I$(NAN_GLEW)/include +CPPFLAGS += -I$(NAN_PYTHON)/include/python$(NAN_PYTHON_VERSION) +CPPFLAGS += -I$(NAN_SOUNDSYSTEM)/include CPPFLAGS += -I../../../kernel/gen_system CPPFLAGS += -I../../Physics/common CPPFLAGS += -I../../Physics/Dummy CPPFLAGS += -I../../Rasterizer +CPPFLAGS += -I../../Ketsji +CPPFLAGS += -I../../Expressions +CPPFLAGS += -I../../GameLogic +CPPFLAGS += -I../../SceneGraph +CPPFLAGS += -I../../../../source/blender/makesdna diff --git a/source/gameengine/Physics/Bullet/SConscript b/source/gameengine/Physics/Bullet/SConscript index db9f3387bfe..ab2c68ddfd5 100644 --- a/source/gameengine/Physics/Bullet/SConscript +++ b/source/gameengine/Physics/Bullet/SConscript @@ -1,11 +1,23 @@ #!/usr/bin/python Import ('env') -sources = 'CcdPhysicsEnvironment.cpp CcdPhysicsController.cpp' +sources = 'CcdPhysicsEnvironment.cpp CcdPhysicsController.cpp CcdGraphicController.cpp' -incs = '. ../common #source/kernel/gen_system #intern/string #intern/moto/include #source/gameengine/Rasterizer' +incs = '. ../common' +incs += ' #source/kernel/gen_system' +incs += ' #intern/string' +incs += ' #intern/moto/include' +incs += ' #extern/glew/include' +incs += ' #source/gameengine/Rasterizer' +incs += ' #source/gameengine/Ketsji' +incs += ' #source/gameengine/Expressions' +incs += ' #source/gameengine/GameLogic' +incs += ' #source/gameengine/SceneGraph' +incs += ' #source/blender/makesdna' +incs += ' #intern/SoundSystem' incs += ' ' + env['BF_BULLET_INC'] +incs += ' ' + env['BF_PYTHON_INC'] cxxflags = [] if env['OURPLATFORM']=='win32-vc': |