diff --git a/rwengine/include/engine/GameWorld.hpp b/rwengine/include/engine/GameWorld.hpp index f56b7b67..9f064c00 100644 --- a/rwengine/include/engine/GameWorld.hpp +++ b/rwengine/include/engine/GameWorld.hpp @@ -197,10 +197,10 @@ public: btDiscreteDynamicsWorld* dynamicsWorld; /** - * @brief handleCollisionResponses performs physics response checking - * for collisions between vehicles, objects etc. + * @brief physicsNearCallback + * Used to implement uprooting and other physics oddities. */ - void handleCollisionResponses(); + static bool ContactProcessedCallback(btManifoldPoint& mp, void* body0, void* body1); /** * Work related diff --git a/rwengine/include/objects/InstanceObject.hpp b/rwengine/include/objects/InstanceObject.hpp index 08ab798d..1573760d 100644 --- a/rwengine/include/objects/InstanceObject.hpp +++ b/rwengine/include/objects/InstanceObject.hpp @@ -16,6 +16,7 @@ struct InstanceObject : public GameObject std::shared_ptr LODinstance; std::shared_ptr dynamics; float _collisionHeight; + bool _enablePhysics; InstanceObject( GameWorld* engine, diff --git a/rwengine/src/engine/GameWorld.cpp b/rwengine/src/engine/GameWorld.cpp index 762ddaf8..0cccafc1 100644 --- a/rwengine/src/engine/GameWorld.cpp +++ b/rwengine/src/engine/GameWorld.cpp @@ -11,6 +11,59 @@ #include #include +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(obA->getUserPointer()); + GameObject* b = static_cast(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(a); + instBody = static_cast(obA); + otherBody = static_cast(obB); + } + else { + dynInst = static_cast(b); + instBody = static_cast(obB); + otherBody = static_cast(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) : gameTime(0.f), gameData(path), renderer(this), randomEngine(rand()), _work( new WorkContext( this ) ) @@ -27,13 +80,14 @@ GameWorld::~GameWorld() bool GameWorld::load() { collisionConfig = new btDefaultCollisionConfiguration; - collisionDispatcher = new btCollisionDispatcher(collisionConfig); + collisionDispatcher = new WorldCollisionDispatcher(collisionConfig); broadphase = new btDbvtBroadphase(); solver = new btSequentialImpulseConstraintSolver; dynamicsWorld = new btDiscreteDynamicsWorld(collisionDispatcher, broadphase, solver, collisionConfig); dynamicsWorld->setGravity(btVector3(0.f, 0.f, -9.81f)); broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback()); - + gContactProcessedCallback = ContactProcessedCallback; + gameData.load(); return true; @@ -358,58 +412,58 @@ int GameWorld::getMinute() return fmod(gameTime, 60.f); } -void GameWorld::handleCollisionResponses() +bool GameWorld::ContactProcessedCallback(btManifoldPoint &mp, void *body0, void *body1) { - int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds(); - for (int i = 0; i < numManifolds; i++) - { - btPersistentManifold* contactManifold = dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i); - auto obA = static_cast(contactManifold->getBody0()); - auto obB = static_cast(contactManifold->getBody1()); + auto obA = static_cast(body0); + auto obB = static_cast(body1); - int numContacts = contactManifold->getNumContacts(); - for (int j = 0; j < numContacts; j++) - { - // Check that at least one of the objects involved is an instance. - GameObject* a = static_cast(obA->getUserPointer()); - GameObject* b = static_cast(obB->getUserPointer()); - btManifoldPoint& pt = contactManifold->getContactPoint(j); - - if(a && a->type() == GameObject::Instance) { - auto inst = static_cast(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(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() - }); - } - } - } - } + if( !( obA->getUserPointer() && obB->getUserPointer() ) ) { + return false; } + GameObject* a = static_cast(obA->getUserPointer()); + GameObject* b = static_cast(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(a); + instBody = static_cast(obA); + otherBody = static_cast(obB); + } + else { + dynInst = static_cast(b); + instBody = static_cast(obB); + otherBody = static_cast(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; } + diff --git a/rwengine/src/objects/InstanceObject.cpp b/rwengine/src/objects/InstanceObject.cpp index 91bd4833..af0b6890 100644 --- a/rwengine/src/objects/InstanceObject.cpp +++ b/rwengine/src/objects/InstanceObject.cpp @@ -11,7 +11,7 @@ InstanceObject::InstanceObject(GameWorld* engine, std::shared_ptr lod, std::shared_ptr dyn) : 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); if( phyit != engine->gameData.collisions.end()) { @@ -89,6 +89,11 @@ InstanceObject::InstanceObject(GameWorld* engine, body->setUserPointer(this); engine->dynamicsWorld->addRigidBody(body); + if( dynamics && dynamics->uprootForce > 0.f ) { + body->setCollisionFlags(body->getCollisionFlags() + | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); + } + body->setActivationState(ISLAND_SLEEPING); } @@ -105,6 +110,17 @@ InstanceObject::InstanceObject(GameWorld* engine, void InstanceObject::tick(float dt) { 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(); 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)); @@ -174,12 +190,7 @@ bool InstanceObject::takeDamage(const GameObject::DamageInfo& dmg) smash = dynamics->collDamageFlags == 80; if( dmg.impulse >= dynamics->uprootForce && (body->getCollisionFlags() & btRigidBody::CF_STATIC_OBJECT) != 0 ) { - // 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); + _enablePhysics = true; } } if(explodeOnHit || smash) diff --git a/rwgame/main.cpp b/rwgame/main.cpp index 20933c20..7d87d79c 100644 --- a/rwgame/main.cpp +++ b/rwgame/main.cpp @@ -351,8 +351,6 @@ void update(float dt) } gta->dynamicsWorld->stepSimulation(dt, 2, dt); - - gta->handleCollisionResponses(); } }