diff --git a/README.md b/README.md index 1b54a96..a8e7723 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,6 @@ The Web Site of the book: http://raytracerchallenge.com/ | :------------------------: | :------------------------: | | ![05](data/chapter_05.png) | ![06](data/chapter_06.png) | -| Chapiter 07 | Chapiter 08 | -| :------------------------: | :----------------------------: | -| ![07](data/chapter_07.png) | ![08](data/chapter_08.png) | +| Chapiter 07 | Chapiter 08 | Chapiter 09 | +|:------------------------: | :------------------------: | :----------------------------: | +|![07](data/chapter_07.png) | ![08](data/chapter_08.png) | ![09](data/chapter_09.png) | diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 7636efd..21c1b3e 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -13,3 +13,6 @@ target_link_libraries(chapter_06 PRIVATE raytracing gcov) add_executable(chapter_07 chapter_07.cpp) target_link_libraries(chapter_07 PRIVATE raytracing gcov) + +add_executable(chapter_09 chapter_09.cpp) +target_link_libraries(chapter_09 PRIVATE raytracing gcov) \ No newline at end of file diff --git a/apps/chapter_09.cpp b/apps/chapter_09.cpp new file mode 100644 index 0000000..c7a05df --- /dev/null +++ b/apps/chapter_09.cpp @@ -0,0 +1,101 @@ +/*! + * chapter_09.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: 26/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 + +#include + +/* ------------------------------------------------------------------------- */ + +using namespace Raytracer; +using namespace std; + +/* ------------------------------------------------------------------------- */ + +int main(void) +{ + World the_world; + Camera the_camera; + Canvas the_canvas; + Plane *the_floor; + Sphere *the_middle, *the_right, *the_left; + chrono::time_point the_start, the_end; + + printf("Chapter 09 example.\n"); + + // Floor is an extremely flattened sphere with a matte texture. + the_floor = new Plane(); + the_floor->material().set_color(Color(1, 0.9, 0.9)); + the_floor->material().set_specular(0); + the_world.add_object(the_floor); + + // The large sphere in the middle is a unit sphere, translated upward slightly and colored green. + the_middle = new Sphere(); + the_middle->set_transform(Matrix::translation(-0.5, 1, 0.5)); + the_middle->material().set_color(Color(0.1, 1, 0.5)); + the_middle->material().set_diffuse(0.7); + the_middle->material().set_specular(0.3); + the_world.add_object(the_middle); + + // The smaller green sphere on the right is scaled in half + the_right = new Sphere(); + the_right->set_transform(Matrix::translation(1.5, 0.5, -0.5) * Matrix::scaling(0.5, 0.5, 0.5)); + the_right->material().set_color(Color(0.5, 1, 0.1)); + the_right->material().set_diffuse(0.7); + the_right->material().set_specular(0.3); + the_world.add_object(the_right); + + // The smallest sphere is scaled by a third, before being translated + the_left = new Sphere(); + the_left->set_transform(Matrix::translation(-1.5, 0.33, -0.75) * Matrix::scaling(0.33, 0.33, 0.33)); + the_left->material().set_color(Color(1, 0.8, 0.1)); + the_left->material().set_diffuse(0.7); + the_left->material().set_specular(0.3); + the_world.add_object(the_left); + + // The Light source is white, shining from above and to the left + the_world.set_light(PointLight(Tuple::Point(-10, 10, -10), Color(1, 1, 1))); + + // Configure the camera. + // the_camera = Camera(100, 50, std::numbers::pi / 2); + the_camera = Camera(320, 200, std::numbers::pi / 2); + the_camera.set_transform( + Matrix::view_transform(Tuple::Point(0, 1.5, -3.5), Tuple::Point(0, 1, 0), Tuple::Vector(0, 1, 0))); + + the_start = chrono::high_resolution_clock::now(); + the_canvas = the_camera.render(the_world); + the_end = chrono::high_resolution_clock::now(); + + the_canvas.save_to_file("chapter09.ppm"); + + chrono::duration the_elapsed_time = the_end - the_start; + printf("Execution Time: %f secondes\n", the_elapsed_time.count()); + + return 0; +} diff --git a/data/chapter_09.png b/data/chapter_09.png new file mode 100644 index 0000000..037223c Binary files /dev/null and b/data/chapter_09.png differ diff --git a/raytracing/CMakeLists.txt b/raytracing/CMakeLists.txt index 25d56e2..4a1edb0 100644 --- a/raytracing/CMakeLists.txt +++ b/raytracing/CMakeLists.txt @@ -24,6 +24,7 @@ add_library(raytracing src/renderer/material.cpp src/renderer/ray.cpp src/renderer/world.cpp + src/shapes/plane.cpp src/shapes/shape.cpp src/shapes/sphere.cpp ) diff --git a/raytracing/include/raytracing.h b/raytracing/include/raytracing.h index 70abbb6..66ee8b5 100644 --- a/raytracing/include/raytracing.h +++ b/raytracing/include/raytracing.h @@ -38,4 +38,5 @@ #include "renderer/material.h" #include "renderer/ray.h" #include "renderer/world.h" -#include "shapes/sphere.h" \ No newline at end of file +#include "shapes/plane.h" +#include "shapes/sphere.h" diff --git a/raytracing/src/core/intersection-data.cpp b/raytracing/src/core/intersection-data.cpp index de8a4f4..df02dbf 100644 --- a/raytracing/src/core/intersection-data.cpp +++ b/raytracing/src/core/intersection-data.cpp @@ -34,7 +34,7 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -IntersectionData::IntersectionData(void) : m_is_inside(false), m_distance(0) +IntersectionData::IntersectionData(void) : m_is_inside(false), m_distance(0), m_shape(nullptr) { } @@ -56,12 +56,12 @@ void IntersectionData::set_distance_t(double a_value) const Shape &IntersectionData::object(void) const { - return m_shape; + return *m_shape; } /* ------------------------------------------------------------------------- */ -void IntersectionData::set_object(const Shape &a_shape) +void IntersectionData::set_object(Shape *a_shape) { m_shape = a_shape; } diff --git a/raytracing/src/core/intersection-data.h b/raytracing/src/core/intersection-data.h index c51d209..eb67bc5 100644 --- a/raytracing/src/core/intersection-data.h +++ b/raytracing/src/core/intersection-data.h @@ -45,7 +45,7 @@ namespace Raytracer void set_distance_t(double a_value); const Shape &object(void) const; - void set_object(const Shape &a_shape); + void set_object(Shape *a_shape); const Tuple &point(void) const; void set_point(const Tuple &a_point); @@ -65,7 +65,7 @@ namespace Raytracer private: bool m_is_inside; double m_distance; - Shape m_shape; // TODO ?? + Shape *m_shape; Tuple m_point; Tuple m_over_point; Tuple m_eyev; diff --git a/raytracing/src/core/intersection.cpp b/raytracing/src/core/intersection.cpp index 66e490e..16da343 100644 --- a/raytracing/src/core/intersection.cpp +++ b/raytracing/src/core/intersection.cpp @@ -36,13 +36,13 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -Intersection::Intersection(void) : m_is_nothing(true), m_distance_t(0.0), m_shape() +Intersection::Intersection(void) : m_is_nothing(true), m_distance_t(0.0), m_shape(nullptr) { } /* ------------------------------------------------------------------------- */ -Intersection::Intersection(double a_distance_t, const Shape &a_shape) : +Intersection::Intersection(double a_distance_t, Shape *a_shape) : m_is_nothing(false), m_distance_t(a_distance_t), m_shape(a_shape) @@ -143,7 +143,7 @@ double Intersection::distance_t(void) const const Shape &Intersection::object(void) const { - return m_shape; + return *m_shape; } /* ------------------------------------------------------------------------- */ @@ -173,7 +173,7 @@ IntersectionData Intersection::prepare_computations(Ray a_ray) const // Precompute some useful values the_data.set_point(a_ray.position(m_distance_t)); the_data.set_eyev(-a_ray.direction()); - the_data.set_normalv(m_shape.normal_at(the_data.point())); + the_data.set_normalv(m_shape->normal_at(the_data.point())); the_data.set_over_point(the_data.point() + the_data.normalv() * kEpsilon); the_data.set_inside(); diff --git a/raytracing/src/core/intersection.h b/raytracing/src/core/intersection.h index a732f77..c2e394e 100644 --- a/raytracing/src/core/intersection.h +++ b/raytracing/src/core/intersection.h @@ -41,7 +41,7 @@ namespace Raytracer { public: Intersection(void); - Intersection(double a_distance_t, const Shape &a_shape); + Intersection(double a_distance_t, Shape *a_shape); Intersection(Intersection &an_intersection); Intersection(const Intersection &an_intersection); @@ -65,7 +65,7 @@ namespace Raytracer private: bool m_is_nothing; double m_distance_t; - Shape m_shape; + Shape *m_shape; }; }; // namespace Raytracer diff --git a/raytracing/src/shapes/plane.cpp b/raytracing/src/shapes/plane.cpp new file mode 100644 index 0000000..b20dc57 --- /dev/null +++ b/raytracing/src/shapes/plane.cpp @@ -0,0 +1,63 @@ +/*! + * sphere.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: 05/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 "core/intersections.h" + +#include "plane.h" + +using namespace Raytracer; + +/* ------------------------------------------------------------------------- */ + +Intersections Plane::local_intersect(const Ray &a_ray) +{ + Intersections the_intersections; + + if (std::abs(a_ray.direction().y()) < kEpsilon) + { + return the_intersections; + } + + double the_t = -a_ray.origin().y() / a_ray.direction().y(); + + the_intersections.add(Intersection(the_t, this)); + + return the_intersections; +} + +/* ------------------------------------------------------------------------- */ + +Tuple Plane::local_normal_at(const Tuple &a_local_point) const +{ + return Tuple::Vector(0, 1, 0); +} diff --git a/raytracing/src/shapes/plane.h b/raytracing/src/shapes/plane.h new file mode 100644 index 0000000..122d642 --- /dev/null +++ b/raytracing/src/shapes/plane.h @@ -0,0 +1,46 @@ +/*! + * plane.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: 27/02/2024 + * + */ + +#ifndef _RAYTRACER_PLANE_H +#define _RAYTRACER_PLANE_H + +/* ------------------------------------------------------------------------- */ + +#include "shapes/shape.h" + +/* ------------------------------------------------------------------------- */ + +namespace Raytracer +{ + class Plane : public Shape + { + public: + Plane(void) = default; + Intersections local_intersect(const Ray &a_ray) override; + Tuple local_normal_at(const Tuple &a_local_point) const override; + }; +}; // namespace Raytracer + +#endif // _RAYTRACER_PLANE_H diff --git a/raytracing/src/shapes/shape.cpp b/raytracing/src/shapes/shape.cpp index 42e1d4b..d942592 100644 --- a/raytracing/src/shapes/shape.cpp +++ b/raytracing/src/shapes/shape.cpp @@ -38,23 +38,21 @@ using namespace Raytracer; #define kNothing 0 -uint32_t Shape::s_current_index = 0; - /* ------------------------------------------------------------------------- */ -Shape::Shape(void) : m_id(kNothing), m_transform(Matrix::identity()) +Shape::Shape(void) : m_transform(Matrix::identity()) { } /* ------------------------------------------------------------------------- */ -Shape::Shape(Shape &a_copy) : m_id(a_copy.m_id), m_transform(a_copy.m_transform), m_material(a_copy.m_material) +Shape::Shape(Shape &a_copy) : m_transform(a_copy.m_transform), m_material(a_copy.m_material) { } /* ------------------------------------------------------------------------- */ -Shape::Shape(const Shape &a_copy) : m_id(a_copy.m_id), m_transform(a_copy.m_transform), m_material(a_copy.m_material) +Shape::Shape(const Shape &a_copy) : m_transform(a_copy.m_transform), m_material(a_copy.m_material) { } @@ -67,7 +65,6 @@ const Shape &Shape::operator=(const Shape &a_shape) return *this; } - m_id = a_shape.m_id; m_transform = a_shape.m_transform; m_material = a_shape.m_material; @@ -78,7 +75,6 @@ const Shape &Shape::operator=(const Shape &a_shape) bool Shape::operator==(const Shape &a_shape) const { - // (m_id == a_shape.m_id) && ( return (m_transform == a_shape.m_transform) && (m_material == a_shape.m_material); } @@ -159,10 +155,3 @@ Intersections Shape::local_intersect(const Ray &a_ray) return the_ret; } - -/* ------------------------------------------------------------------------- */ - -void Shape::inc_id(void) -{ - m_id = s_current_index++; -} diff --git a/raytracing/src/shapes/shape.h b/raytracing/src/shapes/shape.h index 3916e59..3ec5de0 100644 --- a/raytracing/src/shapes/shape.h +++ b/raytracing/src/shapes/shape.h @@ -66,14 +66,9 @@ namespace Raytracer Intersections intersect(const Ray &a_ray); virtual Intersections local_intersect(const Ray &a_ray); - protected: - void inc_id(void); - private: - uint32_t m_id; Matrix m_transform; Material m_material; - static uint32_t s_current_index; }; }; // namespace Raytracer diff --git a/raytracing/src/shapes/sphere.cpp b/raytracing/src/shapes/sphere.cpp index 83848c8..b653fee 100644 --- a/raytracing/src/shapes/sphere.cpp +++ b/raytracing/src/shapes/sphere.cpp @@ -39,13 +39,6 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -Sphere::Sphere(void) -{ - inc_id(); -} - -/* ------------------------------------------------------------------------- */ - Intersections Sphere::local_intersect(const Ray &a_ray) { Intersections the_intersections; @@ -61,8 +54,8 @@ Intersections Sphere::local_intersect(const Ray &a_ray) if (discriminant >= 0) { double the_sqrt = std::sqrt(discriminant); - the_intersections.add(Intersection((-the_b - the_sqrt) / (2 * the_a), *this)); - the_intersections.add(Intersection((-the_b + the_sqrt) / (2 * the_a), *this)); + the_intersections.add(Intersection((-the_b - the_sqrt) / (2 * the_a), this)); + the_intersections.add(Intersection((-the_b + the_sqrt) / (2 * the_a), this)); } return the_intersections; diff --git a/raytracing/src/shapes/sphere.h b/raytracing/src/shapes/sphere.h index 768b2b5..8f3bc6e 100644 --- a/raytracing/src/shapes/sphere.h +++ b/raytracing/src/shapes/sphere.h @@ -37,7 +37,7 @@ namespace Raytracer class Sphere : public Shape { public: - Sphere(void); + Sphere(void) = default; Intersections local_intersect(const Ray &a_ray) override; Tuple local_normal_at(const Tuple &a_local_point) const override; }; diff --git a/tests/05_rays.cpp b/tests/05_rays.cpp index c2be4fd..a798fc8 100644 --- a/tests/05_rays.cpp +++ b/tests/05_rays.cpp @@ -234,7 +234,7 @@ SCENARIO("An intersection encapsulates t and object", "[features/intersections.f Sphere s; WHEN("intersection(3.5,s)") { - Intersection i(3.5, s); + Intersection i(3.5, &s); THEN("i.t = 3.5") { @@ -257,7 +257,7 @@ SCENARIO("An intersection could be affected", "[features/intersections.feature]" Sphere s; AND_GIVEN("i1 <- intersection(3.5,s) and i2 <- intersection()") { - Intersection i1(3.5, s); + Intersection i1(3.5, &s); Intersection i2; WHEN("i2 <- i1") @@ -286,8 +286,8 @@ SCENARIO("Intersection could be compared", "[features/intersections.feature]") Sphere s; AND_GIVEN("i1 <- intersection(3,s) and i2 <- intersection(4,s)") { - Intersection i1(3.0, s); - Intersection i2(4.0, s); + Intersection i1(3.0, &s); + Intersection i2(4.0, &s); THEN("i2 > i1") { @@ -326,10 +326,10 @@ SCENARIO("Aggregating intersections", "[features/intersections.feature]") Sphere s; AND_GIVEN("i1 <- intersection(1,s)") { - Intersection i1(1, s); + Intersection i1(1, &s); AND_GIVEN("i2 <- intersection(2,s)") { - Intersection i2(2, s); + Intersection i2(2, &s); WHEN("xs <- intersections(i1,i2)") { Intersections xs = Intersections({i1, i2}); @@ -361,7 +361,7 @@ SCENARIO("Operations with intersections", "[features/intersections.feature]") AND_GIVEN("s <- sphere()") { Sphere s; - Intersection i1(1, s); + Intersection i1(1, &s); AND_GIVEN("xs2 <- intersections({i1})") { Intersections xs2({i1}); @@ -423,10 +423,10 @@ SCENARIO("The hit, when all intersections have positive t", "[features/intersect Sphere s; AND_GIVEN("i1 <- intersection(1,s)") { - Intersection i1(1, s); + Intersection i1(1, &s); AND_GIVEN("i2 <- intersection(2,s)") { - Intersection i2(2, s); + Intersection i2(2, &s); AND_GIVEN("xs <- intersections(i1,i2)") { Intersections xs = Intersections({i2, i1}); @@ -453,10 +453,10 @@ SCENARIO("The hit, when some intersections have negative t", "[features/intersec Sphere s; AND_GIVEN("i1 <- intersection(-1,s)") { - Intersection i1(-1, s); + Intersection i1(-1, &s); AND_GIVEN("i2 <- intersection(2,s)") { - Intersection i2(1, s); + Intersection i2(1, &s); AND_GIVEN("xs <- intersections(i1,i2)") { Intersections xs = Intersections({i2, i1}); @@ -483,10 +483,10 @@ SCENARIO("The hit, when all intersections have negative t", "[features/intersect Sphere s; AND_GIVEN("i1 <- intersection(-2,s)") { - Intersection i1(-2, s); + Intersection i1(-2, &s); AND_GIVEN("i2 <- intersection(-1,s)") { - Intersection i2(-1, s); + Intersection i2(-1, &s); AND_GIVEN("xs <- intersections(i1,i2)") { Intersections xs = Intersections({i1, i2}); @@ -514,16 +514,16 @@ SCENARIO("The hit is always the lowest nonnegative intersection", "[features/int Sphere s; AND_GIVEN("i1 <- intersection(5,s)") { - Intersection i1(5, s); + Intersection i1(5, &s); AND_GIVEN("i2 <- intersection(7,s)") { - Intersection i2(7, s); + Intersection i2(7, &s); AND_GIVEN("i3 <- intersection(-3,s)") { - Intersection i3(-3, s); + Intersection i3(-3, &s); AND_GIVEN("i4 <- intersection(2,s)") { - Intersection i4(2, s); + Intersection i4(2, &s); AND_GIVEN("xs <- intersections(i1, i2, i3, i4)") { Intersections xs = Intersections({i1, i2, i3, i4}); diff --git a/tests/07_making_scene.cpp b/tests/07_making_scene.cpp index 255a5e1..a2c9ff9 100644 --- a/tests/07_making_scene.cpp +++ b/tests/07_making_scene.cpp @@ -143,7 +143,7 @@ SCENARIO("Precompute the state of an intersection", "[features/intersections.fea Sphere shape; AND_GIVEN("i <- intersection(4, shape)") { - Intersection i(4, shape); + Intersection i(4, &shape); WHEN("comps <- prepare_computations(i,r)") { IntersectionData comps = i.prepare_computations(r); @@ -185,7 +185,7 @@ SCENARIO("The hit, when an intersection occurs on the outside", "[features/inter Sphere shape; AND_GIVEN("i <- intersection(4, shape)") { - Intersection i(4, shape); + Intersection i(4, &shape); WHEN("comps <- prepare_computations(i,r)") { IntersectionData comps = i.prepare_computations(r); @@ -211,7 +211,7 @@ SCENARIO("The hit, when an intersection occurs on the inside", "[features/inters Sphere shape; AND_GIVEN("i <- intersection(1, shape)") { - Intersection i(1, shape); + Intersection i(1, &shape); WHEN("comps <- prepare_computations(i,r)") { IntersectionData comps = i.prepare_computations(r); @@ -252,7 +252,7 @@ SCENARIO("Shading an intersection", "[features/world.feature]") Shape *shape = w.objects(0); AND_GIVEN("i <- intersection(4, shape)") { - Intersection i(4, *shape); + Intersection i(4, shape); WHEN("comps <- prepare_computations(i,r)") { IntersectionData comps = i.prepare_computations(r); @@ -289,7 +289,7 @@ SCENARIO("Shading an intersection from the inside", "[features/world.feature]") Shape *shape = w.objects(1); AND_GIVEN("i <- intersection(0.5, shape)") { - Intersection i(0.5, *shape); + Intersection i(0.5, shape); WHEN("comps <- prepare_computations(i,r)") { IntersectionData comps = i.prepare_computations(r); diff --git a/tests/08_shadows.cpp b/tests/08_shadows.cpp index 03c2c1c..2ca90bc 100644 --- a/tests/08_shadows.cpp +++ b/tests/08_shadows.cpp @@ -164,7 +164,7 @@ SCENARIO("shade_hit() is given an intersection in the shadow", "[features/world. Ray r(Tuple::Point(0, 0, 5), Tuple::Vector(0, 0, 1)); AND_GIVEN("i <- intersection(4, s2)") { - Intersection i(4, *s2); + Intersection i(4, s2); WHEN("comps <- prepare_computatons(i,r)") { IntersectionData comps = i.prepare_computations(r); @@ -200,7 +200,7 @@ SCENARIO("The hit should offset the point", "[features/intersections.feature]") shape.set_transform(Matrix::translation(0, 0, 1)); AND_GIVEN("i <- intersection(5, shape)") { - Intersection i(5, shape); + Intersection i(5, &shape); WHEN("comps <- prepare_computatons(i,r)") { IntersectionData comps = i.prepare_computations(r); diff --git a/tests/09_planes.cpp b/tests/09_planes.cpp index b7422dc..60203a0 100644 --- a/tests/09_planes.cpp +++ b/tests/09_planes.cpp @@ -244,3 +244,142 @@ SCENARIO("Computing the normal on a transformed shape", "[features/shapes.featur } } } + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The normal of a plane is constant everywhere", "[features/planes.feature]") +{ + GIVEN("p <- plane()") + { + Plane p; + WHEN("n1 <- local_normal_at(p, point(0, 0, 0)))") + { + Tuple n1 = p.local_normal_at(Tuple::Point(0, 0, 0)); + AND_WHEN("n2 <- local_normal_at(p, point(10, 0, 0)))") + { + Tuple n2 = p.local_normal_at(Tuple::Point(10, 0, 0)); + AND_WHEN("n3 <- local_normal_at(p, point(-5, 0, 150)))") + { + Tuple n3 = p.local_normal_at(Tuple::Point(-5, 0, 150)); + + THEN("n1 = vector(0, 1, 0)") + { + REQUIRE(n1 == Tuple::Vector(0, 1, 0)); + } + AND_THEN("n2 = vector(0, 1, 0)") + { + REQUIRE(n1 == Tuple::Vector(0, 1, 0)); + } + AND_THEN("n3 = vector(0, 1, 0)") + { + REQUIRE(n1 == Tuple::Vector(0, 1, 0)); + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Intersect with a ray parallel to the plane", "[features/planes.feature]") +{ + GIVEN("p <- plane()") + { + Plane p; + AND_GIVEN("r <- ray(point(0, 10, 0), vector(0, 0, 1))") + { + Ray r(Tuple::Point(0, 10, 0), Tuple::Vector(0, 0, 1)); + WHEN("xs <-local_intersect(p, r)") + { + Intersections xs = p.local_intersect(r); + THEN("xs is empty") + { + REQUIRE(xs.count() == 0); + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Intersect with a coplanar ray", "[features/planes.feature]") +{ + GIVEN("p <- plane()") + { + Plane p; + AND_GIVEN("r <- ray(point(0, 0, 0), vector(0, 0, 1))") + { + Ray r(Tuple::Point(0, 0, 0), Tuple::Vector(0, 0, 1)); + WHEN("xs <-local_intersect(p, r)") + { + Intersections xs = p.local_intersect(r); + THEN("xs is empty") + { + REQUIRE(xs.count() == 0); + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("A ray Intersecting a plane from above", "[features/planes.feature]") +{ + GIVEN("p <- plane()") + { + Plane p; + AND_GIVEN("r <- ray(point(0, 1, 0), vector(0, -1, 0))") + { + Ray r(Tuple::Point(0, 1, 0), Tuple::Vector(0, -1, 0)); + WHEN("xs <-local_intersect(p, r)") + { + Intersections xs = p.local_intersect(r); + THEN("xs.count = 1") + { + REQUIRE(xs.count() == 1); + } + AND_THEN("xs[0].t = 1") + { + REQUIRE(xs[0].distance_t() == 1); + } + AND_THEN("xs[0].object = p") + { + REQUIRE(xs[0].object() == p); + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("A ray Intersecting a plane from below", "[features/planes.feature]") +{ + GIVEN("p <- plane()") + { + Plane p; + AND_GIVEN("r <- ray(point(0, -1, 0), vector(0, 1, 0))") + { + Ray r(Tuple::Point(0, -1, 0), Tuple::Vector(0, 1, 0)); + WHEN("xs <-local_intersect(p, r)") + { + Intersections xs = p.local_intersect(r); + THEN("xs.count = 1") + { + REQUIRE(xs.count() == 1); + } + AND_THEN("xs[0].t = 1") + { + REQUIRE(xs[0].distance_t() == 1); + } + AND_THEN("xs[0].object = p") + { + REQUIRE(xs[0].object() == p); + } + } + } + } +}