Added Polygon Collisions #1
parent
d879b44021
commit
1c12d5b946
@ -0,0 +1,434 @@ |
|||||||
|
/*
|
||||||
|
Convex Polygon Collision Detection |
||||||
|
"Don't you dare try concave ones..." - javidx9 |
||||||
|
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2018-2019 OneLoneCoder.com |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above |
||||||
|
copyright notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce |
||||||
|
the above copyright notice. This list of conditions and the following |
||||||
|
disclaimer must be reproduced in the documentation and/or other |
||||||
|
materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
Instructions: |
||||||
|
~~~~~~~~~~~~~ |
||||||
|
Use arrow keys to control pentagon |
||||||
|
Use WASD to control triangle |
||||||
|
F1..F4 selects algorithm |
||||||
|
|
||||||
|
Relevant Video: https://youtu.be/7Ik2vowGcU0
|
||||||
|
|
||||||
|
Links |
||||||
|
~~~~~ |
||||||
|
YouTube: https://www.youtube.com/javidx9
|
||||||
|
https://www.youtube.com/javidx9extra
|
||||||
|
Discord: https://discord.gg/WhwHUMV
|
||||||
|
Twitter: https://www.twitter.com/javidx9
|
||||||
|
Twitch: https://www.twitch.tv/javidx9
|
||||||
|
GitHub: https://www.github.com/onelonecoder
|
||||||
|
Patreon: https://www.patreon.com/javidx9
|
||||||
|
Homepage: https://www.onelonecoder.com
|
||||||
|
|
||||||
|
Author |
||||||
|
~~~~~~ |
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019 |
||||||
|
*/ |
||||||
|
|
||||||
|
#define OLC_PGE_APPLICATION |
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
#include <algorithm> |
||||||
|
|
||||||
|
// Override base class with your custom functionality
|
||||||
|
class PolygonCollisions : public olc::PixelGameEngine |
||||||
|
{ |
||||||
|
public: |
||||||
|
PolygonCollisions() |
||||||
|
{ |
||||||
|
sAppName = "Polygon Collisions"; |
||||||
|
} |
||||||
|
|
||||||
|
struct vec2d |
||||||
|
{ |
||||||
|
float x; |
||||||
|
float y; |
||||||
|
}; |
||||||
|
|
||||||
|
struct polygon |
||||||
|
{ |
||||||
|
std::vector<vec2d> p; // Transformed Points
|
||||||
|
vec2d pos; // Position of shape
|
||||||
|
float angle; // Direction of shape
|
||||||
|
std::vector<vec2d> o; // "Model" of shape
|
||||||
|
bool overlap = false; // Flag to indicate if overlap has occurred
|
||||||
|
}; |
||||||
|
|
||||||
|
std::vector<polygon> vecShapes; |
||||||
|
|
||||||
|
int nMode = 0; |
||||||
|
|
||||||
|
public: |
||||||
|
bool OnUserCreate() override |
||||||
|
{
|
||||||
|
// Create Pentagon
|
||||||
|
polygon s1; |
||||||
|
float fTheta = 3.14159f * 2.0f / 5.0f; |
||||||
|
s1.pos = { 100, 100 }; |
||||||
|
s1.angle = 0.0f; |
||||||
|
for (int i = 0; i < 5; i++) |
||||||
|
{ |
||||||
|
s1.o.push_back({ 30.0f * cosf(fTheta * i), 30.0f * sinf(fTheta * i) }); |
||||||
|
s1.p.push_back({ 30.0f * cosf(fTheta * i), 30.0f * sinf(fTheta * i) }); |
||||||
|
} |
||||||
|
|
||||||
|
// Create Triangle
|
||||||
|
polygon s2; |
||||||
|
fTheta = 3.14159f * 2.0f / 3.0f; |
||||||
|
s2.pos = { 200, 150 }; |
||||||
|
s2.angle = 0.0f; |
||||||
|
for (int i = 0; i < 3; i++) |
||||||
|
{ |
||||||
|
s2.o.push_back({ 20.0f * cosf(fTheta * i), 20.0f * sinf(fTheta * i) }); |
||||||
|
s2.p.push_back({ 20.0f * cosf(fTheta * i), 20.0f * sinf(fTheta * i) }); |
||||||
|
} |
||||||
|
|
||||||
|
// Create Quad
|
||||||
|
polygon s3; |
||||||
|
s3.pos = { 50, 200 }; |
||||||
|
s3.angle = 0.0f; |
||||||
|
s3.o.push_back({ -30, -30 }); |
||||||
|
s3.o.push_back({ -30, +30 }); |
||||||
|
s3.o.push_back({ +30, +30 }); |
||||||
|
s3.o.push_back({ +30, -30 }); |
||||||
|
s3.p.resize(4); |
||||||
|
|
||||||
|
|
||||||
|
vecShapes.push_back(s1); |
||||||
|
vecShapes.push_back(s2); |
||||||
|
vecShapes.push_back(s3); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool ShapeOverlap_SAT(polygon &r1, polygon &r2) |
||||||
|
{ |
||||||
|
polygon *poly1 = &r1; |
||||||
|
polygon *poly2 = &r2; |
||||||
|
|
||||||
|
for (int shape = 0; shape < 2; shape++) |
||||||
|
{ |
||||||
|
if (shape == 1) |
||||||
|
{ |
||||||
|
poly1 = &r2; |
||||||
|
poly2 = &r1; |
||||||
|
} |
||||||
|
|
||||||
|
for (int a = 0; a < poly1->p.size(); a++) |
||||||
|
{ |
||||||
|
int b = (a + 1) % poly1->p.size(); |
||||||
|
vec2d axisProj = { -(poly1->p[b].y - poly1->p[a].y), poly1->p[b].x - poly1->p[a].x }; |
||||||
|
float d = sqrtf(axisProj.x * axisProj.x + axisProj.y * axisProj.y); |
||||||
|
axisProj = { axisProj.x / d, axisProj.y / d }; |
||||||
|
|
||||||
|
// Work out min and max 1D points for r1
|
||||||
|
float min_r1 = INFINITY, max_r1 = -INFINITY; |
||||||
|
for (int p = 0; p < poly1->p.size(); p++) |
||||||
|
{ |
||||||
|
float q = (poly1->p[p].x * axisProj.x + poly1->p[p].y * axisProj.y); |
||||||
|
min_r1 = std::min(min_r1, q); |
||||||
|
max_r1 = std::max(max_r1, q); |
||||||
|
} |
||||||
|
|
||||||
|
// Work out min and max 1D points for r2
|
||||||
|
float min_r2 = INFINITY, max_r2 = -INFINITY; |
||||||
|
for (int p = 0; p < poly2->p.size(); p++) |
||||||
|
{ |
||||||
|
float q = (poly2->p[p].x * axisProj.x + poly2->p[p].y * axisProj.y); |
||||||
|
min_r2 = std::min(min_r2, q); |
||||||
|
max_r2 = std::max(max_r2, q); |
||||||
|
} |
||||||
|
|
||||||
|
if (!(max_r2 >= min_r1 && max_r1 >= min_r2)) |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool ShapeOverlap_SAT_STATIC(polygon &r1, polygon &r2) |
||||||
|
{ |
||||||
|
polygon *poly1 = &r1; |
||||||
|
polygon *poly2 = &r2; |
||||||
|
|
||||||
|
float overlap = INFINITY; |
||||||
|
|
||||||
|
for (int shape = 0; shape < 2; shape++) |
||||||
|
{ |
||||||
|
if (shape == 1) |
||||||
|
{ |
||||||
|
poly1 = &r2; |
||||||
|
poly2 = &r1; |
||||||
|
} |
||||||
|
|
||||||
|
for (int a = 0; a < poly1->p.size(); a++) |
||||||
|
{ |
||||||
|
int b = (a + 1) % poly1->p.size(); |
||||||
|
vec2d axisProj = { -(poly1->p[b].y - poly1->p[a].y), poly1->p[b].x - poly1->p[a].x }; |
||||||
|
|
||||||
|
// Optional normalisation of projection axis enhances stability slightly
|
||||||
|
//float d = sqrtf(axisProj.x * axisProj.x + axisProj.y * axisProj.y);
|
||||||
|
//axisProj = { axisProj.x / d, axisProj.y / d };
|
||||||
|
|
||||||
|
// Work out min and max 1D points for r1
|
||||||
|
float min_r1 = INFINITY, max_r1 = -INFINITY; |
||||||
|
for (int p = 0; p < poly1->p.size(); p++) |
||||||
|
{ |
||||||
|
float q = (poly1->p[p].x * axisProj.x + poly1->p[p].y * axisProj.y); |
||||||
|
min_r1 = std::min(min_r1, q); |
||||||
|
max_r1 = std::max(max_r1, q); |
||||||
|
} |
||||||
|
|
||||||
|
// Work out min and max 1D points for r2
|
||||||
|
float min_r2 = INFINITY, max_r2 = -INFINITY; |
||||||
|
for (int p = 0; p < poly2->p.size(); p++) |
||||||
|
{ |
||||||
|
float q = (poly2->p[p].x * axisProj.x + poly2->p[p].y * axisProj.y); |
||||||
|
min_r2 = std::min(min_r2, q); |
||||||
|
max_r2 = std::max(max_r2, q); |
||||||
|
} |
||||||
|
|
||||||
|
// Calculate actual overlap along projected axis, and store the minimum
|
||||||
|
overlap = std::min(std::min(max_r1, max_r2) - std::max(min_r1, min_r2), overlap); |
||||||
|
|
||||||
|
if (!(max_r2 >= min_r1 && max_r1 >= min_r2)) |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// If we got here, the objects have collided, we will displace r1
|
||||||
|
// by overlap along the vector between the two object centers
|
||||||
|
vec2d d = { r2.pos.x - r1.pos.x, r2.pos.y - r1.pos.y }; |
||||||
|
float s = sqrtf(d.x*d.x + d.y*d.y); |
||||||
|
r1.pos.x -= overlap * d.x / s; |
||||||
|
r1.pos.y -= overlap * d.y / s; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// Use edge/diagonal intersections.
|
||||||
|
bool ShapeOverlap_DIAGS(polygon &r1, polygon &r2) |
||||||
|
{ |
||||||
|
polygon *poly1 = &r1; |
||||||
|
polygon *poly2 = &r2; |
||||||
|
|
||||||
|
for (int shape = 0; shape < 2; shape++) |
||||||
|
{ |
||||||
|
if (shape == 1) |
||||||
|
{ |
||||||
|
poly1 = &r2; |
||||||
|
poly2 = &r1; |
||||||
|
} |
||||||
|
|
||||||
|
// Check diagonals of polygon...
|
||||||
|
for (int p = 0; p < poly1->p.size(); p++) |
||||||
|
{ |
||||||
|
vec2d line_r1s = poly1->pos; |
||||||
|
vec2d line_r1e = poly1->p[p]; |
||||||
|
|
||||||
|
// ...against edges of the other
|
||||||
|
for (int q = 0; q < poly2->p.size(); q++) |
||||||
|
{ |
||||||
|
vec2d line_r2s = poly2->p[q]; |
||||||
|
vec2d line_r2e = poly2->p[(q + 1) % poly2->p.size()]; |
||||||
|
|
||||||
|
// Standard "off the shelf" line segment intersection
|
||||||
|
float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y); |
||||||
|
float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h; |
||||||
|
float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h; |
||||||
|
|
||||||
|
if (t1 >= 0.0f && t1 < 1.0f && t2 >= 0.0f && t2 < 1.0f) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
}
|
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// Use edge/diagonal intersections.
|
||||||
|
bool ShapeOverlap_DIAGS_STATIC(polygon &r1, polygon &r2) |
||||||
|
{ |
||||||
|
polygon *poly1 = &r1; |
||||||
|
polygon *poly2 = &r2; |
||||||
|
|
||||||
|
for (int shape = 0; shape < 2; shape++) |
||||||
|
{ |
||||||
|
if (shape == 1) |
||||||
|
{ |
||||||
|
poly1 = &r2; |
||||||
|
poly2 = &r1; |
||||||
|
} |
||||||
|
|
||||||
|
// Check diagonals of this polygon...
|
||||||
|
for (int p = 0; p < poly1->p.size(); p++) |
||||||
|
{ |
||||||
|
vec2d line_r1s = poly1->pos; |
||||||
|
vec2d line_r1e = poly1->p[p]; |
||||||
|
|
||||||
|
vec2d displacement = { 0,0 }; |
||||||
|
|
||||||
|
// ...against edges of this polygon
|
||||||
|
for (int q = 0; q < poly2->p.size(); q++) |
||||||
|
{ |
||||||
|
vec2d line_r2s = poly2->p[q]; |
||||||
|
vec2d line_r2e = poly2->p[(q + 1) % poly2->p.size()]; |
||||||
|
|
||||||
|
// Standard "off the shelf" line segment intersection
|
||||||
|
float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y); |
||||||
|
float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h; |
||||||
|
float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h; |
||||||
|
|
||||||
|
if (t1 >= 0.0f && t1 < 1.0f && t2 >= 0.0f && t2 < 1.0f) |
||||||
|
{
|
||||||
|
displacement.x += (1.0f - t1) * (line_r1e.x - line_r1s.x); |
||||||
|
displacement.y += (1.0f - t1) * (line_r1e.y - line_r1s.y);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
r1.pos.x += displacement.x * (shape == 0 ? -1 : +1); |
||||||
|
r1.pos.y += displacement.y * (shape == 0 ? -1 : +1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Cant overlap if static collision is resolved
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool OnUserUpdate(float fElapsedTime) override |
||||||
|
{ |
||||||
|
if (GetKey(olc::Key::F1).bReleased) nMode = 0; |
||||||
|
if (GetKey(olc::Key::F2).bReleased) nMode = 1; |
||||||
|
if (GetKey(olc::Key::F3).bReleased) nMode = 2; |
||||||
|
if (GetKey(olc::Key::F4).bReleased) nMode = 3; |
||||||
|
|
||||||
|
// Shape 1
|
||||||
|
if (GetKey(olc::Key::LEFT).bHeld) vecShapes[0].angle -= 2.0f * fElapsedTime; |
||||||
|
if (GetKey(olc::Key::RIGHT).bHeld) vecShapes[0].angle += 2.0f * fElapsedTime; |
||||||
|
|
||||||
|
if (GetKey(olc::Key::UP).bHeld) |
||||||
|
{ |
||||||
|
vecShapes[0].pos.x += cosf(vecShapes[0].angle) * 60.0f * fElapsedTime; |
||||||
|
vecShapes[0].pos.y += sinf(vecShapes[0].angle) * 60.0f * fElapsedTime; |
||||||
|
} |
||||||
|
|
||||||
|
if (GetKey(olc::Key::DOWN).bHeld) |
||||||
|
{ |
||||||
|
vecShapes[0].pos.x -= cosf(vecShapes[0].angle) * 60.0f * fElapsedTime; |
||||||
|
vecShapes[0].pos.y -= sinf(vecShapes[0].angle) * 60.0f * fElapsedTime; |
||||||
|
} |
||||||
|
|
||||||
|
// Shape 2
|
||||||
|
if (GetKey(olc::Key::A).bHeld) vecShapes[1].angle -= 2.0f * fElapsedTime; |
||||||
|
if (GetKey(olc::Key::D).bHeld) vecShapes[1].angle += 2.0f * fElapsedTime; |
||||||
|
|
||||||
|
if (GetKey(olc::Key::W).bHeld) |
||||||
|
{ |
||||||
|
vecShapes[1].pos.x += cosf(vecShapes[1].angle) * 60.0f * fElapsedTime; |
||||||
|
vecShapes[1].pos.y += sinf(vecShapes[1].angle) * 60.0f * fElapsedTime; |
||||||
|
} |
||||||
|
|
||||||
|
if (GetKey(olc::Key::S).bHeld) |
||||||
|
{ |
||||||
|
vecShapes[1].pos.x -= cosf(vecShapes[1].angle) * 60.0f * fElapsedTime; |
||||||
|
vecShapes[1].pos.y -= sinf(vecShapes[1].angle) * 60.0f * fElapsedTime; |
||||||
|
} |
||||||
|
|
||||||
|
// Update Shapes and reset flags
|
||||||
|
for (auto &r : vecShapes) |
||||||
|
{ |
||||||
|
for (int i = 0; i < r.o.size(); i++) |
||||||
|
r.p[i] = |
||||||
|
{ // 2D Rotation Transform + 2D Translation
|
||||||
|
(r.o[i].x * cosf(r.angle)) - (r.o[i].y * sinf(r.angle)) + r.pos.x, |
||||||
|
(r.o[i].x * sinf(r.angle)) + (r.o[i].y * cosf(r.angle)) + r.pos.y, |
||||||
|
}; |
||||||
|
|
||||||
|
r.overlap = false; |
||||||
|
} |
||||||
|
|
||||||
|
// Check for overlap
|
||||||
|
for (int m = 0; m < vecShapes.size(); m++) |
||||||
|
for (int n = m + 1; n < vecShapes.size(); n++) |
||||||
|
{ |
||||||
|
switch (nMode) |
||||||
|
{ |
||||||
|
case 0: vecShapes[m].overlap |= ShapeOverlap_SAT(vecShapes[m], vecShapes[n]); break; |
||||||
|
case 1: vecShapes[m].overlap |= ShapeOverlap_SAT_STATIC(vecShapes[m], vecShapes[n]); break; |
||||||
|
case 2: vecShapes[m].overlap |= ShapeOverlap_DIAGS(vecShapes[m], vecShapes[n]); break; |
||||||
|
case 3: vecShapes[m].overlap |= ShapeOverlap_DIAGS_STATIC(vecShapes[m], vecShapes[n]); break; |
||||||
|
}
|
||||||
|
} |
||||||
|
|
||||||
|
// === Render Display ===
|
||||||
|
Clear(olc::BLUE); |
||||||
|
|
||||||
|
// Draw Shapes
|
||||||
|
for (auto &r : vecShapes) |
||||||
|
{ |
||||||
|
// Draw Boundary
|
||||||
|
for (int i = 0; i < r.p.size(); i++) |
||||||
|
DrawLine(r.p[i].x, r.p[i].y, r.p[(i + 1) % r.p.size()].x, r.p[(i + 1) % r.p.size()].y, (r.overlap ? olc::RED : olc::WHITE)); |
||||||
|
|
||||||
|
// Draw Direction
|
||||||
|
DrawLine(r.p[0].x, r.p[0].y, r.pos.x, r.pos.y, (r.overlap ? olc::RED : olc::WHITE)); |
||||||
|
} |
||||||
|
|
||||||
|
// Draw HUD
|
||||||
|
DrawString(8, 10, "F1: SAT", (nMode == 0 ? olc::RED : olc::YELLOW)); |
||||||
|
DrawString(8, 20, "F2: SAT/STATIC", (nMode == 1 ? olc::RED : olc::YELLOW)); |
||||||
|
DrawString(8, 30, "F3: DIAG", (nMode == 2 ? olc::RED : olc::YELLOW)); |
||||||
|
DrawString(8, 40, "F4: DIAG/STATIC", (nMode == 3 ? olc::RED : olc::YELLOW)); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
PolygonCollisions demo; |
||||||
|
if (demo.Construct(256, 240, 4, 4)) |
||||||
|
demo.Start(); |
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue