You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
8.2 KiB
C++
262 lines
8.2 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
// include required headers
|
|
#include "Ray.h"
|
|
#include "AABB.h"
|
|
#include "BoundingSphere.h"
|
|
#include "PlaneEq.h"
|
|
#include "FastMath.h"
|
|
#include "Algorithms.h"
|
|
|
|
|
|
namespace MCore
|
|
{
|
|
// ray-boundingsphere
|
|
bool Ray::Intersects(const BoundingSphere& s, AZ::Vector3* intersectA, AZ::Vector3* intersectB) const
|
|
{
|
|
const AZ::Vector3 rayOrg = m_origin - s.GetCenter(); // ray in space of the sphere
|
|
|
|
// The Intersection can be solved by finding the solutions of the quadratic equation:
|
|
// (m_origin + t * m_direction)^2 - s.GetRadiusSquared() = 0
|
|
// Where t is a value that makes (m_origin + t * m_direction) intersect the sphere
|
|
// Expanding the above equation we have to find t1 and t2:
|
|
// t1 = (-2 * m_origin * m_direction + sqrt(delta)) / (2 * m_direction^2)
|
|
// t2 = (-2 * m_origin * m_direction - sqrt(delta)) / (2 * m_direction^2)
|
|
// where delta = (2 * m_origin * m_direction) ^ 2 - 4 * m_direction^2 * (m_origin^2 - s.GetRadiusSquared())
|
|
// The two intersection points will be:
|
|
// m_origin + m_direction * t1
|
|
// m_origin + m_direction * t2
|
|
// If delta < 0, then there is no intersection
|
|
// If delta == 0, it intersects int he same point
|
|
//
|
|
const float a = m_direction.GetLengthSq();
|
|
const float b = 2.0f * m_direction.Dot(rayOrg);
|
|
const float c = rayOrg.GetLengthSq() - s.GetRadiusSquared();
|
|
const float delta = ((b * b) - 4.0f * a * c);
|
|
|
|
if (delta < 0.0f)
|
|
{
|
|
return false; // no intersection
|
|
}
|
|
|
|
if (intersectA == nullptr && intersectB == nullptr)
|
|
{
|
|
// Early out if we are not interested in getting the intersection but to know if there was or not
|
|
// intersection. If delta is positive, then we have solutions
|
|
return true;
|
|
}
|
|
|
|
if (delta < Math::epsilon)
|
|
{
|
|
const float q = (b > 0)
|
|
? (-0.5f * (b + Math::Sqrt(delta)))
|
|
: (-0.5f * (b - Math::Sqrt(delta)));
|
|
const float t1 = q / a;
|
|
const float t2 = c / q;
|
|
if (t1 < t2)
|
|
{
|
|
if (intersectA)
|
|
{
|
|
(*intersectA) = m_origin + m_direction * t1;
|
|
}
|
|
if (intersectB)
|
|
{
|
|
(*intersectB) = m_origin + m_direction * t2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (intersectA)
|
|
{
|
|
(*intersectA) = m_origin + m_direction * t2;
|
|
}
|
|
if (intersectB)
|
|
{
|
|
(*intersectB) = m_origin + m_direction * t1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we are here it means that delta equals zero and we have only one solution
|
|
const float t = -0.5f * b / a;
|
|
if (intersectA)
|
|
{
|
|
(*intersectA) = m_origin + m_direction * t;
|
|
}
|
|
if (intersectB)
|
|
{
|
|
(*intersectB) = m_origin + m_direction * t;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ray-plane
|
|
bool Ray::Intersects(const PlaneEq& p, AZ::Vector3* intersect) const
|
|
{
|
|
// check if ray is parallel to plane (no intersection) or ray pointing away from plane (no intersection)
|
|
float dot1 = p.GetNormal().Dot(m_direction);
|
|
//if (dot1 >= 0) return false; // backface cull
|
|
|
|
// calc second dot product
|
|
float dot2 = -(p.GetNormal().Dot(m_origin) + p.GetDist());
|
|
|
|
// calc t value
|
|
float t = dot2 / dot1;
|
|
|
|
// if t<0 then the line defined by the ray, intersects the plane behind the rays origin and so no
|
|
// intersection occurs. else we can calculate the intersection point
|
|
if (t < 0.0f)
|
|
{
|
|
return false;
|
|
}
|
|
if (t > Length())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// calc intersection point
|
|
if (intersect)
|
|
{
|
|
intersect->SetX(m_origin.GetX() + (m_direction.GetX() * t));
|
|
intersect->SetY(m_origin.GetY() + (m_direction.GetY() * t));
|
|
intersect->SetZ(m_origin.GetZ() + (m_direction.GetZ() * t));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ray-triangle intersection test
|
|
bool Ray::Intersects(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3, AZ::Vector3* intersect, float* baryU, float* baryV) const
|
|
{
|
|
// calculate two vectors of the polygon
|
|
const AZ::Vector3 edge1 = p2 - p1;
|
|
const AZ::Vector3 edge2 = p3 - p1;
|
|
|
|
// begin calculating determinant - also used to calculate U parameter
|
|
const AZ::Vector3 dir = m_dest - m_origin;
|
|
const AZ::Vector3 pvec = dir.Cross(edge2);
|
|
|
|
// if determinant is near zero, ray lies in plane of triangle
|
|
const float det = edge1.Dot(pvec);
|
|
if (det > -Math::epsilon && det < Math::epsilon)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// calculate distance from vert0 to ray origin
|
|
const AZ::Vector3 tvec = m_origin - p1;
|
|
|
|
// calculate U parameter and test bounds
|
|
const float inv_det = 1.0f / det;
|
|
const float u = tvec.Dot(pvec) * inv_det;
|
|
if (u < 0.0f || u > 1.0f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// prepare to test V parameter
|
|
const AZ::Vector3 qvec = tvec.Cross(edge1);
|
|
|
|
// calculate V parameter and test bounds
|
|
const float v = dir.Dot(qvec) * inv_det;
|
|
if (v < 0.0f || u + v > 1.0f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// calculate t, ray intersects triangle
|
|
const float t = edge2.Dot(qvec) * inv_det;
|
|
if (t < 0.0f || t > 1.0f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// output the results
|
|
if (baryU)
|
|
{
|
|
*baryU = u;
|
|
}
|
|
if (baryV)
|
|
{
|
|
*baryV = v;
|
|
}
|
|
if (intersect)
|
|
{
|
|
*intersect = m_origin + t * dir;
|
|
}
|
|
|
|
// yes, there was an intersection
|
|
return true;
|
|
}
|
|
|
|
|
|
// ray-axis aligned bounding box
|
|
bool Ray::Intersects(const AABB& b, AZ::Vector3* intersectA, AZ::Vector3* intersectB) const
|
|
{
|
|
float tNear = -FLT_MAX, tFar = FLT_MAX;
|
|
|
|
const AZ::Vector3& minVec = b.GetMin();
|
|
const AZ::Vector3& maxVec = b.GetMax();
|
|
|
|
// For all three axes, check the near and far intersection point on the two slabs
|
|
for (int32 i = 0; i < 3; i++)
|
|
{
|
|
if (Math::Abs(m_direction.GetElement(i)) < Math::epsilon)
|
|
{
|
|
// direction is parallel to this plane, check if we're somewhere between min and max
|
|
if ((m_origin.GetElement(i) < minVec.GetElement(i)) || (m_origin.GetElement(i) > maxVec.GetElement(i)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// calculate t's at the near and far slab, see if these are min or max t's
|
|
float t1 = (minVec.GetElement(i) - m_origin.GetElement(i)) / m_direction.GetElement(i);
|
|
float t2 = (maxVec.GetElement(i) - m_origin.GetElement(i)) / m_direction.GetElement(i);
|
|
if (t1 > t2)
|
|
{
|
|
float temp = t1;
|
|
t1 = t2;
|
|
t2 = temp;
|
|
}
|
|
if (t1 > tNear)
|
|
{
|
|
tNear = t1; // accept nearest value
|
|
}
|
|
if (t2 < tFar)
|
|
{
|
|
tFar = t2; // accept farthest value
|
|
}
|
|
if ((tNear > tFar) || (tFar < 0.0f))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (intersectA)
|
|
{
|
|
*intersectA = m_origin + m_direction * tNear;
|
|
}
|
|
|
|
if (intersectB)
|
|
{
|
|
*intersectB = m_origin + m_direction * tFar;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
} // namespace MCore
|