| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494 |
- /*
- Copyright (c) 2013 Daniele Bartolini, Michele Rossi
- Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use,
- copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following
- conditions:
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
- #pragma once
- #include "Ray.h"
- #include "Plane.h"
- #include "Sphere.h"
- #include "Frustum.h"
- #include "MathUtils.h"
- #include "Types.h"
- #include "AABB.h"
- namespace crown
- {
- class Intersection
- {
- public:
- static bool test_ray_plane(const Ray& r, const Plane& p, float& distance, Vector3& inttersectionPoint_t);
- static bool test_ray_sphere(const Ray& r, const Sphere& s, float& distance, Vector3& intersectionPoint);
- static bool test_ray_box(const Ray& r, const AABB& b, float& distance, Vector3& intersectionPoint);
- static bool test_plane_3(const Plane& p1, const Plane& p2, const Plane& p3, Vector3& ip);
- static bool test_static_sphere_plane(const Sphere& s, const Plane& p);
- static bool test_static_sphere_sphere(const Sphere& a, const Sphere& b);
- static bool test_dynamic_sphere_plane(const Sphere& s, const Vector3& d, const Plane& p, float& it, Vector3& intersectionPoint);
- static bool test_dynamic_sphere_sphere(const Sphere& s1, const Vector3& d1, const Sphere& s2, const Vector3& d2, float& it, Vector3& intersectionPoint);
- static bool test_static_box_box(const AABB& b1, const AABB& b2);
- static bool test_dynamic_box_box(const AABB& b1, const Vector3& v1, const AABB& b2, const Vector3& v2, float& it);
- static bool test_frustum_sphere(const Frustum& f, const Sphere& s);
- static bool test_frustum_box(const Frustum& f, const AABB& box);
- private:
- // Disable construction
- Intersection();
- };
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_ray_plane(const Ray& r, const Plane& p, float& distance, Vector3& intersectionPoint)
- {
- float nd = vector3::dot(r.direction(), p.n);
- float orpn = vector3::dot(r.origin(), p.n);
- if (nd < 0.0)
- {
- float dist = (-p.d - orpn) / nd;
- if (dist > 0.0)
- {
- distance = dist;
- intersectionPoint = r.origin() + r.direction() * distance;
- return true;
- }
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_ray_sphere(const Ray& r, const Sphere& s, float& distance, Vector3& intersectionPoint)
- {
- Vector3 v = s.center() - r.origin();
- float b = vector3::dot(v, r.direction());
- float det = (s.radius() * s.radius()) - vector3::dot(v, v) + (b * b);
- if (det < 0.0 || b < s.radius())
- {
- return false;
- }
- distance = b - math::sqrt(det);
- intersectionPoint = r.origin() + r.direction() * distance;
- return true;
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_ray_box(const Ray& r, const AABB& b, float& /*distance*/, Vector3& /*intersectionPoint*/)
- {
- if (r.origin().x < b.min.x)
- {
- if (r.direction().x < 0.0)
- {
- return false;
- }
- }
- if (r.origin().x > b.max.x)
- {
- if (r.direction().x > 0.0)
- {
- return false;
- }
- }
- if (r.origin().y < b.min.y)
- {
- if (r.direction().y < 0.0)
- {
- return false;
- }
- }
- if (r.origin().y > b.max.y)
- {
- if (r.direction().y > 0.0)
- {
- return false;
- }
- }
- if (r.origin().z < b.min.z)
- {
- if (r.direction().z < 0.0)
- {
- return false;
- }
- }
- if (r.origin().z > b.max.z)
- {
- if (r.direction().z > 0.0)
- {
- return false;
- }
- }
- // Possibly int32_tersects
- return true;
- // TODO
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_plane_3(const Plane& p1, const Plane& p2, const Plane& p3, Vector3& ip)
- {
- const Vector3& n1 = p1.n;
- const Vector3& n2 = p2.n;
- const Vector3& n3 = p3.n;
- float den = -vector3::dot(vector3::cross(n1, n2), n3);
- if (math::equals(den, (float)0.0))
- {
- return false;
- }
- Vector3 res = p1.d * vector3::cross(n2, n3) + p2.d * vector3::cross(n3, n1) + p3.d * vector3::cross(n1, n2);
- ip = res / den;
- return true;
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_static_sphere_plane(const Sphere& s, const Plane& p)
- {
- if (math::abs(plane::distance_to_point(p, s.center())) < s.radius())
- {
- return true;
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_static_sphere_sphere(const Sphere& a, const Sphere& b)
- {
- float dist = vector3::squared_length(b.center() - a.center());
- return (dist < (b.radius() + a.radius()) * (b.radius() + a.radius()));
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_dynamic_sphere_plane(const Sphere& s, const Vector3& d, const Plane& p, float& it, Vector3& intersectionPoint)
- {
- const Vector3& sphereCenter = s.center();
- const float sphereRadius = s.radius();
- float t0; // Time at which the sphere int32_tersects the plane remaining at the front side of the plane
- float t1; // Time at which the sphere int32_tersects the plane remaining at the back side of the plane
- float sphereToPlaneDistance = plane::distance_to_point(p, sphereCenter);
- float planeNormalDotVelocity = vector3::dot(p.n, d);
- if (planeNormalDotVelocity > 0.0)
- {
- return false;
- }
- // If the sphere is travelling parallel to the plane
- if (planeNormalDotVelocity == 0.0)
- {
- // If the sphere is embedded in the plane
- if (math::abs(sphereToPlaneDistance) < sphereRadius)
- {
- t0 = 0.0;
- t1 = 1.0;
- it = t0;
- intersectionPoint = s.center() - p.n * s.radius();
- return true;
- }
- return false;
- }
- t0 = (sphereRadius - sphereToPlaneDistance) / planeNormalDotVelocity;
- t1 = (-sphereRadius - sphereToPlaneDistance) / planeNormalDotVelocity;
- // If _both_ t0 and t1 are outside [0,1] then collision can never happen
- if (t0 >= 0.0 && t0 <= 1.0)
- {
- it = math::min(t0, t1);
- intersectionPoint = s.center() - p.n * s.radius() + (d * it);
- return true;
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_dynamic_sphere_sphere(const Sphere& s1, const Vector3& d1, const Sphere& s2, const Vector3& d2, float& it, Vector3& /*intersectionPoint*/)
- {
- // s1 == static sphere
- // s2 == moving sphere
- Vector3 d = d2 - d1;
- vector3::normalize(d);
- const Vector3& cs = s1.center();
- const Vector3& cm = s2.center();
- Vector3 e = cs - cm;
- float r = s1.radius() + s2.radius();
- // If ||e|| < r, int32_tersection occurs at t = 0
- if (vector3::length(e) < r)
- {
- it = 0.0;
- return true;
- }
- // it == Intersection Time
- float ed = vector3::dot(e, d);
- float squared = (ed * ed) + (r * r) - vector3::dot(e, e);
- // If the value inside the square root is neg, then no int32_tersection
- if (squared < 0.0)
- {
- return false;
- }
- float t = ed - math::sqrt(squared);
- float l = vector3::length(d2 - d1);
- // If t < 0 || t > l, then non int32_tersection in the considered period of time
- if (t < 0.0 || t > l)
- {
- return false;
- }
- it = t / l;
- return true;
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_static_box_box(const AABB& b1, const AABB& b2)
- {
- if (b1.min.x > b2.max.x || b1.max.x < b2.min.x)
- {
- return false;
- }
- if (b1.min.y > b2.max.y || b1.max.y < b2.min.y)
- {
- return false;
- }
- if (b1.min.z > b2.max.z || b1.max.z < b2.min.z)
- {
- return false;
- }
- return true;
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_dynamic_box_box(const AABB& b1, const Vector3& v1, const AABB& b2, const Vector3& v2, float& it)
- {
- // b1 == static box
- // b2 == moving box
- Vector3 d = v2 - v1;
- // Start time of int32_tersection aint64_t each axis
- Vector3 tEnterXYZ(0.0, 0.0, 0.0);
- // Stop time of int32_tersection aint64_t each axis
- Vector3 tLeaveXYZ(1.0, 1.0, 1.0);
- // If the resulting displacement equals zero, then fallback to static int32_tersection test
- if (math::equals(d.x, (float)0.0))
- {
- if (b1.min.x > b2.max.x || b1.max.x < b2.min.x)
- {
- return false;
- }
- }
- if (math::equals(d.y, (float)0.0))
- {
- if (b1.min.y > b2.max.y || b1.max.y < b2.min.y)
- {
- return false;
- }
- }
- if (math::equals(d.z, (float)0.0))
- {
- if (b1.min.z > b2.max.z || b1.max.z < b2.min.z)
- {
- return false;
- }
- }
- // Otherwise, compute the enter/leave times aint64_t each axis
- float oneOverD = (float)(1.0 / d.x);
- tEnterXYZ.x = (b1.min.x - b2.max.x) * oneOverD;
- tLeaveXYZ.x = (b1.max.x - b2.min.x) * oneOverD;
- oneOverD = (float)(1.0 / d.y);
- tEnterXYZ.y = (b1.min.y - b2.max.y) * oneOverD;
- tLeaveXYZ.y = (b1.max.y - b2.min.y) * oneOverD;
- oneOverD = (float)(1.0 / d.z);
- tEnterXYZ.z = (b1.min.z - b2.max.z) * oneOverD;
- tLeaveXYZ.z = (b1.max.z - b2.min.z) * oneOverD;
- // We must ensure that enter time < leave time
- if (tLeaveXYZ.x < tEnterXYZ.x)
- {
- math::swap(tLeaveXYZ.x, tEnterXYZ.x);
- }
- if (tLeaveXYZ.y < tEnterXYZ.y)
- {
- math::swap(tLeaveXYZ.y, tEnterXYZ.y);
- }
- if (tLeaveXYZ.z < tEnterXYZ.z)
- {
- math::swap(tLeaveXYZ.z, tEnterXYZ.z);
- }
- float tEnter = math::max(tEnterXYZ.x, math::max(tEnterXYZ.y, tEnterXYZ.z));
- float tLeave = math::min(tLeaveXYZ.x, math::min(tLeaveXYZ.y, tLeaveXYZ.z));
- // If tEnter > 1, then there is no int32_tersection in the period
- // of time cosidered
- if (tEnter > 1.0)
- {
- return false;
- }
- it = tEnter;
- return tEnter < tLeave;
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_frustum_sphere(const Frustum& f, const Sphere& s)
- {
- if (plane::distance_to_point(f.left, s.center()) < -s.radius() ||
- plane::distance_to_point(f.right, s.center()) < -s.radius())
- {
- return false;
- }
- if (plane::distance_to_point(f.bottom, s.center()) < -s.radius() ||
- plane::distance_to_point(f.top, s.center()) < -s.radius())
- {
- return false;
- }
- if (plane::distance_to_point(f.near, s.center()) < -s.radius() ||
- plane::distance_to_point(f.far, s.center()) < -s.radius())
- {
- return false;
- }
- return true;
- }
- //-----------------------------------------------------------------------------
- inline bool Intersection::test_frustum_box(const Frustum& f, const AABB& b)
- {
- uint8_t out;
- out = 0;
- if (plane::distance_to_point(f.left, aabb::vertex(b, 0)) < 0.0) out++;
- if (plane::distance_to_point(f.left, aabb::vertex(b, 1)) < 0.0) out++;
- if (plane::distance_to_point(f.left, aabb::vertex(b, 2)) < 0.0) out++;
- if (plane::distance_to_point(f.left, aabb::vertex(b, 3)) < 0.0) out++;
- if (plane::distance_to_point(f.left, aabb::vertex(b, 4)) < 0.0) out++;
- if (plane::distance_to_point(f.left, aabb::vertex(b, 5)) < 0.0) out++;
- if (plane::distance_to_point(f.left, aabb::vertex(b, 6)) < 0.0) out++;
- if (plane::distance_to_point(f.left, aabb::vertex(b, 7)) < 0.0) out++;
- // If all vertices are outside one face, then the box doesn't intersect the frustum
- if (out == 8) return false;
- out = 0;
- if (plane::distance_to_point(f.right, aabb::vertex(b, 0)) < 0.0) out++;
- if (plane::distance_to_point(f.right, aabb::vertex(b, 1)) < 0.0) out++;
- if (plane::distance_to_point(f.right, aabb::vertex(b, 2)) < 0.0) out++;
- if (plane::distance_to_point(f.right, aabb::vertex(b, 3)) < 0.0) out++;
- if (plane::distance_to_point(f.right, aabb::vertex(b, 4)) < 0.0) out++;
- if (plane::distance_to_point(f.right, aabb::vertex(b, 5)) < 0.0) out++;
- if (plane::distance_to_point(f.right, aabb::vertex(b, 6)) < 0.0) out++;
- if (plane::distance_to_point(f.right, aabb::vertex(b, 7)) < 0.0) out++;
- if (out == 8) return false;
- out = 0;
- if (plane::distance_to_point(f.bottom, aabb::vertex(b, 0)) < 0.0) out++;
- if (plane::distance_to_point(f.bottom, aabb::vertex(b, 1)) < 0.0) out++;
- if (plane::distance_to_point(f.bottom, aabb::vertex(b, 2)) < 0.0) out++;
- if (plane::distance_to_point(f.bottom, aabb::vertex(b, 3)) < 0.0) out++;
- if (plane::distance_to_point(f.bottom, aabb::vertex(b, 4)) < 0.0) out++;
- if (plane::distance_to_point(f.bottom, aabb::vertex(b, 5)) < 0.0) out++;
- if (plane::distance_to_point(f.bottom, aabb::vertex(b, 6)) < 0.0) out++;
- if (plane::distance_to_point(f.bottom, aabb::vertex(b, 7)) < 0.0) out++;
- if (out == 8) return false;
- out = 0;
- if (plane::distance_to_point(f.top, aabb::vertex(b, 0)) < 0.0) out++;
- if (plane::distance_to_point(f.top, aabb::vertex(b, 1)) < 0.0) out++;
- if (plane::distance_to_point(f.top, aabb::vertex(b, 2)) < 0.0) out++;
- if (plane::distance_to_point(f.top, aabb::vertex(b, 3)) < 0.0) out++;
- if (plane::distance_to_point(f.top, aabb::vertex(b, 4)) < 0.0) out++;
- if (plane::distance_to_point(f.top, aabb::vertex(b, 5)) < 0.0) out++;
- if (plane::distance_to_point(f.top, aabb::vertex(b, 6)) < 0.0) out++;
- if (plane::distance_to_point(f.top, aabb::vertex(b, 7)) < 0.0) out++;
- if (out == 8) return false;
- out = 0;
- if (plane::distance_to_point(f.near, aabb::vertex(b, 0)) < 0.0) out++;
- if (plane::distance_to_point(f.near, aabb::vertex(b, 1)) < 0.0) out++;
- if (plane::distance_to_point(f.near, aabb::vertex(b, 2)) < 0.0) out++;
- if (plane::distance_to_point(f.near, aabb::vertex(b, 3)) < 0.0) out++;
- if (plane::distance_to_point(f.near, aabb::vertex(b, 4)) < 0.0) out++;
- if (plane::distance_to_point(f.near, aabb::vertex(b, 5)) < 0.0) out++;
- if (plane::distance_to_point(f.near, aabb::vertex(b, 6)) < 0.0) out++;
- if (plane::distance_to_point(f.near, aabb::vertex(b, 7)) < 0.0) out++;
- if (out == 8) return false;
- out = 0;
- if (plane::distance_to_point(f.far, aabb::vertex(b, 0)) < 0.0) out++;
- if (plane::distance_to_point(f.far, aabb::vertex(b, 1)) < 0.0) out++;
- if (plane::distance_to_point(f.far, aabb::vertex(b, 2)) < 0.0) out++;
- if (plane::distance_to_point(f.far, aabb::vertex(b, 3)) < 0.0) out++;
- if (plane::distance_to_point(f.far, aabb::vertex(b, 4)) < 0.0) out++;
- if (plane::distance_to_point(f.far, aabb::vertex(b, 5)) < 0.0) out++;
- if (plane::distance_to_point(f.far, aabb::vertex(b, 6)) < 0.0) out++;
- if (plane::distance_to_point(f.far, aabb::vertex(b, 7)) < 0.0) out++;
- if (out == 8) return false;
- // If we are here, it is because either the box intersects or it is contained in the frustum
- return true;
- }
- } // namespace crown
|