From da21395e4900bf283ece7364c67d9bad27dca279 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Tue, 24 Nov 2015 17:43:38 +0100 Subject: [PATCH] reorder world update also pushed physics integration details down to entity class --- doc/concepts | 7 +- src/client/net.cpp | 2 +- src/geometry/distance.hpp | 9 +++ src/world/ChunkStore.hpp | 3 +- src/world/Entity.hpp | 13 +++- src/world/World.hpp | 31 ++------- src/world/chunk.cpp | 12 +++- src/world/world.cpp | 138 ++++++++++++++++---------------------- 8 files changed, 104 insertions(+), 111 deletions(-) diff --git a/doc/concepts b/doc/concepts index d8d2d84..e9bb13e 100644 --- a/doc/concepts +++ b/doc/concepts @@ -119,10 +119,13 @@ World update ------------ - spawner (servers only) - - physics simulation - for each entity update: - - transform caches - controller (input for players and AI for others) + - physics simulation + - transform caches - model (animation state) - rebase player chunk indices - remove dead entities + +Note that AI actually depends on transform caches, but t's okay if it +lags one frame behind until that's sorted. diff --git a/src/client/net.cpp b/src/client/net.cpp index bd2a43a..bb28f15 100644 --- a/src/client/net.cpp +++ b/src/client/net.cpp @@ -426,7 +426,7 @@ void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corr vector col; while (entry != end) { SetMovement(entry->movement); - GetWorld().Update(replay, entry->delta_t); + replay.Update(GetWorld(), entry->delta_t); entry->state.pos = replay.GetState().pos; ++entry; } diff --git a/src/geometry/distance.hpp b/src/geometry/distance.hpp index b6955af..ef2f8a2 100644 --- a/src/geometry/distance.hpp +++ b/src/geometry/distance.hpp @@ -15,6 +15,15 @@ inline bool iszero(const T &v) noexcept { return length2(v) < std::numeric_limits::epsilon(); } +template +inline void limit(Vec &v, float max) noexcept { + float len2 = length2(v); + float max2 = max * max; + if (len2 > max2) { + v = normalize(v) * max; + } +} + template T manhattan_distance(const glm::tvec3 &a, const glm::tvec3 &b) noexcept { return compAdd(abs(a - b)); diff --git a/src/world/ChunkStore.hpp b/src/world/ChunkStore.hpp index 12debef..79a9db1 100644 --- a/src/world/ChunkStore.hpp +++ b/src/world/ChunkStore.hpp @@ -26,7 +26,8 @@ public: ChunkIndex *ClosestIndex(const ExactLocation::Coarse &pos); /// returns nullptr if given position is not loaded - Chunk *Get(const ExactLocation::Coarse &); + Chunk *Get(const ExactLocation::Coarse &) noexcept; + const Chunk *Get(const ExactLocation::Coarse &) const noexcept; /// returns nullptr if given position is not indexed Chunk *Allocate(const ExactLocation::Coarse &); diff --git a/src/world/Entity.hpp b/src/world/Entity.hpp index 1a6fccf..0117fa9 100644 --- a/src/world/Entity.hpp +++ b/src/world/Entity.hpp @@ -2,6 +2,7 @@ #define BLANK_WORLD_ENTITY_HPP_ #include "Chunk.hpp" +#include "EntityDerivative.hpp" #include "EntityState.hpp" #include "../geometry/primitive.hpp" #include "../model/Instance.hpp" @@ -17,6 +18,7 @@ namespace blank { class DirectionalLighting; class EntityController; class Shape; +class World; class Entity { @@ -114,13 +116,14 @@ public: bool Dead() const noexcept { return dead; } bool CanRemove() const noexcept { return dead && ref_count <= 0; } - void Update(float dt); + void Update(World &, float dt); void Render(const glm::mat4 &M, DirectionalLighting &prog) noexcept { if (model) model.Render(M, prog); } private: + void UpdatePhysics(World &, float dt); void UpdateTransforms() noexcept; void UpdateHeading() noexcept; void UpdateModel(float dt) noexcept; @@ -130,6 +133,14 @@ public: private: void OrientHead(float dt) noexcept; + EntityDerivative CalculateStep( + World &, + const EntityState &cur, + float dt, + const EntityDerivative &prev + ) const; + + private: EntityController *ctrl; Instance model; diff --git a/src/world/World.hpp b/src/world/World.hpp index d17cba6..7936f98 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -17,7 +17,6 @@ namespace blank { class BlockTypeRegistry; class EntityCollision; -struct EntityDerivative; class Viewport; class WorldCollision; @@ -100,8 +99,11 @@ public: // dt in ms void Update(int dt); - // dt in s - void Update(Entity &, float dt); + + /// fix state so entity doesn't intersect with the solid world + void ResolveWorldCollision(const Entity &, EntityState &); + /// get force due to gravity at given location + glm::vec3 GravityAt(const ExactLocation &) const noexcept; void Render(Viewport &); void RenderDebug(Viewport &); @@ -110,29 +112,6 @@ private: using EntityHandle = std::list::iterator; EntityHandle RemoveEntity(EntityHandle &); - EntityDerivative CalculateStep( - const Entity &, - const EntityState &cur, - float dt, - const EntityDerivative &prev - ); - glm::vec3 CalculateForce( - const Entity &, - const EntityState &cur - ); - glm::vec3 ControlForce( - const Entity &, - const EntityState & - ); - void CollisionFix( - const Entity &, - EntityState & - ); - glm::vec3 Gravity( - const Entity &, - const EntityState & - ); - /// calculate light direction and intensity at entity's location void GetLight( const Entity &entity, diff --git a/src/world/chunk.cpp b/src/world/chunk.cpp index e93966e..60ece9a 100644 --- a/src/world/chunk.cpp +++ b/src/world/chunk.cpp @@ -1075,7 +1075,7 @@ ChunkIndex *ChunkStore::ClosestIndex(const ExactLocation::Coarse &pos) { return closest_index; } -Chunk *ChunkStore::Get(const ExactLocation::Coarse &pos) { +Chunk *ChunkStore::Get(const ExactLocation::Coarse &pos) noexcept { for (ChunkIndex &index : indices) { Chunk *chunk = index.Get(pos); if (chunk) { @@ -1085,6 +1085,16 @@ Chunk *ChunkStore::Get(const ExactLocation::Coarse &pos) { return nullptr; } +const Chunk *ChunkStore::Get(const ExactLocation::Coarse &pos) const noexcept { + for (const ChunkIndex &index : indices) { + const Chunk *chunk = index.Get(pos); + if (chunk) { + return chunk; + } + } + return nullptr; +} + Chunk *ChunkStore::Allocate(const ExactLocation::Coarse &pos) { Chunk *chunk = Get(pos); if (chunk) { diff --git a/src/world/world.cpp b/src/world/world.cpp index d7771f3..28e4195 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -90,11 +90,14 @@ void Entity::UnsetController() noexcept { } glm::vec3 Entity::ControlForce(const EntityState &s) const noexcept { + glm::vec3 force; if (HasController()) { - return GetController().ControlForce(*this, s); + force = GetController().ControlForce(*this, s); } else { - return -s.velocity; + force = -s.velocity; } + limit(force, max_force); + return force; } void Entity::Position(const glm::ivec3 &c, const glm::vec3 &b) noexcept { @@ -129,15 +132,58 @@ Ray Entity::Aim(const ExactLocation::Coarse &chunk_offset) const noexcept { return Ray{ glm::vec3(transform[3]), -glm::vec3(transform[2]) }; } -void Entity::Update(float dt) { - UpdateTransforms(); - UpdateHeading(); +void Entity::Update(World &world, float dt) { if (HasController()) { GetController().Update(*this, dt); } + UpdatePhysics(world, dt); + UpdateTransforms(); + UpdateHeading(); UpdateModel(dt); } +void Entity::UpdatePhysics(World &world, float dt) { + EntityState s(state); + + EntityDerivative a(CalculateStep(world, s, 0.0f, EntityDerivative())); + EntityDerivative b(CalculateStep(world, s, dt * 0.5f, a)); + EntityDerivative c(CalculateStep(world, s, dt * 0.5f, b)); + EntityDerivative d(CalculateStep(world, s, dt, c)); + + EntityDerivative f; + constexpr float sixth = 1.0f / 6.0f; + f.position = sixth * (a.position + 2.0f * (b.position + c.position) + d.position); + f.velocity = sixth * (a.velocity + 2.0f * (b.velocity + c.velocity) + d.velocity); + + s.pos.block += f.position * dt; + s.velocity += f.velocity * dt; + limit(s.velocity, max_vel); + world.ResolveWorldCollision(*this, s); + s.AdjustPosition(); + + SetState(s); +} + +EntityDerivative Entity::CalculateStep( + World &world, + const EntityState &cur, + float dt, + const EntityDerivative &delta +) const { + EntityState next(cur); + next.pos.block += delta.position * dt; + next.velocity += delta.velocity * dt; + limit(next.velocity, max_vel); + world.ResolveWorldCollision(*this, next); + next.AdjustPosition(); + + EntityDerivative out; + out.position = next.velocity; + out.velocity = ControlForce(next) + world.GravityAt(next.pos); // by mass = 1kg + return out; +} + + void Entity::UpdateTransforms() noexcept { // model transform is the one given by current state model_transform = state.Transform(state.pos.chunk); @@ -564,6 +610,7 @@ bool World::Intersection( if (manhattan_radius(cur_chunk.Position() - reference) > 1) { // chunk is not one of the 3x3x3 surrounding the entity // since there's no entity which can extent over 16 blocks, they can be skipped + // TODO: change to indexed (like with entity) continue; } if (cur_chunk.Intersection(box, M, cur_chunk.Transform(reference), col)) { @@ -576,10 +623,7 @@ bool World::Intersection( void World::Update(int dt) { float fdt(dt * 0.001f); for (Entity &entity : entities) { - Update(entity, fdt); - } - for (Entity &entity : entities) { - entity.Update(fdt); + entity.Update(*this, fdt); } for (Player &player : players) { player.Update(dt); @@ -593,74 +637,13 @@ void World::Update(int dt) { } } -void World::Update(Entity &entity, float dt) { - EntityState state(entity.GetState()); - - EntityDerivative a(CalculateStep(entity, state, 0.0f, EntityDerivative())); - EntityDerivative b(CalculateStep(entity, state, dt * 0.5f, a)); - EntityDerivative c(CalculateStep(entity, state, dt * 0.5f, b)); - EntityDerivative d(CalculateStep(entity, state, dt, c)); - - EntityDerivative f; - constexpr float sixth = 1.0f / 6.0f; - f.position = sixth * (a.position + 2.0f * (b.position + c.position) + d.position); - f.velocity = sixth * (a.velocity + 2.0f * (b.velocity + c.velocity) + d.velocity); - - state.pos.block += f.position * dt; - state.velocity += f.velocity * dt; - CollisionFix(entity, state); - state.AdjustPosition(); - - entity.SetState(state); -} - -EntityDerivative World::CalculateStep( - const Entity &entity, - const EntityState &cur, - float dt, - const EntityDerivative &delta -) { - EntityState next(cur); - next.pos.block += delta.position * dt; - next.velocity += delta.velocity * dt; - CollisionFix(entity, next); - next.AdjustPosition(); - - if (dot(next.velocity, next.velocity) > entity.MaxVelocity() * entity.MaxVelocity()) { - next.velocity = normalize(next.velocity) * entity.MaxVelocity(); - } - - EntityDerivative out; - out.position = next.velocity; - out.velocity = CalculateForce(entity, next); // by mass = 1kg - return out; -} - -glm::vec3 World::CalculateForce( - const Entity &entity, - const EntityState &state -) { - glm::vec3 force(ControlForce(entity, state)); - if (dot(force, force) > entity.MaxControlForce() * entity.MaxControlForce()) { - force = normalize(force) * entity.MaxControlForce(); - } - return force + Gravity(entity, state); -} - -glm::vec3 World::ControlForce( - const Entity &entity, - const EntityState &state -) { - return entity.ControlForce(state); -} - namespace { std::vector col; } -void World::CollisionFix( +void World::ResolveWorldCollision( const Entity &entity, EntityState &state ) { @@ -725,20 +708,17 @@ glm::vec3 World::CombinedInterpenetration( return pen; } -glm::vec3 World::Gravity( - const Entity &entity, - const EntityState &state -) { +glm::vec3 World::GravityAt(const ExactLocation &loc) const noexcept { glm::vec3 force(0.0f); - ExactLocation::Coarse begin(state.pos.chunk - 1); - ExactLocation::Coarse end(state.pos.chunk + 2); + ExactLocation::Coarse begin(loc.chunk - 1); + ExactLocation::Coarse end(loc.chunk + 2); for (ExactLocation::Coarse pos(begin); pos.z < end.z; ++pos.z) { for (pos.y = begin.y; pos.y < end.y; ++pos.y) { for (pos.x = begin.x; pos.x < end.x; ++pos.x) { - Chunk *chunk = chunks.Get(pos); + const Chunk *chunk = chunks.Get(pos); if (chunk) { - force += chunk->GravityAt(state.pos); + force += chunk->GravityAt(loc); } } } -- 2.39.2