summaryrefslogtreecommitdiff
path: root/2d/_collisions/polygon_polygon/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to '2d/_collisions/polygon_polygon/main.cpp')
-rw-r--r--2d/_collisions/polygon_polygon/main.cpp414
1 files changed, 414 insertions, 0 deletions
diff --git a/2d/_collisions/polygon_polygon/main.cpp b/2d/_collisions/polygon_polygon/main.cpp
new file mode 100644
index 0000000..b7e3ce5
--- /dev/null
+++ b/2d/_collisions/polygon_polygon/main.cpp
@@ -0,0 +1,414 @@
+#include "../../../shared_cpp/OrthographicRenderer.h"
+#include "../../../shared_cpp/types.h"
+#include "../../../shared_cpp/WebglContext.h"
+#include "../../../shared_cpp/mathlib.h"
+#include "../../../shared_cpp/MainLoop.h"
+#include <cstdio>
+#include <cmath>
+#include <emscripten/html5.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <cmath>
+#include <cfloat>
+
+struct Rigidbody {
+ Vector2 force = { 0, 0 };
+ Vector2 velocity = { 0, 0 };
+ Vector2 position = { 0, 0 };
+ float32 rotationalVelocity = 0.f;
+ float32 rotation = 0.f;
+ float32 mass = 1.f;
+ float32 cofOfRestition = 1.f;
+ float32 momentOfInertia = 0.f;
+
+ void reset() {
+ force = { 0, 0 };
+ velocity = { 0, 0 };
+ rotationalVelocity = 0.f;
+ rotation = 0.f;
+ }
+
+ void applyForce(Vector2 f) {
+ force += f;
+ }
+
+ void applyGravity(float32 deltaTimeSeconds) {
+ velocity += (Vector2 { 0.f, -50.f } * deltaTimeSeconds);
+ }
+
+ void update(float32 deltaTimeSeconds) {
+ applyGravity(deltaTimeSeconds);
+
+ Vector2 acceleration = force / mass;
+ velocity += (acceleration * deltaTimeSeconds);
+ position += (velocity * deltaTimeSeconds);
+ force = Vector2 { 0.f, 0.f };
+
+ rotation += (rotationalVelocity * deltaTimeSeconds);
+ }
+
+ void setMomentOfInertia(float32 moi) {
+ momentOfInertia = moi;
+ }
+};
+
+struct IntersectionResult {
+ bool intersect = false;
+ Vector2 collisionNormal;
+ Vector2 relativeVelocity;
+ Vector2 firstPointOfApplication;
+ Vector2 secondPointOfApplication;
+};
+
+struct Edge {
+ Vector2 normal;
+ Vector2 start;
+ Vector2 end;
+};
+
+struct ConvexPolygon {
+ OrthographicShape shape;
+ Rigidbody body;
+ Rigidbody previousBody;
+ Vector4 color;
+ int32 numVertices = 3;
+ float32 width = 0.f;
+ float32 height = 0.f;
+
+ void load(OrthographicRenderer* renderer) {
+ // Generate the shape with numVertices many sides in a "fan" shape (i.e. 3 vertices per vertex)
+ // The shape will have all equal sides, just to make it easier on me. Therefore, it will fit inside
+ // the conditions of a circle, which is fun. Before anyone gets mad: I know I can avoid recalculating
+ // a lot of these cosines and sines. I will be okay; I will live. The bigger problem with this demo
+ // is the 2k lines of JavaScript that are required to display it.
+ int32 verticesNeeded = numVertices * 3;
+ float32 angleIncrements = (2.f * PI) / static_cast<float32>(numVertices);
+ OrthographicVertex* vertices = new OrthographicVertex[verticesNeeded];
+ for (int32 vidx = 0; vidx < numVertices; vidx++) {
+ int32 indexPosition = vidx * 3;
+
+ float32 firstAngle = angleIncrements * vidx;
+ vertices[indexPosition].position = { cosf(firstAngle) * width, sinf(firstAngle) * height };
+
+ vertices[indexPosition + 1].position = { 0.f, 0.f };
+
+ float32 secondAngle = angleIncrements * (vidx + 1);
+ vertices[indexPosition + 2].position = { cosf(secondAngle) * width, sinf(secondAngle) * height };
+
+ // Apply some global stylings
+ for (int subIdx = 0; subIdx < 3; subIdx++) {
+ vertices[indexPosition + subIdx].color = color.toNormalizedColor();
+ }
+ }
+
+ shape.load(vertices, verticesNeeded, renderer);
+ delete[] vertices;
+ }
+
+ void update(float32 dtSeconds) {
+ previousBody = body;
+
+ body.update(dtSeconds);
+ shape.model = Mat4x4().translateByVec2(body.position).rotate2D(body.rotation);
+ }
+
+ void render(OrthographicRenderer* renderer) {
+ shape.render(renderer);
+ }
+
+ void unload() {
+ shape.unload();
+ }
+};
+
+
+
+EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData);
+EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData);
+
+void load();
+void update(float32 time, void* userData);
+void unload();
+
+WebglContext context;
+OrthographicRenderer renderer;
+MainLoop mainLoop;
+ConvexPolygon polygons[4];
+
+int main() {
+ context.init("#gl_canvas");
+ emscripten_set_click_callback("#gl_canvas_play", NULL, false, onPlayClicked);
+ emscripten_set_click_callback("#gl_canvas_stop", NULL, false, onStopClicked);
+ return 0;
+}
+
+void load() {
+ renderer.load(&context);
+
+ for (int index = 0; index < 4; index++) {
+ polygons[index].width = 50.f;
+ polygons[index].height = 50.f;
+
+ if (index == 0) {
+ polygons[index].body.position = { context.width / 4.f, context.height / 4.f };
+ } else if (index == 1) {
+ polygons[index].body.position = { context.width / 4.f, context.height * (3.f /4.f) };
+ } else if (index == 2) {
+ polygons[index].body.position = { context.width * (3.f / 4.f), context.height * (3.f / 4.f) };
+ } else if (index == 3) {
+ polygons[index].body.position = { context.width * (3.f / 4.f), context.height / 4.f };
+ }
+
+ polygons[index].numVertices = (index + 1) * 3;
+ polygons[index].color = Vector4 {
+ index == 0 || index == 3 ? 255.f : 0.f,
+ index == 1 || index == 3 ? 255.f : 0.f,
+ index == 2 ? 255.f : 0.f,
+ 255.f
+ };
+ polygons[index].load(&renderer);
+ }
+ mainLoop.run(update);
+}
+
+Vector2 getProjection(Vector2* vertices, Vector2 axis) {
+ float32 min = axis.dot(vertices[0]);
+ float32 max = min;
+
+ for (int v = 1; v < 4; v++) {
+ float32 d = axis.dot(vertices[v]);
+
+ if (d < min) {
+ min = d;
+ } else if (d > max) {
+ max = d;
+ }
+ }
+
+ return Vector2 { min, max };
+}
+
+/*bool projectionsOverlap(Vector2 first, Vector2 second) {
+ return first.x <= second.y && second.x <= first.y;
+}
+
+float32 getProjectionOverlap(Vector2 first, Vector2 second) {
+ float32 firstOverlap = fabs(first.x - second.y);
+ float32 secondOverlap = fabs(second.x - first.y);
+ return firstOverlap > secondOverlap ? secondOverlap : firstOverlap;
+}
+
+const float32 EPSILON = 1.f;
+IntersectionResult getIntersection(Rectangle* first, Po* second) {
+ IntersectionResult ir;
+
+ // For two rectangles to overlap, it means that at least one of the corners of one is inside of the other
+ Edge firstEdges[4];
+ first->getEdges(firstEdges);
+ Vector2 firstPoints[4];
+ first->getPoints(firstPoints);
+
+ Edge secondEdges[4];
+ second->getEdges(secondEdges);
+ Vector2 secondPoints[4];
+ second->getPoints(secondPoints);
+
+ float32 minOverlap = FLT_MAX;
+ Vector2 minOverlapAxis;
+ Edge* minOverlapEdge = NULL;
+ bool minOverlapWasFirstRect = false;
+
+ for (int i = 0; i < 4; i++) {
+ Vector2 normal = firstEdges[i].normal;
+
+ Vector2 firstProj = getProjection(firstPoints, normal);
+ Vector2 secondProj = getProjection(secondPoints, normal);
+
+ if (!projectionsOverlap(firstProj, secondProj)) {
+ return ir;
+ }
+
+ float32 overlap = getProjectionOverlap(firstProj, secondProj);
+ if (overlap < minOverlap) {
+ minOverlap = overlap;
+ minOverlapAxis = normal;
+ minOverlapEdge = &firstEdges[i];
+ minOverlapWasFirstRect = true;
+ }
+ }
+
+ for (int i = 0; i < 4; i++) {
+ Vector2 normal = secondEdges[i].normal;
+
+ Vector2 firstProj = getProjection(firstPoints, normal);
+ Vector2 secondProj = getProjection(secondPoints, normal);
+
+ if (!projectionsOverlap(firstProj, secondProj)) {
+ return ir;
+ }
+
+ float32 overlap = getProjectionOverlap(firstProj, secondProj);
+ if (overlap < minOverlap) {
+ minOverlap = overlap;
+ minOverlapAxis = normal;
+ minOverlapEdge = &secondEdges[i];
+ }
+ }
+
+ ir.intersect = true;
+ ir.relativeVelocity = first->body.velocity - second->body.velocity;
+ ir.collisionNormal = minOverlapAxis;
+
+ // Find the point of collision, this is kind of tricky, and it is just an approximation for now.
+ // At this point, we know that we intersected along the minOverlapAxis, but we do not know where
+ // that exactly happened. To remedy this will, we create two parallel lines: one at the top of the
+ // normal area, and one at the bottom. For point on both of the Rectangles, we will check:
+ // (1) if it is between these two planes
+ // (2) if, for that rectangle, it is the closest point to the original normal vector
+ // (3) or if it is equally distant from normal vector as another point (then this is a "flat" collision)
+ //
+ // The collision point MUST be between these two planes. We can then say the corner/face of the non-monoverlapAxis
+ // Rectangle is the collision point. This enables us to then solve for their respective points of application fairly
+ // easily. If the collision "point" is an entire face, we make the collision point be the center point.
+ //
+
+ Vector2 closestPoint;
+ float32 minDistance = FLT_MAX;
+
+ for (int p = 0; p < 4; p++) {
+ Vector2 point = minOverlapWasFirstRect ? secondPoints[p] : firstPoints[p];
+
+ float32 distFromPointToStart = (minOverlapEdge->start - point).length();
+ float32 distFromPointToEnd = (minOverlapEdge->end - point).length();
+ float32 potentialMin = MIN(distFromPointToStart, distFromPointToEnd);
+
+ if (potentialMin < minDistance) {
+ closestPoint = point;
+ minDistance = potentialMin;
+ }
+ }
+
+ ir.firstPointOfApplication = closestPoint - first->body.position;
+ ir.secondPointOfApplication = closestPoint - second->body.position;;
+
+ return ir;
+}*/
+
+void resolveCollision(Rigidbody* first, Rigidbody* second, IntersectionResult* ir) {
+ Vector2 relativeVelocity = ir->relativeVelocity;
+ Vector2 collisionNormal = ir->collisionNormal;
+ Vector2 firstPerp = ir->firstPointOfApplication.getPerp();
+ Vector2 secondPerp = ir->secondPointOfApplication.getPerp();
+ float32 firstPerpNorm = firstPerp.dot(collisionNormal);
+ float32 sndPerpNorm = secondPerp.dot(collisionNormal);
+
+ float32 cofOfRestition = (first->cofOfRestition + second->cofOfRestition) / 2.f;
+ float32 numerator = (relativeVelocity * (-1 * (1.f + cofOfRestition))).dot(collisionNormal);
+ float32 linearDenomPart = collisionNormal.dot(collisionNormal * (1.f / first->mass + 1.f / second->mass));
+ float32 rotationalDenomPart = (firstPerpNorm * firstPerpNorm) / first->momentOfInertia + (sndPerpNorm * sndPerpNorm) / second->momentOfInertia;
+
+ float32 impulseMagnitude = numerator / (linearDenomPart + rotationalDenomPart);
+ first->velocity = first->velocity + (collisionNormal * (impulseMagnitude / first->mass));
+ second->velocity = second->velocity - (collisionNormal * (impulseMagnitude / second->mass));
+
+ first->rotationalVelocity = first->rotationalVelocity + firstPerp.dot(collisionNormal * impulseMagnitude) / first->momentOfInertia;
+ second->rotationalVelocity = second->rotationalVelocity - secondPerp.dot(collisionNormal * impulseMagnitude) / second->momentOfInertia;
+}
+
+void update(float32 deltaTimeSeconds, void* userData) {
+ // Update
+ for (int p = 0; p < 4; p++) {
+ polygons[p].update(deltaTimeSeconds);
+ }
+
+ // Check collisions with other rectangles
+ /*for (int i = 0; i < 4; i++) {
+ Rectangle* first = &rectangleList[i];
+ for (int j = i + 1; j < 4; j++) {
+ Rectangle* second = &rectangleList[j];
+
+ IntersectionResult ir = getIntersection(first, second);
+ if (!ir.intersect) {
+ continue;
+ }
+
+ // Handle collison here
+ IntersectionResult irCopy = ir;
+ float32 copyDt = deltaTimeSeconds;
+
+ do {
+ first->restorePreviousBody();
+ second->restorePreviousBody();
+
+ ir = irCopy;
+ copyDt = copyDt /= 2.f;
+
+ first->update(copyDt);
+ second->update(copyDt);
+
+ irCopy = getIntersection(first, second);
+
+ if (copyDt <= 0.f) {
+ printf("Error: Should not be happening.\n");
+ break;
+ }
+
+ } while (irCopy.intersect);
+
+ printf("Found intersection at timestamp: %f\n", copyDt);
+
+ resolveCollision(&first->body, &second->body, &ir);
+ float32 frameTimeRemaining = deltaTimeSeconds - copyDt;
+
+ first->update(frameTimeRemaining);
+ second->update(frameTimeRemaining);
+ }
+ }*/
+
+ // Check collisions with walls
+ for (int p = 0; p < 4; p++) {
+ ConvexPolygon* polygon = &polygons[p];
+ if (polygon->body.position.x <= 0.f) {
+ polygon->body.velocity = polygon->body.velocity - Vector2 { 1.f, 0.f } * (2 * (polygon->body.velocity.dot(Vector2 { 1.f, 0.f })));
+ }
+ if (polygon->body.position.y <= 0.f) {
+ polygon->body.velocity = polygon->body.velocity - Vector2 { 0.f, 1.f } * (2 * (polygon->body.velocity.dot(Vector2 { 0.f, 1.f })));
+ }
+ if (polygon->body.position.x >= 640.f) {
+ polygon->body.velocity = polygon->body.velocity - Vector2 { -1.f, 0.f } * (2 * (polygon->body.velocity.dot(Vector2{ -1.f, 0.f })));
+ }
+ if (polygon->body.position.y >= 480.f) {
+ polygon->body.velocity = polygon->body.velocity - Vector2 { 0.f, -1.f } * (2 * (polygon->body.velocity.dot(Vector2 { 0.f, -1.f }))) ;
+ }
+ }
+
+ // Renderer
+ renderer.render();
+ for (int p = 0; p < 4; p++) {
+ polygons[p].render(&renderer);
+ }
+}
+
+void unload() {
+ mainLoop.stop();
+ renderer.unload();
+ for (int p = 0; p < 4; p++) {
+ polygons[p].unload();
+ }
+}
+
+//
+// Interactions with DOM handled below
+//
+EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) {
+ printf("Play clicked\n");
+
+ load();
+ return true;
+}
+
+EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) {
+ printf("Stop clicked\n");
+ unload();
+ return true;
+}