// 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; }