144 lines
4.1 KiB
C++
144 lines
4.1 KiB
C++
// Copyright (c) 2023 Dominic Masters
|
|
//
|
|
// This software is released under the MIT License.
|
|
// https://opensource.org/licenses/MIT
|
|
|
|
#include "Ray3D.hpp"
|
|
|
|
using namespace Dawn;
|
|
|
|
struct Ray3D {
|
|
glm::vec3 origin;
|
|
glm::vec3 direction;
|
|
};
|
|
|
|
struct PhysicsSphere {
|
|
glm::vec3 center;
|
|
float_t radius;
|
|
};
|
|
|
|
bool_t Dawn::raytestSphere(
|
|
struct Ray3D ray,
|
|
struct PhysicsSphere sphere,
|
|
glm::vec3 *hit,
|
|
glm::vec3 *normal
|
|
) {
|
|
assertNotNull(hit);
|
|
assertNotNull(normal);
|
|
|
|
glm::vec3 h, n;
|
|
auto result = glm::intersectRaySphere(
|
|
ray.origin, ray.direction,
|
|
sphere.center, sphere.radius,
|
|
h, n
|
|
);
|
|
|
|
*hit = h;
|
|
*normal = n;
|
|
|
|
return result;
|
|
}
|
|
|
|
bool_t Dawn::raytestTriangle(
|
|
struct Ray3D ray,
|
|
struct PhysicsTriangle triangle,
|
|
glm::vec3 *hitPoint,
|
|
glm::vec3 *hitNormal,
|
|
float_t *hitDistance
|
|
) {
|
|
assertNotNull(hitPoint);
|
|
assertNotNull(hitNormal);
|
|
assertNotNull(hitDistance);
|
|
|
|
// Calculate the normal of the triangle
|
|
glm::vec3 e0 = triangle.v1 - triangle.v0;
|
|
glm::vec3 e1 = triangle.v2 - triangle.v0;
|
|
glm::vec3 normal = glm::normalize(glm::cross(e0, e1));
|
|
|
|
// Calculate the denominator of the ray-triangle intersection formula
|
|
float_t denominator = glm::dot(normal, ray.direction);
|
|
|
|
// If the denominator is zero, the ray and triangle are parallel and there is no intersection
|
|
if(denominator == 0) return -1;
|
|
|
|
// Calculate the distance from the ray origin to the plane of the triangle
|
|
float_t d = glm::dot(triangle.v0 - ray.origin, normal) / denominator;
|
|
|
|
// If the distance is negative, the intersection point is behind the ray origin and there is no intersection
|
|
if(d < 0) return -1;
|
|
|
|
// Calculate the intersection point
|
|
glm::vec3 intersectionPoint = ray.origin + d * ray.direction;
|
|
|
|
// Check if the intersection point is inside the triangle
|
|
glm::vec3 edge0 = triangle.v1 - triangle.v0;
|
|
glm::vec3 edge1 = triangle.v2 - triangle.v1;
|
|
glm::vec3 edge2 = triangle.v0 - triangle.v2;
|
|
glm::vec3 c0 = intersectionPoint - triangle.v0;
|
|
glm::vec3 c1 = intersectionPoint - triangle.v1;
|
|
glm::vec3 c2 = intersectionPoint - triangle.v2;
|
|
glm::vec3 n0 = glm::cross(edge0, c0);
|
|
glm::vec3 n1 = glm::cross(edge1, c1);
|
|
glm::vec3 n2 = glm::cross(edge2, c2);
|
|
if (glm::dot(n0, normal) >= 0 && glm::dot(n1, normal) >= 0 && glm::dot(n2, normal) >= 0) {
|
|
// If the intersection point is inside the triangle, set the hit point, normal and distance
|
|
*hitPoint = intersectionPoint;
|
|
*hitNormal = normal;
|
|
*hitDistance = d;
|
|
return true;
|
|
}
|
|
|
|
// If the intersection point is outside the triangle, there is no intersection
|
|
return false;
|
|
}
|
|
|
|
bool_t Dawn::raytestAABB(
|
|
struct Ray3D ray,
|
|
struct AABB3D box,
|
|
glm::vec3 *point,
|
|
glm::vec3 *normal,
|
|
float_t *distance
|
|
) {
|
|
// Compute the inverse direction of the ray, for numerical stability
|
|
glm::vec3 invDir(1.0f / ray.direction.x, 1.0f / ray.direction.y, 1.0f / ray.direction.z);
|
|
|
|
// Compute the t-values for the two intersection candidates
|
|
glm::vec3 tMin = (box.min - ray.origin) * invDir;
|
|
glm::vec3 tMax = (box.max - ray.origin) * invDir;
|
|
|
|
// Make sure tMin is less than or equal to tMax for all components
|
|
glm::vec3 t1 = glm::min(tMin, tMax);
|
|
glm::vec3 t2 = glm::max(tMin, tMax);
|
|
float tNear = glm::compMax(t1);
|
|
float tFar = glm::compMin(t2);
|
|
|
|
// If tNear is greater than or equal to tFar, there is no intersection
|
|
if (tNear >= tFar) return false;
|
|
|
|
// If tFar is negative, the ray is pointing away from the box
|
|
if(tFar < 0.0f) return false;
|
|
|
|
// Compute the hit point and normal
|
|
glm::vec3 hitPoint = ray.origin + tNear * ray.direction;
|
|
|
|
if(point != nullptr) *point = hitPoint;
|
|
if(distance != nullptr) *distance = tNear;
|
|
if(normal != nullptr) {
|
|
if (hitPoint.x == box.min.x) {
|
|
*normal = glm::vec3(-1, 0, 0);
|
|
} else if (hitPoint.x == box.max.x) {
|
|
*normal = glm::vec3(1, 0, 0);
|
|
} else if (hitPoint.y == box.min.y) {
|
|
*normal = glm::vec3(0, -1, 0);
|
|
} else if (hitPoint.y == box.max.y) {
|
|
*normal = glm::vec3(0, 1, 0);
|
|
} else if (hitPoint.z == box.min.z) {
|
|
*normal = glm::vec3(0, 0, -1);
|
|
} else if (hitPoint.z == box.max.z) {
|
|
*normal = glm::vec3(0, 0, 1);
|
|
}
|
|
}
|
|
|
|
// The ray intersects the box
|
|
return true;
|
|
} |