]> git.localhorst.tv Git - blobs.git/commitdiff
primitive collision response
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Sat, 2 Dec 2017 13:50:09 +0000 (14:50 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Sat, 2 Dec 2017 13:50:09 +0000 (14:50 +0100)
src/app/states.cpp
src/blobs.cpp
src/creature/Creature.hpp
src/creature/creature.cpp
src/math/geometry.cpp [new file with mode: 0644]
src/math/geometry.hpp [new file with mode: 0644]
src/world/Body.hpp
src/world/CreatureCreatureCollision.hpp [new file with mode: 0644]
src/world/world.cpp

index e45356b5a29f6779a878ca07a6d4c490d44c591f..49ee8e23ac1793b88709cb6862fcb42d2d08a01c 100644 (file)
@@ -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;
        }
 }
 
index 85f992304eae8fa8cf120a415cc7fb519f59788a..9a94b96e0f55b87a4855efff5d1d434e0300fa92 100644 (file)
@@ -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();
                        }
index 44107bea32dd62b3f390b1e1b0575cb39d232fdf..13716878b06f2c6141ffe577e814b2fb2e489edd 100644 (file)
@@ -8,6 +8,7 @@
 #include "Situation.hpp"
 #include "Steering.hpp"
 #include "../graphics/SimpleVAO.hpp"
+#include "../math/geometry.hpp"
 #include "../math/glm.hpp"
 
 #include <memory>
@@ -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();
index ccd174db221548dfb9a7def3825d54d4b5d13792..dc857ceb08bd637460ee7b4954a95e7c17dd0275 100644 (file)
@@ -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 (file)
index 0000000..aa0389d
--- /dev/null
@@ -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<double>::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<double>::infinity();
+               double a_max = -std::numeric_limits<double>::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<double>::infinity();
+               double b_max = -std::numeric_limits<double>::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 (file)
index 0000000..4d1b07b
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef BLOBS_MATH_GEOMETRY_HPP_
+#define BLOBS_MATH_GEOMETRY_HPP_
+
+#include "glm.hpp"
+
+#include <algorithm>
+
+
+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
index df68984915bdf8e22b375280c7df6a10f073a449..3cbb788114c723dcf25108013b8627651290662e 100644 (file)
@@ -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 (file)
index 0000000..1a903ec
--- /dev/null
@@ -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
index 1b2e058f90d666dfd79d52474ad12ad0fe652686..9cd1e02df56a7f185a299f3337248cfbb22f417e 100644 (file)
@@ -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<creature::Creature *> ccache;
+std::vector<CreatureCreatureCollision> 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)