Refactor collision functions into Collider

This commit is contained in:
Mishura
2024-05-06 18:15:29 -04:00
parent 05392398ab
commit 64151c71e6
7 changed files with 191 additions and 153 deletions

View File

@@ -11,24 +11,23 @@ enum class ColliderType : uint8_t {
using J3ML::Geometry::Triangle;
class Collider : public VertexArray {
class Collider {
public:
VertexArray shape;
ColliderType type;
void draw() override;
void draw();
bool contains(const Position &position) const;
bool collides(const Collider& other) const;
bool collides(const Triangle& triangle) const;
[[nodiscard]] std::vector<Triangle> getTriangles() const;
static Collider calculateOBB(const VertexArray& vArray, const Vector3& angle);
static Collider calculateAABB(const VertexArray& vArray);
//Useful for everything except AABB.
Vector3 angle;
};
namespace Collision {
Collider calculateOrientedBoundingBox(const VertexArray& vArray, const Vector3& angle);
Collider calculateAxisAlignedBoundingBox(const VertexArray& vArray);
bool vertexInsideAxisAlignedBoundingBox(const Vector3& position, const Collider& boundingBox);
bool vertexInsideOrientedBoundingBox(const Vector3& position, const Collider& boundingBox);
bool triangleVSAxisAlignedBoundingBoxCollides(const Triangle& triangle, const Collider& boundingBox);
bool orientedBoundingBoxCollides(const Collider& boundingBox, const Collider& boundingBox2);
bool axisAlignedBoundingBoxCollides(const Collider& boundingBox, const Collider& boundingBox2);
bool collisionMapVSAxisAlignedBoundingBoxCollides(const Collider& boundingBox, const Collider& collisionMap);
}

View File

@@ -40,17 +40,17 @@ void Cube::update(float elapsed) {
}
Collider Cube::getCollider() {
Collider collider = Collision::calculateAxisAlignedBoundingBox(getGeometry()->getBoundingVolume(angle));
Collider collider = Collider::calculateAABB(getGeometry()->getBoundingVolume(angle));
//Scale.
for (auto& vertex : collider.vertices) {
for (auto& vertex : collider.shape.vertices) {
vertex.x *= getScale();
vertex.y *= getScale();
vertex.z *= getScale();
}
//To world space.
for (auto& vertex : collider.vertices) {
for (auto& vertex : collider.shape.vertices) {
vertex.x = vertex.x + position.x;
vertex.y = vertex.y + position.y;
vertex.z = vertex.z + position.z;

View File

@@ -23,19 +23,19 @@ Sphere2::Sphere2() {
Collider Sphere2::getCollider() {
Collider collider;
//collider.angle = angle;
collider.vertices = getGeometry()->vertices;
collider.indices = getGeometry()->indices;
collider.shape.vertices = getGeometry()->vertices;
collider.shape.indices = getGeometry()->indices;
collider.type = ColliderType::CollisionMap;
//Scale.
for (auto& vertex : collider.vertices) {
for (auto& vertex : collider.shape.vertices) {
vertex.x *= getScale();
vertex.y *= getScale();
vertex.z *= getScale();
}
//To world space.
for (auto& vertex : collider.vertices) {
for (auto& vertex : collider.shape.vertices) {
vertex.x = vertex.x + position.x;
vertex.y = vertex.y + position.y;
vertex.z = vertex.z + position.z;

View File

@@ -4,126 +4,183 @@
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/Triangle.h>
bool Collision::vertexInsideAxisAlignedBoundingBox(const Vector3& position, const Collider& boundingBox) {
Vector3 minimum = {boundingBox.vertices.front().x, boundingBox.vertices.front().y, boundingBox.vertices.front().z};
Vector3 maximum = {boundingBox.vertices.back().x, boundingBox.vertices.back().y, boundingBox.vertices.back().z};
namespace {
bool orientedBoundingBoxCollides(const Collider &boundingBox, const Collider &boundingBox2) {
Vector3 axes[3] = {
Vector3(1, 0, 0),
Vector3(0, 1, 0),
Vector3(0, 0, 1)
};
return (
position.x >= minimum.x && position.x <= maximum.x &&
position.y >= minimum.y && position.y <= maximum.y &&
position.z >= minimum.z && position.z <= maximum.z
);
}
for (const auto& axis : axes) {
float projection1Min = FLT_MAX, projection1Max = -FLT_MAX;
float projection2Min = FLT_MAX, projection2Max = -FLT_MAX;
bool Collision::vertexInsideOrientedBoundingBox(const Vector3& position, const Collider& boundingBox) {
for (const auto& vertex : boundingBox.shape.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection1Min) projection1Min = projection;
if (projection > projection1Max) projection1Max = projection;
}
Vector3 minimum = {FLT_MAX, FLT_MAX, FLT_MAX};
Vector3 maximum = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
for (const auto& vertex : boundingBox2.shape.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection2Min) projection2Min = projection;
if (projection > projection2Max) projection2Max = projection;
}
for (const auto& vertex : boundingBox.vertices) {
if (vertex.x < minimum.x) minimum.x = vertex.x;
if (vertex.y < minimum.y) minimum.y = vertex.y;
if (vertex.z < minimum.z) minimum.z = vertex.z;
if (projection1Max < projection2Min || projection2Max < projection1Min)
return false;
}
if (vertex.x > maximum.x) maximum.x = vertex.x;
if (vertex.y > maximum.y) maximum.y = vertex.y;
if (vertex.z > maximum.z) maximum.z = vertex.z;
for (const auto& axis : axes) {
float projection1Min = FLT_MAX, projection1Max = -FLT_MAX;
float projection2Min = FLT_MAX, projection2Max = -FLT_MAX;
for (const auto& vertex : boundingBox.shape.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection1Min) projection1Min = projection;
if (projection > projection1Max) projection1Max = projection;
}
for (const auto& vertex : boundingBox2.shape.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection2Min) projection2Min = projection;
if (projection > projection2Max) projection2Max = projection;
}
if (projection1Max < projection2Min || projection2Max < projection1Min)
return false;
}
return true; //OBBs collide.
}
return (
position.x >= minimum.x && position.x <= maximum.x &&
position.y >= minimum.y && position.y <= maximum.y &&
position.z >= minimum.z && position.z <= maximum.z
);
bool axisAlignedBoundingBoxCollides(const Collider &boundingBox, const Collider &boundingBox2) {
Vector3 aabb1Min = {boundingBox.shape.vertices.front().x, boundingBox.shape.vertices.front().y, boundingBox.shape.vertices.front().z};
Vector3 aabb1Max = {boundingBox.shape.vertices.back().x, boundingBox.shape.vertices.back().y, boundingBox.shape.vertices.back().z};
Vector3 aabb2Min = {boundingBox2.shape.vertices.front().x, boundingBox2.shape.vertices.front().y, boundingBox2.shape.vertices.front().z};
Vector3 aabb2Max = {boundingBox2.shape.vertices.back().x, boundingBox2.shape.vertices.back().y, boundingBox2.shape.vertices.back().z};
if (aabb1Max.x < aabb2Min.x || aabb2Max.x < aabb1Min.x)
return false;
if (aabb1Max.y < aabb2Min.y || aabb2Max.y < aabb1Min.y)
return false;
if (aabb1Max.z < aabb2Min.z || aabb2Max.z < aabb1Min.z)
return false;
return true; //AABBs Collide.
}
//Broken
bool triangleVSAxisAlignedBoundingBoxCollides(const Triangle &triangle, const Collider &boundingBox) {
return false;
}
bool collisionMapVSAxisAlignedBoundingBoxCollides(const Collider &boundingBox, const Collider& collisionMap) {
for (auto& triangle : collisionMap.getTriangles())
if (triangleVSAxisAlignedBoundingBoxCollides(triangle, boundingBox))
return true;
return false;
}
}
bool Collision::orientedBoundingBoxCollides(const Collider &boundingBox, const Collider &boundingBox2) {
Vector3 axes[3] = {
Vector3(1, 0, 0),
Vector3(0, 1, 0),
Vector3(0, 0, 1)
};
bool Collider::contains(const Position& position) const {
switch (type) {
case ColliderType::AxisAlignedBoundingBox: {
Vector3 minimum = {shape.vertices.front().x, shape.vertices.front().y, shape.vertices.front().z};
Vector3 maximum = {shape.vertices.back().x, shape.vertices.back().y, shape.vertices.back().z};
for (const auto& axis : axes) {
float projection1Min = FLT_MAX, projection1Max = -FLT_MAX;
float projection2Min = FLT_MAX, projection2Max = -FLT_MAX;
for (const auto& vertex : boundingBox.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection1Min) projection1Min = projection;
if (projection > projection1Max) projection1Max = projection;
return (
position.x >= minimum.x && position.x <= maximum.x &&
position.y >= minimum.y && position.y <= maximum.y &&
position.z >= minimum.z && position.z <= maximum.z
);
}
for (const auto& vertex : boundingBox2.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection2Min) projection2Min = projection;
if (projection > projection2Max) projection2Max = projection;
case ColliderType::OrientedBoundingBox: {
Vector3 minimum = {FLT_MAX, FLT_MAX, FLT_MAX};
Vector3 maximum = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
for (const auto& vertex : shape.vertices) {
if (vertex.x < minimum.x) minimum.x = vertex.x;
if (vertex.y < minimum.y) minimum.y = vertex.y;
if (vertex.z < minimum.z) minimum.z = vertex.z;
if (vertex.x > maximum.x) maximum.x = vertex.x;
if (vertex.y > maximum.y) maximum.y = vertex.y;
if (vertex.z > maximum.z) maximum.z = vertex.z;
}
return (
position.x >= minimum.x && position.x <= maximum.x &&
position.y >= minimum.y && position.y <= maximum.y &&
position.z >= minimum.z && position.z <= maximum.z
);
}
if (projection1Max < projection2Min || projection2Max < projection1Min)
default:
return false;
}
}
for (const auto& axis : axes) {
float projection1Min = FLT_MAX, projection1Max = -FLT_MAX;
float projection2Min = FLT_MAX, projection2Max = -FLT_MAX;
bool Collider::collides(const Collider& other) const {
switch (type) {
case ColliderType::AxisAlignedBoundingBox:
switch (other.type) {
case ColliderType::AxisAlignedBoundingBox:
return axisAlignedBoundingBoxCollides(*this, other);
for (const auto& vertex : boundingBox.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection1Min) projection1Min = projection;
if (projection > projection1Max) projection1Max = projection;
}
case ColliderType::OrientedBoundingBox:
return false; // not implemented
case ColliderType::CollisionMap:
return collisionMapVSAxisAlignedBoundingBoxCollides(other, *this);
}
break;
case ColliderType::OrientedBoundingBox:
switch (other.type) {
case ColliderType::AxisAlignedBoundingBox:
return false; // not implemented
for (const auto& vertex : boundingBox2.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection2Min) projection2Min = projection;
if (projection > projection2Max) projection2Max = projection;
}
case ColliderType::OrientedBoundingBox:
return orientedBoundingBoxCollides(*this, other);
case ColliderType::CollisionMap:
return false; // not implemented
}
break;
if (projection1Max < projection2Min || projection2Max < projection1Min)
return false;
case ColliderType::CollisionMap:
switch (other.type) {
case ColliderType::AxisAlignedBoundingBox:
return collisionMapVSAxisAlignedBoundingBoxCollides(*this, other);
case ColliderType::OrientedBoundingBox:
return false; // not implemented
case ColliderType::CollisionMap:
return false; // not implemented
}
break;
}
return true; //OBBs collide.
}
bool Collision::axisAlignedBoundingBoxCollides(const Collider &boundingBox, const Collider &boundingBox2) {
Vector3 aabb1Min = {boundingBox.vertices.front().x, boundingBox.vertices.front().y, boundingBox.vertices.front().z};
Vector3 aabb1Max = {boundingBox.vertices.back().x, boundingBox.vertices.back().y, boundingBox.vertices.back().z};
Vector3 aabb2Min = {boundingBox2.vertices.front().x, boundingBox2.vertices.front().y, boundingBox2.vertices.front().z};
Vector3 aabb2Max = {boundingBox2.vertices.back().x, boundingBox2.vertices.back().y, boundingBox2.vertices.back().z};
if (aabb1Max.x < aabb2Min.x || aabb2Max.x < aabb1Min.x)
return false;
if (aabb1Max.y < aabb2Min.y || aabb2Max.y < aabb1Min.y)
return false;
if (aabb1Max.z < aabb2Min.z || aabb2Max.z < aabb1Min.z)
return false;
return true; //AABBs Collide.
}
//Broken
bool Collision::triangleVSAxisAlignedBoundingBoxCollides(const Triangle &triangle, const Collider &boundingBox) {
return false;
}
bool Collision::collisionMapVSAxisAlignedBoundingBoxCollides(const Collider &boundingBox, const Collider& collisionMap) {
for (auto& triangle : collisionMap.getTriangles())
if (triangleVSAxisAlignedBoundingBoxCollides(triangle, boundingBox))
return true;
return false;
bool Collider::collides(const Triangle& triangle) const {
return triangleVSAxisAlignedBoundingBoxCollides(triangle, *this);
}
void Collider::draw() {
if (type != ColliderType::CollisionMap) {
glBegin(GL_LINES);
auto& vertices = shape.vertices;
for (int i = 0; i < 4; ++i) {
glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
glVertex3f(vertices[(i + 1) % 4].x, vertices[(i + 1) % 4].y, vertices[(i + 1) % 4].z);
@@ -139,7 +196,7 @@ void Collider::draw() {
}
if (type == ColliderType::CollisionMap) {
drawWireframe();
shape.drawWireframe();
for (const auto& triangle : getTriangles()) {
Vector3 edge1 = triangle.V1 - triangle.V0;
@@ -162,12 +219,12 @@ void Collider::draw() {
std::vector<Triangle> Collider::getTriangles() const {
std::vector<Triangle> triangles;
for (size_t i = 0; i < indices.size(); i += 3) {
if (i + 2 < indices.size()) {
for (size_t i = 0; i < shape.indices.size(); i += 3) {
if (i + 2 < shape.indices.size()) {
Triangle triangle;
triangle.V0 = vertices[indices[i]];
triangle.V1 = vertices[indices[i + 1]];
triangle.V2 = vertices[indices[i + 2]];
triangle.V0 = shape.vertices[shape.indices[i]];
triangle.V1 = shape.vertices[shape.indices[i + 1]];
triangle.V2 = shape.vertices[shape.indices[i + 2]];
triangles.push_back(triangle);
}
}

View File

@@ -1,8 +1,8 @@
#include <engine/collision.h>
#include <limits>
Collider Collision::calculateOrientedBoundingBox(const VertexArray& vArray, const Vector3& angle) {
Collider output;
Collider Collider::calculateOBB(const VertexArray& vArray, const Vector3& angle) {
Collider ret;
float minX = std::numeric_limits<float>::max();
float minY = std::numeric_limits<float>::max();
float minZ = std::numeric_limits<float>::max();
@@ -26,6 +26,7 @@ Collider Collision::calculateOrientedBoundingBox(const VertexArray& vArray, cons
maxZ = vertex.z;
}
auto& output = ret.shape;
output.vertices.emplace_back(minX, minY, minZ);
output.vertices.emplace_back(maxX, minY, minZ);
output.vertices.emplace_back(minX, maxY, minZ);
@@ -36,13 +37,12 @@ Collider Collision::calculateOrientedBoundingBox(const VertexArray& vArray, cons
output.vertices.emplace_back(maxX, maxY, maxZ);
output.rotate(angle);
output.type = ColliderType::OrientedBoundingBox;
return output;
ret.type = ColliderType::OrientedBoundingBox;
return ret;
}
Collider Collision::calculateAxisAlignedBoundingBox(const VertexArray& vArray) {
Collider output;
Collider Collider::calculateAABB(const VertexArray& vArray) {
Collider ret;
float minX = std::numeric_limits<float>::max();
float minY = std::numeric_limits<float>::max();
float minZ = std::numeric_limits<float>::max();
@@ -65,6 +65,7 @@ Collider Collision::calculateAxisAlignedBoundingBox(const VertexArray& vArray) {
if (vertex.z > maxZ)
maxZ = vertex.z;
}
auto& output = ret.shape;
output.vertices.emplace_back(minX, minY, minZ);
output.vertices.emplace_back(maxX, minY, minZ);
output.vertices.emplace_back(minX, maxY, minZ);
@@ -74,6 +75,6 @@ Collider Collision::calculateAxisAlignedBoundingBox(const VertexArray& vArray) {
output.vertices.emplace_back(minX, maxY, maxZ);
output.vertices.emplace_back(maxX, maxY, maxZ);
output.type = ColliderType::AxisAlignedBoundingBox;
return output;
}
ret.type = ColliderType::AxisAlignedBoundingBox;
return ret;
}

View File

@@ -58,17 +58,17 @@ GLfloat Entity::getScale() const {
Collider Entity::getCollider() {
//Default Collider is AABB.
Collider collider = Collision::calculateAxisAlignedBoundingBox(getGeometry()->getBoundingVolume(angle));
Collider collider = Collider::calculateAABB(getGeometry()->getBoundingVolume(angle));
collider.angle = angle;
//Scale.
for (auto& vertex : collider.vertices) {
for (auto& vertex : collider.shape.vertices) {
vertex.x *= getScale();
vertex.y *= getScale();
vertex.z *= getScale();
}
//To world space.
for (auto& vertex : collider.vertices) {
for (auto& vertex : collider.shape.vertices) {
vertex.x = vertex.x + position.x;
vertex.y = vertex.y + position.y;
vertex.z = vertex.z + position.z;
@@ -110,16 +110,7 @@ void Entity::render() {
camera = c;
if (camera) {
switch (getCollider().type) {
case ColliderType::AxisAlignedBoundingBox:
if (Collision::vertexInsideAxisAlignedBoundingBox(camera->position, getCollider()))
cameraInside = true;
break;
case ColliderType::OrientedBoundingBox:
if (Collision::vertexInsideOrientedBoundingBox(camera->position, getCollider()))
cameraInside = true;
break;
}
cameraInside = getCollider().contains(camera->position);
}
if (cameraInside)
glCullFace(GL_FRONT);
@@ -231,17 +222,7 @@ bool Entity::isCollidingWith(Entity *entity) {
Collider thisCollider = this->getCollider();
Collider otherCollider = entity->getCollider();
if (thisCollider.type == ColliderType::OrientedBoundingBox && otherCollider.type == ColliderType::OrientedBoundingBox)
return Collision::orientedBoundingBoxCollides(thisCollider, otherCollider);
if (thisCollider.type == ColliderType::AxisAlignedBoundingBox && otherCollider.type == ColliderType::AxisAlignedBoundingBox)
return Collision::axisAlignedBoundingBoxCollides(thisCollider, otherCollider);
if ((thisCollider.type == ColliderType::AxisAlignedBoundingBox && otherCollider.type == ColliderType::OrientedBoundingBox) || (thisCollider.type == ColliderType::OrientedBoundingBox && otherCollider.type == ColliderType::AxisAlignedBoundingBox))
return Collision::orientedBoundingBoxCollides(thisCollider, otherCollider);
if (thisCollider.type == ColliderType::AxisAlignedBoundingBox && otherCollider.type == ColliderType::CollisionMap)
return Collision::collisionMapVSAxisAlignedBoundingBoxCollides(thisCollider, otherCollider);
return thisCollider.collides(otherCollider);
}
Texture* Entity::getTexture() {

View File

@@ -137,7 +137,7 @@ void VertexArray::load (const std::string& filename) {
//TODO: This will probably need to be changed when pill collider & skeletal animation is added.
//Create OBB once when object is loaded because recalculating it every frame is a LOT slower than rotating it every frame.
boundingVolume = Collision::calculateOrientedBoundingBox(*this, {0,0,0}).vertices;
boundingVolume = Collider::calculateOBB(*this, {0,0,0}).shape.vertices;
}