From 5ece0acc773e046bcc5f8176533d4a789a3612fe Mon Sep 17 00:00:00 2001 From: ohyzha Date: Tue, 29 Oct 2024 12:40:52 +0200 Subject: [PATCH 1/5] calculate AABB for geometry --- openVulkanoCpp/Scene/Geometry.cpp | 40 +++++++++++++++++++++++++++++++ openVulkanoCpp/Scene/Geometry.hpp | 1 + 2 files changed, 41 insertions(+) diff --git a/openVulkanoCpp/Scene/Geometry.cpp b/openVulkanoCpp/Scene/Geometry.cpp index 68dd790..080bd51 100644 --- a/openVulkanoCpp/Scene/Geometry.cpp +++ b/openVulkanoCpp/Scene/Geometry.cpp @@ -83,6 +83,46 @@ namespace OpenVulkano::Scene Geometry::Close(); } + void Geometry::CalculateAABB() + { + if (vertexCount == 0 || !vertices) + { + return; + } + for (int i = 0; i < vertexCount; i++) + { + float x = vertices[i].position.x; + float y = vertices[i].position.y; + float z = vertices[i].position.z; + + if (x < aabb.min.x) + { + aabb.min.x = x; + } + if (y < aabb.min.y) + { + aabb.min.y = y; + } + if (z < aabb.min.z) + { + aabb.min.z = z; + } + + if (x > aabb.max.x) + { + aabb.max.x = x; + } + if (y > aabb.max.y) + { + aabb.max.y = y; + } + if (z > aabb.max.z) + { + aabb.max.z = z; + } + } + } + void Geometry::Swap(Geometry& other) noexcept { RenderResourceHolder::Swap(other); diff --git a/openVulkanoCpp/Scene/Geometry.hpp b/openVulkanoCpp/Scene/Geometry.hpp index 6e12da0..df2d101 100644 --- a/openVulkanoCpp/Scene/Geometry.hpp +++ b/openVulkanoCpp/Scene/Geometry.hpp @@ -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; }; From ced45f7b72c657a459a51b5d02cbbbf55f48f4dd Mon Sep 17 00:00:00 2001 From: ohyzha Date: Tue, 29 Oct 2024 12:41:07 +0200 Subject: [PATCH 2/5] add ray intersection checks --- openVulkanoCpp/Math/Math.hpp | 1 + openVulkanoCpp/Scene/Ray.cpp | 240 +++++++++++++++++++++++++++++++++++ openVulkanoCpp/Scene/Ray.hpp | 55 ++++++++ tests/RayTests.cpp | 216 +++++++++++++++++++++++++++++++ 4 files changed, 512 insertions(+) create mode 100644 openVulkanoCpp/Scene/Ray.cpp create mode 100644 openVulkanoCpp/Scene/Ray.hpp create mode 100644 tests/RayTests.cpp diff --git a/openVulkanoCpp/Math/Math.hpp b/openVulkanoCpp/Math/Math.hpp index 4dc74ec..cfd8cc7 100644 --- a/openVulkanoCpp/Math/Math.hpp +++ b/openVulkanoCpp/Math/Math.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/openVulkanoCpp/Scene/Ray.cpp b/openVulkanoCpp/Scene/Ray.cpp new file mode 100644 index 0000000..155ab74 --- /dev/null +++ b/openVulkanoCpp/Scene/Ray.cpp @@ -0,0 +1,240 @@ +/* + * 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 + +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 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 Ray::IntersectTriangle(const Math::Vector3f& v0, const Math::Vector3f& v1, + const Math::Vector3f& v2) const + { + RayHit hitRes; + vec2 baryPos; + if (intersectRayTriangle(m_origin, m_dir, v0, v1, v2, baryPos, hitRes.distance) && hitRes.distance >= 0) + { + hitRes.point = (1.f - baryPos.x - baryPos.y) * v0 + baryPos.x * v1 + baryPos.y * v2; + // calculate like cross product or leave empty ? what if current triangle is smoothly shaded ? + hitRes.normal = Math::Vector3f(0); + return hitRes; + } + return {}; + } + + std::optional 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 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 bMax = bbox.max; + const auto bMin = bbox.min; + + float tmin = (bMin.x - m_origin.x) / m_dir.x; + float tmax = (bMax.x - m_origin.x) / m_dir.x; + if (tmin > tmax) + { + std::swap(tmin, tmax); + } + + float tymin = (bMin.y - m_origin.y) / m_dir.y; + float tymax = (bMax.y - m_origin.y) / m_dir.y; + if (tymin > tymax) + { + std::swap(tymin, tymax); + } + + if ((tmin > tymax) || (tymin > tmax)) + { + return 0; + } + + if (tymin > tmin) + { + tmin = tymin; + } + if (tymax < tmax) + { + tmax = tymax; + } + + float tzmin = (bMin.z - m_origin.z) / m_dir.z; + float tzmax = (bMax.z - m_origin.z) / m_dir.z; + if (tzmin > tzmax) + { + std::swap(tzmin, tzmax); + } + + if ((tmin > tzmax) || (tzmin > tmax)) + { + return 0; + } + + if (tzmin > tmin) + { + tmin = tzmin; + } + if (tzmax < tmax) + { + tmax = tzmax; + } + + int intersections = 2; + if (tmin < 0) + { + if (tmax < 0) + { + return 0; + } + intersections--; + tmin = tmax; + } + + p1.point = m_origin + tmin * m_dir; + p2.point = m_origin + tmax * 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 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 = dot(m_dir, m_dir); // equals to length^2 + const float b = 2 * dot(m_dir, L); + const float c = dot(L, 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; + } +} diff --git a/openVulkanoCpp/Scene/Ray.hpp b/openVulkanoCpp/Scene/Ray.hpp new file mode 100644 index 0000000..889b945 --- /dev/null +++ b/openVulkanoCpp/Scene/Ray.hpp @@ -0,0 +1,55 @@ +/* + * 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 + +namespace OpenVulkano::Scene +{ + + struct RayHit + { + Math::Vector3f point; + Math::Vector3f normal; + float distance; + bool operator==(const RayHit& other) const + { + return point == other.point && normal == other.normal && distance == other.distance; + } + bool operator!=(const RayHit& other) const { return !((*this) == other); } + }; + + class Ray + { + public: + Ray() : m_origin(0), m_dir(0) {} + 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; } + std::optional IntersectSphere(const Math::Vector3f& center, float radius) const; + int IntersectSphere(const Math::Vector3f& center, float radius, RayHit& p1, RayHit& p2) const; + std::optional IntersectTriangle(const Math::Vector3f& v0, const Math::Vector3f& v1, + const Math::Vector3f& v2) const; + std::optional IntersectQuad(const Math::Vector3f& v0, const Math::Vector3f& v1, + const Math::Vector3f& v2, const Math::Vector3f& v3) const; + std::optional IntersectAABB(const Math::AABB& bbox) const; + int IntersectAABB(const Math::AABB& bbox, RayHit& p1, RayHit& p2) const; + std::optional 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; + }; +} diff --git a/tests/RayTests.cpp b/tests/RayTests.cpp new file mode 100644 index 0000000..11b0060 --- /dev/null +++ b/tests/RayTests.cpp @@ -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 +#include + +#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); + const auto& value = ray.IntersectSphere(Vector3f(0), 1).value(); + 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 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 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 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)); + } +} From 7fca7cff6452024ade5a1f1420d3cf6129e61bb6 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Tue, 29 Oct 2024 12:51:23 +0200 Subject: [PATCH 3/5] add verbose output to CTest --- .gitea/workflows/build_pc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build_pc.yml b/.gitea/workflows/build_pc.yml index 2da5da6..6ba330f 100644 --- a/.gitea/workflows/build_pc.yml +++ b/.gitea/workflows/build_pc.yml @@ -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 From 002914861e4d624bf3e398345b638b8765a5cd42 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Tue, 29 Oct 2024 13:08:42 +0200 Subject: [PATCH 4/5] try to fix mac test --- tests/RayTests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/RayTests.cpp b/tests/RayTests.cpp index 11b0060..0f25abd 100644 --- a/tests/RayTests.cpp +++ b/tests/RayTests.cpp @@ -63,10 +63,10 @@ TEST_CASE("RaySphereIntersection") REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 1); REQUIRE(h1 == h2); ::CompareVec3Approx(h1.normal, h1.point); - const auto& value = ray.IntersectSphere(Vector3f(0), 1).value(); - REQUIRE(value.distance == h1.distance); - ::CompareVec3Approx(value.normal, h1.normal); - ::CompareVec3Approx(value.point, 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 { From 9fca3040090e23c284e98ebb83cd5ca67fce3c3b Mon Sep 17 00:00:00 2001 From: ohyzha Date: Tue, 29 Oct 2024 18:57:40 +0200 Subject: [PATCH 5/5] code refactoring --- openVulkanoCpp/Scene/Geometry.cpp | 30 +----------- openVulkanoCpp/Scene/Ray.cpp | 80 ++++++++++++++++++------------- openVulkanoCpp/Scene/Ray.hpp | 24 +++++----- 3 files changed, 60 insertions(+), 74 deletions(-) diff --git a/openVulkanoCpp/Scene/Geometry.cpp b/openVulkanoCpp/Scene/Geometry.cpp index 080bd51..a05f0b2 100644 --- a/openVulkanoCpp/Scene/Geometry.cpp +++ b/openVulkanoCpp/Scene/Geometry.cpp @@ -91,35 +91,7 @@ namespace OpenVulkano::Scene } for (int i = 0; i < vertexCount; i++) { - float x = vertices[i].position.x; - float y = vertices[i].position.y; - float z = vertices[i].position.z; - - if (x < aabb.min.x) - { - aabb.min.x = x; - } - if (y < aabb.min.y) - { - aabb.min.y = y; - } - if (z < aabb.min.z) - { - aabb.min.z = z; - } - - if (x > aabb.max.x) - { - aabb.max.x = x; - } - if (y > aabb.max.y) - { - aabb.max.y = y; - } - if (z > aabb.max.z) - { - aabb.max.z = z; - } + aabb.Grow(vertices[i].position); } } diff --git a/openVulkanoCpp/Scene/Ray.cpp b/openVulkanoCpp/Scene/Ray.cpp index 155ab74..c082328 100644 --- a/openVulkanoCpp/Scene/Ray.cpp +++ b/openVulkanoCpp/Scene/Ray.cpp @@ -51,17 +51,31 @@ namespace OpenVulkano::Scene const Math::Vector3f& v2) const { RayHit hitRes; - vec2 baryPos; - if (intersectRayTriangle(m_origin, m_dir, v0, v1, v2, baryPos, hitRes.distance) && hitRes.distance >= 0) + if (intersectRayTriangle(m_origin, m_dir, v0, v1, v2, m_baryPos, hitRes.distance) && hitRes.distance >= 0) { - hitRes.point = (1.f - baryPos.x - baryPos.y) * v0 + baryPos.x * v1 + baryPos.y * v2; - // calculate like cross product or leave empty ? what if current triangle is smoothly shaded ? - hitRes.normal = Math::Vector3f(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 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 Ray::IntersectQuad(const Math::Vector3f& v0, const Math::Vector3f& v1, const Math::Vector3f& v2, const Math::Vector3f& v3) const { @@ -92,71 +106,69 @@ namespace OpenVulkano::Scene int Ray::IntersectAABB(const Math::AABB& bbox, RayHit& p1, RayHit& p2) const { - const auto bMax = bbox.max; - const auto bMin = bbox.min; + 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; - float tmin = (bMin.x - m_origin.x) / m_dir.x; - float tmax = (bMax.x - m_origin.x) / m_dir.x; - if (tmin > tmax) + if (txmin > txmax) { - std::swap(tmin, tmax); + std::swap(txmin, txmax); } - - float tymin = (bMin.y - m_origin.y) / m_dir.y; - float tymax = (bMax.y - m_origin.y) / m_dir.y; if (tymin > tymax) { std::swap(tymin, tymax); } - - if ((tmin > tymax) || (tymin > tmax)) + if ((txmin > tymax) || (tymin > txmax)) { return 0; } - if (tymin > tmin) + if (tymin > txmin) { - tmin = tymin; + txmin = tymin; } - if (tymax < tmax) + if (tymax < txmax) { - tmax = tymax; + txmax = tymax; } - float tzmin = (bMin.z - m_origin.z) / m_dir.z; - float tzmax = (bMax.z - m_origin.z) / m_dir.z; if (tzmin > tzmax) { std::swap(tzmin, tzmax); } - if ((tmin > tzmax) || (tzmin > tmax)) + if ((txmin > tzmax) || (tzmin > txmax)) { return 0; } - if (tzmin > tmin) + if (tzmin > txmin) { - tmin = tzmin; + txmin = tzmin; } - if (tzmax < tmax) + if (tzmax < txmax) { - tmax = tzmax; + txmax = tzmax; } int intersections = 2; - if (tmin < 0) + if (txmin < 0) { - if (tmax < 0) + if (txmax < 0) { return 0; } intersections--; - tmin = tmax; + txmin = txmax; } - p1.point = m_origin + tmin * m_dir; - p2.point = m_origin + tmax * m_dir; + 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); @@ -179,9 +191,9 @@ namespace OpenVulkano::Scene int Ray::IntersectSphere(const Math::Vector3f& center, float radius, RayHit& p1, RayHit& p2) const { const Math::Vector3f L = m_origin - center; - const float a = dot(m_dir, m_dir); // equals to length^2 + const float a = length2(m_dir); const float b = 2 * dot(m_dir, L); - const float c = dot(L, L) - radius * radius; + const float c = length2(L) - radius * radius; float x1, x2; int roots = ::SolveQuadraticEquation(a, b, c, x1, x2); diff --git a/openVulkanoCpp/Scene/Ray.hpp b/openVulkanoCpp/Scene/Ray.hpp index 889b945..080766c 100644 --- a/openVulkanoCpp/Scene/Ray.hpp +++ b/openVulkanoCpp/Scene/Ray.hpp @@ -18,32 +18,33 @@ namespace OpenVulkano::Scene Math::Vector3f point; Math::Vector3f normal; float distance; - bool operator==(const RayHit& other) const - { - return point == other.point && normal == other.normal && distance == other.distance; - } - bool operator!=(const RayHit& other) const { return !((*this) == other); } + bool operator==(const RayHit& other) const = default; + bool operator!=(const RayHit& other) const = default; }; class Ray { public: - Ray() : m_origin(0), m_dir(0) {} + 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; } - std::optional IntersectSphere(const Math::Vector3f& center, float radius) const; + [[nodiscard]] std::optional IntersectSphere(const Math::Vector3f& center, float radius) const; int IntersectSphere(const Math::Vector3f& center, float radius, RayHit& p1, RayHit& p2) const; - std::optional IntersectTriangle(const Math::Vector3f& v0, const Math::Vector3f& v1, + [[nodiscard]] std::optional IntersectTriangle(const Math::Vector3f& v0, const Math::Vector3f& v1, const Math::Vector3f& v2) const; - std::optional IntersectQuad(const Math::Vector3f& v0, const Math::Vector3f& v1, + [[nodiscard]] std::optional 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 IntersectQuad(const Math::Vector3f& v0, const Math::Vector3f& v1, const Math::Vector3f& v2, const Math::Vector3f& v3) const; - std::optional IntersectAABB(const Math::AABB& bbox) const; + [[nodiscard]] std::optional IntersectAABB(const Math::AABB& bbox) const; int IntersectAABB(const Math::AABB& bbox, RayHit& p1, RayHit& p2) const; - std::optional IntersectPlane(const Math::Vector3f& pOrigin, const Math::Vector3f& pNorm) const; + [[nodiscard]] std::optional 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; } @@ -51,5 +52,6 @@ namespace OpenVulkano::Scene private: Math::Vector3f m_origin; Math::Vector3f m_dir; + mutable Math::Vector2f m_baryPos; }; }