/* ** Various 3D intersection tests ** Copyright (c) 1997-2015 The UICore Team ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** */ #include #include "templates.h" #include "doomdef.h" #include "r_poly_intersection.h" IntersectionTest::Result IntersectionTest::plane_aabb(const Vec4f &plane, const AxisAlignedBoundingBox &aabb) { Vec3f center = aabb.center(); Vec3f extents = aabb.extents(); float e = extents.x * std::abs(plane.x) + extents.y * std::abs(plane.y) + extents.z * std::abs(plane.z); float s = center.x * plane.x + center.y * plane.y + center.z * plane.z + plane.w; if (s - e > 0) return inside; else if (s + e < 0) return outside; else return intersecting; } IntersectionTest::Result IntersectionTest::plane_obb(const Vec4f &plane, const OrientedBoundingBox &obb) { Vec3f n(plane); float d = plane.w; float e = obb.extents.x * std::abs(Vec3f::dot(obb.axis_x, n)) + obb.extents.y * std::abs(Vec3f::dot(obb.axis_y, n)) + obb.extents.z * std::abs(Vec3f::dot(obb.axis_z, n)); float s = Vec3f::dot(obb.center, n) + d; if (s - e > 0) return inside; else if (s + e < 0) return outside; else return intersecting; } IntersectionTest::OverlapResult IntersectionTest::sphere(const Vec3f ¢er1, float radius1, const Vec3f ¢er2, float radius2) { Vec3f h = center1 - center2; float square_distance = Vec3f::dot(h, h); float radius_sum = radius1 + radius2; if (square_distance > radius_sum * radius_sum) return disjoint; else return overlap; } IntersectionTest::OverlapResult IntersectionTest::sphere_aabb(const Vec3f ¢er, float radius, const AxisAlignedBoundingBox &aabb) { Vec3f a = aabb.aabb_min - center; Vec3f b = center - aabb.aabb_max; a.x = std::max(a.x, 0.0f); a.y = std::max(a.y, 0.0f); a.z = std::max(a.z, 0.0f); b.x = std::max(b.x, 0.0f); b.y = std::max(b.y, 0.0f); b.z = std::max(b.z, 0.0f); Vec3f e = a + b; float d = Vec3f::dot(e, e); if (d > radius * radius) return disjoint; else return overlap; } IntersectionTest::OverlapResult IntersectionTest::aabb(const AxisAlignedBoundingBox &a, const AxisAlignedBoundingBox &b) { if (a.aabb_min.x > b.aabb_max.x || b.aabb_min.x > a.aabb_max.x || a.aabb_min.y > b.aabb_max.y || b.aabb_min.y > a.aabb_max.y || a.aabb_min.z > b.aabb_max.z || b.aabb_min.z > a.aabb_max.z) { return disjoint; } else { return overlap; } } IntersectionTest::Result IntersectionTest::frustum_aabb(const FrustumPlanes &frustum, const AxisAlignedBoundingBox &box) { bool is_intersecting = false; for (int i = 0; i < 6; i++) { Result result = plane_aabb(frustum.planes[i], box); if (result == outside) return outside; else if (result == intersecting) is_intersecting = true; break; } if (is_intersecting) return intersecting; else return inside; } IntersectionTest::Result IntersectionTest::frustum_obb(const FrustumPlanes &frustum, const OrientedBoundingBox &box) { bool is_intersecting = false; for (int i = 0; i < 6; i++) { Result result = plane_obb(frustum.planes[i], box); if (result == outside) return outside; else if (result == intersecting) is_intersecting = true; } if (is_intersecting) return intersecting; else return inside; } IntersectionTest::OverlapResult IntersectionTest::ray_aabb(const Vec3f &ray_start, const Vec3f &ray_end, const AxisAlignedBoundingBox &aabb) { Vec3f c = (ray_start + ray_end) * 0.5f; Vec3f w = ray_end - c; Vec3f h = aabb.extents(); c -= aabb.center(); Vec3f v(std::abs(w.x), std::abs(w.y), std::abs(w.z)); if (std::abs(c.x) > v.x + h.x || std::abs(c.y) > v.y + h.y || std::abs(c.z) > v.z + h.z) return disjoint; if (std::abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y || std::abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x || std::abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x) return disjoint; return overlap; } ///////////////////////////////////////////////////////////////////////////// FrustumPlanes::FrustumPlanes() { } FrustumPlanes::FrustumPlanes(const Mat4f &world_to_projection) { planes[0] = near_frustum_plane(world_to_projection); planes[1] = far_frustum_plane(world_to_projection); planes[2] = left_frustum_plane(world_to_projection); planes[3] = right_frustum_plane(world_to_projection); planes[4] = top_frustum_plane(world_to_projection); planes[5] = bottom_frustum_plane(world_to_projection); } Vec4f FrustumPlanes::left_frustum_plane(const Mat4f &m) { Vec4f plane( m.matrix[3 + 0 * 4] + m.matrix[0 + 0 * 4], m.matrix[3 + 1 * 4] + m.matrix[0 + 1 * 4], m.matrix[3 + 2 * 4] + m.matrix[0 + 2 * 4], m.matrix[3 + 3 * 4] + m.matrix[0 + 3 * 4]); plane /= plane.length3(); return plane; } Vec4f FrustumPlanes::right_frustum_plane(const Mat4f &m) { Vec4f plane( m.matrix[3 + 0 * 4] - m.matrix[0 + 0 * 4], m.matrix[3 + 1 * 4] - m.matrix[0 + 1 * 4], m.matrix[3 + 2 * 4] - m.matrix[0 + 2 * 4], m.matrix[3 + 3 * 4] - m.matrix[0 + 3 * 4]); plane /= plane.length3(); return plane; } Vec4f FrustumPlanes::top_frustum_plane(const Mat4f &m) { Vec4f plane( m.matrix[3 + 0 * 4] - m.matrix[1 + 0 * 4], m.matrix[3 + 1 * 4] - m.matrix[1 + 1 * 4], m.matrix[3 + 2 * 4] - m.matrix[1 + 2 * 4], m.matrix[3 + 3 * 4] - m.matrix[1 + 3 * 4]); plane /= plane.length3(); return plane; } Vec4f FrustumPlanes::bottom_frustum_plane(const Mat4f &m) { Vec4f plane( m.matrix[3 + 0 * 4] + m.matrix[1 + 0 * 4], m.matrix[3 + 1 * 4] + m.matrix[1 + 1 * 4], m.matrix[3 + 2 * 4] + m.matrix[1 + 2 * 4], m.matrix[3 + 3 * 4] + m.matrix[1 + 3 * 4]); plane /= plane.length3(); return plane; } Vec4f FrustumPlanes::near_frustum_plane(const Mat4f &m) { Vec4f plane( m.matrix[3 + 0 * 4] + m.matrix[2 + 0 * 4], m.matrix[3 + 1 * 4] + m.matrix[2 + 1 * 4], m.matrix[3 + 2 * 4] + m.matrix[2 + 2 * 4], m.matrix[3 + 3 * 4] + m.matrix[2 + 3 * 4]); plane /= plane.length3(); return plane; } Vec4f FrustumPlanes::far_frustum_plane(const Mat4f &m) { Vec4f plane( m.matrix[3 + 0 * 4] - m.matrix[2 + 0 * 4], m.matrix[3 + 1 * 4] - m.matrix[2 + 1 * 4], m.matrix[3 + 2 * 4] - m.matrix[2 + 2 * 4], m.matrix[3 + 3 * 4] - m.matrix[2 + 3 * 4]); plane /= plane.length3(); return plane; }