#include #include #include #include #include #include #include #include #include #include "Components.h" #include "GameData.h" #include "CharacterModule.h" #include "WaterModule.h" #include "BoatModule.h" #include "EventTriggerModule.h" #include "LuaData.h" #include "physics.h" #include "loader.h" #include "EventModule.h" #include "TerrainModule.h" #include "PhysicsModule.h" namespace ECS { struct PhysicsShape { JPH::ShapeRefC shape; }; struct ConvexHull {}; struct WaterBody { std::set inWater; bool isInWater(const JPH::BodyID &id) const; }; struct TriggerBody { void *foo; }; PhysicsModule::PhysicsModule(flecs::world &ecs) { ecs.module(); ecs.import (); ecs.import (); ecs.import (); flecs::entity PhysicsPreUpdate = ecs.entity().add(flecs::Phase).depends_on(flecs::OnUpdate); flecs::entity PhysicsUpdate = ecs.entity().add(flecs::Phase).depends_on(PhysicsPreUpdate); flecs::entity PhysicsPostUpdate = ecs.entity().add(flecs::Phase).depends_on(PhysicsUpdate); ecs.component().add(flecs::Singleton); ecs.component().member("mID"); /* for terrain */ ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component().add(flecs::Singleton); ecs.component(); ecs.import (); ecs.import (); ecs.system("physics_init") .kind(PhysicsPreUpdate) .without() .each([&](const EngineData &e, const Camera &c) { Physics &ph = ECS::get().ensure(); ph.physics = new JoltPhysicsWrapper(e.mScnMgr, c.mCameraNode); ECS::modified(); }); #if 0 ecs.system("create_body") .kind(flecs::OnUpdate) .without() .each([&](flecs::entity e, PhysicsBody &rb) { JPH::BodyID id = JoltPhysicsWrapper::getSingleton().createBody( rb.shape.get(), rb.node, (JPH::EMotionType)rb.motion, (JPH::ObjectLayer)rb.layer); e.set(id); }); #endif ecs.system("physics_update") .kind(PhysicsUpdate) .each([&](EngineData &e, Physics &ph) { ph.physics->update(e.delta); }); ecs.observer( "create_shape_mesh_name") .event(flecs::OnSet) .without() .with() .write() .each([&](flecs::entity e, const EngineData &eng, PhysicsMeshName &name) { Ogre::DefaultHardwareBufferManagerBase dmgr; Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().getByName( name.meshName); mesh->setHardwareBufferManager(&dmgr); mesh->load(); JPH::ShapeRefC shape = JoltPhysicsWrapper::getSingleton() .createMeshShape(mesh); PhysicsShape &s = e.ensure(); s.shape = shape; e.modified(); }); ecs.observer("create_shape_mesh_ptr") .event(flecs::OnSet) .without() .with() .write() .each([&](flecs::entity e, const EngineData &eng, PhysicsMeshPtr &meshPtr) { Ogre::DefaultHardwareBufferManager dmgr; Ogre::MeshPtr mesh = meshPtr.mesh; if (!mesh->isLoaded()) { mesh->setHardwareBufferManager(&dmgr); mesh->load(); } JPH::ShapeRefC shape = JoltPhysicsWrapper::getSingleton() .createMeshShape(mesh); PhysicsShape &s = e.ensure(); s.shape = shape; e.modified(); }); ecs.observer( "create_shape_heightfield") .event(flecs::OnSet) .without() .with() .write() .each([&](flecs::entity e, const EngineData &eng, PhysicsHeightfieldData &hfd) { JPH::ShapeRefC shape = JoltPhysicsWrapper::getSingleton() .createHeightfieldShape( hfd.samples, hfd.offset, hfd.scale, hfd.sampleCount); PhysicsShape &s = e.ensure(); s.shape = shape; e.modified(); e.remove(); }); #if 1 ecs.observer("create_body_from_shape") .event(flecs::OnSet) .without() .with() .write() .write() .each([&](flecs::entity e, const EngineData &eng, const PhysicsShape &shape, const PhysicsNode &node, const PhysicsBody &body) { JPH::BodyID id = JoltPhysicsWrapper::getSingleton().createBody( shape.shape.GetPtr(), 0.0f, node.node, (JPH::EMotionType)body.motion, (JPH::ObjectLayer)body.layer); e.set(id); JoltPhysicsWrapper::getSingleton().addBody( id, JPH::EActivation::Activate); }); #endif ecs.observer("remove_body") .event(flecs::OnRemove) .each([&](flecs::entity e, const JPH::BodyID &id) { JoltPhysicsWrapper::getSingleton().removeBody(id); JoltPhysicsWrapper::getSingleton().destroyBody(id); std::cout << "body destroyed" << std::endl; }); ecs.observer("SetupCharacterPh") .event(flecs::OnSet) .with() .without() .write() .each([](flecs::entity e, const EngineData &eng, const CharacterBase &base) { CharacterBody &b = e.ensure(); b.ch.reset(JoltPhysicsWrapper::getSingleton() .createCharacter(base.mBodyNode, 1.75f, 0.23f)); if (!e.has()) static_cast(b.ch.get()) ->AddToPhysicsSystem( JPH::EActivation::Activate); e.set( static_cast(b.ch.get()) ->GetBodyID()); e.modified(); }); ecs.observer( "CreateTriggerPhysics") .event(flecs::OnSet) .without() .each([](flecs::entity e, const EngineData &eng, const EventTrigger &trigger) { JPH::ShapeRefC shape = JoltPhysicsWrapper::getSingleton() .createCylinderShape(trigger.halfheight, trigger.radius); JPH::BodyID id = JoltPhysicsWrapper::getSingleton().createSensor( shape.GetPtr(), trigger.node, JPH::EMotionType::Kinematic, Layers::MOVING); e.set(id); // JoltPhysicsWrapper::getSingleton().setDebugDraw(true); JoltPhysicsWrapper::getSingleton().addBody( id, JPH::EActivation::Activate); JoltPhysicsWrapper::getSingleton().addContactListener( id, [](const JoltPhysics::ContactListener:: ContactReport &report) -> void { flecs::entity trigger_e = ECS::get() .query_builder< const JPH:: BodyID>() .with() .build() .find([&](const JPH::BodyID &id) { return id == report.id1 || id == report.id2; }); flecs::entity parent_e = trigger_e.parent(); const JPH::BodyID &trigger_body = trigger_e.get(); const JPH::BodyID &other_body = (trigger_body == report.id1) ? report.id2 : report.id1; flecs::entity other_e = ECS::get() .query_builder< const JPH:: BodyID>() .without() .build() .find([&](const JPH::BodyID &id) { return id == other_body; }); if (!trigger_e.is_valid() || !other_e.is_valid()) return; if (parent_e.is_valid() && parent_e == other_e) return; OgreAssert( trigger_e.is_valid(), "trigger entity is not valid"); OgreAssert(other_e.is_valid(), "other entity is not valid"); const EventTrigger &trg = trigger_e.get(); if (report.entered) { trigger_e.get_mut() .add(trigger_e, trg.event + "_enter", trigger_e, other_e); other_e.add( trigger_e); trigger_e.add( other_e); } else { trigger_e.get_mut() .add(trigger_e, trg.event + "_exit", trigger_e, other_e); #if 0 /* do not delete triggers until exit from actuator */ other_e.remove( trigger_e); trigger_e.remove( other_e); #endif } ECS::modified(); }); }); #if 0 ecs.system( "UpdateTriggerPhysicsPre") .kind(PhysicsPreUpdate) .with() .with() .with() .each([](flecs::entity e, const EngineData &eng, const EventTrigger &trigger, const JPH::BodyID &id) { /* FIXME: update positions for triggers, probably need to move somewhere */ JoltPhysicsWrapper::getSingleton() .setPositionAndRotation( id, trigger.node->_getDerivedPosition(), trigger.node->_getDerivedOrientation()); #if 0 std::cout << trigger.node->_getDerivedPosition() << " " << trigger.node->getPosition() << " " << trigger.node->getParent()->getName() << ": " << trigger.node->getName() << std::endl; // OgreAssert(false, "update triggers"); #endif }); ecs.system( "UpdateTriggerPhysicsPost") .kind(PhysicsPostUpdate) .with() .with() .with() .each([](flecs::entity e, const EngineData &eng, const EventTrigger &trigger, const JPH::BodyID &id) { /* FIXME: update positions for triggers, probably need to move somewhere */ Ogre::Vector3 position; Ogre::Quaternion rotation; JoltPhysicsWrapper::getSingleton() .getPositionAndRotation(id, position, rotation); trigger.node->_setDerivedPosition(position); trigger.node->_setDerivedOrientation(rotation); }); #endif ecs.system("init_water") .kind(PhysicsPreUpdate) .with() .with() .without() .each([this](const EngineData &eng) { ECS::get().set({}); }); #if 0 ecs.system("DebugData") .kind(PhysicsPostUpdate) .each([this](const EngineData &eng) { std::cout << "TerrainReady: " << ECS::get().has(); std::cout << " WaterReady: " << ECS::get().has() << std::endl; }); #endif ecs.system("update_water") .kind(PhysicsPostUpdate) .with() .with() .each([this](const EngineData &eng, WaterBody &body) { const WaterSurface &water = ECS::get(); body.inWater.clear(); JoltPhysicsWrapper::getSingleton().broadphaseQuery( eng.delta, water.mWaterNode->_getDerivedPosition(), body.inWater); #if 0 for (JPH::BodyID inBodyID : body.inWater) { if (JoltPhysicsWrapper::getSingleton().isActive( inBodyID)) { float my = JoltPhysicsWrapper::getSingleton() .getPosition(inBodyID) .y; float myv = JoltPhysicsWrapper::getSingleton() .getLinearVelocity( inBodyID) .y; float b = 1.0f; float drag = 0.5f; float adrag = 0.05f; float level = -1.3f; float mdec = 1.0f; float minc = 1.0f; float h = -my + level; if (h < 0) h = 0; if (my < level - 0.1f) b *= 1.1f * (1.0f + 1.2f * h); else if (my > level + 0.1f) { b *= 0.8f; if (myv > 0.1f) b *= 0.9f; if (my > level + 0.4f) b *= 0.5f; } if (myv < 0.0f) drag = 0.7f; JoltPhysicsWrapper::getSingleton().applyBuoyancyImpulse( inBodyID, water.mWaterNode->_getDerivedPosition() - Ogre::Vector3(0, 0.1f, 0), Ogre::Vector3::UNIT_Y, b, drag, adrag, Ogre::Vector3::ZERO, eng.delta); // std::cout << b << std::endl; #if 0 std::cout << "addHit: " << JoltPhysics::convert( body.GetPosition()) << std::endl; #endif } } #endif ECS::get().add(); }); ecs.system("update_water_status1") .kind(PhysicsPostUpdate) .with() .with() .with() .each([this](flecs::entity e, const JPH::BodyID &id, const WaterBody &body) { if (!body.isInWater(id)) e.remove(); }); ecs.system("update_water_status2") .kind(PhysicsPostUpdate) .with() .with() .without() .each([this](flecs::entity e, const JPH::BodyID &id, const WaterBody &body) { if (body.isInWater(id)) e.add(); }); ecs.system( "update_water_character1") .kind(PhysicsPostUpdate) .with() .with() .with() .each([this](flecs::entity e, const CharacterBody &ch, const WaterBody &body) { JPH::Character *chptr = static_cast(ch.ch.get()); if (!body.isInWater(chptr->GetBodyID())) e.remove(); }); ecs.system( "update_water_character2") .kind(PhysicsPostUpdate) .with() .with() .without() .each([this](flecs::entity e, const CharacterBody &ch, const WaterBody &body) { JPH::Character *chptr = static_cast(ch.ch.get()); if (body.isInWater(chptr->GetBodyID())) e.add(); }); ecs.system("update_water_boat_enable") .kind(PhysicsPreUpdate) .with() .with() .each([this](flecs::entity e, const EngineData &eng, const BoatBase &boat, const WaterBody &body, const JPH::BodyID &id) { if (!JoltPhysicsWrapper::getSingleton().isAdded(id)) JoltPhysicsWrapper::getSingleton().addBody( id, JPH::EActivation::Activate); }); ecs.system("update_water_boat_activation") .kind(PhysicsPreUpdate) .with() .with() .with() .each([this](flecs::entity e, const EngineData &eng, const BoatBase &boat, const WaterBody &body, const JPH::BodyID &id) { if (!JoltPhysicsWrapper::getSingleton().isActive(id)) JoltPhysicsWrapper::getSingleton().activate(id); }); ecs.system( "update_water_boat_buoyancy") .kind(PhysicsPreUpdate) .with() .with() .with() .each([this](flecs::entity e, const EngineData &eng, const BoatBase &boat, const WaterBody &body, const JPH::BodyID &id, const CachedMass &mass) { const WaterSurface &water = ECS::get(); float b = 1.0f, drag = 0.5f, adrag = 0.5f; float level = 0.25f; float my = JoltPhysicsWrapper::getSingleton() .getPosition(id) .y; float myv = JoltPhysicsWrapper::getSingleton() .getLinearVelocity(id) .y; #if 0 if (my < level && myv < 0) b = 10.0f; else if (my > level && myv > 0) b = 0.8f; #endif /* max = 2.7; min = 1.7 */ if (my < level) b = 2.2f; else if (my > level) b = 0.9f; // std::cout << my << std::endl; JoltPhysicsWrapper::getSingleton().applyBuoyancyImpulse( id, water.mWaterNode->_getDerivedPosition(), Ogre::Vector3::UNIT_Y, b, drag, adrag, Ogre::Vector3::ZERO, eng.delta); /* stabilisation */ Ogre::Vector3 currentAngularVelocity = JoltPhysicsWrapper::getSingleton() .getAngularVelocity(id); Ogre::Quaternion rotation = JoltPhysicsWrapper::getSingleton().getRotation( id); Ogre::Vector3 rotComp = rotation.Inverse() * Ogre::Vector3::UNIT_Y; rotComp.y = 0; if (Ogre::Math::Abs(rotComp.x) > 0.1f || Ogre::Math::Abs(rotComp.z) > 0.1f) { Ogre::Vector3 localAngularVelocity = rotation.Inverse() * currentAngularVelocity; Ogre::Vector3 localAngularVelocityChange( -localAngularVelocity.x, 0, -localAngularVelocity.z); localAngularVelocityChange -= rotComp * 20.0f * eng.delta; Ogre::Vector3 angularVelocityChange = rotation * localAngularVelocityChange; Ogre::Vector3 angularImpulse = mass.mass * angularVelocityChange; JoltPhysicsWrapper::getSingleton() .addAngularImpulse(id, angularImpulse); } }); ecs.system( "update_water_character_buoyancy") .kind(PhysicsPreUpdate) .with() .with() .with() .without() .with() .each([this](flecs::entity e, const EngineData &eng, const CharacterBody &ch, const WaterBody &body) { JPH::Character *chptr = static_cast(ch.ch.get()); JPH::BodyID id = chptr->GetBodyID(); if (JoltPhysicsWrapper::getSingleton().isActive(id)) { const WaterSurface &water = ECS::get(); float my = JoltPhysicsWrapper::getSingleton() .getPosition(id) .y; float myv = JoltPhysicsWrapper::getSingleton() .getLinearVelocity(id) .y; float b = 1.0f; float drag = 0.5f; float adrag = 0.05f; float level = -1.3f; float mdec = 1.0f; float minc = 1.0f; float h = -my + level; if (h < 0) h = 0; if (my < level - 0.1f) b *= 1.1f * (1.0f + 1.2f * h); else if (my > level + 0.1f) { b *= 0.8f; if (myv > 0.1f) b *= 0.9f; if (my > level + 0.4f) b *= 0.5f; } if (myv < 0.0f) drag = 0.7f; JoltPhysicsWrapper::getSingleton() .applyBuoyancyImpulse( id, water.mWaterNode ->_getDerivedPosition(), Ogre::Vector3::UNIT_Y, b, drag, adrag, Ogre::Vector3::ZERO, eng.delta); // std::cout << b << std::endl; #if 0 std::cout << "addHit: " << JoltPhysics::convert( body.GetPosition()) << std::endl; #endif } }); ecs.system("UpdatePhysics") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::entity e, const EngineData &eng, const CharacterBody &body) { if (e.has()) PhysicsModule::controlPhysics(e, false); else PhysicsModule::controlPhysics(e, true); e.remove(); }); ecs.system("HandleVelocity") .kind(PhysicsPostUpdate) .with() .with() .without() .without() .each([this](flecs::entity e, const EngineData &eng, const CharacterBase &chbase, const CharacterBody &body, CharacterVelocity &gr) { if (e.has() && chbase.mBodyNode->_getDerivedPosition().y > -0.5f) e.remove(); Ogre::Vector3 v = gr.velocity; v.y = 0.0f; JPH::Character *ch = static_cast(body.ch.get()); if (!e.has()) { if (ch->IsSupported()) { v.y = gr.velocity.y; gr.gvelocity.y = 0; } else { v.y = gr.velocity.y; v.y += gr.gvelocity.y; gr.gvelocity.y += -9.8f * eng.delta; } } else { v = JoltPhysics::convert( ch->GetLinearVelocity()); v.x = gr.velocity.x; v.z = gr.velocity.z; } // gr.velocity.y = 0.0f; // v.y = 0.0f; ch->SetLinearVelocity(JoltPhysics::convert(v)); gr.velocity = Ogre::Vector3::ZERO; }); ecs.system("HandleVelocityNoPhysics") .kind(PhysicsPostUpdate) .with() .with() .with() .without() .each([this](flecs::entity e, const EngineData &eng, CharacterBase &ch, const CharacterBody &body, CharacterVelocity &gr) { Ogre::Vector3 v = gr.velocity; // v.y = 0.0f; ch.mBodyNode->_setDerivedPosition( ch.mBodyNode->_getDerivedPosition() + v * eng.delta); gr.velocity = Ogre::Vector3::ZERO; if (e.has()) JoltPhysicsWrapper::getSingleton() .setPositionAndRotation( e.get(), ch.mBodyNode ->_getDerivedPosition(), ch.mBodyNode ->_getDerivedOrientation(), false); if (e.has() && ch.mBodyNode->_getDerivedPosition().y > -0.5f) { e.remove(); ch.is_submerged = false; } if (!e.has() && ch.is_submerged) ch.is_submerged = false; }); } flecs::entity PhysicsModule::createTerrainChunkBody(Ogre::SceneNode *node, float *samples, const Ogre::Vector3 &offset, const Ogre::Vector3 &scale, int sampleCount) { flecs::entity e = ECS::get().entity(); e.set({ samples, offset, scale, sampleCount }); e.set({ (uint32_t)JPH::EMotionType::Static, (uint32_t)Layers::NON_MOVING }); e.set({ node }); return e; } void PhysicsModule::controlPhysics(flecs::entity e, bool enable) { if (enable) { if (e.has()) { e.remove(); OgreAssert(e.has(), "No body component"); OgreAssert(e.has(), "No body id in entity"); } if (!JoltPhysicsWrapper::getSingleton().isAdded( e.get())) { Ogre::Vector3 position = e.get() .mBodyNode->_getDerivedPosition(); Ogre::Quaternion orientation = e.get() .mBodyNode->_getDerivedOrientation(); if (position.y >= -0.5f) e.remove(); JoltPhysicsWrapper::getSingleton() .setPositionAndRotation(e.get(), position, orientation, false); JoltPhysicsWrapper::getSingleton().addBody( e.get(), JPH::EActivation::Activate); } } else { if (e.has()) { e.add(); if (!e.has()) return; OgreAssert(e.has(), "No body component"); OgreAssert(e.has(), "No body id in entity"); } if (JoltPhysicsWrapper::getSingleton().isAdded( e.get())) JoltPhysicsWrapper::getSingleton().removeBody( e.get()); Ogre::Vector3 position = e.get().mBodyNode->_getDerivedPosition(); e.remove(); } } bool WaterBody::isInWater(const JPH::BodyID &id) const { flecs::entity e = ECS::get().query_builder().build().find( [&](const JPH::BodyID &bodyid) { return bodyid == id; }); return inWater.find(id) != inWater.end(); } }