346 lines
12 KiB
C++
346 lines
12 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "physic_bullet/bullet_world.h"
|
|
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
|
|
#include "physic_bullet/bullet_item.h"
|
|
#include "physic_bullet/bullet_constraint.h"
|
|
#include "physic_bullet/bullet_debug.h"
|
|
#include "core/geometry.h"
|
|
#include "metafile/nml_object.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS;
|
|
using namespace GS::S3D;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
PhysicItem *BulletWorld::NewItem()
|
|
{ return new BulletPhysicItem(world); }
|
|
PhysicConstraint *BulletWorld::NewConstraint()
|
|
{ return new BulletConstraint(world); }
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
BulletConvex *BulletWorld::LoadConvex(const char *_name)
|
|
{
|
|
String name(_name);
|
|
|
|
// Check cache.
|
|
ListForeachPtr(BulletConvex *, convex, convex_cache)
|
|
if (convex->name == name)
|
|
return convex;
|
|
|
|
// Load geometry.
|
|
AutoPtr <Core::Geometry> g(new Core::Geometry);
|
|
if (!NML::LoadFromFile(*g, name))
|
|
return NULL;
|
|
|
|
// Setup convex.
|
|
BulletConvex *bullet_convex = new BulletConvex;
|
|
if (!bullet_convex)
|
|
__ERR__(__LOG_E__ << "Failed to allocate bullet convex.\n", NULL)
|
|
|
|
Array <btScalar> bt_vtx(g->vtx.GetCount() * 3);
|
|
btScalar *p_bt_vtx = bt_vtx.c_ptr();
|
|
|
|
Vector4 gcenter(0, 0, 0);
|
|
for (uint n = 0; n < g->vtx.GetCount(); ++n)
|
|
{
|
|
gcenter += g->vtx[n];
|
|
*p_bt_vtx++ = g->vtx[n].x;
|
|
*p_bt_vtx++ = g->vtx[n].y;
|
|
*p_bt_vtx++ = g->vtx[n].z;
|
|
}
|
|
|
|
bullet_convex->name = name;
|
|
bullet_convex->center = (gcenter / (float)g->vtx.GetCount());
|
|
bullet_convex->convex = new btConvexHullShape(bt_vtx.c_ptr(), g->vtx.GetCount(), 3 * sizeof(btScalar));
|
|
convex_cache.Add(bullet_convex);
|
|
|
|
return bullet_convex;
|
|
}
|
|
BulletMesh *BulletWorld::LoadMesh(const char *_name, const char *_suffix)
|
|
{
|
|
String name(_name), suffix(_suffix);
|
|
|
|
// Check cache.
|
|
ListForeachPtr(BulletMesh *, mesh, mesh_cache)
|
|
if ((mesh->name == name) && (mesh->suffix == suffix))
|
|
{
|
|
__LOG_V__ << "Reusing cached Bullet btMesh for " << _name << " (suffix: " << _suffix << ").\n";
|
|
return mesh;
|
|
}
|
|
|
|
// Load bullet mesh.
|
|
AutoPtr <Core::Geometry> g(new Core::Geometry);
|
|
if (g.IsNull())
|
|
return NULL;
|
|
|
|
g->name = name;
|
|
if (!NML::LoadFromFile(*g, name))
|
|
return NULL;
|
|
|
|
if (!g->vtx.GetCount() || !g->pol.GetCount())
|
|
__ERR__(__LOG_E__ << "No geometry data in '" << g->name << "' to build collision shape.\n", NULL)
|
|
|
|
BulletMesh *bullet_mesh = new BulletMesh;
|
|
if (!bullet_mesh)
|
|
__ERR__(__LOG_E__ << "Failed to allocate bullet mesh.\n", NULL)
|
|
|
|
int triangle_count = g->GetTriangleCount();
|
|
|
|
bullet_mesh->bt_vtx.Allocate(g->vtx.GetCount() * 3);
|
|
btScalar *p_bt_vtx = bullet_mesh->bt_vtx;
|
|
bullet_mesh->bt_idx.Allocate(triangle_count * 3);
|
|
int *p_bt_idx = bullet_mesh->bt_idx;
|
|
|
|
Vector4 gcenter(0, 0, 0);
|
|
for (uint n = 0; n < g->vtx.GetCount(); ++n)
|
|
{
|
|
gcenter += g->vtx[n];
|
|
*p_bt_vtx++ = g->vtx[n].x;
|
|
*p_bt_vtx++ = g->vtx[n].y;
|
|
*p_bt_vtx++ = g->vtx[n].z;
|
|
}
|
|
bullet_mesh->center = gcenter / (float)g->vtx.GetCount();
|
|
|
|
// Triangulate geometry on the fly, transfer material indices.
|
|
bullet_mesh->bt_mat.Allocate(g->material_table.GetCount());
|
|
for (uint n = 0; n < g->material_table.GetCount(); ++n)
|
|
bullet_mesh->bt_mat[n] = g->material_table[n].name;
|
|
|
|
bullet_mesh->bt_id_mat.Allocate(triangle_count);
|
|
ushort *p_bt_id_mat = bullet_mesh->bt_id_mat;
|
|
|
|
for (uint n = 0; n < g->pol.GetCount(); ++n)
|
|
for (int p = 1; p < (g->pol[n].vtx_count - 1); ++p)
|
|
{
|
|
*p_bt_idx++ = g->pol[n].binding[0];
|
|
*p_bt_idx++ = g->pol[n].binding[p];
|
|
*p_bt_idx++ = g->pol[n].binding[p + 1];
|
|
*p_bt_id_mat++ = g->pol[n].material;
|
|
}
|
|
|
|
bullet_mesh->name = name;
|
|
bullet_mesh->suffix = suffix;
|
|
bullet_mesh->mesh_interface = new btTriangleIndexVertexArray(triangle_count, bullet_mesh->bt_idx, 3 * sizeof(int), g->vtx.GetCount(), bullet_mesh->bt_vtx, 3 * sizeof(btScalar));
|
|
bullet_mesh->mesh = new btBvhTriangleMeshShape(bullet_mesh->mesh_interface, true);
|
|
|
|
mesh_cache.Add(bullet_mesh);
|
|
|
|
return bullet_mesh;
|
|
}
|
|
void BulletWorld::ClearConvexMeshCache()
|
|
{
|
|
convex_cache.Clear();
|
|
mesh_cache.Clear();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool BulletWorld::HasDebugger() const
|
|
{ return debug_draw.IsValid(); }
|
|
void BulletWorld::CreateDebugger(Renderer *renderer)
|
|
{
|
|
debug_draw = renderer ? new BulletDebugDraw(*renderer) : NULL;
|
|
if (world)
|
|
world->setDebugDrawer(debug_draw);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void bullet_pretick_callback(btDynamicsWorld *world, btScalar timeStep)
|
|
{
|
|
BulletWorld *physic_world = (BulletWorld *)world->getWorldUserInfo();
|
|
if (physic_world->GetWorldInterface())
|
|
physic_world->GetWorldInterface()->PhysicStep(timeStep, true);
|
|
}
|
|
static void bullet_posttick_callback(btDynamicsWorld *world, btScalar timeStep)
|
|
{
|
|
BulletWorld *physic_world = (BulletWorld *)world->getWorldUserInfo();
|
|
if (physic_world->GetWorldInterface())
|
|
physic_world->GetWorldInterface()->PhysicStep(timeStep, false);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void BulletWorld::DrawDebug(Renderer &, Camera *c, RasterFont *f, bool xray_first_pass)
|
|
{
|
|
if (BulletDebugDraw *dd = (BulletDebugDraw *)world->getDebugDrawer())
|
|
{
|
|
dd->camera = c;
|
|
dd->raster_font = f;
|
|
dd->SetDebugMode(btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawConstraints | btIDebugDraw::DBG_DrawConstraintLimits | btIDebugDraw::DBG_DrawContactPoints);
|
|
// dd->SetDebugMode(btIDebugDraw::DBG_DrawAabb | btIDebugDraw::DBG_FastWireframe);
|
|
dd->SetXRayFirstPass(xray_first_pass);
|
|
|
|
world->debugDrawWorld();
|
|
dd->Flush();
|
|
}
|
|
}
|
|
uint BulletWorld::GetCollisionPairCount()
|
|
{ return world->getDispatcher()->getNumManifolds(); }
|
|
bool BulletWorld::GetCollisionPair(uint n, CollisionPair &pair)
|
|
{
|
|
btPersistentManifold *manifold = world->getDispatcher()->getInternalManifoldPointer()[n];
|
|
if (!manifold || !manifold->getNumContacts()) // Manifolds are valid as long as the bodies overlap in the broadphase.
|
|
return false;
|
|
|
|
pair.a = (PhysicItem *)((btRigidBody *)manifold->getBody0())->getUserPointer();
|
|
pair.b = (PhysicItem *)((btRigidBody *)manifold->getBody1())->getUserPointer();
|
|
|
|
pair.contact_count = 0;
|
|
for (int i = 0; (i < manifold->getNumContacts()) && (i < 4); ++i)
|
|
{
|
|
btVector3 p = manifold->getContactPoint(i).getPositionWorldOnB();
|
|
pair.contact[i].Set(p.x(), p.y(), p.z());
|
|
btVector3 n = manifold->getContactPoint(i).m_normalWorldOnB;
|
|
pair.normal[i].Set(n.x(), n.y(), n.z());
|
|
|
|
pair.contact_count++;
|
|
}
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool BulletWorld::Create()
|
|
{
|
|
collision_config = new btDefaultCollisionConfiguration();
|
|
|
|
#if __ENABLE_BULLET_MULTITHREAD__
|
|
thread_support_collision = new Win32ThreadSupport(Win32ThreadSupport::Win32ThreadConstructionInfo("Bullet Collision", processCollisionTask, createCollisionLocalStoreMemory, __BULLET_THREAD_COUNT__));
|
|
dispatcher = new SpuGatheringCollisionDispatcher(thread_support_collision, __BULLET_THREAD_COUNT__, collision_config);
|
|
#else
|
|
dispatcher = new btCollisionDispatcher(collision_config);
|
|
#endif
|
|
|
|
broadphase = new btDbvtBroadphase();
|
|
broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(pair_callback = new btGhostPairCallback);
|
|
|
|
solver = new btSequentialImpulseConstraintSolver;
|
|
world = new btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collision_config);
|
|
world->setInternalTickCallback(bullet_pretick_callback, (void *)this, true);
|
|
world->setInternalTickCallback(bullet_posttick_callback, (void *)this, false);
|
|
|
|
// world->getSolverInfo().m_numIterations = 10;
|
|
// world->getDispatchInfo().m_enableSPU = true;
|
|
world->getSolverInfo().m_solverMode = SOLVER_SIMD + SOLVER_USE_WARMSTARTING;// + SOLVER_RANDMIZE_ORDER;
|
|
// world->getSolverInfo().m_splitImpulse = 1;
|
|
// world->getSolverInfo().m_splitImpulsePenetrationThreshold = 0.2;
|
|
|
|
world->setDebugDrawer(debug_draw);
|
|
return true;
|
|
}
|
|
void BulletWorld::Delete()
|
|
{
|
|
ClearConvexMeshCache();
|
|
|
|
collision_config = NULL;
|
|
dispatcher = NULL;
|
|
broadphase = NULL;
|
|
solver = NULL;
|
|
world = NULL;
|
|
#if __ENABLE_BULLET_MULTITHREAD__
|
|
thread_support_collision = NULL;
|
|
#endif
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void BulletWorld::Step(const GS::Time &dt)
|
|
{
|
|
ScopedBenchmark bench(bench_step);
|
|
|
|
#if 1
|
|
substep_dt -= dt.toSec();
|
|
|
|
int limit = 4;
|
|
while (substep_dt < 0)
|
|
{
|
|
world->stepSimulation(GetTimestep(), 0, GetTimestep());
|
|
substep_dt += GetTimestep();
|
|
|
|
if (--limit <= 0)
|
|
{
|
|
substep_dt = 0;
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
world->stepSimulation(dt, 12, GetTimestep());
|
|
#endif
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool BulletWorld::Raytrace(const Vector4 &s, const Vector4 &d, PhysicTrace &hit, int collision_mask, int shape_mask, float max_distance)
|
|
{
|
|
struct ClosestRayResultWithTriangleIndexCallback : public btCollisionWorld::ClosestRayResultCallback
|
|
{
|
|
ClosestRayResultWithTriangleIndexCallback(const btVector3 &rayFromWorld, const btVector3 &rayToWorld) : ClosestRayResultCallback(rayFromWorld, rayToWorld) {}
|
|
|
|
int m_TriangleIndex;
|
|
int m_shapePart;
|
|
|
|
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult &rayResult, bool normalInWorldSpace)
|
|
{
|
|
if (rayResult.m_localShapeInfo)
|
|
{
|
|
m_TriangleIndex = rayResult.m_localShapeInfo->m_triangleIndex;
|
|
m_shapePart = rayResult.m_localShapeInfo->m_shapePart;
|
|
}
|
|
else
|
|
{
|
|
m_TriangleIndex = -1;
|
|
m_shapePart = -1;
|
|
}
|
|
return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
|
}
|
|
};
|
|
|
|
Vector4 e = s + d * (max_distance > 0 ? max_distance : 5000.f);
|
|
btVector3 from(s.x, s.y, s.z), to(e.x, e.y, e.z);
|
|
|
|
ClosestRayResultWithTriangleIndexCallback trace(from, to);
|
|
trace.m_collisionFilterGroup = btBroadphaseProxy::AllFilter;
|
|
trace.m_collisionFilterMask = (short)collision_mask;
|
|
world->rayTest(from, to, trace);
|
|
if (!trace.hasHit())
|
|
return false;
|
|
|
|
hit.p.Set(trace.m_hitPointWorld.x(), trace.m_hitPointWorld.y(), trace.m_hitPointWorld.z());
|
|
hit.n.Set(trace.m_hitNormalWorld.x(), trace.m_hitNormalWorld.y(), trace.m_hitNormalWorld.z());
|
|
hit.i = (PhysicItem *)trace.m_collisionObject->getUserPointer();
|
|
|
|
if (BulletPhysicItem *bi = (BulletPhysicItem *)hit.i)
|
|
if ((trace.m_shapePart >= 0) && ((uint)trace.m_shapePart < bi->shapes.GetCount()))
|
|
if (BulletMesh *mesh = bi->shapes[trace.m_shapePart].mesh.c_ptr())
|
|
if (uint(trace.m_TriangleIndex) < mesh->bt_id_mat.GetCount())
|
|
hit.m = mesh->bt_mat[mesh->bt_id_mat[trace.m_TriangleIndex]];
|
|
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void *bulletAlloc(size_t s) { return MemAllocPhysics::Alloc(s, Alloc::Physics); }
|
|
static void bulletFree(void *p) { MemAllocPhysics::Delete(p, Alloc::Physics); }
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
BulletWorld::BulletWorld()
|
|
{
|
|
btAlignedAllocSetCustom(bulletAlloc, bulletFree);
|
|
substep_dt = 0;
|
|
}
|
|
BulletWorld::~BulletWorld()
|
|
{
|
|
Delete();
|
|
}
|
|
//------------------------------------------------------------------------------
|