summaryrefslogtreecommitdiff
path: root/2d/_collisions/rectangle_line/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to '2d/_collisions/rectangle_line/main.cpp')
-rw-r--r--2d/_collisions/rectangle_line/main.cpp326
1 files changed, 326 insertions, 0 deletions
diff --git a/2d/_collisions/rectangle_line/main.cpp b/2d/_collisions/rectangle_line/main.cpp
new file mode 100644
index 0000000..25e124c
--- /dev/null
+++ b/2d/_collisions/rectangle_line/main.cpp
@@ -0,0 +1,326 @@
+#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>
+
+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 Rectangle {
+ OrthographicShape shape;
+ Rigidbody body;
+ Vector4 color;
+ float32 width = 0.f;
+ float32 height = 0.f;
+
+ void load(OrthographicRenderer* renderer, Vector4 inColor, float32 inWidth, float32 inHeight) {
+ color = inColor.toNormalizedColor();
+ width = inWidth;;
+ height = inHeight;
+
+ float32 halfWidth = width / 2.f;
+ float32 halfHeight = height / 2.f;
+
+ OrthographicVertex vertices[6];
+ vertices[0].position = Vector2 { -halfWidth, -halfHeight };
+ vertices[1].position = Vector2 { -halfWidth, halfHeight };
+ vertices[2].position = Vector2 { halfWidth, halfHeight };
+ vertices[3].position = Vector2 { -halfWidth, -halfHeight };
+ vertices[4].position = Vector2 { halfWidth, -halfHeight };
+ vertices[5].position = Vector2 { halfWidth, halfHeight };
+
+ for (int32 i = 0; i < 6; i++) {
+ vertices[i].color = color;
+ }
+
+ shape.load(vertices, 6, renderer);
+ body.reset();
+ body.momentOfInertia = (1.f / 3.f) * body.mass * (width + height * height * height);
+ }
+
+ void update(float32 dtSeconds) {
+ body.update(dtSeconds);
+ shape.model = Mat4x4().translateByVec2(body.position).rotate2D(body.rotation);
+ }
+
+ void render(OrthographicRenderer* renderer) {
+ shape.render(renderer);
+ }
+
+ void unload() {
+ shape.unload();
+ }
+};
+
+struct LineSegment {
+ OrthographicShape shape;
+ Rigidbody body;
+ Vector2 start;
+ Vector2 end;
+ float32 length;
+ Vector2 normal;
+ OrthographicVertex vertices[2];
+
+ void load(OrthographicRenderer* renderer, Vector4 color, Vector2 inStart, Vector2 inEnd) {
+ start = inStart;
+ end = inEnd;
+ length = (start - end).length();
+ vertices[0].position = start;
+ vertices[0].color = color;
+ vertices[1].position = end;
+ vertices[1].color = color;
+ normal = (end - start).getPerp().normalize();
+ shape.load(vertices, 2, renderer);
+
+ body.reset();
+ body.mass = 10000000000000.f;
+ body.cofOfRestition = 1.f;
+ body.rotationalVelocity = 0;
+ body.velocity = Vector2();
+ body.momentOfInertia = body.mass * (length / 2.f);
+ }
+
+ void render(OrthographicRenderer* renderer) {
+ shape.render(renderer, GL_LINES);
+ }
+
+ void unload() {
+ shape.unload();
+ }
+
+ Vector2 getPointOnLine(float32 t) {
+ return {
+ start.x + (end.x - start.x) * t,
+ start.y + (end.y - start.y) * t,
+ };
+ }
+
+ Vector2 getNormal() {
+ return (end - start).getPerp().normalize();
+ }
+};
+
+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;
+LineSegment segmentList[4];
+Rectangle rectangle;
+
+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);
+
+ segmentList[0].load(&renderer, Vector4().fromColor(191, 251, 146, 255.f), Vector2 { 50.f, 0.f }, Vector2 { 50.f, static_cast<float>(context.height) });
+ segmentList[1].load(&renderer, Vector4().fromColor(159, 224, 210, 255.f), Vector2 { context.width - 50.f, 0.f }, Vector2 { context.width - 50.f, static_cast<float>(context.height) });
+ segmentList[2].load(&renderer, Vector4().fromColor(248, 255, 156, 255.f), Vector2 { 50.f, 50.f }, Vector2 { context.width - 50.f, 150.f });
+ segmentList[3].load(&renderer, Vector4().fromColor(205, 178, 214, 255.f), Vector2 { 50.f, 150.f }, Vector2 { context.width - 50.f, 50.f });
+
+ rectangle.load(&renderer, Vector4 { 230.f, 182.f, 35.f, 255.f }, 64.f, 32.f);
+ rectangle.body.position = Vector2 { context.width / 3.f, context.height - 100.f };
+
+ mainLoop.run(update);
+}
+
+float32 getDistancePointToLine(Vector2 point, LineSegment* segment) {
+ float32 xLength = segment->end.x - segment->start.x;
+ float32 yLength = segment->end.y - segment->start.y;
+ return fabs(xLength * (segment->start.y - point.y) - (segment->start.x - point.x) * yLength)
+ / sqrtf((xLength * xLength) + (yLength * yLength));
+}
+
+const float32 EPSILON = 1.f;
+IntersectionResult getIntersection(Rectangle* rectangle, LineSegment* segment) {
+ IntersectionResult ir;
+
+ float32 halfWidth = rectangle->width / 2.f;
+ float32 halfHeight = rectangle->height / 2.f;
+
+ Vector2 bottomLeft = rectangle->shape.model * Vector2 { -halfWidth, -halfHeight };
+ Vector2 topLeft = rectangle->shape.model * Vector2 { -halfWidth, halfHeight };
+ Vector2 bottomRight = rectangle->shape.model * Vector2 { halfWidth, -halfHeight };
+ Vector2 topRight = rectangle->shape.model * Vector2 { halfWidth, halfHeight };
+
+ Vector2 collisionPoint;
+
+ if (getDistancePointToLine(bottomLeft, segment) <= EPSILON) {
+ ir.intersect = true;
+ collisionPoint = bottomLeft;
+ } else if (getDistancePointToLine(topLeft, segment) <= EPSILON) {
+ ir.intersect = true;
+ collisionPoint = topLeft;
+ } else if (getDistancePointToLine(bottomRight, segment) <= EPSILON) {
+ ir.intersect = true;
+ collisionPoint = bottomRight;
+ } else if (getDistancePointToLine(topRight, segment) <= EPSILON) {
+ ir.intersect = true;
+ collisionPoint = topRight;
+ } else {
+ ir.intersect = false;
+ return ir;
+ }
+
+ ir.collisionNormal = segment->getNormal();
+ ir.relativeVelocity = rectangle->body.velocity - segment->body.velocity;
+ ir.firstPointOfApplication = (collisionPoint - rectangle->body.position);
+ ir.secondPointOfApplication = collisionPoint - segment->getPointOnLine(0.5f);
+
+ 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
+ Rectangle rectCopy = rectangle;
+ rectangle.update(deltaTimeSeconds);
+
+ for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) {
+ IntersectionResult ir = getIntersection(&rectangle, &segmentList[segmentIndex]);
+ if (!ir.intersect) {
+ continue;
+ }
+
+ // Handle collison here
+ IntersectionResult irCopy = ir;
+ float32 copyDt = deltaTimeSeconds;
+
+ do {
+ ir = irCopy;
+ rectangle = rectCopy;
+ copyDt = copyDt /= 2.f;
+
+ rectangle.update(copyDt);
+ irCopy = getIntersection(&rectangle, &segmentList[segmentIndex]);
+
+ if (copyDt <= 0.f) {
+ printf("Error: Should not be happening.\n");
+ break;
+ }
+
+ } while (irCopy.intersect);
+
+ printf("Found intersection at timestamp: %f\n", copyDt);
+
+ resolveCollision(&rectangle.body, &segmentList[segmentIndex].body, &ir);
+ float32 frameTimeRemaining = deltaTimeSeconds - copyDt;
+
+ update(frameTimeRemaining, userData);
+ return;
+ }
+
+ // Renderer
+ renderer.render();
+ rectangle.render(&renderer);
+ for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) {
+ segmentList[segmentIndex].render(&renderer);
+ }
+}
+
+void unload() {
+ mainLoop.stop();
+ renderer.unload();
+ for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) {
+ segmentList[segmentIndex].unload();
+ }
+ rectangle.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;
+}