From f5fc0c2bd1c0d1e2737d2b4ed49c3de16aa67c67 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Sat, 2 Dec 2017 14:50:09 +0100 Subject: [PATCH] primitive collision response --- src/app/states.cpp | 9 ++- src/blobs.cpp | 1 + src/creature/Creature.hpp | 4 + src/creature/creature.cpp | 19 ++++- src/math/geometry.cpp | 98 +++++++++++++++++++++++++ src/math/geometry.hpp | 41 +++++++++++ src/world/Body.hpp | 1 + src/world/CreatureCreatureCollision.hpp | 52 +++++++++++++ src/world/world.cpp | 45 ++++++++++++ 9 files changed, 266 insertions(+), 4 deletions(-) create mode 100644 src/math/geometry.cpp create mode 100644 src/math/geometry.hpp create mode 100644 src/world/CreatureCreatureCollision.hpp diff --git a/src/app/states.cpp b/src/app/states.cpp index e45356b..49ee8e2 100644 --- a/src/app/states.cpp +++ b/src/app/states.cpp @@ -50,8 +50,15 @@ void MasterState::OnResize(int w, int h) { void MasterState::OnUpdate(int dt) { remain += dt; - while (remain >= FrameMS()) { +#ifdef NDEBUG + int max_tick = 10; +#else + // one tick per frame when debugging so pausing execution doesn't result in more ticks + int max_tick = 1; +#endif + while (remain >= FrameMS() && max_tick > 0) { Tick(); + --max_tick; } } diff --git a/src/blobs.cpp b/src/blobs.cpp index 85f9923..9a94b96 100644 --- a/src/blobs.cpp +++ b/src/blobs.cpp @@ -25,6 +25,7 @@ struct SwitchPanel { void operator ()(creature::Creature &c) { if (planet.Creatures().empty()) { std::cout << "no more creatures, game over" << std::endl; + state.GetCreaturePanel().Hide(); while (app.HasState()) { app.PopState(); } diff --git a/src/creature/Creature.hpp b/src/creature/Creature.hpp index 44107be..1371687 100644 --- a/src/creature/Creature.hpp +++ b/src/creature/Creature.hpp @@ -8,6 +8,7 @@ #include "Situation.hpp" #include "Steering.hpp" #include "../graphics/SimpleVAO.hpp" +#include "../math/geometry.hpp" #include "../math/glm.hpp" #include @@ -157,6 +158,9 @@ public: Steering &GetSteering() noexcept { return steering; } const Steering &GetSteering() const noexcept { return steering; } + math::AABB CollisionBox() const noexcept; + glm::dmat4 CollisionTransform() const noexcept; + glm::dmat4 LocalTransform() noexcept; void BuildVAO(); diff --git a/src/creature/creature.cpp b/src/creature/creature.cpp index ccd174d..dc857ce 100644 --- a/src/creature/creature.cpp +++ b/src/creature/creature.cpp @@ -322,6 +322,10 @@ void Creature::TickStats(double dt) { constexpr double dps = 1.0 / 128.0; Hurt(dps * dt); } + if (!situation.Moving()) { + // double exhaustion recovery when standing still + stats.Exhaustion().Add(stats.Exhaustion().gain * dt); + } } void Creature::TickBrain(double dt) { @@ -349,13 +353,22 @@ void Creature::TickBrain(double dt) { } } -glm::dmat4 Creature::LocalTransform() noexcept { +math::AABB Creature::CollisionBox() const noexcept { + return { glm::dvec3(size * -0.5), glm::dvec3(size * 0.5) }; +} + +glm::dmat4 Creature::CollisionTransform() const noexcept { const double half_size = size * 0.5; const glm::dvec3 &pos = situation.Position(); const glm::dmat3 srf(world::Planet::SurfaceOrientation(situation.Surface())); return glm::translate(glm::dvec3(pos.x, pos.y, pos.z + half_size)) * glm::rotate(glm::orientedAngle(-srf[2], situation.Heading(), srf[1]), srf[1]) - * glm::dmat4(srf) + * glm::dmat4(srf); +} + +glm::dmat4 Creature::LocalTransform() noexcept { + const double half_size = size * 0.5; + return CollisionTransform() * glm::scale(glm::dvec3(half_size, half_size, half_size)); } @@ -480,7 +493,7 @@ void Spawn(Creature &c, world::Planet &p) { Genome genome; genome.properties.Strength() = { 2.0, 0.1 }; - genome.properties.Stamina() = { 4.0, 0.1 }; + genome.properties.Stamina() = { 2.0, 0.1 }; genome.properties.Dexerty() = { 2.0, 0.1 }; genome.properties.Intelligence() = { 1.0, 0.1 }; genome.properties.Lifetime() = { 480.0, 60.0 }; diff --git a/src/math/geometry.cpp b/src/math/geometry.cpp new file mode 100644 index 0000000..aa0389d --- /dev/null +++ b/src/math/geometry.cpp @@ -0,0 +1,98 @@ +#include "geometry.hpp" + + +namespace blobs { +namespace math { + +bool Intersect( + const AABB &a_box, + const glm::dmat4 &a_m, + const AABB &b_box, + const glm::dmat4 &b_m, + glm::dvec3 &normal, + double &depth +) noexcept { + glm::dvec3 a_corners[8] = { + glm::dvec3(a_m * glm::dvec4(a_box.min.x, a_box.min.y, a_box.min.z, 1.0)), + glm::dvec3(a_m * glm::dvec4(a_box.min.x, a_box.min.y, a_box.max.z, 1.0)), + glm::dvec3(a_m * glm::dvec4(a_box.min.x, a_box.max.y, a_box.min.z, 1.0)), + glm::dvec3(a_m * glm::dvec4(a_box.min.x, a_box.max.y, a_box.max.z, 1.0)), + glm::dvec3(a_m * glm::dvec4(a_box.max.x, a_box.min.y, a_box.min.z, 1.0)), + glm::dvec3(a_m * glm::dvec4(a_box.max.x, a_box.min.y, a_box.max.z, 1.0)), + glm::dvec3(a_m * glm::dvec4(a_box.max.x, a_box.max.y, a_box.min.z, 1.0)), + glm::dvec3(a_m * glm::dvec4(a_box.max.x, a_box.max.y, a_box.max.z, 1.0)), + }; + + glm::dvec3 b_corners[8] = { + glm::dvec3(b_m * glm::dvec4(b_box.min.x, b_box.min.y, b_box.min.z, 1.0)), + glm::dvec3(b_m * glm::dvec4(b_box.min.x, b_box.min.y, b_box.max.z, 1.0)), + glm::dvec3(b_m * glm::dvec4(b_box.min.x, b_box.max.y, b_box.min.z, 1.0)), + glm::dvec3(b_m * glm::dvec4(b_box.min.x, b_box.max.y, b_box.max.z, 1.0)), + glm::dvec3(b_m * glm::dvec4(b_box.max.x, b_box.min.y, b_box.min.z, 1.0)), + glm::dvec3(b_m * glm::dvec4(b_box.max.x, b_box.min.y, b_box.max.z, 1.0)), + glm::dvec3(b_m * glm::dvec4(b_box.max.x, b_box.max.y, b_box.min.z, 1.0)), + glm::dvec3(b_m * glm::dvec4(b_box.max.x, b_box.max.y, b_box.max.z, 1.0)), + }; + + glm::dvec3 axes[15] = { + glm::dvec3(a_m[0]), + glm::dvec3(a_m[1]), + glm::dvec3(a_m[2]), + glm::dvec3(b_m[0]), + glm::dvec3(b_m[1]), + glm::dvec3(b_m[2]), + normalize(cross(glm::dvec3(a_m[0]), glm::dvec3(b_m[0]))), + normalize(cross(glm::dvec3(a_m[0]), glm::dvec3(b_m[1]))), + normalize(cross(glm::dvec3(a_m[0]), glm::dvec3(b_m[2]))), + normalize(cross(glm::dvec3(a_m[1]), glm::dvec3(b_m[0]))), + normalize(cross(glm::dvec3(a_m[1]), glm::dvec3(b_m[1]))), + normalize(cross(glm::dvec3(a_m[1]), glm::dvec3(b_m[2]))), + normalize(cross(glm::dvec3(a_m[2]), glm::dvec3(b_m[0]))), + normalize(cross(glm::dvec3(a_m[2]), glm::dvec3(b_m[1]))), + normalize(cross(glm::dvec3(a_m[2]), glm::dvec3(b_m[2]))), + }; + + depth = std::numeric_limits::infinity(); + int min_axis = 0; + + int cur_axis = 0; + for (const glm::dvec3 &axis : axes) { + if (any(isnan(axis))) { + // can result from the cross products if A and B have parallel axes + ++cur_axis; + continue; + } + double a_min = std::numeric_limits::infinity(); + double a_max = -std::numeric_limits::infinity(); + for (const glm::dvec3 &corner : a_corners) { + double val = dot(corner, axis); + a_min = std::min(a_min, val); + a_max = std::max(a_max, val); + } + + double b_min = std::numeric_limits::infinity(); + double b_max = -std::numeric_limits::infinity(); + for (const glm::dvec3 &corner : b_corners) { + double val = dot(corner, axis); + b_min = std::min(b_min, val); + b_max = std::max(b_max, val); + } + + if (a_max < b_min || b_max < a_min) return false; + + double overlap = std::min(a_max, b_max) - std::max(a_min, b_min); + if (overlap < depth) { + depth = overlap; + min_axis = cur_axis; + } + + ++cur_axis; + } + + normal = axes[min_axis]; + return true; +} + + +} +} diff --git a/src/math/geometry.hpp b/src/math/geometry.hpp new file mode 100644 index 0000000..4d1b07b --- /dev/null +++ b/src/math/geometry.hpp @@ -0,0 +1,41 @@ +#ifndef BLOBS_MATH_GEOMETRY_HPP_ +#define BLOBS_MATH_GEOMETRY_HPP_ + +#include "glm.hpp" + +#include + + +namespace blobs { +namespace math { + +struct AABB { + + glm::dvec3 min; + glm::dvec3 max; + + void Adjust() noexcept { + if (max.x < min.x) std::swap(max.x, min.x); + if (max.y < min.y) std::swap(max.y, min.y); + if (max.z < min.z) std::swap(max.z, min.z); + } + + glm::dvec3 Center() const noexcept { + return min + (max - min) * 0.5; + } + +}; + +/// matrices must not scale +bool Intersect( + const AABB &a_box, + const glm::dmat4 &a_m, + const AABB &b_box, + const glm::dmat4 &b_m, + glm::dvec3 &normal, + double &depth) noexcept; + +} +} + +#endif diff --git a/src/world/Body.hpp b/src/world/Body.hpp index df68984..3cbb788 100644 --- a/src/world/Body.hpp +++ b/src/world/Body.hpp @@ -86,6 +86,7 @@ public: void Tick(double dt); void Cache() noexcept; + void CheckCollision() noexcept; // body takes over ownership of given pointer void AddCreature(creature::Creature *); diff --git a/src/world/CreatureCreatureCollision.hpp b/src/world/CreatureCreatureCollision.hpp new file mode 100644 index 0000000..1a903ec --- /dev/null +++ b/src/world/CreatureCreatureCollision.hpp @@ -0,0 +1,52 @@ +#ifndef BLOBS_WORLD_CREATURECREATURECOLLISION_HPP_ +#define BLOBS_WORLD_CREATURECREATURECOLLISION_HPP_ + +#include "../math/glm.hpp" + + +namespace blobs { +namespace creature { + class Creature; +} +namespace world { + +class CreatureCreatureCollision { + +public: + CreatureCreatureCollision( + creature::Creature &a, + creature::Creature &b, + const glm::dvec3 &n, + double depth + ) : a(&a), b(&b), normal(n), depth(depth) { + if (dot(normal, BPos() - APos()) < 0.0) { + // make sure normal always is in direction from A to B + normal *= -1.0; + } + } + ~CreatureCreatureCollision(); + +public: + creature::Creature &A() noexcept { return *a; } + const creature::Creature &A() const noexcept { return *a; } + const glm::dvec3 &APos() const noexcept; + + creature::Creature &B() noexcept { return *b; } + const creature::Creature &B() const noexcept { return *b; } + const glm::dvec3 &BPos() const noexcept; + + const glm::dvec3 &Normal() const noexcept { return normal; } + double Depth() const noexcept { return depth; } + +private: + creature::Creature *a; + creature::Creature *b; + glm::dvec3 normal; + double depth; + +}; + +} +} + +#endif diff --git a/src/world/world.cpp b/src/world/world.cpp index 1b2e058..9cd1e02 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -1,4 +1,5 @@ #include "Body.hpp" +#include "CreatureCreatureCollision.hpp" #include "Orbit.hpp" #include "Planet.hpp" #include "Resource.hpp" @@ -13,6 +14,7 @@ #include "../creature/Creature.hpp" #include "../graphics/Viewport.hpp" #include "../math/const.hpp" +#include "../math/geometry.hpp" #include "../math/OctaveNoise.hpp" #include "../math/SimplexNoise.hpp" @@ -141,6 +143,7 @@ glm::dmat4 Body::FromUniverse() const noexcept { namespace { std::vector ccache; +std::vector collisions; } void Body::Tick(double dt) { @@ -150,6 +153,7 @@ void Body::Tick(double dt) { for (creature::Creature *c : ccache) { c->Tick(dt); } + // first remove creatures so they don't collide for (auto c = Creatures().begin(); c != Creatures().end();) { if ((*c)->Removable()) { delete *c; @@ -158,6 +162,7 @@ void Body::Tick(double dt) { ++c; } } + CheckCollision(); } void Body::Cache() noexcept { @@ -180,6 +185,34 @@ void Body::Cache() noexcept { * glm::eulerAngleY(-rotation); } +void Body::CheckCollision() noexcept { + if (Creatures().size() < 2) return; + collisions.clear(); + auto end = Creatures().end(); + for (auto i = Creatures().begin(); i != end; ++i) { + math::AABB i_box((*i)->CollisionBox()); + glm::dmat4 i_mat((*i)->CollisionTransform()); + for (auto j = (i + 1); j != end; ++j) { + glm::dvec3 diff((*i)->GetSituation().Position() - (*j)->GetSituation().Position()); + double max_dist = ((*i)->Size() + (*j)->Size()) * 1.74; + if (length2(diff) > max_dist * max_dist) continue; + math::AABB j_box((*j)->CollisionBox()); + glm::dmat4 j_mat((*j)->CollisionTransform()); + glm::dvec3 normal; + double depth; + if (Intersect(i_box, i_mat, j_box, j_mat, normal, depth)) { + collisions.push_back({ **i, **j, normal, depth }); + } + } + } + for (auto &c : collisions) { + c.A().GetSituation().Move(c.Normal() * (c.Depth() * -0.5)); + c.B().GetSituation().Move(c.Normal() * (c.Depth() * 0.5)); + // TODO: adjust velocities as well + // TODO: notify participants so they can be annoyed + } +} + void Body::AddCreature(creature::Creature *c) { creatures.push_back(c); } @@ -192,6 +225,18 @@ void Body::RemoveCreature(creature::Creature *c) { } +CreatureCreatureCollision::~CreatureCreatureCollision() { +} + +const glm::dvec3 &CreatureCreatureCollision::APos() const noexcept { + return a->GetSituation().Position(); +} + +const glm::dvec3 &CreatureCreatureCollision::BPos() const noexcept { + return b->GetSituation().Position(); +} + + Orbit::Orbit() : sma(1.0) , ecc(0.0) -- 2.39.2