Merge pull request 'Ray intersections' (#151) from ray_intersections into master
Reviewed-on: https://git.madvoxel.net/OpenVulkano/OpenVulkano/pulls/151 Reviewed-by: Georg Hagen <georg.hagen@madvoxel.com>
This commit is contained in:
@@ -57,7 +57,7 @@ jobs:
|
||||
run: cmake --build "${{github.workspace}}/build" --config ${{env.BUILD_TYPE}}
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: ctest -C ${{env.BUILD_TYPE}}
|
||||
run: ctest --verbose -C ${{env.BUILD_TYPE}}
|
||||
- name: Archive artifacts Windows
|
||||
if: matrix.os == 'windows_x64'
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <glm/gtx/matrix_decompose.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <glm/gtx/intersect.hpp>
|
||||
#include <glm/gtx/io.hpp>
|
||||
#include <numbers>
|
||||
|
||||
|
||||
@@ -83,6 +83,18 @@ namespace OpenVulkano::Scene
|
||||
Geometry::Close();
|
||||
}
|
||||
|
||||
void Geometry::CalculateAABB()
|
||||
{
|
||||
if (vertexCount == 0 || !vertices)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
{
|
||||
aabb.Grow(vertices[i].position);
|
||||
}
|
||||
}
|
||||
|
||||
void Geometry::Swap(Geometry& other) noexcept
|
||||
{
|
||||
RenderResourceHolder<Geometry>::Swap(other);
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace OpenVulkano
|
||||
void SetOwnsMemory(bool val) { ownsMemory = val; }
|
||||
bool FreeAfterUpload() const { return freeAfterUpload; }
|
||||
void SetFreeAfterUpload(bool val) { freeAfterUpload = val; }
|
||||
void CalculateAABB();
|
||||
private:
|
||||
void Swap(Geometry& other) noexcept;
|
||||
};
|
||||
|
||||
252
openVulkanoCpp/Scene/Ray.cpp
Normal file
252
openVulkanoCpp/Scene/Ray.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.distance = distance(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.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.distance < h2.distance) ? 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.distance = distance(m_origin, p1.point);
|
||||
p2.distance = distance(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.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.distance = distance(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.distance = distance(m_origin, p1.point);
|
||||
p1.normal = normalize(p1.point - center);
|
||||
p2.point = m_origin + x2 * m_dir;
|
||||
p2.distance = distance(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.distance = distance(m_origin, p1.point);
|
||||
p1.normal = normalize(p1.point - center);
|
||||
p2 = p1;
|
||||
}
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
}
|
||||
57
openVulkanoCpp/Scene/Ray.hpp
Normal file
57
openVulkanoCpp/Scene/Ray.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Math/Math.hpp"
|
||||
#include "Math/AABB.hpp"
|
||||
#include <optional>
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
|
||||
struct RayHit
|
||||
{
|
||||
Math::Vector3f point;
|
||||
Math::Vector3f normal;
|
||||
float distance;
|
||||
bool operator==(const RayHit& other) const = default;
|
||||
bool operator!=(const RayHit& other) const = default;
|
||||
};
|
||||
|
||||
class Ray
|
||||
{
|
||||
public:
|
||||
Ray() = default;
|
||||
Ray(const Math::Vector3f& origin, const Math::Vector3f& dir)
|
||||
: m_origin(origin), m_dir(Math::Utils::normalize(dir))
|
||||
{
|
||||
}
|
||||
void SetPosition(const Math::Vector3f& origin) { m_origin = origin; }
|
||||
void SetDir(const Math::Vector3f& dir) { m_dir = dir; }
|
||||
[[nodiscard]] std::optional<RayHit> IntersectSphere(const Math::Vector3f& center, float radius) const;
|
||||
int IntersectSphere(const Math::Vector3f& center, float radius, RayHit& p1, RayHit& p2) const;
|
||||
[[nodiscard]] std::optional<RayHit> IntersectTriangle(const Math::Vector3f& v0, const Math::Vector3f& v1,
|
||||
const Math::Vector3f& v2) const;
|
||||
[[nodiscard]] std::optional<RayHit> 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;
|
||||
[[nodiscard]] std::optional<RayHit> IntersectQuad(const Math::Vector3f& v0, const Math::Vector3f& v1,
|
||||
const Math::Vector3f& v2, const Math::Vector3f& v3) const;
|
||||
[[nodiscard]] std::optional<RayHit> IntersectAABB(const Math::AABB& bbox) const;
|
||||
int IntersectAABB(const Math::AABB& bbox, RayHit& p1, RayHit& p2) const;
|
||||
[[nodiscard]] std::optional<RayHit> IntersectPlane(const Math::Vector3f& pOrigin,
|
||||
const Math::Vector3f& pNorm) const;
|
||||
[[nodiscard]] Math::Vector3f& GetOrigin() { return m_origin; }
|
||||
[[nodiscard]] Math::Vector3f& GetDir() { return m_dir; }
|
||||
[[nodiscard]] const Math::Vector3f& GetOrigin() const { return m_origin; }
|
||||
[[nodiscard]] const Math::Vector3f& GetDir() const { return m_dir; }
|
||||
private:
|
||||
Math::Vector3f m_origin;
|
||||
Math::Vector3f m_dir;
|
||||
mutable Math::Vector2f m_baryPos;
|
||||
};
|
||||
}
|
||||
216
tests/RayTests.cpp
Normal file
216
tests/RayTests.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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 <catch2/catch_all.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
|
||||
#include "Math/Math.hpp"
|
||||
#include "Scene/Ray.hpp"
|
||||
#include "Scene/GeometryFactory.hpp"
|
||||
|
||||
using namespace OpenVulkano::Math;
|
||||
using namespace OpenVulkano::Math::Utils;
|
||||
using namespace OpenVulkano::Scene;
|
||||
|
||||
namespace
|
||||
{
|
||||
void CompareVec3Approx(const Vector3f& f, const Vector3f& s)
|
||||
{
|
||||
REQUIRE_THAT(f.x, Catch::Matchers::WithinRel(s.x));
|
||||
REQUIRE_THAT(f.x, Catch::Matchers::WithinRel(s.x));
|
||||
REQUIRE_THAT(f.x, Catch::Matchers::WithinRel(s.x));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("RaySphereIntersection")
|
||||
{
|
||||
auto sphere = GeometryFactory::MakeSphere(1, 32, 16);
|
||||
RayHit h1, h2;
|
||||
// 2 intersections
|
||||
{
|
||||
Ray ray(Vector3f(0, 0, -5), Vector3f(0, 0, 1));
|
||||
REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 2);
|
||||
REQUIRE((h1.point == Vector3f(0, 0, -1) && h2.point == Vector3f(0, 0, 1)));
|
||||
REQUIRE((h1.normal == Vector3f(0, 0, -1) && h2.normal == Vector3f(0, 0, 1)));
|
||||
REQUIRE(h1.distance < h2.distance);
|
||||
REQUIRE((h1.distance == distance(ray.GetOrigin(), h1.point) && h2.distance == distance(ray.GetOrigin(), h2.point)));
|
||||
// this returns just closest point
|
||||
if (auto opt = ray.IntersectSphere(Vector3f(0), 1))
|
||||
{
|
||||
REQUIRE(opt.value() == h1);
|
||||
}
|
||||
}
|
||||
// 1 intersection
|
||||
{
|
||||
Ray ray(Vector3f(1, 0, -1), Vector3f(0, 0, 1));
|
||||
REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 1);
|
||||
REQUIRE(h1 == h2);
|
||||
REQUIRE(h1.point == Vector3f(1, 0, 0));
|
||||
REQUIRE(ray.IntersectSphere(Vector3f(0), 1).value() == h1);
|
||||
}
|
||||
// 0 intersections
|
||||
{
|
||||
Ray ray(Vector3f(2, 0, -1), Vector3f(0, 0, 1));
|
||||
REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 0);
|
||||
REQUIRE(!ray.IntersectSphere(Vector3f(0), 1).has_value());
|
||||
}
|
||||
// ray is inside sphere
|
||||
{
|
||||
Ray ray(Vector3f(0, 0, 0), Vector3f(0.5, 0.5, 1));
|
||||
REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 1);
|
||||
REQUIRE(h1 == h2);
|
||||
::CompareVec3Approx(h1.normal, h1.point);
|
||||
auto value = ray.IntersectSphere(Vector3f(0), 1);
|
||||
REQUIRE(value->distance == h1.distance);
|
||||
::CompareVec3Approx(value->normal, h1.normal);
|
||||
::CompareVec3Approx(value->point, h1.point);
|
||||
}
|
||||
// ray intersects sphere behind the origin
|
||||
{
|
||||
Ray ray(Vector3f(0, 0, 3), Vector3f(0, 0, 1));
|
||||
REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 0);
|
||||
REQUIRE(!ray.IntersectSphere(Vector3f(0), 1).has_value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("RayTriangleIntersection")
|
||||
{
|
||||
auto tri = GeometryFactory::MakeTriangle(Vector3f(0), Vector3f(3, 0, 0), Vector3f(1.5, 2, 0));
|
||||
std::optional<RayHit> hit;
|
||||
// intersects
|
||||
{
|
||||
Ray ray(Vector3f(1.5, 2, -5), Vector3f(0, 0, 1));
|
||||
hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position);
|
||||
REQUIRE(hit.has_value());
|
||||
REQUIRE(hit->distance == distance(ray.GetOrigin(), hit->point));
|
||||
REQUIRE(hit->point == Vector3f(1.5, 2, 0));
|
||||
}
|
||||
{
|
||||
Ray ray(Vector3f(1.5, 1, -1), Vector3f(0, 0, 1));
|
||||
hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position);
|
||||
REQUIRE(hit.has_value());
|
||||
REQUIRE(hit->distance == distance(ray.GetOrigin(), hit->point));
|
||||
REQUIRE(hit->point == Vector3f(1.5, 1, 0));
|
||||
}
|
||||
// no intersections
|
||||
{
|
||||
Ray ray(Vector3f(5, 0, 0), Vector3f(0, 0, 1));
|
||||
hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position);
|
||||
REQUIRE(!hit.has_value());
|
||||
}
|
||||
{
|
||||
Ray ray(Vector3f(1.5, 1, 0.5), Vector3f(0, 0, 1));
|
||||
hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position);
|
||||
REQUIRE(!hit.has_value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("RayQuadIntersection")
|
||||
{
|
||||
auto cube = GeometryFactory::MakeCube();
|
||||
std::optional<RayHit> hit;
|
||||
// intersects
|
||||
{
|
||||
Ray ray(Vector3f(0), Vector3f(0, 0, 1));
|
||||
// front face
|
||||
hit = ray.IntersectQuad(cube.vertices[0].position, cube.vertices[1].position, cube.vertices[2].position,
|
||||
cube.vertices[3].position);
|
||||
REQUIRE(hit.has_value());
|
||||
REQUIRE(hit->point == Vector3f(0, 0, 0.5));
|
||||
}
|
||||
// no intersections
|
||||
{
|
||||
Ray ray(Vector3f(0), Vector3f(0, 0, -1));
|
||||
hit = ray.IntersectQuad(cube.vertices[0].position, cube.vertices[1].position, cube.vertices[2].position,
|
||||
cube.vertices[3].position);
|
||||
REQUIRE(!hit.has_value());
|
||||
}
|
||||
{
|
||||
Ray ray(Vector3f(1, 1, 0), Vector3f(0, 0, 1));
|
||||
hit = ray.IntersectQuad(cube.vertices[0].position, cube.vertices[1].position, cube.vertices[2].position,
|
||||
cube.vertices[3].position);
|
||||
REQUIRE(!hit.has_value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("RayAABBIntersection")
|
||||
{
|
||||
auto sphere = GeometryFactory::MakeSphere(1, 32, 16);
|
||||
sphere.CalculateAABB();
|
||||
std::optional<RayHit> hit;
|
||||
RayHit h1, h2;
|
||||
// intersects
|
||||
{
|
||||
Ray ray(Vector3f(0, 0, -2), Vector3f(0, 0, 1));
|
||||
REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 2);
|
||||
REQUIRE(h1.distance < h2.distance);
|
||||
REQUIRE(h1.point == Vector3f(0, 0, -1));
|
||||
REQUIRE(h2.point == Vector3f(0, 0, 1));
|
||||
auto p = ray.IntersectAABB(sphere.aabb);
|
||||
REQUIRE(p->point == h1.point);
|
||||
REQUIRE(p->distance == h1.distance);
|
||||
}
|
||||
{
|
||||
Ray ray(Vector3f(0, 0, 1), Vector3f(0, 0, 1));
|
||||
REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 1);
|
||||
REQUIRE(h1.distance == h2.distance);
|
||||
CompareVec3Approx(h1.point, h2.point);
|
||||
REQUIRE(h1.point == Vector3f(0, 0, 1));
|
||||
REQUIRE(ray.IntersectAABB(sphere.aabb)->distance == h1.distance);
|
||||
}
|
||||
{
|
||||
// inside sphere
|
||||
Ray ray(Vector3f(0), Vector3f(0.3));
|
||||
REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 1);
|
||||
REQUIRE(h1 == h2);
|
||||
auto val = ray.IntersectAABB(sphere.aabb);
|
||||
REQUIRE(val.has_value());
|
||||
REQUIRE_THAT(val->distance, Catch::Matchers::WithinRel(h1.distance));
|
||||
CompareVec3Approx(val->point, h1.point);
|
||||
}
|
||||
{
|
||||
Ray ray(Vector3f(2, -0.5, 1.5), Vector3f(-2, 0.5, -1.5));
|
||||
REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 2);
|
||||
REQUIRE(h1.distance < h2.distance);
|
||||
auto val = ray.IntersectAABB(sphere.aabb);
|
||||
REQUIRE(val.has_value());
|
||||
REQUIRE(val->distance == h1.distance);
|
||||
CompareVec3Approx(val->point, h1.point);
|
||||
}
|
||||
// no intersections
|
||||
{
|
||||
Ray ray(Vector3f(3, 0, 1), Vector3f(0, 0, 1));
|
||||
REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 0);
|
||||
REQUIRE(!ray.IntersectAABB(sphere.aabb).has_value());
|
||||
}
|
||||
{
|
||||
Ray ray(Vector3f(0, 0, 1.1), Vector3f(0, 0, 1));
|
||||
REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 0);
|
||||
REQUIRE(!ray.IntersectAABB(sphere.aabb).has_value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("RayPlaneIntersection")
|
||||
{
|
||||
Vector3f pOrigin = Vector3f(0);
|
||||
Vector3f pNorm = Vector3f(0, 1, 0);
|
||||
{
|
||||
Ray ray(Vector3f(2, -2, 2), Vector3f(0, 1, 0));
|
||||
auto hit = ray.IntersectPlane(pOrigin, pNorm);
|
||||
REQUIRE(hit.has_value());
|
||||
REQUIRE(hit->normal == pNorm);
|
||||
REQUIRE(hit->distance == 2.f);
|
||||
REQUIRE(hit->point == Vector3f(2, 0, 2));
|
||||
}
|
||||
{
|
||||
Ray ray(Vector3f(2, -2, 2), Vector3f(1, 0, 0));
|
||||
REQUIRE(!ray.IntersectPlane(pOrigin, pNorm));
|
||||
}
|
||||
{
|
||||
Ray ray(Vector3f(0), Vector3f(1, 0, 0));
|
||||
REQUIRE(!ray.IntersectPlane(pOrigin, pNorm));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user