diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index a0f5909..b1da617 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -3,9 +3,11 @@ # First most is build as a library add_library(rayonnement STATIC) -set(RAY_HEADERS include/tuple.h include/math_helper.h include/colour.h include/canvas.h include/matrix.h include/transformation.h - include/ray.h) -set(RAY_SOURCES tuple.cpp math_helper.cpp colour.cpp canvas.cpp matrix.cpp transformation.cpp objects/ray.cpp) +set(RAY_HEADERS include/tuple.h include/math_helper.h include/colour.h include/canvas.h + include/matrix.h include/transformation.h include/intersect.h include/intersection.h + include/object.h include/ray.h include/sphere.h) +set(RAY_SOURCES tuple.cpp math_helper.cpp colour.cpp canvas.cpp matrix.cpp transformation.cpp intersect.cpp + objects/object.cpp objects/ray.cpp objects/sphere.cpp) target_include_directories(rayonnement PUBLIC include) target_sources(rayonnement PRIVATE ${RAY_HEADERS} ${RAY_SOURCES}) diff --git a/source/include/intersect.h b/source/include/intersect.h new file mode 100644 index 0000000..6bedc4a --- /dev/null +++ b/source/include/intersect.h @@ -0,0 +1,28 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Intersect header + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#ifndef DORAYME_INTERSECT_H +#define DORAYME_INTERSECT_H + +#include +#include + +class Intersect +{ +private: + Intersection **list; + uint32_t num; + uint32_t allocated; +public: + Intersect(); + void add(Intersection *i); + int count() { return this->num; }; + Intersection *operator[](const int p) { return this->list[p]; } +}; + +#endif //DORAYME_INTERSECT_H diff --git a/source/include/intersection.h b/source/include/intersection.h new file mode 100644 index 0000000..1d29148 --- /dev/null +++ b/source/include/intersection.h @@ -0,0 +1,43 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Intersection header + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#ifndef DORAYME_INTERSECTION_H +#define DORAYME_INTERSECTION_H + +#include + +class Object; + +struct Intersection +{ + double t; + Object *object; +}; + +static Intersection *newIntersection(double t, Object *object) +{ + Intersection *ret = (Intersection *)calloc(sizeof(Intersection), 1); + + if (ret != nullptr) + { + ret->t = t; + ret->object = object; + } + + return ret; +} + +static void freeIntersection(Intersection *i) +{ + if ( i != nullptr ) + { + free(i); + } +} + +#endif //DORAYME_INTERSECTION_H diff --git a/source/include/object.h b/source/include/object.h new file mode 100644 index 0000000..d08e570 --- /dev/null +++ b/source/include/object.h @@ -0,0 +1,24 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Object header + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#ifndef DORAYME_OBJECT_H +#define DORAYME_OBJECT_H + +class Object; + +#include +#include + +/* Base class for all object that can be presented in the world */ +class Object +{ +public: + virtual Intersect intersect(Ray r); +}; + +#endif //DORAYME_OBJECT_H diff --git a/source/include/sphere.h b/source/include/sphere.h new file mode 100644 index 0000000..19151f3 --- /dev/null +++ b/source/include/sphere.h @@ -0,0 +1,23 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Sphere header + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#ifndef DORAYME_SPHERE_H +#define DORAYME_SPHERE_H + +#include +#include +#include + +class Sphere : public Object +{ +public: + /* All sphere are at (0, 0, 0) and radius 1*/ + virtual Intersect intersect(Ray r); +}; + +#endif //DORAYME_SPHERE_H diff --git a/source/intersect.cpp b/source/intersect.cpp new file mode 100644 index 0000000..ab0cc7c --- /dev/null +++ b/source/intersect.cpp @@ -0,0 +1,30 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Intersect implementation + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include + +#include + +#define MIN_ALLOC (2) + +Intersect::Intersect() +{ + this->allocated = MIN_ALLOC; + this->list = (Intersection **)calloc(sizeof(Object *), MIN_ALLOC); + this->num = 0; +} + +void Intersect::add(Intersection *i) +{ + if ((this->num + 1) < this->allocated) + { + this->allocated *= 2; + this->list = (Intersection **)realloc(this->list, sizeof(Object *) * this->allocated); + } + this->list[this->num++] = i; +} \ No newline at end of file diff --git a/source/objects/object.cpp b/source/objects/object.cpp new file mode 100644 index 0000000..9ecf9b1 --- /dev/null +++ b/source/objects/object.cpp @@ -0,0 +1,17 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Object implementation + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ + +#include +#include +#include + +Intersect Object::intersect(Ray r) +{ + return Intersect(); +}; \ No newline at end of file diff --git a/source/objects/sphere.cpp b/source/objects/sphere.cpp new file mode 100644 index 0000000..67366fb --- /dev/null +++ b/source/objects/sphere.cpp @@ -0,0 +1,35 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Sphere implementation + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include + +#include +#include +#include +#include + +Intersect Sphere::intersect(Ray r) +{ + Intersect ret; + double a, b, c, discriminant; + Tuple sphere_to_ray = r.origin - Point(0, 0, 0); + + a = r.direction.dot(r.direction); + b = 2 * r.direction.dot(sphere_to_ray); + c = sphere_to_ray.dot(sphere_to_ray) - 1; + + discriminant = b * b - 4 * a * c; + + if (discriminant >= 0) + { + ret.add(newIntersection((-b - sqrt(discriminant)) / (2 * a), this)); + ret.add(newIntersection((-b + sqrt(discriminant)) / (2 * a), this)); + } + + return ret; +} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 90cbc26..7d9b873 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,7 +3,8 @@ project(DoRayTested) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) -set(TESTS_SRC tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp ray_test.cpp) +set(TESTS_SRC tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp ray_test.cpp + intersect_test.cpp sphere_test.cpp) add_executable(testMyRays) target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) diff --git a/tests/intersect_test.cpp b/tests/intersect_test.cpp new file mode 100644 index 0000000..5b0ca56 --- /dev/null +++ b/tests/intersect_test.cpp @@ -0,0 +1,51 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Intersect unit tests + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include +#include +#include + + +TEST(IntersectTest, Creating_an_intersect_and_do_some_check) +{ + Intersect i; + + ASSERT_EQ(i.count(), 0); + + i.add(newIntersection(1.0, nullptr)); + i.add(newIntersection(4.2, nullptr)); + + ASSERT_EQ(i.count(), 2); + + ASSERT_EQ(i[0]->t, 1.0); + ASSERT_EQ(i[1]->t, 4.2); +} + +TEST(IntersectTest, An_intersection_encapsulate_t_and_object) +{ + Sphere s = Sphere(); + Intersection *i = newIntersection(3.5, &s); + + ASSERT_EQ(i->t, 3.5); + ASSERT_EQ(i->object, (Object *)&s); +} + +TEST(IntersectTest, Aggregating_intersections) +{ + Sphere s = Sphere(); + Intersection *i1 = newIntersection(1, &s); + Intersection *i2 = newIntersection(2, &s); + + Intersect xs = Intersect(); + xs.add(i1); + xs.add(i2); + + ASSERT_EQ(xs.count(), 2); + ASSERT_EQ(xs[0]->t, 1); + ASSERT_EQ(xs[1]->t, 2); +} \ No newline at end of file diff --git a/tests/sphere_test.cpp b/tests/sphere_test.cpp new file mode 100644 index 0000000..e58f073 --- /dev/null +++ b/tests/sphere_test.cpp @@ -0,0 +1,65 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Sphere unit tests + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include +#include +#include + + +TEST(SphereTest, A_ray_intersect_a_sphere_at_two_points) +{ + Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1)); + Sphere s = Sphere(); + Intersect xs = s.intersect(r); + + ASSERT_EQ(xs.count(), 2); + ASSERT_EQ(xs[0]->t, 4.0); + ASSERT_EQ(xs[1]->t, 6.0); +} + +TEST(SphereTest, A_ray_intersect_a_sphere_at_a_tangent) +{ + Ray r = Ray(Point(0, 1, -5), Vector(0, 0, 1)); + Sphere s = Sphere(); + Intersect xs = s.intersect(r); + + ASSERT_EQ(xs.count(), 2); + ASSERT_EQ(xs[0]->t, 5.0); + ASSERT_EQ(xs[1]->t, 5.0); +} + +TEST(SphereTest, A_ray_miss_a_sphere) +{ + Ray r = Ray(Point(0, 2, -5), Vector(0, 0, 1)); + Sphere s = Sphere(); + Intersect xs = s.intersect(r); + + ASSERT_EQ(xs.count(), 0); +} + +TEST(SphereTest, A_ray_originate_inside_a_sphere) +{ + Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1)); + Sphere s = Sphere(); + Intersect xs = s.intersect(r); + + ASSERT_EQ(xs.count(), 2); + ASSERT_EQ(xs[0]->t, -1.0); + ASSERT_EQ(xs[1]->t, 1.0); +} + +TEST(SphereTest, A_sphere_is_behind_a_ray) +{ + Ray r = Ray(Point(0, 0, 5), Vector(0, 0, 1)); + Sphere s = Sphere(); + Intersect xs = s.intersect(r); + + ASSERT_EQ(xs.count(), 2); + ASSERT_EQ(xs[0]->t, -6.0); + ASSERT_EQ(xs[1]->t, -4.0); +} \ No newline at end of file