262 lines
6.0 KiB
C++
262 lines
6.0 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "Ray.hpp"
|
|
#include <vector>
|
|
|
|
namespace
|
|
{
|
|
int SolveQuadraticEquation(float a, float b, float c, float& x0, float& x1)
|
|
{
|
|
float discr = b * b - 4 * a * c;
|
|
if (discr < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
if (discr == 0)
|
|
{
|
|
x0 = x1 = (-b) / (2 * a);
|
|
return 1;
|
|
}
|
|
float q = (b > 0) ? -0.5 * (b + std::sqrt(discr)) : -0.5 * (b - std::sqrt(discr));
|
|
x0 = q / a;
|
|
x1 = c / q;
|
|
if (x0 > x1)
|
|
{
|
|
std::swap(x0, x1);
|
|
}
|
|
return 2;
|
|
}
|
|
};
|
|
|
|
namespace OpenVulkano::Scene
|
|
{
|
|
using namespace Math::Utils;
|
|
|
|
std::optional<RayHit> Ray::IntersectSphere(const Math::Vector3f& center, float radius) const
|
|
{
|
|
RayHit hitRes;
|
|
if (intersectRaySphere(m_origin, m_dir, center, radius, hitRes.point, hitRes.normal))
|
|
{
|
|
hitRes.distance2 = distance2(m_origin, hitRes.point);
|
|
return hitRes;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::optional<RayHit> Ray::IntersectTriangle(const Math::Vector3f& v0, const Math::Vector3f& v1,
|
|
const Math::Vector3f& v2) const
|
|
{
|
|
RayHit hitRes;
|
|
if (intersectRayTriangle(m_origin, m_dir, v0, v1, v2, m_baryPos, hitRes.distance) && hitRes.distance >= 0)
|
|
{
|
|
hitRes.point = (1.f - m_baryPos.x - m_baryPos.y) * v0 + m_baryPos.x * v1 + m_baryPos.y * v2;
|
|
Math::Vector3f e = v1 - v0;
|
|
Math::Vector3f e2 = v2 - v0;
|
|
// triangle normal. won't work if triangle is smoothly shaded. use other overloaded method instead.
|
|
hitRes.distance2 = distance2(m_origin, hitRes.point);
|
|
hitRes.normal = normalize(cross(e, e2));
|
|
return hitRes;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::optional<RayHit> Ray::IntersectTriangle(const Math::Vector3f& v0, const Math::Vector3f& v1,
|
|
const Math::Vector3f& v2, const Math::Vector3f& n0,
|
|
const Math::Vector3f& n1, const Math::Vector3f& n2) const
|
|
{
|
|
auto hit = IntersectTriangle(v0, v1, v2);
|
|
if (hit && (n0 != n1 || n0 != n2 || n1 != n2))
|
|
{
|
|
// TODO: NEED ACTUAL TESTING
|
|
hit->normal = normalize((1.f - m_baryPos.x - m_baryPos.y) * n0 + m_baryPos.x * n1 + m_baryPos.y * n2);
|
|
}
|
|
return hit;
|
|
}
|
|
|
|
std::optional<RayHit> Ray::IntersectQuad(const Math::Vector3f& v0, const Math::Vector3f& v1,
|
|
const Math::Vector3f& v2, const Math::Vector3f& v3) const
|
|
{
|
|
if (auto hitRes = IntersectTriangle(v0, v1, v2))
|
|
{
|
|
return hitRes;
|
|
}
|
|
if (auto hitRes = IntersectTriangle(v0, v2, v3))
|
|
{
|
|
return hitRes;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::optional<RayHit> Ray::IntersectAABB(const Math::AABB& bbox) const
|
|
{
|
|
RayHit h1, h2;
|
|
const int intersections = this->IntersectAABB(bbox, h1, h2);
|
|
switch (intersections)
|
|
{
|
|
case 1:
|
|
return h1;
|
|
case 2:
|
|
return (h1.distance2 < h2.distance2) ? h1 : h2;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
int Ray::IntersectAABB(const Math::AABB& bbox, RayHit& p1, RayHit& p2) const
|
|
{
|
|
const auto tmin = (bbox.min - m_origin) / m_dir;
|
|
const auto tmax = (bbox.max - m_origin) / m_dir;
|
|
float txmin = tmin.x;
|
|
float txmax = tmax.x;
|
|
float tymin = tmin.y;
|
|
float tymax = tmax.y;
|
|
float tzmin = tmin.z;
|
|
float tzmax = tmax.z;
|
|
|
|
if (txmin > txmax)
|
|
{
|
|
std::swap(txmin, txmax);
|
|
}
|
|
if (tymin > tymax)
|
|
{
|
|
std::swap(tymin, tymax);
|
|
}
|
|
if ((txmin > tymax) || (tymin > txmax))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (tymin > txmin)
|
|
{
|
|
txmin = tymin;
|
|
}
|
|
if (tymax < txmax)
|
|
{
|
|
txmax = tymax;
|
|
}
|
|
|
|
if (tzmin > tzmax)
|
|
{
|
|
std::swap(tzmin, tzmax);
|
|
}
|
|
|
|
if ((txmin > tzmax) || (tzmin > txmax))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (tzmin > txmin)
|
|
{
|
|
txmin = tzmin;
|
|
}
|
|
if (tzmax < txmax)
|
|
{
|
|
txmax = tzmax;
|
|
}
|
|
|
|
int intersections = 2;
|
|
if (txmin < 0)
|
|
{
|
|
if (txmax < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
intersections--;
|
|
txmin = txmax;
|
|
}
|
|
|
|
p1.point = m_origin + txmin * m_dir;
|
|
p2.point = m_origin + txmax * m_dir;
|
|
p1.distance2 = distance2(m_origin, p1.point);
|
|
p2.distance2 = distance2(m_origin, p2.point);
|
|
p1.normal = p2.normal = Math::Vector3f(0);
|
|
return intersections;
|
|
}
|
|
|
|
std::optional<RayHit> Ray::IntersectPlane(const Math::Vector3f& pOrigin, const Math::Vector3f& pNorm) const
|
|
{
|
|
RayHit hit;
|
|
Math::Vector3f norm = normalize(pNorm);
|
|
if (intersectRayPlane(m_origin, m_dir, pOrigin, pNorm, hit.distance))
|
|
{
|
|
hit.point = m_origin + m_dir * hit.distance;
|
|
hit.distance2 = distance2(m_origin, hit.point);
|
|
hit.normal = norm;
|
|
return hit;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
int Ray::IntersectSphere(const Math::Vector3f& center, float radius, RayHit& p1, RayHit& p2) const
|
|
{
|
|
const Math::Vector3f L = m_origin - center;
|
|
const float a = length2(m_dir);
|
|
const float b = 2 * dot(m_dir, L);
|
|
const float c = length2(L) - radius * radius;
|
|
float x1, x2;
|
|
int roots = ::SolveQuadraticEquation(a, b, c, x1, x2);
|
|
|
|
if (roots == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
if (x1 > x2)
|
|
{
|
|
std::swap(x1, x2);
|
|
}
|
|
|
|
if (roots == 1)
|
|
{
|
|
// ray intersects sphere behind the origin
|
|
if (x1 < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
p1.point = m_origin + x1 * m_dir;
|
|
p1.distance2 = distance2(m_origin, p1.point);
|
|
p1.normal = normalize(p1.point - center);
|
|
p2 = p1;
|
|
}
|
|
else if (roots == 2)
|
|
{
|
|
// ray intersects sphere behind the origin
|
|
if (x1 < 0 && x2 < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
if (x1 >= 0 && x2 >= 0)
|
|
{
|
|
p1.point = m_origin + x1 * m_dir;
|
|
p1.distance2 = distance2(m_origin, p1.point);
|
|
p1.normal = normalize(p1.point - center);
|
|
p2.point = m_origin + x2 * m_dir;
|
|
p2.distance2 = distance2(m_origin, p2.point);
|
|
p2.normal = normalize(p2.point - center);
|
|
}
|
|
else
|
|
{
|
|
--roots;
|
|
if (x1 < 0)
|
|
{
|
|
x1 = x2;
|
|
}
|
|
p1.point = m_origin + x1 * m_dir;
|
|
p1.distance2 = distance2(m_origin, p1.point);
|
|
p1.normal = normalize(p1.point - center);
|
|
p2 = p1;
|
|
}
|
|
}
|
|
return roots;
|
|
}
|
|
|
|
bool RayHit::operator==(const RayHit& other) const
|
|
{
|
|
return distance2 == other.distance2 && point == other.point && normal == other.normal;
|
|
}
|
|
|
|
bool RayHit::operator!=(const RayHit& other) const { return !(*this == other); }
|
|
}
|