From 70ef147656d80a6d1fc7d2ee00a0167bad1a969e Mon Sep 17 00:00:00 2001 From: lgf Date: Mon, 7 Jul 2025 22:45:34 -0300 Subject: [PATCH 01/10] feat(CI): Add workflow for build and test --- .github/workflows/build-and-test.yml | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/build-and-test.yml diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..cc471c1 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,46 @@ +name: CMake on multiple platforms + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + preset: [debug, release] + include: + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set compiler environment variables + shell: bash + run: | + echo "CC=${{ matrix.c_compiler }}" >> $GITHUB_ENV + echo "CXX=${{ matrix.cpp_compiler }}" >> $GITHUB_ENV + + - name: Configure CMake using preset + run: cmake --preset ${{ matrix.preset }} + + - name: Build project + run: cmake --build --preset ${{ matrix.preset }} + + - name: Run tests + if: matrix.preset == 'debug' + working-directory: build/${{ matrix.preset }} + run: ctest --output-on-failure From 75f64802c6cdefa29f51e1fd41a7b6cbe1698dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Guimar=C3=A3es?= Date: Mon, 7 Jul 2025 22:56:24 -0300 Subject: [PATCH 02/10] fix(CI): adjust job matrix --- .github/workflows/build-and-test.yml | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index cc471c1..a821529 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -8,21 +8,16 @@ on: jobs: build: - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: preset: [debug, release] - include: - - os: ubuntu-latest - c_compiler: gcc - cpp_compiler: g++ - - os: ubuntu-latest - c_compiler: clang - cpp_compiler: clang++ - - os: windows-latest - c_compiler: cl - cpp_compiler: cl + + config: + - { os: ubuntu-latest, c_compiler: gcc, cpp_compiler: g++ } + - { os: ubuntu-latest, c_compiler: clang, cpp_compiler: clang++ } + - { os: windows-latest, c_compiler: cl, cpp_compiler: cl } steps: - name: Checkout repository @@ -31,8 +26,8 @@ jobs: - name: Set compiler environment variables shell: bash run: | - echo "CC=${{ matrix.c_compiler }}" >> $GITHUB_ENV - echo "CXX=${{ matrix.cpp_compiler }}" >> $GITHUB_ENV + echo "CC=${{ matrix.config.c_compiler }}" >> $GITHUB_ENV + echo "CXX=${{ matrix.config.cpp_compiler }}" >> $GITHUB_ENV - name: Configure CMake using preset run: cmake --preset ${{ matrix.preset }} From f9991c0d4aaef3e30ae4fdb75193e962161173fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Guimar=C3=A3es?= Date: Mon, 7 Jul 2025 23:24:44 -0300 Subject: [PATCH 03/10] fix(build): Resolve compilation errors on MSVC --- libs/Prism/include/Prism/ObjReader.hpp | 2 +- libs/Prism/include/Prism/color.hpp | 5 +++-- libs/Prism/src/color.cpp | 4 ++-- libs/Prism/src/scene.cpp | 1 + src/main.cpp | 4 ++-- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/libs/Prism/include/Prism/ObjReader.hpp b/libs/Prism/include/Prism/ObjReader.hpp index 3706ef1..62ce364 100644 --- a/libs/Prism/include/Prism/ObjReader.hpp +++ b/libs/Prism/include/Prism/ObjReader.hpp @@ -24,7 +24,7 @@ class ObjReader { std::vector> triangles; - ObjReader(const std::string& filename) : cmap(cmap) { + ObjReader(const std::string& filename) { file.open(filename); if (!file.is_open()) { std::cerr << "Erro ao abrir o arquivo: " << filename << std::endl; diff --git a/libs/Prism/include/Prism/color.hpp b/libs/Prism/include/Prism/color.hpp index f9654ae..0360b8a 100644 --- a/libs/Prism/include/Prism/color.hpp +++ b/libs/Prism/include/Prism/color.hpp @@ -7,9 +7,10 @@ namespace Prism { class PRISM_EXPORT Color { public: Color(); - Color(float red, float green, float blue); + Color(double red, double green, double blue); + Color(int red, int green, int blue); Color(const Color& other); - float r, g, b; + double r, g, b; }; } // namespace Prism diff --git a/libs/Prism/src/color.cpp b/libs/Prism/src/color.cpp index 74823e3..5c2be0a 100644 --- a/libs/Prism/src/color.cpp +++ b/libs/Prism/src/color.cpp @@ -3,8 +3,8 @@ namespace Prism { Color::Color() : r(0), g(0), b(0) {} -Color::Color(float red, float green, float blue) : r(red), g(green), b(blue) {} -// Color::Color(int red, int green, int blue): r(static_cast(red) / 255.0f), g(static_cast(green) / 255.0f), b(static_cast(blue) / 255.0f) {} +Color::Color(double red, double green, double blue) : r(red), g(green), b(blue) {} +Color::Color(int red, int green, int blue): r(static_cast(red) / 255.0f), g(static_cast(green) / 255.0f), b(static_cast(blue) / 255.0f) {} Color::Color(const Color& other) : r(other.r), g(other.g), b(other.b) {} } // namespace Prism \ No newline at end of file diff --git a/libs/Prism/src/scene.cpp b/libs/Prism/src/scene.cpp index bf3c018..de8c466 100644 --- a/libs/Prism/src/scene.cpp +++ b/libs/Prism/src/scene.cpp @@ -1,6 +1,7 @@ #include "Prism/scene.hpp" #include "Prism/color.hpp" #include "Prism/material.hpp" +#include #include #include #include diff --git a/src/main.cpp b/src/main.cpp index 9bc0547..ed8e8a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,7 +42,7 @@ int main() { Prism::Matrix::scaling(3, {0.5, 0.5, 0.5}) ); - scene.addObject(move(cube)); + scene.addObject(std::move(cube)); auto plane = std::make_unique( Prism::Point3(0, -0.5, 0), Prism::Vector3(0, 1, 0), material_chao @@ -51,7 +51,7 @@ int main() { // plane->setTransform(Prism::Matrix::rotation3d(45, {1,0,0})); - scene.addObject(move(plane)); + scene.addObject(std::move(plane)); // scene.addObject(std::make_unique( // Prism::Point3(0, 0, -1), 0.5, material_esfera_1 From 3ed990e3d3669943c2f168dc207e10164449c8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Guimar=C3=A3es?= Date: Mon, 7 Jul 2025 23:59:31 -0300 Subject: [PATCH 04/10] fix(scene): Improve local time retrieval for filename generation making it thread safe --- libs/Prism/src/scene.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/libs/Prism/src/scene.cpp b/libs/Prism/src/scene.cpp index de8c466..6d6c43d 100644 --- a/libs/Prism/src/scene.cpp +++ b/libs/Prism/src/scene.cpp @@ -17,14 +17,31 @@ void Scene::addObject(std::unique_ptr object) { objects_.push_back(std::move(object)); } +bool get_local_time(std::tm* tm_out, const std::time_t* time_in) { + #if defined(_WIN32) || defined(_MSC_VER) + // Usa a versão segura do Windows (MSVC) + return localtime_s(tm_out, time_in) == 0; + #else + // Usa a versão reentrante/segura do Linux e macOS (GCC/Clang) + return localtime_r(time_in, tm_out) != nullptr; + #endif +} + std::filesystem::path generate_filename() { auto now = std::chrono::system_clock::now(); auto in_time_t = std::chrono::system_clock::to_time_t(now); - std::stringstream ss; - ss << "render_" << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d_%H-%M-%S") << ".ppm"; - std::string filename = ss.str(); - return filename; + std::tm time_info; + + if (get_local_time(&time_info, &in_time_t)) { + std::stringstream ss; + ss << "render_" << std::put_time(&time_info, "%Y-%m-%d_%H-%M-%S") << ".ppm"; + return ss.str(); + } + + std::cerr << "Error: Could not get local time for filename generation.\n"; + std::cerr << "Using fallback filename.\n"; + return "render_fallback.ppm"; } void Scene::render() const { From 10d1ba1ede6d70c0889996256b7ed2103c56e7f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Guimar=C3=A3es?= Date: Mon, 7 Jul 2025 23:59:54 -0300 Subject: [PATCH 05/10] fix(scene): Delete copy constructor and assignment operator for Scene class --- libs/Prism/include/Prism/scene.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/Prism/include/Prism/scene.hpp b/libs/Prism/include/Prism/scene.hpp index 4f5913c..791ca63 100644 --- a/libs/Prism/include/Prism/scene.hpp +++ b/libs/Prism/include/Prism/scene.hpp @@ -34,6 +34,12 @@ class PRISM_EXPORT Scene { */ Scene(Camera camera); + Scene(const Scene&) = delete; + Scene& operator=(const Scene&) = delete; + + Scene(Scene&&) = default; + Scene& operator=(Scene&&) = default; + /** * @brief Adiciona um objeto à cena. * A posse do objeto é transferida para a cena. From b69f25fecb4cb37c6c526dcce351b9125e89320b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Guimar=C3=A3es?= Date: Tue, 8 Jul 2025 01:48:37 -0300 Subject: [PATCH 06/10] refactor(core): Ensure cross-platform support and refactor Triangle class --- libs/Prism/include/Prism/matrix.hpp | 4 +- libs/Prism/include/Prism/mesh.hpp | 2 +- libs/Prism/include/Prism/scene.hpp | 11 -- libs/Prism/include/Prism/triangle.hpp | 152 +++++--------------------- libs/Prism/src/matrix.cpp | 4 +- libs/Prism/src/mesh.cpp | 14 ++- libs/Prism/src/plane.cpp | 18 +-- libs/Prism/src/scene.cpp | 11 ++ libs/Prism/src/sphere.cpp | 4 +- libs/Prism/src/triangle.cpp | 134 +++++++++++++++++++++++ tests/src/MatrixTest.cpp | 5 + tests/src/TransformationsTest.cpp | 7 +- 12 files changed, 208 insertions(+), 158 deletions(-) create mode 100644 libs/Prism/src/triangle.cpp diff --git a/libs/Prism/include/Prism/matrix.hpp b/libs/Prism/include/Prism/matrix.hpp index a56b4af..2ed9650 100644 --- a/libs/Prism/include/Prism/matrix.hpp +++ b/libs/Prism/include/Prism/matrix.hpp @@ -22,7 +22,7 @@ class PRISM_EXPORT Matrix { Matrix(const Matrix& m); // --- Classes aninhadas para acesso --- - class MatrixRow { + class PRISM_EXPORT MatrixRow { private: std::vector& m_row_vector; public: @@ -30,7 +30,7 @@ class PRISM_EXPORT Matrix { double& operator[](int col); }; - class ConstMatrixRow { + class PRISM_EXPORT ConstMatrixRow { private: const std::vector& m_row_vector; public: diff --git a/libs/Prism/include/Prism/mesh.hpp b/libs/Prism/include/Prism/mesh.hpp index dfd210a..ba54775 100644 --- a/libs/Prism/include/Prism/mesh.hpp +++ b/libs/Prism/include/Prism/mesh.hpp @@ -21,7 +21,7 @@ class PRISM_EXPORT Mesh : public Object { private: std::vector> points; - std::vector>> mesh; + std::vector mesh; std::shared_ptr material; }; diff --git a/libs/Prism/include/Prism/scene.hpp b/libs/Prism/include/Prism/scene.hpp index 791ca63..cdbd787 100644 --- a/libs/Prism/include/Prism/scene.hpp +++ b/libs/Prism/include/Prism/scene.hpp @@ -13,17 +13,6 @@ namespace Prism { -PRISM_EXPORT inline int convert_color(double f) { - return static_cast(255.999 * f); -} - -PRISM_EXPORT inline std::ostream& operator<<(std::ostream& os, const Color& color) { - os << static_cast(convert_color(color.r)) << " " - << static_cast(convert_color(color.g)) << " " - << static_cast(convert_color(color.b)); - return os; -} - PRISM_EXPORT std::filesystem::path generate_filename(); class PRISM_EXPORT Scene { diff --git a/libs/Prism/include/Prism/triangle.hpp b/libs/Prism/include/Prism/triangle.hpp index 2d6a10a..5211cd5 100644 --- a/libs/Prism/include/Prism/triangle.hpp +++ b/libs/Prism/include/Prism/triangle.hpp @@ -4,142 +4,46 @@ #include "prism_export.h" #include "Prism/objects.hpp" #include "Prism/point.hpp" -#include "Prism/vector.hpp" -#include "Prism/ray.hpp" -#include "Prism/utils.hpp" #include "Prism/material.hpp" -#include +#include +#include -namespace Prism { +namespace Prism { + class Matrix; // Forward declaration for Matrix + class Ray; // Forward declaration for Ray + class Point3; // Forward declaration for Point3 + struct HitRecord; // Forward declaration for HitRecord -template class PRISM_EXPORT Triangle : public Object { - static_assert(std::is_same::value || std::is_same::value || std::is_same>::value, "T must be Point3 , Point3& or std::shared_ptr"); +class PRISM_EXPORT Triangle : public Object { +public: + Triangle(Point3 p1, Point3 p2, Point3 p3, std::shared_ptr mat = std::make_shared()); - public: - Triangle(Point point1, Point point2, Point point3, std ::shared_ptr material = std::make_shared()): - point1(point1),point2(point2),point3(point3),material(std::move(material)){}; + Point3 getPoint1() const; + Point3 getPoint2() const; + Point3 getPoint3() const; - Point3 getPoint1() const{ - Point3 point; - if constexpr (std::is_same>::value) point = *point1; - else point = point1; - return point; - }; - - Point3 getPoint2() const{ - Point3 point; - if constexpr(std::is_same>::value) point = *point2; - else point = point2; - return point; - }; - - Point3 getPoint3() const{ - Point3 point; - if constexpr (std::is_same>::value) point = *point3; - else point = point3; - return point; - }; - - virtual bool hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) const override; - virtual bool hit_in_mesh(const Ray& ray, double t_min, double t_max, HitRecord& rec, const Matrix& mesh_transform, const Matrix& mesh_inverse, const Matrix& mesh_inverseTranspose) const; + bool hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) const override; +private: + Point3 point1, point2, point3; std::shared_ptr material; - private: - Point point1; - Point point2; - Point point3; }; - -template bool Triangle::hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) const { - // Passo 1: Criar um raio transformado. - Ray transformed_ray = ray.transform(inverseTransform); - - // Passo 2: Realizar o teste de colisão em espaço local (algoritmo Möller-Trumbore). - const double epsilon = std::numeric_limits::epsilon(); - const Vector3 ray_direction = transformed_ray.direction(); - - const Vector3 edge1 = this->getPoint2()- this->getPoint1(); - const Vector3 edge2 = this->getPoint3() - this->getPoint1(); - - const Vector3 h = ray_direction ^ edge2; - const double a = edge1 * h; - - if (a > -epsilon && a < epsilon) - return false; // Raio paralelo ao triângulo. - - const double f = 1.0 / a; - const Vector3 s = transformed_ray.origin() - this->getPoint1(); - const double u = f * (s * h); - - if (u < 0.0 || u > 1.0) - return false; - - const Vector3 q = s ^ edge1; - const double v = f * (ray_direction * q); - - if (v < 0.0 || u + v > 1.0) - return false; - - const double t = f * (edge2 * q); - if (t > t_min && t < t_max) { - rec.t = t; - rec.p = transform * transformed_ray.at(t); - Vector3 local_normal = edge1 ^ edge2; - Vector3 world_normal = (inverseTransposeTransform * local_normal).normalize(); - rec.set_face_normal(ray, world_normal); - rec.material = material; - return true; - } +class PRISM_EXPORT MeshTriangle { +public: + MeshTriangle(std::shared_ptr p1, std::shared_ptr p2, std::shared_ptr p3); + MeshTriangle(Point3 p1, Point3 p2, Point3 p3); + MeshTriangle(std::initializer_list points); - return false; -} + Point3 getPoint1() const; + Point3 getPoint2() const; + Point3 getPoint3() const; + bool hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) const; -template bool Triangle::hit_in_mesh(const Ray& ray, double t_min, double t_max, HitRecord& rec, const Matrix& mesh_transform, const Matrix& mesh_inverse, const Matrix& mesh_inverseTranspose) const { - // Passo 1: Criar um raio transformado. - Ray transformed_ray = ray.transform(mesh_inverse); - - // Passo 2: Realizar o teste de colisão em espaço local (algoritmo Möller-Trumbore). - const double epsilon = std::numeric_limits::epsilon(); - const Vector3 ray_direction = transformed_ray.direction(); - - const Vector3 edge1 = this->getPoint2()- this->getPoint1(); - const Vector3 edge2 = this->getPoint3() - this->getPoint1(); - - const Vector3 h = ray_direction ^ edge2; - const double a = edge1 * h; - - if (a > -epsilon && a < epsilon) - return false; // Raio paralelo ao triângulo. - - const double f = 1.0 / a; - const Vector3 s = transformed_ray.origin() - this->getPoint1(); - const double u = f * (s * h); - - if (u < 0.0 || u > 1.0) - return false; - - const Vector3 q = s ^ edge1; - const double v = f * (ray_direction * q); - - if (v < 0.0 || u + v > 1.0) - return false; - - const double t = f * (edge2 * q); - - if (t > t_min && t < t_max) { - rec.t = t; - rec.p = mesh_transform * transformed_ray.at(t); - Vector3 local_normal = edge1 ^ edge2; - Vector3 world_normal = (mesh_inverseTranspose * local_normal).normalize(); - rec.set_face_normal(ray, world_normal); - rec.material = material; - return true; - } - - return false; -} +private: + std::shared_ptr point1, point2, point3; +}; } // namespace Prism diff --git a/libs/Prism/src/matrix.cpp b/libs/Prism/src/matrix.cpp index be70b3d..26d1af7 100644 --- a/libs/Prism/src/matrix.cpp +++ b/libs/Prism/src/matrix.cpp @@ -268,7 +268,7 @@ Matrix Matrix::translation(int dimension, std::initializer_list values) Matrix t = identity(dimension + 1); auto val_it = values.begin(); - for (size_t i = 0; i < dimension; ++i) { + for (size_t i = 0; i < static_cast(dimension); ++i) { t.data_[i][dimension] = *val_it++; } return t; @@ -280,7 +280,7 @@ Matrix Matrix::scaling(int dimension, std::initializer_list values) { } Matrix s = identity(dimension + 1); auto val_it = values.begin(); - for (size_t i = 0; i < dimension; ++i) { + for (size_t i = 0; i < static_cast(dimension); ++i) { s.data_[i][i] = *val_it++; } return s; diff --git a/libs/Prism/src/mesh.cpp b/libs/Prism/src/mesh.cpp index 7547467..14172aa 100644 --- a/libs/Prism/src/mesh.cpp +++ b/libs/Prism/src/mesh.cpp @@ -1,4 +1,5 @@ #include "Prism/mesh.hpp" +#include "Prism/matrix.hpp" #include namespace Prism { @@ -10,13 +11,10 @@ Mesh::Mesh(ObjReader& reader):material(std::move(reader.curMaterial)){ points.emplace_back(std::make_shared(point[0],point[1],point[2])); } for(auto& triangle: reader.triangles){ - mesh.push_back( - Triangle>( + mesh.push_back({ points[triangle[0]], points[triangle[1]], - points[triangle[2]], - material - ) + points[triangle[2]]} ); } }; @@ -27,8 +25,12 @@ bool Mesh::hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) const bool hit_anything = false; rec.t = INFINITY; for (const auto& triangle : mesh) { - if (triangle.hit_in_mesh(ray, 0.001, rec.t, rec, transform, inverseTransform, inverseTransposeTransform)) { + if (triangle.hit(ray, 0.001, rec.t, rec)) { hit_anything = true; + rec.p = transform * ray.at(rec.t); + Vector3 world_normal = (inverseTransposeTransform * rec.normal).normalize(); + rec.set_face_normal(ray, world_normal); + rec.material = material; } } return hit_anything; diff --git a/libs/Prism/src/plane.cpp b/libs/Prism/src/plane.cpp index 693e69b..bfb8540 100644 --- a/libs/Prism/src/plane.cpp +++ b/libs/Prism/src/plane.cpp @@ -1,23 +1,23 @@ #include "Prism/plane.hpp" -#include "Prism/ray.hpp" -#include "Prism/utils.hpp" +#include "Prism/material.hpp" #include "Prism/point.hpp" +#include "Prism/ray.hpp" +#include "Prism/utils.hpp" #include "Prism/vector.hpp" -#include "Prism/material.hpp" #include namespace Prism { -Plane::Plane(Point3 point_on_plane, Vector3 normal, std::shared_ptrmaterial) - : point_on_plane(point_on_plane), normal(normal), material(std::move(material)) {} - +Plane::Plane(Point3 point_on_plane, Vector3 normal, std::shared_ptr material) + : point_on_plane(point_on_plane), normal(normal), material(std::move(material)) { +} bool Plane::hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) const { Ray transformed_ray = ray.transform(inverseTransform); double denominator = normal.dot(transformed_ray.direction()); double tolerance = 1e-6; - + if (std::abs(denominator) <= tolerance) { return false; } @@ -29,11 +29,11 @@ bool Plane::hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) cons } rec.t = t; - rec.p = transform * transformed_ray.at(t); // Ponto de volta para o espaço global + rec.p = ray.at(t); // Ponto de volta para o espaço global Vector3 outward_normal_world = (inverseTransposeTransform * this->normal).normalize(); rec.set_face_normal(ray, outward_normal_world); // Usa o raio original rec.material = material; - + return true; } diff --git a/libs/Prism/src/scene.cpp b/libs/Prism/src/scene.cpp index 6d6c43d..13ca386 100644 --- a/libs/Prism/src/scene.cpp +++ b/libs/Prism/src/scene.cpp @@ -11,6 +11,17 @@ namespace Prism { +PRISM_EXPORT int convert_color(double f) { + return static_cast(255.999 * f); +} + +PRISM_EXPORT std::ostream& operator<<(std::ostream& os, const Color& color) { + os << static_cast(convert_color(color.r)) << " " + << static_cast(convert_color(color.g)) << " " + << static_cast(convert_color(color.b)); + return os; +} + Scene::Scene(Camera camera) : camera_(std::move(camera)) {} void Scene::addObject(std::unique_ptr object) { diff --git a/libs/Prism/src/sphere.cpp b/libs/Prism/src/sphere.cpp index 1e18c61..494b250 100644 --- a/libs/Prism/src/sphere.cpp +++ b/libs/Prism/src/sphere.cpp @@ -43,9 +43,9 @@ bool Sphere::hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) con Point3 local_hit_point = transformed_ray.at(rec.t); Vector3 outward_normal_local = (local_hit_point - center) / radius; - rec.p = transform * local_hit_point; // Ponto de volta para o espaço global + rec.p = transform * local_hit_point; Vector3 outward_normal_world = (inverseTransposeTransform * outward_normal_local).normalize(); - rec.set_face_normal(ray, outward_normal_world); // Usa o raio original (em espaço global) + rec.set_face_normal(ray, outward_normal_world); rec.material = material; return true; diff --git a/libs/Prism/src/triangle.cpp b/libs/Prism/src/triangle.cpp new file mode 100644 index 0000000..2f6a6af --- /dev/null +++ b/libs/Prism/src/triangle.cpp @@ -0,0 +1,134 @@ +#include "Prism/triangle.hpp" +#include "Prism/matrix.hpp" +#include "Prism/point.hpp" +#include "Prism/ray.hpp" +#include + +namespace Prism { + +Triangle::Triangle(Point3 p1, Point3 p2, Point3 p3, std::shared_ptr mat) + : point1(p1), point2(p2), point3(p3), material(std::move(mat)) { +} + +Point3 Triangle::getPoint1() const { + return point1; +} +Point3 Triangle::getPoint2() const { + return point2; +} +Point3 Triangle::getPoint3() const { + return point3; +} + +bool Triangle::hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) const { + const Ray transformed_ray = ray.transform(inverseTransform); + + const double epsilon = 1e-8; + const Vector3 edge1 = this->getPoint2() - this->getPoint1(); + const Vector3 edge2 = this->getPoint3() - this->getPoint1(); + + const Vector3 h = transformed_ray.direction() ^ edge2; + const double a = edge1 * h; + + if (a > -epsilon && a < epsilon) { + return false; + } + + const double f = 1.0 / a; + const Vector3 s = transformed_ray.origin() - this->getPoint1(); + const double u = f * (s * h); + + if (u < 0.0 || u > 1.0) { + return false; + } + + const Vector3 q = s ^ edge1; + const double v = f * (transformed_ray.direction() * q); + + if (v < 0.0 || u + v > 1.0) { + return false; + } + + const double t = f * (edge2 * q); + + if (t > t_min && t < t_max) { + rec.t = t; + rec.p = ray.at(t); + + Vector3 local_normal = edge1 ^ edge2; + Vector3 world_normal = (inverseTransposeTransform * local_normal).normalize(); + rec.set_face_normal(ray, world_normal); + + rec.material = material; + return true; + } + + return false; +} + +MeshTriangle::MeshTriangle(std::shared_ptr p1, std::shared_ptr p2, + std::shared_ptr p3) + : point1(std::move(p1)), point2(std::move(p2)), point3(std::move(p3)) { +} + +MeshTriangle::MeshTriangle(Point3 p1, Point3 p2, Point3 p3) + : point1(std::make_shared(p1)), point2(std::make_shared(p2)), + point3(std::make_shared(p3)) { +} + +MeshTriangle::MeshTriangle(std::initializer_list points) { + if (points.size() != 3) { + throw std::invalid_argument("MeshTriangle requires exactly three points."); + } + auto it = points.begin(); + point1 = std::make_shared(*it++); + point2 = std::make_shared(*it++); + point3 = std::make_shared(*it); +} + +Point3 MeshTriangle::getPoint1() const { + return *point1; +} +Point3 MeshTriangle::getPoint2() const { + return *point2; +} +Point3 MeshTriangle::getPoint3() const { + return *point3; +} + +bool MeshTriangle::hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) const { + + const double epsilon = 1e-8; + const Vector3 ray_direction = ray.direction(); + const Vector3 edge1 = this->getPoint2() - this->getPoint1(); + const Vector3 edge2 = this->getPoint3() - this->getPoint1(); + const Vector3 h = ray_direction ^ edge2; + const double a = edge1 * h; + + if (a > -epsilon && a < epsilon) + return false; + + const double f = 1.0 / a; + const Vector3 s = ray.origin() - this->getPoint1(); + const double u = f * (s * h); + + if (u < 0.0 || u > 1.0) + return false; + + const Vector3 q = s ^ edge1; + const double v = f * (ray_direction * q); + + if (v < 0.0 || u + v > 1.0) + return false; + + const double t = f * (edge2 * q); + + if (t > t_min && t < t_max) { + rec.t = t; + rec.normal = edge1 ^ edge2; + return true; + } + return false; +} + +} // namespace Prism \ No newline at end of file diff --git a/tests/src/MatrixTest.cpp b/tests/src/MatrixTest.cpp index 142e666..97a268b 100644 --- a/tests/src/MatrixTest.cpp +++ b/tests/src/MatrixTest.cpp @@ -2,6 +2,11 @@ #include "Prism/vector.hpp" #include "TestHelpers.hpp" #include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif using Prism::Matrix; using Prism::Vector3; diff --git a/tests/src/TransformationsTest.cpp b/tests/src/TransformationsTest.cpp index 52cb213..02f9c28 100644 --- a/tests/src/TransformationsTest.cpp +++ b/tests/src/TransformationsTest.cpp @@ -1,5 +1,10 @@ #include #include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif #include "Prism.hpp" #include "TestHelpers.hpp" // Para comparações de ponto flutuante @@ -87,7 +92,7 @@ TEST(TransformationsTest, PlaneHitWithRotation) { // Testa a colisão com um triângulo transladado TEST(TransformationsTest, TriangleHitWithTranslation) { // Arrange - auto tri = std::make_shared>( + auto tri = std::make_shared( Point3(0, 0, 0), Point3(1, 0, 0), Point3(0, 1, 0), From 892e013895dd41202b4347c68e12df3a9f4e504a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Guimar=C3=A3es?= Date: Tue, 8 Jul 2025 01:54:51 -0300 Subject: [PATCH 07/10] fix(ci): Specify configuration for ctest in debug preset --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index a821529..672747b 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -38,4 +38,4 @@ jobs: - name: Run tests if: matrix.preset == 'debug' working-directory: build/${{ matrix.preset }} - run: ctest --output-on-failure + run: ctest -C debug --output-on-failure From 2f9732f1c52ec35ce658622a1b17e12fc476faad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Guimar=C3=A3es?= Date: Tue, 8 Jul 2025 02:03:34 -0300 Subject: [PATCH 08/10] refactor(geometry): roolback to transformation for clarity --- libs/Prism/src/plane.cpp | 2 +- libs/Prism/src/triangle.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/Prism/src/plane.cpp b/libs/Prism/src/plane.cpp index bfb8540..98e8260 100644 --- a/libs/Prism/src/plane.cpp +++ b/libs/Prism/src/plane.cpp @@ -29,7 +29,7 @@ bool Plane::hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) cons } rec.t = t; - rec.p = ray.at(t); // Ponto de volta para o espaço global + rec.p = transform * transformed_ray.at(t); // Ponto de volta para o espaço global Vector3 outward_normal_world = (inverseTransposeTransform * this->normal).normalize(); rec.set_face_normal(ray, outward_normal_world); // Usa o raio original rec.material = material; diff --git a/libs/Prism/src/triangle.cpp b/libs/Prism/src/triangle.cpp index 2f6a6af..f3e487a 100644 --- a/libs/Prism/src/triangle.cpp +++ b/libs/Prism/src/triangle.cpp @@ -53,7 +53,7 @@ bool Triangle::hit(const Ray& ray, double t_min, double t_max, HitRecord& rec) c if (t > t_min && t < t_max) { rec.t = t; - rec.p = ray.at(t); + rec.p = transform * transformed_ray.at(t); Vector3 local_normal = edge1 ^ edge2; Vector3 world_normal = (inverseTransposeTransform * local_normal).normalize(); From 74427137c05fe1cd13a831911b74e126b275f257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Guimar=C3=A3es?= Date: Tue, 8 Jul 2025 02:04:07 -0300 Subject: [PATCH 09/10] fix(color): Correct floating-point division in Color constructor for accuracy --- libs/Prism/src/color.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/Prism/src/color.cpp b/libs/Prism/src/color.cpp index 5c2be0a..cb36f3b 100644 --- a/libs/Prism/src/color.cpp +++ b/libs/Prism/src/color.cpp @@ -4,7 +4,7 @@ namespace Prism { Color::Color() : r(0), g(0), b(0) {} Color::Color(double red, double green, double blue) : r(red), g(green), b(blue) {} -Color::Color(int red, int green, int blue): r(static_cast(red) / 255.0f), g(static_cast(green) / 255.0f), b(static_cast(blue) / 255.0f) {} +Color::Color(int red, int green, int blue): r(static_cast(red) / 255.0), g(static_cast(green) / 255.0), b(static_cast(blue) / 255.0) {} Color::Color(const Color& other) : r(other.r), g(other.g), b(other.b) {} } // namespace Prism \ No newline at end of file From 9708dc8e440113cd55ec65af79bdd8d5777d5a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Guimar=C3=A3es?= Date: Tue, 8 Jul 2025 02:05:53 -0300 Subject: [PATCH 10/10] refactor(style): Replace with for consistency --- tests/src/MatrixTest.cpp | 10 ++++++---- vendor/ObjReader/include/ObjReader/Vector.hpp | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/src/MatrixTest.cpp b/tests/src/MatrixTest.cpp index 97a268b..a848d74 100644 --- a/tests/src/MatrixTest.cpp +++ b/tests/src/MatrixTest.cpp @@ -1,8 +1,8 @@ #include "Prism/matrix.hpp" #include "Prism/vector.hpp" #include "TestHelpers.hpp" +#include #include -#include #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -217,7 +217,8 @@ TEST(MatrixTest, MultiplyByPoint) { AssertPointAlmostEqual(id3 * p, p); // Teste com escala 3x3 - Prism::Matrix scale3 = Prism::Matrix::scaling(3, {2.0, 3.0, 4.0}); // Isso criará uma matriz 4x4, vamos criar uma 3x3 manualmente + Prism::Matrix scale3 = Prism::Matrix::scaling( + 3, {2.0, 3.0, 4.0}); // Isso criará uma matriz 4x4, vamos criar uma 3x3 manualmente Prism::Matrix manual_scale3 = {{2.0, 0.0, 0.0}, {0.0, 3.0, 0.0}, {0.0, 0.0, 4.0}}; Prism::Point3 expected_scaled_p(2.0, 6.0, 12.0); AssertPointAlmostEqual(manual_scale3 * p, expected_scaled_p); @@ -235,7 +236,7 @@ TEST(MatrixTest, MultiplyByPoint) { // Teste com transformação combinada (escala e depois translação) Prism::Matrix s = Prism::Matrix::scaling(3, {2.0, 2.0, 2.0}); Prism::Matrix t = Prism::Matrix::translation(3, {1.0, 1.0, 1.0}); - Prism::Matrix combined = t * s; // Aplica escala, depois translação + Prism::Matrix combined = t * s; // Aplica escala, depois translação Prism::Point3 expected_combined_p(3.0, 5.0, 7.0); // p*2 -> (2,4,6), +1 -> (3,5,7) AssertPointAlmostEqual(combined * p, expected_combined_p); } @@ -269,7 +270,8 @@ TEST(MatrixTest, MultiplyByVector) { AssertVectorAlmostEqual(combined * v, expected_combined_v); // Teste com rotação - Prism::Matrix rot = Prism::Matrix::rotation3d(M_PI / 2.0, {0, 1, 0}); // Rotação de 90 graus em torno do eixo Y + Prism::Matrix rot = + Prism::Matrix::rotation3d(M_PI / 2.0, {0, 1, 0}); // Rotação de 90 graus em torno do eixo Y Prism::Vector3 rotated_v = rot * Prism::Vector3(1, 0, 0); AssertVectorAlmostEqual(rotated_v, Prism::Vector3(0, 0, -1), 1e-9); } diff --git a/vendor/ObjReader/include/ObjReader/Vector.hpp b/vendor/ObjReader/include/ObjReader/Vector.hpp index 0f533d0..38c6bc9 100644 --- a/vendor/ObjReader/include/ObjReader/Vector.hpp +++ b/vendor/ObjReader/include/ObjReader/Vector.hpp @@ -1,7 +1,7 @@ #ifndef VECTORHEADER #define VECTORHEADER #include -#include +#include /* Classe de vetores.