#include "Primitive.hpp"
#include "Debug.hpp"
#include "Frustum.hpp"
#include <algorithm>
#include <cmath>
namespace wren {
namespace primitive {
Plane::Plane(const glm::vec3 &normal, float negativeDistance) : mNormal(normal), mNegativeDistance(negativeDistance) {
}
Aabb::Aabb(const glm::vec3 &min, const glm::vec3 &max) : mBounds{min, max} {
}
Aabb::Aabb(const std::vector<glm::vec3> &vertices) {
assert(vertices.size());
mBounds[0] = mBounds[1] = vertices.front();
for (size_t i = 1; i < vertices.size(); ++i) {
if (vertices[i].x < mBounds[0].x)
mBounds[0].x = vertices[i].x;
if (vertices[i].y < mBounds[0].y)
mBounds[0].y = vertices[i].y;
if (vertices[i].z < mBounds[0].z)
mBounds[0].z = vertices[i].z;
if (vertices[i].x > mBounds[1].x)
mBounds[1].x = vertices[i].x;
if (vertices[i].y > mBounds[1].y)
mBounds[1].y = vertices[i].y;
if (vertices[i].z > mBounds[1].z)
mBounds[1].z = vertices[i].z;
}
}
Aabb::Aabb(const std::vector<Aabb> &aabbs) {
this->extend(aabbs);
}
void Aabb::extend(const glm::vec3 &vertex) {
if (vertex.x < mBounds[0].x)
mBounds[0].x = vertex.x;
if (vertex.y < mBounds[0].y)
mBounds[0].y = vertex.y;
if (vertex.z < mBounds[0].z)
mBounds[0].z = vertex.z;
if (vertex.x > mBounds[1].x)
mBounds[1].x = vertex.x;
if (vertex.y > mBounds[1].y)
mBounds[1].y = vertex.y;
if (vertex.z > mBounds[1].z)
mBounds[1].z = vertex.z;
}
void Aabb::extend(const Aabb &aabb) {
if (aabb.mBounds[0].x < mBounds[0].x)
mBounds[0].x = aabb.mBounds[0].x;
if (aabb.mBounds[0].y < mBounds[0].y)
mBounds[0].y = aabb.mBounds[0].y;
if (aabb.mBounds[0].z < mBounds[0].z)
mBounds[0].z = aabb.mBounds[0].z;
if (aabb.mBounds[1].x > mBounds[1].x)
mBounds[1].x = aabb.mBounds[1].x;
if (aabb.mBounds[1].y > mBounds[1].y)
mBounds[1].y = aabb.mBounds[1].y;
if (aabb.mBounds[1].z > mBounds[1].z)
mBounds[1].z = aabb.mBounds[1].z;
}
void Aabb::extend(const std::vector<Aabb> &aabbs) {
assert(aabbs.size());
mBounds[0] = aabbs.front().mBounds[0];
mBounds[1] = aabbs.front().mBounds[1];
for (size_t i = 1; i < aabbs.size(); ++i) {
if (aabbs[i].mBounds[0].x < mBounds[0].x)
mBounds[0].x = aabbs[i].mBounds[0].x;
if (aabbs[i].mBounds[0].y < mBounds[0].y)
mBounds[0].y = aabbs[i].mBounds[0].y;
if (aabbs[i].mBounds[0].z < mBounds[0].z)
mBounds[0].z = aabbs[i].mBounds[0].z;
if (aabbs[i].mBounds[1].x > mBounds[1].x)
mBounds[1].x = aabbs[i].mBounds[1].x;
if (aabbs[i].mBounds[1].y > mBounds[1].y)
mBounds[1].y = aabbs[i].mBounds[1].y;
if (aabbs[i].mBounds[1].z > mBounds[1].z)
mBounds[1].z = aabbs[i].mBounds[1].z;
}
}
std::vector<glm::vec3> Aabb::vertices() const {
std::vector<glm::vec3> vertices{
glm::vec3(mBounds[0].x, mBounds[0].y, mBounds[0].z), glm::vec3(mBounds[0].x, mBounds[0].y, mBounds[1].z),
glm::vec3(mBounds[0].x, mBounds[1].y, mBounds[0].z), glm::vec3(mBounds[1].x, mBounds[0].y, mBounds[0].z),
glm::vec3(mBounds[0].x, mBounds[1].y, mBounds[1].z), glm::vec3(mBounds[1].x, mBounds[0].y, mBounds[1].z),
glm::vec3(mBounds[1].x, mBounds[1].y, mBounds[0].z), glm::vec3(mBounds[1].x, mBounds[1].y, mBounds[1].z)};
return vertices;
}
Sphere::Sphere(const glm::vec3 ¢er, float radius) : mCenter(center), mRadius(radius) {
}
Point::Point(const glm::vec3 &position) : mPosition(position) {
}
Ray::Ray(const glm::vec3 &origin, const glm::vec3 &direction) : mOrigin(origin), mDirection(direction) {
}
Box::Box(const glm::vec3 ¢er, const glm::vec3 &extents) : mCenter(center), mExtents(extents) {
}
Rectangle::Rectangle(const glm::vec3 ¢er, const glm::vec2 &extents) : mCenter(center), mExtents(extents) {
}
Capsule::Capsule(const glm::vec3 ¢er, float radius, float height, bool hasSide, bool hasTop, bool hasBottom) :
mCenter(center),
mRadius(radius),
mHalfHeight(0.5f * height),
mHasSide(hasSide),
mHasTop(hasTop),
mHasBottom(hasBottom) {
}
Cone::Cone(const glm::vec3 ¢er, float radius, float height, bool hasSide, bool hasBottom) :
mCenter(center),
mRadius(radius),
mHalfHeight(0.5f * height),
mHasSide(hasSide),
mHasBottom(hasBottom) {
}
Cylinder::Cylinder(const glm::vec3 ¢er, float radius, float height, bool hasSide, bool hasTop, bool hasBottom) :
mCenter(center),
mRadius(radius),
mHalfHeight(0.5f * height),
mHasSide(hasSide),
mHasTop(hasTop),
mHasBottom(hasBottom) {
}
TriangleMesh::TriangleMesh(const glm::vec3 &position) : mPosition(position) {
}
Sphere Capsule::computeBoundingSphere() const {
Sphere sphere(mCenter, 0.0f);
if (!mHasTop && !mHasBottom && !mHasSide)
return sphere;
if (mHasTop + mHasBottom + mHasSide == 1) {
if (mHasTop || mHasBottom) {
const float offsetY = mHasTop ? mHalfHeight : -mHalfHeight;
sphere.mCenter += glm::vec3(0.0f, offsetY, 0.0f);
sphere.mRadius = mRadius;
return sphere;
} else {
sphere.mRadius = glm::length(glm::vec2(mRadius, mHalfHeight));
return sphere;
}
} else if (mHasTop != mHasBottom) { const float maxY = mHasTop ? mHalfHeight + mRadius : mHalfHeight;
const float minY = mHasBottom ? -mHalfHeight - mRadius : -mHalfHeight;
const float totalHeight = maxY - minY;
sphere.mRadius = (0.5f * totalHeight) + (0.5f * mRadius * mRadius) / totalHeight;
const float offsetY = mHasTop ? (maxY - sphere.mRadius) : (minY + sphere.mRadius);
sphere.mCenter += glm::vec3(0.0f, offsetY, 0.0f);
return sphere;
} else { sphere.mRadius = mHalfHeight + mRadius;
return sphere;
}
}
Sphere Cone::computeBoundingSphere() const {
Sphere sphere(mCenter, 0.0f);
if (!mHasBottom && !mHasSide)
return sphere;
if (!mHasSide || 2.0f * mHalfHeight <= mRadius) { sphere.mCenter += glm::vec3(0.0f, -mHalfHeight, 0.0f);
sphere.mRadius = mRadius;
return sphere;
} else {
sphere.mRadius = mHalfHeight + (mRadius * mRadius) / (4.0f * mHalfHeight);
sphere.mCenter += glm::vec3(0.0f, mHalfHeight - sphere.mRadius, 0.0f);
return sphere;
}
}
Sphere Cylinder::computeBoundingSphere() const {
Sphere sphere(mCenter, 0.0f);
if (!mHasTop && !mHasBottom && !mHasSide)
return sphere;
if (mHasTop + mHasBottom + mHasSide == 1 && !mHasSide) { const float offsetY = mHasTop ? mHalfHeight : -mHalfHeight;
sphere.mCenter += glm::vec3(0.0f, offsetY, 0.0f);
sphere.mRadius = mRadius;
return sphere;
} else {
sphere.mRadius = glm::length(glm::vec2(mRadius, mHalfHeight));
return sphere;
}
}
Sphere TriangleMesh::computeBoundingSphere() const {
return computeBoundingSphereFromVertices(mVertices);
}
bool isPointAbovePlane(const Plane &plane, const glm::vec3 &point) {
const float distance =
plane.mNormal.x * point.x + plane.mNormal.y * point.y + plane.mNormal.z * point.z + plane.mNegativeDistance;
return distance >= 0.0f;
}
bool isAabbAbovePlane(const Plane &plane, const Aabb &aabb) {
const int px = static_cast<int>(plane.mNormal.x > 0.0f);
const int py = static_cast<int>(plane.mNormal.y > 0.0f);
const int pz = static_cast<int>(plane.mNormal.z > 0.0f);
const float distance = plane.mNormal.x * aabb.mBounds[px].x + plane.mNormal.y * aabb.mBounds[py].y +
plane.mNormal.z * aabb.mBounds[pz].z + plane.mNegativeDistance;
return distance >= 0.0f;
}
bool isSphereAbovePlane(const Plane &plane, const Sphere &sphere) {
const float distance = glm::dot(plane.mNormal, sphere.mCenter) + plane.mNegativeDistance;
return distance >= -sphere.mRadius;
}
bool isPointInsideAabb(const glm::vec3 &point, const Aabb &aabb) {
return (point.x < aabb.mBounds[0].x - glm::epsilon<float>() || point.y < aabb.mBounds[0].y - glm::epsilon<float>() ||
point.z < aabb.mBounds[0].z - glm::epsilon<float>() || point.x > aabb.mBounds[1].x + glm::epsilon<float>() ||
point.y > aabb.mBounds[1].y + glm::epsilon<float>() || point.z > aabb.mBounds[1].z + glm::epsilon<float>());
}
bool rayIntersectAabb(const glm::vec3 &origin, const glm::vec3 &direction, const Aabb &aabb, bool inverse) {
glm::vec3 inv;
if (inverse)
inv = 1.0f / direction;
else
inv = direction;
const float t1 = (aabb.mBounds[0].x - origin.x) * inv.x;
const float t2 = (aabb.mBounds[1].x - origin.x) * inv.x;
const float t3 = (aabb.mBounds[0].y - origin.y) * inv.y;
const float t4 = (aabb.mBounds[1].y - origin.y) * inv.y;
const float t5 = (aabb.mBounds[0].z - origin.z) * inv.z;
const float t6 = (aabb.mBounds[1].z - origin.z) * inv.z;
const float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6));
if (tmax < 0)
return false;
const float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6));
return tmin <= tmax;
}
bool aabbCollision(const Aabb &aabb1, const Aabb &aabb2) {
return !(glm::any(glm::lessThan(aabb1.mBounds[1], aabb2.mBounds[0])) ||
glm::any(glm::greaterThan(aabb1.mBounds[0], aabb2.mBounds[1])));
}
glm::vec3 projectPointOnAabb(const glm::vec3 &point, const Aabb &aabb) {
glm::vec3 result = point;
for (int i = 0; i < 3; ++i) {
if (result[i] < aabb.mBounds[0][i])
result[i] = aabb.mBounds[0][i];
else if (result[i] > aabb.mBounds[1][i])
result[i] = aabb.mBounds[1][i];
}
return result;
}
float closestDistanceToPlane(const Plane &plane, const Aabb &aabb) {
const int nx = static_cast<int>(plane.mNormal.x < 0.0f);
const int ny = static_cast<int>(plane.mNormal.y < 0.0f);
const int nz = static_cast<int>(plane.mNormal.z < 0.0f);
return plane.mNormal.x * aabb.mBounds[nx].x + plane.mNormal.y * aabb.mBounds[ny].y +
plane.mNormal.z * aabb.mBounds[nz].z + plane.mNegativeDistance;
}
float computeRayPlaneIntersection(const Ray &ray, const Plane &plane) {
const float denominator = glm::dot(plane.mNormal, ray.mDirection);
const float nominator = glm::dot(plane.mNormal, ray.mOrigin);
return -(nominator + plane.mNegativeDistance) / denominator;
}
Sphere computeBoundingSphereFromVertices(const std::vector<glm::vec3> &vertices) {
const glm::vec3 &p1 = vertices.front();
glm::vec3 p2 = p1;
float maxDistanceSquared = 0.0f;
for (const glm::vec3 &vertex : vertices) {
const float distanceSquared = glm::distance2(p1, vertex);
if (distanceSquared > maxDistanceSquared) {
maxDistanceSquared = distanceSquared;
p2 = vertex;
}
}
primitive::Sphere sphere(0.5f * (p1 + p2), 0.5f * sqrt(maxDistanceSquared));
for (const glm::vec3 &vertex : vertices) {
const glm::vec3 centerToVertex = vertex - sphere.mCenter;
const float length = glm::length(centerToVertex);
const float delta = 0.5f * (length - sphere.mRadius);
if (delta > 0.0f) {
const glm::vec3 direction = centerToVertex / length;
sphere.mCenter += delta * direction;
sphere.mRadius += delta;
}
}
return sphere;
}
Sphere mergeBoundingSpheres(const std::vector<Sphere> &spheres) {
assert(spheres.size());
glm::vec3 center(0.0f);
for (const Sphere &sphere : spheres)
center += sphere.mCenter;
center /= static_cast<float>(spheres.size());
float maxDistance = 0.0f;
for (const Sphere &sphere : spheres) {
const float distance = glm::length(sphere.mCenter - center) + sphere.mRadius;
if (distance > maxDistance)
maxDistance = distance;
}
return Sphere(center, maxDistance);
}
} }