add more reason to physics simulation substeps

This commit is contained in:
MihailRis 2026-03-11 22:08:40 +03:00
parent 794ce07bd5
commit 4b4f2439c0
5 changed files with 119 additions and 114 deletions

View file

@ -80,7 +80,7 @@ entityid_t Entities::spawn(
entities[id] = entity; entities[id] = entity;
uids[entity] = id; uids[entity] = id;
registry->emplace<EntityId>(entity, static_cast<entityid_t>(id), def); registry->emplace<EntityId>(entity, id, def);
const auto& tsf = registry->emplace<Transform>( const auto& tsf = registry->emplace<Transform>(
entity, entity,
position, position,
@ -92,7 +92,7 @@ entityid_t Entities::spawn(
auto& body = registry->emplace<Rigidbody>( auto& body = registry->emplace<Rigidbody>(
entity, entity,
true, true,
Hitbox {def.bodyType, position, def.hitbox * 0.5f}, Hitbox {id, def.bodyType, position, def.hitbox * 0.5f},
std::vector<Sensor> {} std::vector<Sensor> {}
); );
body.initialize(def, id, *this); body.initialize(def, id, *this);
@ -280,6 +280,7 @@ void Entities::updateSensors(
void Entities::preparePhysics(float delta) { void Entities::preparePhysics(float delta) {
auto& physics = *level.physics; auto& physics = *level.physics;
auto& hitboxes = physics.getHitboxesWriteable();
auto& solidHitboxes = physics.getSolidHitboxesWriteable(); auto& solidHitboxes = physics.getSolidHitboxesWriteable();
if (sensorsTickClock.update(delta)) { if (sensorsTickClock.update(delta)) {
@ -301,11 +302,16 @@ void Entities::preparePhysics(float delta) {
} }
} }
hitboxes.clear();
solidHitboxes.clear(); solidHitboxes.clear();
auto view = registry->view<EntityId, Rigidbody>(); auto view = registry->view<EntityId, Rigidbody>();
for (auto [entity, eid, rigidbody] : view.each()) { for (auto [entity, eid, rigidbody] : view.each()) {
if (!eid.def.solid || eid.destroyFlag || !rigidbody.enabled) { if (eid.destroyFlag || !rigidbody.enabled) {
continue;
}
hitboxes.emplace_back(&rigidbody.hitbox);
if (!eid.def.solid) {
continue; continue;
} }
solidHitboxes.emplace_back(&rigidbody.hitbox); solidHitboxes.emplace_back(&rigidbody.hitbox);
@ -318,43 +324,31 @@ void Entities::updatePhysics(float delta) {
auto view = registry->view<EntityId, Transform, Rigidbody>(); auto view = registry->view<EntityId, Transform, Rigidbody>();
auto physics = level.physics.get(); auto physics = level.physics.get();
for (int solid = false; solid <= true; solid++) { int substeps = std::max<int>(std::min<int>(delta * 1000, 200), 8);
for (auto [entity, eid, transform, rigidbody] : view.each()) { physics->step(*level.chunks, delta, substeps);
if (!rigidbody.enabled ||
rigidbody.hitbox.type == BodyType::STATIC) {
continue;
}
if (eid.def.solid == solid) {
continue;
}
auto& hitbox = rigidbody.hitbox;
auto prevVel = hitbox.velocity;
bool grounded = hitbox.grounded;
float vel = glm::length(prevVel); for (auto [entity, eid, transform, rigidbody] : view.each()) {
int substeps = static_cast<int>(delta * (vel + 2.0f) * 20); if (!rigidbody.enabled ||
substeps = std::min(100, std::max(2, substeps)); rigidbody.hitbox.type == BodyType::STATIC) {
physics->step(*level.chunks, hitbox, delta, substeps, eid.uid); continue;
hitbox.friction = glm::abs(hitbox.gravityScale <= 1e-7f) }
? 8.0f auto& hitbox = rigidbody.hitbox;
: (!grounded ? 2.0f : 10.0f); hitbox.scale = transform.size;
hitbox.scale = transform.size; if (util::is_nan_or_inf(hitbox.position)) {
if (util::is_nan_or_inf(hitbox.position)) { logger.error()
logger.error() << "physics simulation produced nan or inf (entity "
<< "physics simulation produced nan or inf (entity " << eid.def.name << "#" << eid.uid << ")";
<< eid.def.name << "#" << eid.uid << ")"; hitbox.position = transform.pos;
hitbox.position = transform.pos; } else {
} else { transform.setPos(hitbox.position);
transform.setPos(hitbox.position); }
} if (hitbox.grounded && !hitbox.prevGrounded) {
if (hitbox.grounded && !grounded) { scripting::on_entity_grounded(
scripting::on_entity_grounded( *get(eid.uid), glm::length(hitbox.prevVelocity - hitbox.velocity)
*get(eid.uid), glm::length(prevVel - hitbox.velocity) );
); }
} if (!hitbox.grounded && hitbox.prevGrounded) {
if (!hitbox.grounded && grounded) { scripting::on_entity_fall(*get(eid.uid));
scripting::on_entity_fall(*get(eid.uid));
}
} }
} }
} }

View file

@ -2,10 +2,13 @@
#include <stdexcept> #include <stdexcept>
Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize) Hitbox::Hitbox(
: type(type), entityid_t entity, BodyType type, glm::vec3 position, glm::vec3 halfsize
position(position), )
halfsize(halfsize), : entity(entity),
velocity(0.0f,0.0f,0.0f), type(type),
prevPosition(position) position(position),
{} halfsize(halfsize),
velocity(0.0f, 0.0f, 0.0f),
prevPosition(position) {
}

View file

@ -51,6 +51,7 @@ VC_ENUM_METADATA(BodyType)
VC_ENUM_END VC_ENUM_END
struct Hitbox { struct Hitbox {
entityid_t entity;
BodyType type; BodyType type;
glm::vec3 position; glm::vec3 position;
glm::vec3 halfsize; glm::vec3 halfsize;
@ -65,17 +66,18 @@ struct Hitbox {
bool crouching = false; bool crouching = false;
float stepHeight = 0.5f; float stepHeight = 0.5f;
std::string material; std::string material;
glm::vec3 groundVelocity {};
std::string groundMaterial; std::string groundMaterial;
glm::vec3 groundVelocity {};
// garbage tbh
glm::vec3 prevPosition {}; glm::vec3 prevPosition {};
float delta = 0.0f; glm::vec3 prevVelocity {};
bool prevGrounded = false;
static inline constexpr float TELEPORT_THRESOLD_SQR = 0.5f; static inline constexpr float TELEPORT_THRESOLD_SQR = 0.5f;
Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize); Hitbox(
entityid_t entity, BodyType type, glm::vec3 position, glm::vec3 halfsize
);
AABB getAABB() const { AABB getAABB() const {
return AABB(position - halfsize, position + halfsize); return AABB(position - halfsize, position + halfsize);

View file

@ -141,13 +141,10 @@ static bool calc_collision_neg_y(
if (pos.y < newy && glm::abs(pos.y - newy) < boxhalf.y) { if (pos.y < newy && glm::abs(pos.y - newy) < boxhalf.y) {
pos.y = newy; pos.y = newy;
} }
if (glm::abs(box->delta) > 1e-5f) { hitbox.groundVelocity = box->position - box->prevPosition;
hitbox.groundVelocity =
(box->position - box->prevPosition) / box->delta;
}
if (vel.y < 0.0f) { if (vel.y < 0.0f) {
vel.y = 0.0f; vel.y = 0.0f;
if (hitbox.groundMaterial.empty()) { if (hitbox.groundMaterial.empty() && !box->material.empty()) {
hitbox.groundMaterial = box->material; hitbox.groundMaterial = box->material;
} }
return true; return true;
@ -297,16 +294,15 @@ void PhysicsSolver::calcSubstep(
Hitbox& hitbox, Hitbox& hitbox,
glm::vec3& vel, glm::vec3& vel,
glm::vec3& pos, glm::vec3& pos,
bool prevGrounded,
float dt, float dt,
int substeps int substeps
) { ) {
if (hitbox.grounded) { if (glm::length2(hitbox.groundVelocity) > 1e-6f) {
pos.x += hitbox.groundVelocity.x * dt * substeps; pos.x += hitbox.groundVelocity.x;
if (hitbox.groundVelocity.y < 0.0f) { if (hitbox.groundVelocity.y < 0.0f) {
pos.y += hitbox.groundVelocity.y * dt * substeps; pos.y += hitbox.groundVelocity.y;
} }
pos.z += hitbox.groundVelocity.z * dt * substeps; pos.z += hitbox.groundVelocity.z;
} }
auto initpos = pos; auto initpos = pos;
@ -320,7 +316,7 @@ void PhysicsSolver::calcSubstep(
vel, vel,
pos, pos,
half, half,
(prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f (hitbox.prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f
); );
} }
@ -374,64 +370,72 @@ void PhysicsSolver::calcSubstep(
void PhysicsSolver::step( void PhysicsSolver::step(
const GlobalChunks& chunks, const GlobalChunks& chunks,
Hitbox& hitbox,
float delta, float delta,
uint substeps, uint substeps
entityid_t entity
) { ) {
hitbox.prevPosition = hitbox.position; for (auto hitbox : hitboxes) {
hitbox.delta = delta; hitbox->groundMaterial.clear();
hitbox.groundMaterial.clear(); hitbox->prevGrounded = hitbox->grounded;
hitbox->grounded = false;
hitbox->prevVelocity = hitbox->velocity;
}
float dt = delta / static_cast<float>(substeps); float dt = delta / static_cast<float>(substeps);
float linearDamping = hitbox.linearDamping * hitbox.friction;
glm::vec3& pos = hitbox.position;
glm::vec3& vel = hitbox.velocity;
bool prevGrounded = hitbox.grounded;
hitbox.grounded = false;
for (uint i = 0; i < substeps; i++) { for (uint i = 0; i < substeps; i++) {
calcSubstep(chunks, hitbox, vel, pos, prevGrounded, dt, substeps); for (auto hitbox : hitboxes) {
} glm::vec3& pos = hitbox->position;
glm::vec3& vel = hitbox->velocity;
vel.x /= 1.0f + delta * linearDamping; hitbox->prevPosition = hitbox->position;
vel.z /= 1.0f + delta * linearDamping; calcSubstep(chunks, *hitbox, vel, pos, dt, substeps);
if (hitbox.verticalDamping > 0.0f) {
vel.y /= 1.0f + delta * linearDamping * hitbox.verticalDamping;
}
if (prevGrounded && !hitbox.grounded) {
auto appliedVelocity = hitbox.groundVelocity;
vel += appliedVelocity;
hitbox.groundVelocity = {};
}
AABB aabb;
aabb.a = pos - hitbox.getHalfSize();
aabb.b = pos + hitbox.getHalfSize();
for (size_t i = 0; i < sensors.size(); i++) {
auto& sensor = *sensors[i];
if (sensor.entity == entity) {
continue;
} }
}
bool triggered = false; for (auto hitbox : hitboxes) {
switch (sensor.type) { float linearDamping = hitbox->linearDamping * hitbox->friction;
case SensorType::AABB: glm::vec3& pos = hitbox->position;
triggered = aabb.intersects(sensor.calculated.aabb); glm::vec3& vel = hitbox->velocity;
break;
case SensorType::RADIUS: vel.x /= 1.0f + delta * linearDamping;
triggered = glm::distance2( vel.z /= 1.0f + delta * linearDamping;
pos, glm::vec3(sensor.calculated.radial)) if (hitbox->verticalDamping > 0.0f) {
< sensor.calculated.radial.w; vel.y /= 1.0f + delta * linearDamping * hitbox->verticalDamping;
break;
} }
if (triggered) { if (hitbox->prevGrounded && !hitbox->grounded) {
if (sensor.prevEntered.find(entity) == sensor.prevEntered.end()) { auto appliedVelocity = hitbox->groundVelocity / dt;
sensor.enterCallback(sensor.entity, sensor.index, entity); vel += appliedVelocity;
hitbox->groundVelocity = {};
}
AABB aabb;
aabb.a = pos - hitbox->getHalfSize();
aabb.b = pos + hitbox->getHalfSize();
for (size_t i = 0; i < sensors.size(); i++) {
auto& sensor = *sensors[i];
if (sensor.entity == hitbox->entity) {
continue;
} }
sensor.nextEntered.insert(entity); bool triggered = false;
switch (sensor.type) {
case SensorType::AABB:
triggered = aabb.intersects(sensor.calculated.aabb);
break;
case SensorType::RADIUS:
triggered = glm::distance2(
pos, glm::vec3(sensor.calculated.radial))
< sensor.calculated.radial.w;
break;
}
if (!triggered) {
continue;
}
if (sensor.prevEntered.find(hitbox->entity) == sensor.prevEntered.end()) {
sensor.enterCallback(sensor.entity, sensor.index, hitbox->entity);
}
sensor.nextEntered.insert(hitbox->entity);
} }
hitbox->friction = glm::abs(hitbox->gravityScale <= 1e-7f)
? 8.0f
: (!hitbox->prevGrounded ? 2.0f : 10.0f);
} }
} }

View file

@ -18,10 +18,8 @@ public:
PhysicsSolver(glm::vec3 gravity); PhysicsSolver(glm::vec3 gravity);
void step( void step(
const GlobalChunks& chunks, const GlobalChunks& chunks,
Hitbox& hitbox,
float delta, float delta,
uint substeps, uint substeps
entityid_t entity
); );
auto& getSensorsWriteable() { auto& getSensorsWriteable() {
@ -32,11 +30,16 @@ public:
return solidHitboxes; return solidHitboxes;
} }
auto& getHitboxesWriteable() {
return hitboxes;
}
void removeSensor(Sensor* sensor); void removeSensor(Sensor* sensor);
private: private:
glm::vec3 gravity; glm::vec3 gravity;
std::vector<Sensor*> sensors; std::vector<Sensor*> sensors;
std::vector<Hitbox*> solidHitboxes; std::vector<Hitbox*> solidHitboxes;
std::vector<Hitbox*> hitboxes;
void calcCollisions( void calcCollisions(
const GlobalChunks& chunks, const GlobalChunks& chunks,
@ -52,7 +55,6 @@ private:
Hitbox& hitbox, Hitbox& hitbox,
glm::vec3& vel, glm::vec3& vel,
glm::vec3& pos, glm::vec3& pos,
bool prevGrounded,
float dt, float dt,
int substeps int substeps
); );