void World::Update(float dt) {
+ constexpr float fricDyn = 0.75f;
+ constexpr float fricStat = 2.5f;
+ constexpr float dragFact = 0.05f;
+
for (Entity &e : entities) {
- e.acc = ForceAt(e.pos, e.mass) / e.mass;
+ const Vector<float> gravity = ForceAt(e.pos, e.mass);
+ const Vector<float> drag = Rotate180(e.vel) * dragFact;
+ // TODO: drag should be inverse square. and not onmipresent
+
+ e.acc = (gravity + drag) / e.mass;
e.Update(dt);
+
+ Vector<float> at;
+ if (WorldCollision(e, at)) {
+ float speed = Length(e.vel);
+ Vector<float> normVel = e.vel / speed;
+ float steps = 0;
+ while (MassAt(e.pos) > 0) {
+ e.pos -= normVel;
+ steps += 1.0f;
+ }
+ const Vector<float> normal = NormalAt(e.pos + normVel);
+ e.vel = Reflect(e.vel, normal);
+ speed = Length(e.vel);
+ normVel = e.vel / speed;
+ // some kind of friction, depends on angle to normal
+ speed *= (fricDyn * (std::abs(Dot(normVel, normal))));
+ if (speed > fricStat) {
+ speed -= fricStat;
+ e.vel = normVel * speed;
+ e.pos += normVel * steps;
+ } else {
+ e.vel = Vector<float>(0, 0);
+ }
+ }
+ }
+}
+
+
+bool World::WorldCollision(const Entity &e, Vector<float> &at) const {
+ if (InBounds(e.pos) && MassAt(e.pos) > 0) {
+ at = e.pos;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+Vector<float> World::NormalAt(Vector<float> pos) const {
+ constexpr Vector<float> check[] = {
+ { 0, -2 },
+ { 0.765367, -1.84776 },
+ { 1.41421, -1.41421 },
+ { 1.84776, -0.765367 },
+ { 2, 0 },
+ { 1.84776, 0.765367 },
+ { 1.41421, 1.41421 },
+ { 0.765367, 1.84776 },
+ { 0, 2 },
+ { -0.765367, 1.84776 },
+ { -1.41421, 1.41421 },
+ { -1.84776, 0.765367 },
+ { -2, 0 },
+ { -1.84776, -0.765367 },
+ { -1.41421, -1.41421 },
+ { -0.765367, -1.84776 },
+ };
+ Vector<float> normal;
+ for (auto v : check) {
+ if (MassAt(pos + v) == 0) {
+ normal += v;
+ }
}
+ return Norm(normal);
+}
+
+//Vector<float> World::NormalAt(Vector<float> pos) const {
+// Vector<int> begin(pos - Vector<float>(3, 3));
+// Vector<int> end(pos + Vector<float>(3, 3));
+//
+// Vector<float> normal;
+// for (Vector<int> cur(begin); cur.y < end.y; ++cur.y) {
+// for (cur.x = begin.x; cur.x < end.x; ++cur.x) {
+// if (IsSurface(cur) > 0) {
+// const Vector<float> dir = pos - Vector<float>(cur);
+// normal += Rotate90(dir);
+// }
+// }
+// }
+//
+// return Norm(normal);
+//}
+
+bool World::IsSurface(Vector<int> pos) const {
+ return MassAt(pos) > 0 && (
+ MassAt(pos - Vector<int>(1, 0)) == 0 ||
+ MassAt(pos + Vector<int>(1, 0)) == 0 ||
+ MassAt(pos - Vector<int>(0, 1)) == 0 ||
+ MassAt(pos + Vector<int>(0, 1)) == 0);
}
}
+// returns <1.37136364,1.37136734> for 1 mass at <0,0>
+/*
Vector<float> World::ForceAt(Vector<float> p1, float m1) const {
Vector<float> force(0, 0);
return force;
}
+*/
+
+// returns <1.37136674,1.37136662> for 1 mass at <0,0>
+// (should be less prone to rounding loss, i.e. more accurate)
+Vector<float> World::ForceAt(Vector<float> p1, float m1) const {
+ Vector<float> force(0, 0);
+
+ for (Vector<int> p2(0, 0); p2.y < size.y; ++p2.y) {
+ Vector<float> rowForce(0, 0);
+ for (p2.x = 0; p2.x < size.x; ++p2.x) {
+ const int i = Index(p2);
+ if (p1 == Vector<float>(p2)) continue;
+
+ const Vector<float> diff(Vector<float>(p2) - p1);
+ const Vector<float> dir(Norm(diff));
+
+ const float m2 = masses[i];
+ const float r2 = Dot(diff, diff);
+
+ const float mag = G * ((m1 * m2) / r2) * 2; // double because m2 is our reference frame
+
+ rowForce += dir * mag;
+ }
+ force += rowForce;
+ }
+
+ return force;
+}
}