converted to Jolt physics

This commit is contained in:
2025-11-23 02:00:31 +03:00
parent 3f0484e87c
commit cd82fb0eed
52 changed files with 4302 additions and 1458 deletions

View File

@@ -0,0 +1,535 @@
#include <iostream>
#include <Ogre.h>
#include <Jolt/Jolt.h>
#include <Jolt/Physics/Body/BodyID.h>
#include <Jolt/Physics/Character/CharacterBase.h>
#include <Jolt/Physics/Character/Character.h>
#include <Jolt/Physics/Collision/BroadPhase/BroadPhase.h>
#include <Jolt/Physics/Body/BodyLock.h>
#include "Components.h"
#include "GameData.h"
#include "CharacterModule.h"
#include "WaterModule.h"
#include "BoatModule.h"
#include "EventTriggerModule.h"
#include "physics.h"
#include "loader.h"
#include "PhysicsModule.h"
namespace ECS
{
struct PhysicsShape {
JPH::ShapeRefC shape;
};
struct ConvexHull {};
struct WaterBody {
std::set<JPH::BodyID> inWater;
bool isInWater(const JPH::BodyID &id) const;
};
struct TriggerBody {
void *foo;
};
PhysicsModule::PhysicsModule(flecs::world &ecs)
{
ecs.module<PhysicsModule>();
ecs.import <EventTriggerModule>();
ecs.import <BoatModule>();
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<Physics>().add(flecs::Singleton);
ecs.component<JPH::BodyID>().member<uint32_t>("mID");
/* for terrain */
ecs.component<PhysicsBody>();
ecs.component<PhysicsShape>();
ecs.component<PhysicsNode>();
ecs.component<PhysicsMeshName>();
ecs.component<PhysicsMeshPtr>();
ecs.component<PhysicsHeightfieldData>();
ecs.component<CharacterBody>();
ecs.component<TriggerBody>();
ecs.component<CharacterVelocity>();
ecs.component<WaterBody>().add(flecs::Singleton);
ecs.system<const EngineData, const Camera>("physics_init")
.kind(PhysicsPreUpdate)
.without<Physics>()
.each([&](const EngineData &e, const Camera &c) {
Physics &ph = ECS::get().ensure<Physics>();
ph.physics = new JoltPhysicsWrapper(e.mScnMgr,
c.mCameraNode);
ECS::modified<Physics>();
});
#if 0
ecs.system<PhysicsBody>("create_body")
.kind(flecs::OnUpdate)
.without<JPH::BodyID>()
.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<JPH::BodyID>(id);
});
#endif
ecs.system<EngineData, Physics>("physics_update")
.kind(PhysicsUpdate)
.each([&](EngineData &e, Physics &ph) {
ph.physics->update(e.delta);
});
ecs.observer<const EngineData, PhysicsMeshName>(
"create_shape_mesh_name")
.event(flecs::OnSet)
.without<PhysicsShape>()
.with<Physics>()
.write<PhysicsShape>()
.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<PhysicsShape>();
s.shape = shape;
e.modified<PhysicsShape>();
});
ecs.observer<const EngineData, PhysicsMeshPtr>("create_shape_mesh_ptr")
.event(flecs::OnSet)
.without<PhysicsShape>()
.with<Physics>()
.write<PhysicsShape>()
.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<PhysicsShape>();
s.shape = shape;
e.modified<PhysicsShape>();
});
ecs.observer<const EngineData, PhysicsHeightfieldData>(
"create_shape_heightfield")
.event(flecs::OnSet)
.without<PhysicsShape>()
.with<Physics>()
.write<PhysicsShape>()
.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<PhysicsShape>();
s.shape = shape;
e.modified<PhysicsShape>();
delete hfd.samples;
e.remove<PhysicsHeightfieldData>();
});
#if 1
ecs.observer<const EngineData, const PhysicsShape, const PhysicsNode,
const PhysicsBody>("create_body_from_shape")
.event(flecs::OnSet)
.without<JPH::BodyID>()
.with<Physics>()
.write<PhysicsShape>()
.write<JPH::BodyID>()
.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<JPH::BodyID>(id);
JoltPhysicsWrapper::getSingleton().addBody(
id, JPH::EActivation::Activate);
});
#endif
ecs.observer<const JPH::BodyID>("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<const EngineData, const CharacterBase>("SetupCharacterPh")
.event(flecs::OnSet)
.with<Character>()
.without<CharacterBody>()
.write<CharacterBody>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &base) {
CharacterBody &b = e.ensure<CharacterBody>();
b.ch.reset(JoltPhysicsWrapper::getSingleton()
.createCharacter(base.mBodyNode,
1.75f, 0.23f));
if (!e.has<CharacterDisablePhysics>())
static_cast<JPH::Character *>(b.ch.get())
->AddToPhysicsSystem(
JPH::EActivation::Activate);
e.modified<CharacterBody>();
});
ecs.observer<const EngineData, const EventTrigger>(
"CreateTriggerPhysics")
.event(flecs::OnSet)
.without<JPH::BodyID>()
.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<JPH::BodyID>(id);
// JoltPhysicsWrapper::getSingleton().setDebugDraw(true);
JoltPhysicsWrapper::getSingleton().addBody(
id, JPH::EActivation::Activate);
});
#if 0
ecs.system<const EngineData, const EventTrigger, const JPH::BodyID>(
"UpdateTriggerPhysicsPre")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<WaterBody>()
.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<const EngineData, const EventTrigger, const JPH::BodyID>(
"UpdateTriggerPhysicsPost")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<WaterBody>()
.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<const EngineData>("init_water")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterAlmostReady>()
.without<WaterBody>()
.each([this](const EngineData &eng) {
ECS::get().set<WaterBody>({});
});
ecs.system<const EngineData, WaterBody>("update_water")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterAlmostReady>()
.each([this](const EngineData &eng, WaterBody &body) {
const WaterSurface &water = ECS::get<WaterSurface>();
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<WaterReady>();
});
ecs.system<const JPH::BodyID, const WaterBody>("update_water_status1")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, const JPH::BodyID &id,
const WaterBody &body) {
if (!body.isInWater(id))
e.remove<InWater>();
});
ecs.system<const JPH::BodyID, const WaterBody>("update_water_status2")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.without<InWater>()
.each([this](flecs::entity e, const JPH::BodyID &id,
const WaterBody &body) {
if (body.isInWater(id))
e.add<InWater>();
});
ecs.system<const CharacterBody, const WaterBody>(
"update_water_character1")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, const CharacterBody &ch,
const WaterBody &body) {
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
if (!body.isInWater(chptr->GetBodyID()))
e.remove<InWater>();
});
ecs.system<const CharacterBody, const WaterBody>(
"update_water_character2")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.without<InWater>()
.each([this](flecs::entity e, const CharacterBody &ch,
const WaterBody &body) {
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
if (body.isInWater(chptr->GetBodyID()))
e.add<InWater>();
});
ecs.system<const EngineData, const BoatBase, const WaterBody,
const JPH::BodyID>("update_water_boat_enable")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.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<const EngineData, const BoatBase, const WaterBody,
const JPH::BodyID>("update_water_boat_activation")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.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<const EngineData, const BoatBase, const WaterBody,
const JPH::BodyID>("update_water_boat_buoyancy")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, const EngineData &eng,
const BoatBase &boat, const WaterBody &body,
const JPH::BodyID &id) {
const WaterSurface &water = ECS::get<WaterSurface>();
float b = 1.0f, drag = 0.5f, adrag = 0.5f;
float level = 0.1f;
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
if (my < level)
b = 1.7f;
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);
});
ecs.system<const EngineData, const CharacterBody, const WaterBody>(
"update_water_character_buoyancy")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.without<CharacterDisablePhysics>()
.with<CharacterBuoyancy>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBody &ch, const WaterBody &body) {
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
JPH::BodyID id = chptr->GetBodyID();
if (JoltPhysicsWrapper::getSingleton().isActive(id)) {
const WaterSurface &water =
ECS::get<WaterSurface>();
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<const EngineData, const CharacterBody, CharacterVelocity>(
"HandleVelocity")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.without<CharacterDisablePhysics>()
.without<CharacterUpdatePhysicsState>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBody &body, CharacterVelocity &gr) {
Ogre::Vector3 v = gr.velocity;
v.y = 0.0f;
JPH::Character *ch =
static_cast<JPH::Character *>(body.ch.get());
if (!e.has<InWater>()) {
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;
});
}
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<PhysicsHeightfieldData>({ samples, offset, scale, sampleCount });
e.set<PhysicsBody>({ (uint32_t)JPH::EMotionType::Static,
(uint32_t)Layers::NON_MOVING });
e.set<PhysicsNode>({ node });
return e;
}
bool WaterBody::isInWater(const JPH::BodyID &id) const
{
return inWater.find(id) != inWater.end();
}
}