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:
Oleksii_Hyzha
2024-10-29 21:02:24 +01:00
7 changed files with 540 additions and 1 deletions

View File

@@ -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>

View File

@@ -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);

View File

@@ -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;
};

View 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;
}
}

View 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;
};
}