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;
}
}
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();
}
#include "Situation.hpp"
#include "Steering.hpp"
#include "../graphics/SimpleVAO.hpp"
+#include "../math/geometry.hpp"
#include "../math/glm.hpp"
#include <memory>
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();
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) {
}
}
-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));
}
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 };
--- /dev/null
+#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;
+}
+
+
+}
+}
--- /dev/null
+#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
void Tick(double dt);
void Cache() noexcept;
+ void CheckCollision() noexcept;
// body takes over ownership of given pointer
void AddCreature(creature::Creature *);
--- /dev/null
+#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
#include "Body.hpp"
+#include "CreatureCreatureCollision.hpp"
#include "Orbit.hpp"
#include "Planet.hpp"
#include "Resource.hpp"
#include "../creature/Creature.hpp"
#include "../graphics/Viewport.hpp"
#include "../math/const.hpp"
+#include "../math/geometry.hpp"
#include "../math/OctaveNoise.hpp"
#include "../math/SimplexNoise.hpp"
namespace {
std::vector<creature::Creature *> ccache;
+std::vector<CreatureCreatureCollision> collisions;
}
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;
++c;
}
}
+ CheckCollision();
}
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);
}
}
+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)