1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-25 11:52:40 +01:00

Redo uprooting dynamics

This commit is contained in:
Daniel Evans 2014-06-16 01:29:36 +01:00
parent 0a3167d1e9
commit 7063952d56
5 changed files with 128 additions and 64 deletions

View File

@ -197,10 +197,10 @@ public:
btDiscreteDynamicsWorld* dynamicsWorld; btDiscreteDynamicsWorld* dynamicsWorld;
/** /**
* @brief handleCollisionResponses performs physics response checking * @brief physicsNearCallback
* for collisions between vehicles, objects etc. * Used to implement uprooting and other physics oddities.
*/ */
void handleCollisionResponses(); static bool ContactProcessedCallback(btManifoldPoint& mp, void* body0, void* body1);
/** /**
* Work related * Work related

View File

@ -16,6 +16,7 @@ struct InstanceObject : public GameObject
std::shared_ptr<InstanceObject> LODinstance; std::shared_ptr<InstanceObject> LODinstance;
std::shared_ptr<DynamicObjectData> dynamics; std::shared_ptr<DynamicObjectData> dynamics;
float _collisionHeight; float _collisionHeight;
bool _enablePhysics;
InstanceObject( InstanceObject(
GameWorld* engine, GameWorld* engine,

View File

@ -11,6 +11,59 @@
#include <objects/InstanceObject.hpp> #include <objects/InstanceObject.hpp>
#include <objects/VehicleObject.hpp> #include <objects/VehicleObject.hpp>
class WorldCollisionDispatcher : public btCollisionDispatcher
{
public:
WorldCollisionDispatcher(btCollisionConfiguration* collisionConfiguration)
: btCollisionDispatcher(collisionConfiguration)
{}
bool needsResponse(const btCollisionObject *obA, const btCollisionObject *obB) {
if( !( obA->getUserPointer() && obB->getUserPointer() ) ) {
return btCollisionDispatcher::needsResponse(obA, obB);
}
GameObject* a = static_cast<GameObject*>(obA->getUserPointer());
GameObject* b = static_cast<GameObject*>(obB->getUserPointer());
bool valA = a && a->type() == GameObject::Instance;
bool valB = b && b->type() == GameObject::Instance;
if( ! (valA && valB) && (valB || valA) ) {
// Figure out which is the dynamic instance.
InstanceObject* dynInst = nullptr;
const btRigidBody* instBody = nullptr, * otherBody = nullptr;
if( valA ) {
dynInst = static_cast<InstanceObject*>(a);
instBody = static_cast<const btRigidBody*>(obA);
otherBody = static_cast<const btRigidBody*>(obB);
}
else {
dynInst = static_cast<InstanceObject*>(b);
instBody = static_cast<const btRigidBody*>(obB);
otherBody = static_cast<const btRigidBody*>(obA);
}
if( dynInst->dynamics == nullptr || ! instBody->isStaticObject() ) {
return btCollisionDispatcher::needsResponse(obA, obB);
}
// Attempt to determine relative velocity.
auto dV = (otherBody->getLinearVelocity());
auto impulse = dV.length();
// Ignore collision if the object is about to be uprooted.
if( dynInst->dynamics->uprootForce <= impulse / (otherBody->getInvMass()) ) {
return false;
}
}
return btCollisionDispatcher::needsResponse(obA, obB);
}
};
GameWorld::GameWorld(const std::string& path) GameWorld::GameWorld(const std::string& path)
: gameTime(0.f), gameData(path), renderer(this), randomEngine(rand()), : gameTime(0.f), gameData(path), renderer(this), randomEngine(rand()),
_work( new WorkContext( this ) ) _work( new WorkContext( this ) )
@ -27,13 +80,14 @@ GameWorld::~GameWorld()
bool GameWorld::load() bool GameWorld::load()
{ {
collisionConfig = new btDefaultCollisionConfiguration; collisionConfig = new btDefaultCollisionConfiguration;
collisionDispatcher = new btCollisionDispatcher(collisionConfig); collisionDispatcher = new WorldCollisionDispatcher(collisionConfig);
broadphase = new btDbvtBroadphase(); broadphase = new btDbvtBroadphase();
solver = new btSequentialImpulseConstraintSolver; solver = new btSequentialImpulseConstraintSolver;
dynamicsWorld = new btDiscreteDynamicsWorld(collisionDispatcher, broadphase, solver, collisionConfig); dynamicsWorld = new btDiscreteDynamicsWorld(collisionDispatcher, broadphase, solver, collisionConfig);
dynamicsWorld->setGravity(btVector3(0.f, 0.f, -9.81f)); dynamicsWorld->setGravity(btVector3(0.f, 0.f, -9.81f));
broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback()); broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
gContactProcessedCallback = ContactProcessedCallback;
gameData.load(); gameData.load();
return true; return true;
@ -358,58 +412,58 @@ int GameWorld::getMinute()
return fmod(gameTime, 60.f); return fmod(gameTime, 60.f);
} }
void GameWorld::handleCollisionResponses() bool GameWorld::ContactProcessedCallback(btManifoldPoint &mp, void *body0, void *body1)
{ {
int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds(); auto obA = static_cast<btCollisionObject*>(body0);
for (int i = 0; i < numManifolds; i++) auto obB = static_cast<btCollisionObject*>(body1);
{
btPersistentManifold* contactManifold = dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i);
auto obA = static_cast<const btCollisionObject*>(contactManifold->getBody0());
auto obB = static_cast<const btCollisionObject*>(contactManifold->getBody1());
int numContacts = contactManifold->getNumContacts(); if( !( obA->getUserPointer() && obB->getUserPointer() ) ) {
for (int j = 0; j < numContacts; j++) return false;
{
// Check that at least one of the objects involved is an instance.
GameObject* a = static_cast<GameObject*>(obA->getUserPointer());
GameObject* b = static_cast<GameObject*>(obB->getUserPointer());
btManifoldPoint& pt = contactManifold->getContactPoint(j);
if(a && a->type() == GameObject::Instance) {
auto inst = static_cast<InstanceObject*>(a);
if( inst->dynamics ) {
if( pt.getAppliedImpulse() > inst->dynamics->uprootForce ) {
auto dmg = pt.getPositionWorldOnA();
auto src = pt.getPositionWorldOnB();
inst->takeDamage({
{dmg.x(), dmg.y(), dmg.z()},
{src.x(), src.y(), src.z()},
0.f,
GameObject::DamageInfo::Physics,
pt.getAppliedImpulse()
});
}
}
}
if(b && b->type() == GameObject::Instance) {
auto inst = static_cast<InstanceObject*>(b);
if( inst->dynamics ) {
if( pt.getAppliedImpulse() > inst->dynamics->uprootForce ) {
auto dmg = pt.getPositionWorldOnB();
auto src = pt.getPositionWorldOnA();
inst->takeDamage({
{dmg.x(), dmg.y(), dmg.z()},
{src.x(), src.y(), src.z()},
0.f,
GameObject::DamageInfo::Physics,
pt.getAppliedImpulse()
});
}
}
}
}
} }
GameObject* a = static_cast<GameObject*>(obA->getUserPointer());
GameObject* b = static_cast<GameObject*>(obB->getUserPointer());
bool valA = a && a->type() == GameObject::Instance;
bool valB = b && b->type() == GameObject::Instance;
if( ! (valA && valB) && (valB || valA) ) {
// Figure out which is the dynamic instance.
InstanceObject* dynInst = nullptr;
const btRigidBody* instBody = nullptr, * otherBody = nullptr;
if( valA ) {
dynInst = static_cast<InstanceObject*>(a);
instBody = static_cast<const btRigidBody*>(obA);
otherBody = static_cast<const btRigidBody*>(obB);
}
else {
dynInst = static_cast<InstanceObject*>(b);
instBody = static_cast<const btRigidBody*>(obB);
otherBody = static_cast<const btRigidBody*>(obA);
}
if( dynInst->dynamics == nullptr || ! instBody->isStaticObject() ) {
return false;
}
// Attempt to determine relative velocity.
auto dV = (otherBody->getLinearVelocity());
auto impulse = dV.length()/ (otherBody->getInvMass());
auto src = otherBody->getWorldTransform().getOrigin();
// Ignore collision if the object is about to be uprooted.
if( dynInst->dynamics->uprootForce <= impulse ) {
dynInst->takeDamage({
dynInst->getPosition(),
{src.x(), src.y(), src.z()},
0.f,
GameObject::DamageInfo::Physics,
impulse
});
}
}
return true;
} }

View File

@ -11,7 +11,7 @@ InstanceObject::InstanceObject(GameWorld* engine,
std::shared_ptr<InstanceObject> lod, std::shared_ptr<InstanceObject> lod,
std::shared_ptr<DynamicObjectData> dyn) std::shared_ptr<DynamicObjectData> dyn)
: GameObject(engine, pos, rot, model), scale(scale), object(obj), : GameObject(engine, pos, rot, model), scale(scale), object(obj),
LODinstance(lod), dynamics(dyn), _collisionHeight(0.f) LODinstance(lod), dynamics(dyn), _collisionHeight(0.f), _enablePhysics(false)
{ {
auto phyit = engine->gameData.collisions.find(obj->modelName); auto phyit = engine->gameData.collisions.find(obj->modelName);
if( phyit != engine->gameData.collisions.end()) { if( phyit != engine->gameData.collisions.end()) {
@ -89,6 +89,11 @@ InstanceObject::InstanceObject(GameWorld* engine,
body->setUserPointer(this); body->setUserPointer(this);
engine->dynamicsWorld->addRigidBody(body); engine->dynamicsWorld->addRigidBody(body);
if( dynamics && dynamics->uprootForce > 0.f ) {
body->setCollisionFlags(body->getCollisionFlags()
| btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
}
body->setActivationState(ISLAND_SLEEPING); body->setActivationState(ISLAND_SLEEPING);
} }
@ -105,6 +110,17 @@ InstanceObject::InstanceObject(GameWorld* engine,
void InstanceObject::tick(float dt) void InstanceObject::tick(float dt)
{ {
if( dynamics ) { if( dynamics ) {
if( body->isStaticObject() ) {
if( _enablePhysics ) {
// Apparently bodies must be removed and re-added if their mass changes.
engine->dynamicsWorld->removeRigidBody(body);
btVector3 inert;
body->getCollisionShape()->calculateLocalInertia(dynamics->mass, inert);
body->setMassProps(dynamics->mass, inert);
engine->dynamicsWorld->addRigidBody(body);
}
}
auto _bws = body->getWorldTransform().getOrigin(); auto _bws = body->getWorldTransform().getOrigin();
glm::vec3 ws(_bws.x(), _bws.y(), _bws.z()); glm::vec3 ws(_bws.x(), _bws.y(), _bws.z());
auto wX = (int) ((ws.x + WATER_WORLD_SIZE/2.f) / (WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE)); auto wX = (int) ((ws.x + WATER_WORLD_SIZE/2.f) / (WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE));
@ -174,12 +190,7 @@ bool InstanceObject::takeDamage(const GameObject::DamageInfo& dmg)
smash = dynamics->collDamageFlags == 80; smash = dynamics->collDamageFlags == 80;
if( dmg.impulse >= dynamics->uprootForce && (body->getCollisionFlags() & btRigidBody::CF_STATIC_OBJECT) != 0 ) { if( dmg.impulse >= dynamics->uprootForce && (body->getCollisionFlags() & btRigidBody::CF_STATIC_OBJECT) != 0 ) {
// Apparently bodies must be removed and re-added if their mass changes. _enablePhysics = true;
engine->dynamicsWorld->removeRigidBody(body);
btVector3 inert;
body->getCollisionShape()->calculateLocalInertia(dynamics->mass, inert);
body->setMassProps(dynamics->mass, inert);
engine->dynamicsWorld->addRigidBody(body);
} }
} }
if(explodeOnHit || smash) if(explodeOnHit || smash)

View File

@ -351,8 +351,6 @@ void update(float dt)
} }
gta->dynamicsWorld->stepSimulation(dt, 2, dt); gta->dynamicsWorld->stepSimulation(dt, 2, dt);
gta->handleCollisionResponses();
} }
} }