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:
parent
0a3167d1e9
commit
7063952d56
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -351,8 +351,6 @@ void update(float dt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
gta->dynamicsWorld->stepSimulation(dt, 2, dt);
|
gta->dynamicsWorld->stepSimulation(dt, 2, dt);
|
||||||
|
|
||||||
gta->handleCollisionResponses();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user