diff --git a/raytracing/CMakeLists.txt b/raytracing/CMakeLists.txt index dfbc7c6..22bde6f 100644 --- a/raytracing/CMakeLists.txt +++ b/raytracing/CMakeLists.txt @@ -21,11 +21,13 @@ add_library(raytracing src/lights/point-light.cpp + src/patterns/pattern.cpp + src/patterns/stripe-pattern.cpp + src/renderer/camera.cpp src/renderer/canvas.cpp src/renderer/material.cpp src/renderer/ray.cpp - src/renderer/stripe-pattern.cpp src/renderer/world.cpp src/shapes/plane.cpp diff --git a/raytracing/include/raytracing.h b/raytracing/include/raytracing.h index d1bb314..c50b178 100644 --- a/raytracing/include/raytracing.h +++ b/raytracing/include/raytracing.h @@ -35,11 +35,12 @@ #include "lights/point-light.h" +#include "patterns/stripe-pattern.h" + #include "renderer/camera.h" #include "renderer/canvas.h" #include "renderer/material.h" #include "renderer/ray.h" -#include "renderer/stripe-pattern.h" #include "renderer/world.h" #include "shapes/plane.h" diff --git a/raytracing/src/patterns/pattern.cpp b/raytracing/src/patterns/pattern.cpp new file mode 100644 index 0000000..51845c0 --- /dev/null +++ b/raytracing/src/patterns/pattern.cpp @@ -0,0 +1,68 @@ +/*! + * pattern.cpp + * + * Copyright (c) 2024, NADAL Jean-Baptiste. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * @Author: NADAL Jean-Baptiste + * @Date: 29/02/2024 + * + */ + +// This is an independent project of an individual developer. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com + +/* ------------------------------------------------------------------------- */ + +#include + +#include "core/common.h" + +#include "pattern.h" + +using namespace Raytracer; + +/* ------------------------------------------------------------------------- */ + +Pattern::Pattern(void) +{ + m_transform = Matrix::identity(); +} + +/* ------------------------------------------------------------------------- */ + +const Matrix &Pattern::transform(void) const +{ + return m_transform; +} + +/* ------------------------------------------------------------------------- */ + +void Pattern::set_transform(const Matrix &a_transform_matrix) +{ + m_transform = a_transform_matrix; +} + +/* ------------------------------------------------------------------------- */ + +const Color Pattern::pattern_at_shape(Shape *a_shape, const Tuple &a_world_point) const +{ + Tuple the_objet_point = a_shape->transform().inverse() * a_world_point; + Tuple the_pattern_point = m_transform.inverse() * the_objet_point; + + return pattern_at(the_pattern_point); +} diff --git a/raytracing/src/patterns/pattern.h b/raytracing/src/patterns/pattern.h new file mode 100644 index 0000000..7ee978c --- /dev/null +++ b/raytracing/src/patterns/pattern.h @@ -0,0 +1,58 @@ +/*! + * pattern.h + * + * Copyright (c) 2024, NADAL Jean-Baptiste. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * @Author: NADAL Jean-Baptiste + * @Date: 29/02/2024 + * + */ + +#ifndef _RAYTRACER_PATTERN_H +#define _RAYTRACER_PATTERN_H + +/* ------------------------------------------------------------------------- */ + +#include "core/color.h" +#include "core/matrix.h" +#include "core/tuple.h" + +#include "shapes/shape.h" + +/* ------------------------------------------------------------------------- */ + +namespace Raytracer +{ + class Pattern + { + public: + Pattern(void); + + const Matrix &transform(void) const; + void set_transform(const Matrix &a_transform_matrix); + + const Color pattern_at_shape(Shape *a_shape, const Tuple &a_world_point) const; + + virtual const Color pattern_at(const Tuple &a_point) const = 0; + + private: + Matrix m_transform; + }; +}; // namespace Raytracer + +#endif /* _RAYTRACER_PATTERN_H */ diff --git a/raytracing/src/renderer/stripe-pattern.cpp b/raytracing/src/patterns/stripe-pattern.cpp similarity index 74% rename from raytracing/src/renderer/stripe-pattern.cpp rename to raytracing/src/patterns/stripe-pattern.cpp index 6794d6f..6939758 100644 --- a/raytracing/src/renderer/stripe-pattern.cpp +++ b/raytracing/src/patterns/stripe-pattern.cpp @@ -38,9 +38,8 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -StripePattern::StripePattern(const Color &a_color_a, const Color &a_color_b) : m_a(a_color_a), m_b(a_color_b) +StripePattern::StripePattern(const Color &a_color_a, const Color &a_color_b) : Pattern(), m_a(a_color_a), m_b(a_color_b) { - m_transform = Matrix::identity(); } /* ------------------------------------------------------------------------- */ @@ -66,27 +65,10 @@ const Color &StripePattern::b(void) const /* ------------------------------------------------------------------------- */ -const Color &StripePattern::stripe_at(const Tuple &a_point) const +const Color StripePattern::pattern_at(const Tuple &a_point) const { if (((int)std::floor(a_point.x()) % 2) == 0) return m_a; return m_b; } - -/* ------------------------------------------------------------------------- */ - -void StripePattern::set_pattern_transform(const Matrix &a_transform_matrix) -{ - m_transform = a_transform_matrix; -} - -/* ------------------------------------------------------------------------- */ - -const Color &StripePattern::stripe_at_object(Shape *an_object, const Tuple &a_world_point) const -{ - Tuple the_objet_point = an_object->transform().inverse() * a_world_point; - Tuple the_pattern_point = m_transform.inverse() * the_objet_point; - - return stripe_at(the_pattern_point); -} diff --git a/raytracing/src/renderer/stripe-pattern.h b/raytracing/src/patterns/stripe-pattern.h similarity index 85% rename from raytracing/src/renderer/stripe-pattern.h rename to raytracing/src/patterns/stripe-pattern.h index c30e107..7144660 100644 --- a/raytracing/src/renderer/stripe-pattern.h +++ b/raytracing/src/patterns/stripe-pattern.h @@ -32,13 +32,15 @@ #include "core/matrix.h" #include "core/tuple.h" +#include "patterns/pattern.h" + #include "shapes/shape.h" /* ------------------------------------------------------------------------- */ namespace Raytracer { - class StripePattern + class StripePattern : public Pattern { public: StripePattern(const Color &a_color_a, const Color &a_color_b); @@ -48,14 +50,9 @@ namespace Raytracer const Color &a(void) const; const Color &b(void) const; - const Color &stripe_at(const Tuple &a_point) const; - - void set_pattern_transform(const Matrix &a_transform_matrix); - - const Color &stripe_at_object(Shape *an_object, const Tuple &a_world_point) const; + const Color pattern_at(const Tuple &a_point) const override; private: - Matrix m_transform; Color m_a; Color m_b; }; diff --git a/raytracing/src/renderer/material.cpp b/raytracing/src/renderer/material.cpp index c4a3769..6d7a3f4 100644 --- a/raytracing/src/renderer/material.cpp +++ b/raytracing/src/renderer/material.cpp @@ -33,7 +33,7 @@ #include "core/common.h" #include "shapes/shape.h" -#include "renderer/stripe-pattern.h" +#include "patterns/pattern.h" #include "material.h" @@ -133,14 +133,14 @@ void Material::set_shininess(double a_value) /* ------------------------------------------------------------------------- */ -const StripePattern *Material::pattern(void) const +const Pattern *Material::pattern(void) const { return m_pattern; } /* ------------------------------------------------------------------------- */ -void Material::set_pattern(StripePattern *a_pattern) +void Material::set_pattern(Pattern *a_pattern) { m_pattern = a_pattern; } @@ -156,7 +156,7 @@ Color Material::lighting(Shape *an_object, const PointLight &a_light, const Tupl if (m_pattern != nullptr) { - the_color = m_pattern->stripe_at_object(an_object, a_point); + the_color = m_pattern->pattern_at_shape(an_object, a_point); } else { diff --git a/raytracing/src/renderer/material.h b/raytracing/src/renderer/material.h index 06ba290..58464bd 100644 --- a/raytracing/src/renderer/material.h +++ b/raytracing/src/renderer/material.h @@ -38,7 +38,7 @@ namespace Raytracer { class Shape; - class StripePattern; + class Pattern; class Material { @@ -62,8 +62,8 @@ namespace Raytracer const double &shininess(void) const; void set_shininess(double a_value); - const StripePattern *pattern(void) const; - void set_pattern(StripePattern *a_pattern); + const Pattern *pattern(void) const; + void set_pattern(Pattern *a_pattern); Color lighting(Shape *an_object, const PointLight &a_light, const Tuple &a_point, const Tuple &an_eyev, const Tuple &a_normalv, bool is_shadowed) const; @@ -74,7 +74,7 @@ namespace Raytracer double m_diffuse; double m_specular; double m_shininess; - StripePattern *m_pattern; + Pattern *m_pattern; }; }; // namespace Raytracer diff --git a/tests/10_patterns.cpp b/tests/10_patterns.cpp index cecb724..f442ea4 100644 --- a/tests/10_patterns.cpp +++ b/tests/10_patterns.cpp @@ -33,9 +33,22 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ +class TestPattern : public Pattern +{ +public: + TestPattern(void) = default; + + const Color pattern_at(const Tuple &a_point) const override + { + return Color(a_point.x(), a_point.y(), a_point.z()); + } +}; + +/* ------------------------------------------------------------------------- */ + SCENARIO("Creating a stripe pattern", "[features/patterns.feature]") { - GIVEN("pattern <- strip_pattern(white, black)") + GIVEN("pattern <- stripe_pattern(white, black)") { StripePattern pattern(Color::White(), Color::Black()); THEN("pattern.a = white") @@ -53,20 +66,20 @@ SCENARIO("Creating a stripe pattern", "[features/patterns.feature]") SCENARIO("A stripe pattern is constant in y", "[features/patterns.feature]") { - GIVEN("pattern <- strip_pattern(white, black)") + GIVEN("pattern <- stripe_pattern(white, black)") { StripePattern pattern(Color::White(), Color::Black()); - THEN("stripe_at(pattern, point(0, 0, 0) = white") + THEN("pattern_at(pattern, point(0, 0, 0) = white") { - REQUIRE(pattern.stripe_at(Tuple::Point(0, 0, 0)) == Color::White()); + REQUIRE(pattern.pattern_at(Tuple::Point(0, 0, 0)) == Color::White()); } - AND_THEN("stripe_at(pattern, point(0, 1, 0) = white") + AND_THEN("pattern_at(pattern, point(0, 1, 0) = white") { - REQUIRE(pattern.stripe_at(Tuple::Point(0, 1, 0)) == Color::White()); + REQUIRE(pattern.pattern_at(Tuple::Point(0, 1, 0)) == Color::White()); } - AND_THEN("stripe_at(pattern, point(0, 2, 0) = white") + AND_THEN("pattern_at(pattern, point(0, 2, 0) = white") { - REQUIRE(pattern.stripe_at(Tuple::Point(0, 2, 0)) == Color::White()); + REQUIRE(pattern.pattern_at(Tuple::Point(0, 2, 0)) == Color::White()); } } } @@ -75,20 +88,20 @@ SCENARIO("A stripe pattern is constant in y", "[features/patterns.feature]") SCENARIO("A stripe pattern is constant in z", "[features/patterns.feature]") { - GIVEN("pattern <- strip_pattern(white, black)") + GIVEN("pattern <- stripe_pattern(white, black)") { StripePattern pattern(Color::White(), Color::Black()); - THEN("stripe_at(pattern, point(0, 0, 0) = white") + THEN("pattern_at(pattern, point(0, 0, 0) = white") { - REQUIRE(pattern.stripe_at(Tuple::Point(0, 0, 0)) == Color::White()); + REQUIRE(pattern.pattern_at(Tuple::Point(0, 0, 0)) == Color::White()); } - AND_THEN("stripe_at(pattern, point(0, 0, 1) = white") + AND_THEN("pattern_at(pattern, point(0, 0, 1) = white") { - REQUIRE(pattern.stripe_at(Tuple::Point(0, 0, 1)) == Color::White()); + REQUIRE(pattern.pattern_at(Tuple::Point(0, 0, 1)) == Color::White()); } - AND_THEN("stripe_at(pattern, point(0, 0, 2) = white") + AND_THEN("pattern_at(pattern, point(0, 0, 2) = white") { - REQUIRE(pattern.stripe_at(Tuple::Point(0, 0, 2)) == Color::White()); + REQUIRE(pattern.pattern_at(Tuple::Point(0, 0, 2)) == Color::White()); } } } @@ -97,32 +110,32 @@ SCENARIO("A stripe pattern is constant in z", "[features/patterns.feature]") SCENARIO("A stripe pattern alternates in x", "[features/patterns.feature]") { - GIVEN("pattern <- strip_pattern(white, black)") + GIVEN("pattern <- stripe_pattern(white, black)") { StripePattern pattern(Color::White(), Color::Black()); - THEN("stripe_at(pattern, point(0, 0, 0) = white") + THEN("pattern_at(pattern, point(0, 0, 0) = white") { - REQUIRE(pattern.stripe_at(Tuple::Point(0, 0, 0)) == Color::White()); + REQUIRE(pattern.pattern_at(Tuple::Point(0, 0, 0)) == Color::White()); } - AND_THEN("stripe_at(pattern, point(0.9, 0, 0) = white") + AND_THEN("pattern_at(pattern, point(0.9, 0, 0) = white") { - REQUIRE(pattern.stripe_at(Tuple::Point(0.9, 0, 0)) == Color::White()); + REQUIRE(pattern.pattern_at(Tuple::Point(0.9, 0, 0)) == Color::White()); } - AND_THEN("stripe_at(pattern, point(1, 0, 0) = black") + AND_THEN("pattern_at(pattern, point(1, 0, 0) = black") { - REQUIRE(pattern.stripe_at(Tuple::Point(1, 0, 0)) == Color::Black()); + REQUIRE(pattern.pattern_at(Tuple::Point(1, 0, 0)) == Color::Black()); } - AND_THEN("stripe_at(pattern, point(-0.1, 0, 0) = black") + AND_THEN("pattern_at(pattern, point(-0.1, 0, 0) = black") { - REQUIRE(pattern.stripe_at(Tuple::Point(-0.1, 0, 0)) == Color::Black()); + REQUIRE(pattern.pattern_at(Tuple::Point(-0.1, 0, 0)) == Color::Black()); } - AND_THEN("stripe_at(pattern, point(-1, 0, 0) = black") + AND_THEN("pattern_at(pattern, point(-1, 0, 0) = black") { - REQUIRE(pattern.stripe_at(Tuple::Point(-1, 0, 0)) == Color::Black()); + REQUIRE(pattern.pattern_at(Tuple::Point(-1, 0, 0)) == Color::Black()); } - AND_THEN("stripe_at(pattern, point(-1.1, 0, 0) = white") + AND_THEN("pattern_at(pattern, point(-1.1, 0, 0) = white") { - REQUIRE(pattern.stripe_at(Tuple::Point(-1.1, 0, 0)) == Color::White()); + REQUIRE(pattern.pattern_at(Tuple::Point(-1.1, 0, 0)) == Color::White()); } } } @@ -135,7 +148,7 @@ SCENARIO("Lightning with a pattern applied", "[features/materials.feature]") { Sphere object; Material m; - GIVEN("m.pattern <- strip_pattern(color(1, 1, 1), color(0, 0, 0))") + GIVEN("m.pattern <- stripe_pattern(color(1, 1, 1), color(0, 0, 0))") { m.set_pattern(new StripePattern(Color(1, 1, 1), Color(0, 0, 0))); AND_GIVEN("m.ambient <- 1") @@ -198,9 +211,9 @@ SCENARIO("Stripes with an object transformation", "[features/patterns.feature]") AND_GIVEN("pattern <- stripe_pattern(white, black)") { StripePattern pattern(Color::White(), Color::Black()); - WHEN("c <- stripe_at_object(pattern, object, point(1.5, 0, 0))") + WHEN("c <- pattern_at_shape(pattern, object, point(1.5, 0, 0))") { - Color c = pattern.stripe_at_object(&object, Tuple::Point(1.5, 0, 0)); + Color c = pattern.pattern_at_shape(&object, Tuple::Point(1.5, 0, 0)); THEN("c = white") { REQUIRE(c == Color::White()); @@ -223,10 +236,10 @@ SCENARIO("Stripes with an pattern transformation", "[features/patterns.feature]" StripePattern pattern(Color::White(), Color::Black()); AND_GIVEN("set_transform(pattern, scaling(2, 2, 2))") { - pattern.set_pattern_transform(Matrix::scaling(2, 2, 2)); - WHEN("c <- stripe_at_object(pattern, object, point(1.5, 0, 0))") + pattern.set_transform(Matrix::scaling(2, 2, 2)); + WHEN("c <- pattern_at_shape(pattern, object, point(1.5, 0, 0))") { - Color c = pattern.stripe_at_object(&object, Tuple::Point(1.5, 0, 0)); + Color c = pattern.pattern_at_shape(&object, Tuple::Point(1.5, 0, 0)); THEN("c = white") { REQUIRE(c == Color::White()); @@ -252,10 +265,10 @@ SCENARIO("Stripes with both an object and pattern transformation", "[features/pa StripePattern pattern(Color::White(), Color::Black()); AND_GIVEN("set_pattern_transform(pattern, translation(0.5, 0, 0)))") { - pattern.set_pattern_transform(Matrix::translation(0.5, 0, 0)); - WHEN("c <- stripe_at_object(pattern, object, point(2.5, 0, 0))") + pattern.set_transform(Matrix::translation(0.5, 0, 0)); + WHEN("c <- pattern_at_object(pattern, object, point(2.5, 0, 0))") { - Color c = pattern.stripe_at_object(&object, Tuple::Point(1.5, 0, 0)); + Color c = pattern.pattern_at_shape(&object, Tuple::Point(1.5, 0, 0)); THEN("c = white") { REQUIRE(c == Color::White()); @@ -266,3 +279,117 @@ SCENARIO("Stripes with both an object and pattern transformation", "[features/pa } } } + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The default pattern transformation", "[features/patterns.feature]") +{ + GIVEN("pattern <- test_pattern()") + { + TestPattern pattern; + THEN("pattern.transform = identity_matrix") + { + REQUIRE(pattern.transform() == Matrix::identity()); + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Assigning a transformation to the pattern", "[features/patterns.feature]") +{ + GIVEN("pattern <- test_pattern()") + { + TestPattern pattern; + WHEN("set_pattern_transform(pattern, translation(1,2,3))") + { + pattern.set_transform(Matrix::translation(1, 2, 3)); + THEN("pattern.transform = translation(1, 2, 3)") + { + REQUIRE(pattern.transform() == Matrix::translation(1, 2, 3)); + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("A pattern with an object transformation", "[features/patterns.feature]") +{ + GIVEN("shape <- sphere()") + { + Sphere shape; + AND_GIVEN("set_transform(shape, scaling(2, 2, 2))") + { + shape.set_transform(Matrix::scaling(2, 2, 2)); + AND_GIVEN("pattern <- test_pattern()") + { + TestPattern pattern; + WHEN("Color c <- pattern_at_shape(pattern, shape, point(2, 3, 4))") + { + Color c = pattern.pattern_at_shape(&shape, Tuple::Point(2, 3, 4)); + THEN("c = color(1, 1.5, 2)") + { + REQUIRE(c == Color(1, 1.5, 2)); + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("A pattern with a pattern transformation", "[features/patterns.feature]") +{ + GIVEN("shape <- sphere()") + { + Sphere shape; + AND_GIVEN("pattern <- test_pattern()") + { + TestPattern pattern; + AND_GIVEN("set_pattern_transform(pattern, scaling(2, 2, 2))") + { + pattern.set_transform(Matrix::scaling(2, 2, 2)); + WHEN("c <- pattern_at_shape(pattern, shape, point(2, 3, 4))") + { + Color c = pattern.pattern_at_shape(&shape, Tuple::Point(2, 3, 4)); + THEN("c = color(1, 1.5, 2)") + { + REQUIRE(c == Color(1, 1.5, 2)); + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("A pattern with both an object and a pattern transformation", "[features/patterns.feature]") +{ + GIVEN("shape <- sphere()") + { + Sphere shape; + AND_GIVEN("set_transform(shape, scaling(2, 2, 2))") + { + shape.set_transform(Matrix::scaling(2, 2, 2)); + AND_GIVEN("pattern <- test_pattern()") + { + TestPattern pattern; + AND_GIVEN("set_pattern_transform(pattern, translation(0.5, 1, 1.5))") + { + pattern.set_transform(Matrix::translation(0.5, 1, 1.5)); + WHEN("c <- pattern_at_shape(pattern, shape, point(2.5, 3, 3.5))") + { + Color c = pattern.pattern_at_shape(&shape, Tuple::Point(2.5, 3, 3.5)); + THEN("c = color(0.75, 0.5, 0.25)") + { + REQUIRE(c == Color(0.75, 0.5, 0.25)); + } + } + } + } + } + } +}