Change Collider data into a std::variant

This commit is contained in:
Mishura
2024-05-06 19:18:48 -04:00
parent 977f8f6e66
commit e7464e963b
5 changed files with 308 additions and 148 deletions

View File

@@ -1,5 +1,6 @@
#pragma once
#include <variant>
#include <types/vertex.h>
#include <J3ML/Geometry.h>
@@ -11,10 +12,27 @@ enum class ColliderType : uint8_t {
using J3ML::Geometry::Triangle;
struct AxisAlignedBoundingBox {
VertexArray vertexArray;
};
struct OrientedBoundingBox {
VertexArray vertexArray;
Vector3 angle;
};
struct CollisionMap {
VertexArray vertexArray;
Vector3 angle;
[[nodiscard]] std::vector<Triangle> getTriangles() const;
};
class Collider {
public:
VertexArray shape;
using shape_type = std::variant<AxisAlignedBoundingBox, OrientedBoundingBox, CollisionMap>;
shape_type shape;
ColliderType type;
void draw();
@@ -27,12 +45,9 @@ public:
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;
};

View File

@@ -22,9 +22,10 @@ Sphere2::Sphere2() {
Collider Sphere2::getCollider() {
Collider collider;
auto& map = collider.shape.emplace<CollisionMap>();
//collider.angle = angle;
collider.shape.vertices = getGeometry()->vertices;
collider.shape.indices = getGeometry()->indices;
map.vertexArray.vertices = getGeometry()->vertices;
map.vertexArray.indices = getGeometry()->indices;
collider.type = ColliderType::CollisionMap;
collider.rotate(angle);

View File

@@ -1,11 +1,130 @@
#include "types/vertex.h"
#include <cfloat>
#include <engine/collision.h>
#include <J3ML/Geometry.h>
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/Triangle.h>
#include <type_traits>
#include <variant>
namespace {
bool orientedBoundingBoxCollides(const Collider &boundingBox, const Collider &boundingBox2) {
inline constexpr decltype(auto) getVertexArray = [](auto&& shape) {
if constexpr (std::is_rvalue_reference_v<decltype(shape)>) {
return std::move(shape.vertexArray);
} else {
return shape.vertexArray;
}
};
struct CollisionVisitor {
bool operator()(const AxisAlignedBoundingBox& lhs, const AxisAlignedBoundingBox& rhs) const {
Vector3 aabb1Min = {lhs.vertexArray.vertices.front().x, lhs.vertexArray.vertices.front().y, lhs.vertexArray.vertices.front().z};
Vector3 aabb1Max = {lhs.vertexArray.vertices.back().x, lhs.vertexArray.vertices.back().y, lhs.vertexArray.vertices.back().z};
Vector3 aabb2Min = {rhs.vertexArray.vertices.front().x, rhs.vertexArray.vertices.front().y, rhs.vertexArray.vertices.front().z};
Vector3 aabb2Max = {rhs.vertexArray.vertices.back().x, rhs.vertexArray.vertices.back().y, rhs.vertexArray.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.
}
bool operator()(const OrientedBoundingBox& lhs, const OrientedBoundingBox& rhs) const {
Vector3 axes[3] = {
Vector3(1, 0, 0),
Vector3(0, 1, 0),
Vector3(0, 0, 1)
};
for (const auto& axis : axes) {
float projection1Min = FLT_MAX, projection1Max = -FLT_MAX;
float projection2Min = FLT_MAX, projection2Max = -FLT_MAX;
for (const auto& vertex : lhs.vertexArray.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 : rhs.vertexArray.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;
}
for (const auto& axis : axes) {
float projection1Min = FLT_MAX, projection1Max = -FLT_MAX;
float projection2Min = FLT_MAX, projection2Max = -FLT_MAX;
for (const auto& vertex : lhs.vertexArray.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 : rhs.vertexArray.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.
}
bool operator()(const OrientedBoundingBox& lhs, const AxisAlignedBoundingBox& rhs) const {
// Not yet implemented
return false;
}
bool operator()(const AxisAlignedBoundingBox& lhs, const OrientedBoundingBox& rhs) const {
return operator()(rhs, lhs);
}
bool operator()(const CollisionMap& lhs, const AxisAlignedBoundingBox& rhs) const {
for (const auto& triangle : lhs.getTriangles())
if (false /* rhs.Intersects(triangle) */)
return true;
return false;
}
bool operator()(const AxisAlignedBoundingBox& lhs, const CollisionMap& rhs) const {
return operator()(rhs, lhs);
}
bool operator()(const CollisionMap& lhs, const OrientedBoundingBox& rhs) const {
// Not yet implemented
return false;
}
bool operator()(const OrientedBoundingBox& lhs, const CollisionMap& rhs) const {
return operator()(rhs, lhs);
}
bool operator()(const CollisionMap& lhs, const CollisionMap& rhs) const {
// Not yet implemented
return false;
}
};
bool orientedBoundingBoxCollides(const OrientedBoundingBox &lhs, const OrientedBoundingBox &rhs) {
Vector3 axes[3] = {
Vector3(1, 0, 0),
Vector3(0, 1, 0),
@@ -16,14 +135,14 @@ namespace {
float projection1Min = FLT_MAX, projection1Max = -FLT_MAX;
float projection2Min = FLT_MAX, projection2Max = -FLT_MAX;
for (const auto& vertex : boundingBox.shape.vertices) {
for (const auto& vertex : lhs.vertexArray.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) {
for (const auto& vertex : rhs.vertexArray.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection2Min) projection2Min = projection;
@@ -38,14 +157,14 @@ namespace {
float projection1Min = FLT_MAX, projection1Max = -FLT_MAX;
float projection2Min = FLT_MAX, projection2Max = -FLT_MAX;
for (const auto& vertex : boundingBox.shape.vertices) {
for (const auto& vertex : lhs.vertexArray.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) {
for (const auto& vertex : rhs.vertexArray.vertices) {
Vector3 vert = {vertex.x, vertex.y, vertex.z};
float projection = vert.Dot(axis);
if (projection < projection2Min) projection2Min = projection;
@@ -59,42 +178,13 @@ namespace {
return true; //OBBs collide.
}
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};
struct ContainsVisitor {
Vector3 position;
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 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};
bool operator()(const AxisAlignedBoundingBox& aabb) const {
const auto& vertices = aabb.vertexArray.vertices;
Vector3 minimum = {vertices.front().x, vertices.front().y, vertices.front().z};
Vector3 maximum = {vertices.back().x, vertices.back().y, vertices.back().z};
return (
position.x >= minimum.x && position.x <= maximum.x &&
@@ -103,11 +193,12 @@ bool Collider::contains(const Position& position) const {
);
}
case ColliderType::OrientedBoundingBox: {
bool operator()(const OrientedBoundingBox& obb) const {
const auto& vertices = obb.vertexArray.vertices;
Vector3 minimum = {FLT_MAX, FLT_MAX, FLT_MAX};
Vector3 maximum = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
for (const auto& vertex : shape.vertices) {
for (const auto& vertex : 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;
@@ -124,128 +215,181 @@ bool Collider::contains(const Position& position) const {
);
}
default:
bool operator()(const CollisionMap& map) const {
return false;
}
}
};
}
bool Collider::contains(const Position& position) const {
return std::visit(ContainsVisitor{position}, shape);
}
bool Collider::collides(const Collider& other) const {
switch (type) {
case ColliderType::AxisAlignedBoundingBox:
switch (other.type) {
case ColliderType::AxisAlignedBoundingBox:
return axisAlignedBoundingBoxCollides(*this, other);
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
case ColliderType::OrientedBoundingBox:
return orientedBoundingBoxCollides(*this, other);
case ColliderType::CollisionMap:
return false; // not implemented
}
break;
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 false;
return std::visit(CollisionVisitor{}, shape, other.shape);
}
void Collider::translate(const Vector3& translation) {
for (auto& vertex : shape.vertices) {
vertex += translation;
}
struct Visitor {
Vector3 direction;
void operator()(AxisAlignedBoundingBox& aabb) const {
for (auto& vertex : aabb.vertexArray.vertices) {
vertex += direction;
}
}
void operator()(OrientedBoundingBox& obb) const {
for (auto& vertex : obb.vertexArray.vertices) {
vertex += direction;
}
}
void operator()(CollisionMap& map) const {
for (auto& vertex : map.vertexArray.vertices) {
vertex += direction;
}
}
};
std::visit(Visitor{translation}, shape);
}
void Collider::rotate(const Vector3& rotation) {
if (type == ColliderType::AxisAlignedBoundingBox)
return; // Axis-aligned bounding box does not rotate, it is always axis-aligned
shape.rotate(rotation);
angle = rotation;
struct Visitor {
Vector3 ang;
void operator()(AxisAlignedBoundingBox&) const {
// AABB does not rotate, it is axis-aligned
return;
}
void operator()(OrientedBoundingBox& obb) const {
obb.vertexArray.rotate(ang);
obb.angle = ang;
}
void operator()(CollisionMap& map) const {
map.vertexArray.rotate(ang);
map.angle = ang;
}
};
std::visit(Visitor{rotation}, shape);
}
void Collider::scale(const Vector3& scale) {
for (auto& vertex : shape.vertices) {
vertex.x *= scale.x;
vertex.y *= scale.y;
vertex.z *= scale.z;
}
struct Visitor {
Vector3 factor;
void operator()(AxisAlignedBoundingBox& aabb) const {
for (auto& vertex : aabb.vertexArray.vertices) {
vertex.x *= factor.x;
vertex.y *= factor.y;
vertex.z *= factor.z;
}
}
void operator()(OrientedBoundingBox& obb) const {
for (auto& vertex : obb.vertexArray.vertices) {
vertex.x *= factor.x;
vertex.y *= factor.y;
vertex.z *= factor.z;
}
}
void operator()(CollisionMap& map) const {
for (auto& vertex : map.vertexArray.vertices) {
vertex.x *= factor.x;
vertex.y *= factor.y;
vertex.z *= factor.z;
}
}
};
std::visit(Visitor{scale}, shape);
}
bool Collider::collides(const Triangle& triangle) const {
return triangleVSAxisAlignedBoundingBoxCollides(triangle, *this);
return false;
}
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);
glVertex3f(vertices[i + 4].x, vertices[i + 4].y, vertices[i + 4].z);
glVertex3f(vertices[((i + 1) % 4) + 4].x, vertices[((i + 1) % 4) + 4].y, vertices[((i + 1) % 4) + 4].z);
glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
glVertex3f(vertices[i + 4].x, vertices[i + 4].y, vertices[i + 4].z);
}
glEnd();
return;
}
if (type == ColliderType::CollisionMap) {
shape.drawWireframe();
for (const auto& triangle : getTriangles()) {
Vector3 edge1 = triangle.V1 - triangle.V0;
Vector3 edge2 = triangle.V2 - triangle.V0;
Vector3 normal = Vector3::Normalize(Vector3::Cross(edge1, edge2));
Vector3 centroid = (triangle.V0 + triangle.V1 + triangle.V2) / 3.0f;
Vector3 normalPoint = centroid + (normal * 0.25);
glColor3f(0.0f, 1.0f, 0.0f);
struct DrawVisitor {
void operator()(const AxisAlignedBoundingBox& aabb) const {
/*std::array vertices = {
Vertex{ aabb.MinX(), aabb.MinY(), aabb.MinZ() },
Vertex{ aabb.MaxX(), aabb.MinY(), aabb.MinZ() },
Vertex{ aabb.MinX(), aabb.MaxY(), aabb.MinZ() },
Vertex{ aabb.MaxX(), aabb.MaxY(), aabb.MinZ() },
Vertex{ aabb.MinX(), aabb.MinY(), aabb.MaxZ() },
Vertex{ aabb.MaxX(), aabb.MinY(), aabb.MaxZ() },
Vertex{ aabb.MinX(), aabb.MaxY(), aabb.MaxZ() },
Vertex{ aabb.MaxX(), aabb.MaxY(), aabb.MaxZ() },
};*/
auto& vertices = aabb.vertexArray.vertices;
glBegin(GL_LINES);
glVertex3f(centroid.x, centroid.y, centroid.z);
glVertex3f(normalPoint.x, normalPoint.y, normalPoint.z);
glEnd();
glColor4f(1.f, 1.f, 1.f, 1.f);
}
}
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);
glVertex3f(vertices[i + 4].x, vertices[i + 4].y, vertices[i + 4].z);
glVertex3f(vertices[((i + 1) % 4) + 4].x, vertices[((i + 1) % 4) + 4].y, vertices[((i + 1) % 4) + 4].z);
glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
glVertex3f(vertices[i + 4].x, vertices[i + 4].y, vertices[i + 4].z);
}
glEnd();
}
void operator()(OrientedBoundingBox& obb) {
auto& vertices = obb.vertexArray.vertices;
glBegin(GL_LINES);
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);
glVertex3f(vertices[i + 4].x, vertices[i + 4].y, vertices[i + 4].z);
glVertex3f(vertices[((i + 1) % 4) + 4].x, vertices[((i + 1) % 4) + 4].y, vertices[((i + 1) % 4) + 4].z);
glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
glVertex3f(vertices[i + 4].x, vertices[i + 4].y, vertices[i + 4].z);
}
glEnd();
}
void operator()(CollisionMap& map) const {
map.vertexArray.drawWireframe();
for (const auto& triangle : map.getTriangles()) {
Vector3 edge1 = triangle.V1 - triangle.V0;
Vector3 edge2 = triangle.V2 - triangle.V0;
Vector3 normal = Vector3::Normalize(Vector3::Cross(edge1, edge2));
Vector3 centroid = (triangle.V0 + triangle.V1 + triangle.V2) / 3.0f;
Vector3 normalPoint = centroid + (normal * 0.25);
glColor3f(0.0f, 1.0f, 0.0f);
glBegin(GL_LINES);
glVertex3f(centroid.x, centroid.y, centroid.z);
glVertex3f(normalPoint.x, normalPoint.y, normalPoint.z);
glEnd();
glColor4f(1.f, 1.f, 1.f, 1.f);
}
}
};
std::visit(DrawVisitor{}, shape);
}
std::vector<Triangle> Collider::getTriangles() const {
std::vector<Triangle> CollisionMap::getTriangles() const {
std::vector<Triangle> triangles;
for (size_t i = 0; i < shape.indices.size(); i += 3) {
if (i + 2 < shape.indices.size()) {
for (size_t i = 0; i < vertexArray.indices.size(); i += 3) {
if (i + 2 < vertexArray.indices.size()) {
Triangle triangle;
triangle.V0 = shape.vertices[shape.indices[i]];
triangle.V1 = shape.vertices[shape.indices[i + 1]];
triangle.V2 = shape.vertices[shape.indices[i + 2]];
triangle.V0 = vertexArray.vertices[vertexArray.indices[i]];
triangle.V1 = vertexArray.vertices[vertexArray.indices[i + 1]];
triangle.V2 = vertexArray.vertices[vertexArray.indices[i + 2]];
triangles.push_back(triangle);
}
}

View File

@@ -26,7 +26,7 @@ Collider Collider::calculateOBB(const VertexArray& vArray, const Vector3& angle)
maxZ = vertex.z;
}
auto& output = ret.shape;
auto& output = ret.shape.emplace<OrientedBoundingBox>().vertexArray;
output.vertices.emplace_back(minX, minY, minZ);
output.vertices.emplace_back(maxX, minY, minZ);
output.vertices.emplace_back(minX, maxY, minZ);
@@ -65,7 +65,7 @@ Collider Collider::calculateAABB(const VertexArray& vArray) {
if (vertex.z > maxZ)
maxZ = vertex.z;
}
auto& output = ret.shape;
auto& output = ret.shape.emplace<AxisAlignedBoundingBox>().vertexArray;
output.vertices.emplace_back(minX, minY, minZ);
output.vertices.emplace_back(maxX, minY, minZ);
output.vertices.emplace_back(minX, maxY, minZ);

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 = Collider::calculateOBB(*this, {0,0,0}).shape.vertices;
boundingVolume = std::get<OrientedBoundingBox>(Collider::calculateOBB(*this, {0,0,0}).shape).vertexArray.vertices;
}