From 12e2cac55eb203df07912dc2f0103f3caf0d268f Mon Sep 17 00:00:00 2001 From: Philip Abernethy Date: Sun, 20 Sep 2015 15:43:44 +0200 Subject: [PATCH] Added input file reading and support for meshes. - Scenes can now be read from XML files, three examples are included. - Geometry objects (shapes) can be specified from OBJ files. Parsing materials from OBJ is not currently supported. - PugiXML added as additional, external dependency. Tiny OBJ Loader is included directly, as source code. - Lambertien material fixed. --- CMakeLists.txt | 18 +- example1.xml | 42 + example2.xml | 46 + example3.xml | 52 + lib/camera/camera.cpp | 3 +- lib/camera/camera.h | 9 +- lib/camera/perspective_camera.cpp | 8 +- lib/camera/perspective_camera.h | 7 +- lib/geometry/color.cpp | 4 +- lib/geometry/material/lambertian_material.cpp | 15 +- lib/geometry/material/lambertian_material.h | 3 +- lib/geometry/material/material.h | 5 +- lib/geometry/material/phong_material.cpp | 10 +- lib/geometry/material/phong_material.h | 6 +- lib/geometry/point.cpp | 6 +- lib/geometry/point.h | 10 + lib/geometry/shapes/mesh.cpp | 38 + lib/geometry/shapes/mesh.h | 22 + lib/geometry/{ => shapes}/shape.cpp | 5 +- lib/geometry/{ => shapes}/shape.h | 15 +- lib/geometry/{ => shapes}/sphere.cpp | 13 +- lib/geometry/{ => shapes}/sphere.h | 3 +- lib/geometry/shapes/triangle.cpp | 41 + lib/geometry/shapes/triangle.h | 27 + lib/light/ambient_light.cpp | 12 +- lib/light/ambient_light.h | 7 +- lib/light/light.cpp | 7 +- lib/light/light.h | 13 +- lib/light/parallel_light.cpp | 12 +- lib/light/parallel_light.h | 6 +- lib/light/point_light.cpp | 15 + lib/light/point_light.h | 21 + lib/math/helper.cpp | 2 +- lib/math/helper.h | 1 + lib/math/mat4.cpp | 4 +- lib/math/mat4.h | 1 - lib/math/vec2.cpp | 4 +- lib/math/vec4.cpp | 4 +- lib/parser.cpp | 181 ++++ lib/parser.h | 54 ++ lib/tiny_obj_loader.cpp | 912 ++++++++++++++++++ lib/tiny_obj_loader.h | 100 ++ lib/whitted_rt.cpp | 29 +- lib/whitted_rt.h | 15 +- main.cpp | 50 +- open_room.obj | 41 + 46 files changed, 1771 insertions(+), 128 deletions(-) create mode 100644 example1.xml create mode 100644 example2.xml create mode 100644 example3.xml create mode 100644 lib/geometry/shapes/mesh.cpp create mode 100644 lib/geometry/shapes/mesh.h rename lib/geometry/{ => shapes}/shape.cpp (73%) rename lib/geometry/{ => shapes}/shape.h (77%) rename lib/geometry/{ => shapes}/sphere.cpp (74%) rename lib/geometry/{ => shapes}/sphere.h (81%) create mode 100644 lib/geometry/shapes/triangle.cpp create mode 100644 lib/geometry/shapes/triangle.h create mode 100644 lib/light/point_light.cpp create mode 100644 lib/light/point_light.h create mode 100644 lib/parser.cpp create mode 100644 lib/parser.h create mode 100644 lib/tiny_obj_loader.cpp create mode 100644 lib/tiny_obj_loader.h create mode 100644 open_room.obj diff --git a/CMakeLists.txt b/CMakeLists.txt index 20dcad8..59f8454 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,21 @@ project(Ray_Tracer) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -set(SOURCE_FILES lib/whitted_rt.cpp lib/whitted_rt.h lib/math/vec4.cpp lib/math/vec4.h lib/math/vec2.cpp lib/math/vec2.h lib/math/mat4.cpp lib/math/mat4.h lib/camera/perspective_camera.cpp lib/camera/perspective_camera.h lib/geometry/ray.h lib/geometry/sphere.cpp lib/geometry/sphere.h lib/geometry/shape.cpp lib/geometry/shape.h lib/math/helper.cpp lib/math/helper.h lib/light/light.cpp lib/light/light.h lib/light/parallel_light.cpp lib/light/parallel_light.h lib/geometry/material/material.h lib/geometry/material/phong_material.cpp lib/geometry/material/phong_material.h lib/geometry/material/solid_material.h lib/geometry/point.cpp lib/geometry/point.h lib/geometry/direction.cpp lib/geometry/direction.h lib/geometry/normal.cpp lib/geometry/normal.h lib/geometry/transform.cpp lib/geometry/transform.h lib/geometry/intersection.h lib/geometry/color.cpp lib/geometry/color.h lib/geometry/material/lambertian_material.cpp lib/geometry/material/lambertian_material.h lib/light/ambient_light.cpp lib/light/ambient_light.h lib/camera/camera.h lib/camera/camera.cpp lib/geometry/ray.cpp lib/geometry/material/solid_material.cpp) +set(SOURCE_FILES lib/whitted_rt.cpp lib/whitted_rt.h lib/math/vec4.cpp lib/math/vec4.h lib/math/vec2.cpp lib/math/vec2.h + lib/math/mat4.cpp lib/math/mat4.h lib/camera/perspective_camera.cpp lib/camera/perspective_camera.h + lib/geometry/ray.h lib/geometry/shapes/sphere.cpp lib/geometry/shapes/sphere.h lib/geometry/shapes/shape.cpp + lib/geometry/shapes/shape.h lib/math/helper.cpp lib/math/helper.h lib/light/light.cpp lib/light/light.h + lib/light/parallel_light.cpp lib/light/parallel_light.h lib/geometry/material/material.h + lib/geometry/material/phong_material.cpp lib/geometry/material/phong_material.h + lib/geometry/material/solid_material.h lib/geometry/point.cpp lib/geometry/point.h lib/geometry/direction.cpp + lib/geometry/direction.h lib/geometry/normal.cpp lib/geometry/normal.h lib/geometry/transform.cpp + lib/geometry/transform.h lib/geometry/intersection.h lib/geometry/color.cpp lib/geometry/color.h + lib/geometry/material/lambertian_material.cpp lib/geometry/material/lambertian_material.h + lib/light/ambient_light.cpp lib/light/ambient_light.h lib/camera/camera.h lib/camera/camera.cpp + lib/geometry/ray.cpp lib/geometry/material/solid_material.cpp lib/light/point_light.cpp lib/light/point_light.h + lib/parser.cpp lib/parser.h lib/geometry/shapes/mesh.cpp lib/geometry/shapes/mesh.h + lib/geometry/shapes/triangle.cpp lib/geometry/shapes/triangle.h lib/tiny_obj_loader.cpp lib/tiny_obj_loader.h) add_executable(Ray_Tracer ${SOURCE_FILES} main.cpp) add_executable(Test ${SOURCE_FILES} test.cpp) -target_link_libraries(Ray_Tracer png16) \ No newline at end of file +target_link_libraries(Ray_Tracer png16 pugixml) +target_link_libraries(Test png16 pugixml) \ No newline at end of file diff --git a/example1.xml b/example1.xml new file mode 100644 index 0000000..791b06a --- /dev/null +++ b/example1.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example2.xml b/example2.xml new file mode 100644 index 0000000..a144c99 --- /dev/null +++ b/example2.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example3.xml b/example3.xml new file mode 100644 index 0000000..ca77e8a --- /dev/null +++ b/example3.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/camera/camera.cpp b/lib/camera/camera.cpp index 9f9983b..7726dfc 100644 --- a/lib/camera/camera.cpp +++ b/lib/camera/camera.cpp @@ -5,7 +5,8 @@ #include "camera.h" camera::camera(const point &position, const point &look_at, const direction &up, - const std::array &resolution, const unsigned long &samples) : resolution(resolution) { + const std::array &resolution, const unsigned long max_bounces, + const unsigned long &samples) : resolution(resolution), max_bounces(max_bounces) { assert(resolution[0] > 0 && resolution[1] > 0); assert(samples > 0); direction dir = normalise(look_at-position); diff --git a/lib/camera/camera.h b/lib/camera/camera.h index 2943778..a1bd3f2 100644 --- a/lib/camera/camera.h +++ b/lib/camera/camera.h @@ -8,12 +8,14 @@ #include "../geometry/transform.h" #include "../geometry/ray.h" #include "../geometry/color.h" +#include "../math/helper.h" class camera { protected: transform transforms; std::array resolution; std::vector>> data; + const unsigned long max_bounces = 0; camera() { }; @@ -23,9 +25,9 @@ class camera { * Initialises the values common to all camera implementations. Transforms and resolution are set as given. Memory * for the data variable is allocated according to the resolution. * - * @param &position the position of the camera + * @param &offset the offset of the camera * - * @param &look_at the position the camera is centred on + * @param &look_at the offset the camera is centred on * * @param &up the up direction of the image * @@ -34,7 +36,8 @@ class camera { * @param &samples the number of samples per pixel */ camera(const point &position, const point &look_at, const direction &up, - const std::array &resolution, const unsigned long &samples); + const std::array &resolution, const unsigned long max_bounces, + const unsigned long &samples); public: /** diff --git a/lib/camera/perspective_camera.cpp b/lib/camera/perspective_camera.cpp index 7e6040d..a5d4037 100644 --- a/lib/camera/perspective_camera.cpp +++ b/lib/camera/perspective_camera.cpp @@ -1,13 +1,13 @@ #include "perspective_camera.h" perspective_camera::perspective_camera() : perspective_camera(point(0, 0, 0), point(0, 0, -1), direction(0, 1, 0), - {1024, 768}, 1, 45) { + {1024, 768}, 10, 1, 45) { } perspective_camera::perspective_camera(const point &position, const point &look_at, const direction &up, - const std::array &resolution, const unsigned long &samples, - const float &fov) : - camera(position, look_at, up, resolution, samples), fov(fov) { + const std::array &resolution, const unsigned long max_bounces, + const unsigned long &samples, const float &fov) : + camera(position, look_at, up, resolution, max_bounces, samples), fov(fov) { assert(0 < fov && fov < 90); stepwidth = tan(helper::to_radians(this->fov))/(resolution[0]/2); start = direction(0, 0, 1)+direction(1, 0, 0)*(((resolution[0]-1)/2.0)*stepwidth)+ diff --git a/lib/camera/perspective_camera.h b/lib/camera/perspective_camera.h index bfadbba..9bd4599 100644 --- a/lib/camera/perspective_camera.h +++ b/lib/camera/perspective_camera.h @@ -31,9 +31,9 @@ class perspective_camera : public camera { * * Creates a perspective camera with the given parameters * - * @param &position the position of the camera in world coordinates + * @param &offset the offset of the camera in world coordinates * - * @param &look_at the position the camera is centered on in world coordinates + * @param &look_at the offset the camera is centered on in world coordinates * * @param &up the up direction of the image * @@ -44,7 +44,8 @@ class perspective_camera : public camera { * @param &fov the half horizontal field-of-view angle of the camera */ perspective_camera(const point &position, const point &look_at, const direction &up, - const std::array &resolution, const unsigned long &samples, const float &fov); + const std::array &resolution, const unsigned long max_bounces, + const unsigned long &samples, const float &fov); virtual std::vector *get_rays(const unsigned long &x, const unsigned long &y); }; diff --git a/lib/geometry/color.cpp b/lib/geometry/color.cpp index ff0768e..3945804 100644 --- a/lib/geometry/color.cpp +++ b/lib/geometry/color.cpp @@ -17,12 +17,12 @@ color::color(const color &in) { } float &color::operator[](const unsigned long i) { - assert(0 <= i <= 2); + assert(0 <= i && i <= 2); return this->v[i]; } const float &color::operator[](const unsigned long i) const { - assert(0 <= i <= 2); + assert(0 <= i && i <= 2); return this->v[i]; } diff --git a/lib/geometry/material/lambertian_material.cpp b/lib/geometry/material/lambertian_material.cpp index 8e793e7..fb8603f 100644 --- a/lib/geometry/material/lambertian_material.cpp +++ b/lib/geometry/material/lambertian_material.cpp @@ -14,6 +14,17 @@ std::ostream &operator<<(std::ostream &out, const lambertian_material &a) { } color lambertian_material::shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, - const bool internal) { + const bool internal) const { + if (l != direction()) { + // Directional light + if (!internal) { + float phi = std::max(0.0, dot(n, l)); + if (phi > 0) { + return scale(this->col, lcol*(this->diffuse*phi)); + } + } + } else + // Ambient light + return scale(this->col, lcol*this->ambient); return color(); -} +} \ No newline at end of file diff --git a/lib/geometry/material/lambertian_material.h b/lib/geometry/material/lambertian_material.h index ecc9ce2..371297c 100644 --- a/lib/geometry/material/lambertian_material.h +++ b/lib/geometry/material/lambertian_material.h @@ -13,7 +13,8 @@ class lambertian_material : public solid_material { public: lambertian_material(color col, float ambient, float diffuse); friend std::ostream &operator<<(std::ostream &out, const lambertian_material &a); - virtual color shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, const bool internal); + virtual color shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, + const bool internal) const; }; #endif //RAY_TRACER_LAMBERTIAN_MATERIAL_H diff --git a/lib/geometry/material/material.h b/lib/geometry/material/material.h index c076d0e..71a141b 100644 --- a/lib/geometry/material/material.h +++ b/lib/geometry/material/material.h @@ -17,7 +17,7 @@ */ class material { protected: - material() {}; + material() { }; public: /** * @brief Shading @@ -32,7 +32,8 @@ class material { * to. * @return the base color multiplied by a factor according to the viewing situation */ - virtual color shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, const bool internal) = 0; + virtual color shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, + const bool internal) const = 0; }; #endif //RAY_TRACER_MATERIAL_H diff --git a/lib/geometry/material/phong_material.cpp b/lib/geometry/material/phong_material.cpp index b5c42e4..a3b1476 100644 --- a/lib/geometry/material/phong_material.cpp +++ b/lib/geometry/material/phong_material.cpp @@ -4,9 +4,9 @@ #include "phong_material.h" -phong_material::phong_material(color col, float ambient, float diffuse, float specular, - float exponent) : solid_material(col), ambient(ambient), diffuse(diffuse), specular(specular), - exponent(exponent) { } +phong_material::phong_material(const color &col, const float &ambient, const float &diffuse, const float &specular, + const float &exponent) : solid_material(col), ambient(ambient), diffuse(diffuse), + specular(specular), exponent(exponent) { } std::ostream &operator<<(std::ostream &out, const phong_material &a) { out << "Phong material: Color: " << a.col << " Ambient intensity: " << a.ambient << ", Diffuse intensity: " << @@ -16,7 +16,7 @@ std::ostream &operator<<(std::ostream &out, const phong_material &a) { } color phong_material::shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, - const bool internal) { + const bool internal) const { if (l != direction()) { // Directional light if (!internal) { @@ -24,7 +24,7 @@ color phong_material::shade(const color &lcol, const direction &l, const normal if (phi > 0) { direction r = n*(2*phi)-l; return scale(this->col, lcol*(this->diffuse*phi))+ - lcol*(std::pow(std::max(0.0, dot(v, r)), this->exponent)*this->specular); + lcol*(std::pow(std::max(0.0, dot(v, r)), this->exponent)*this->specular); } } } else diff --git a/lib/geometry/material/phong_material.h b/lib/geometry/material/phong_material.h index 945f596..adcbf96 100644 --- a/lib/geometry/material/phong_material.h +++ b/lib/geometry/material/phong_material.h @@ -12,11 +12,13 @@ struct phong_material : public solid_material { private: float ambient, diffuse, specular, exponent; public: - phong_material(color col, float ambient, float diffuse, float specular, float exponent); + phong_material(const color &col, const float &ambient, const float &diffuse, const float &specular, + const float &exponent); friend std::ostream &operator<<(std::ostream &out, const phong_material &a); - virtual color shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, const bool internal); + virtual color shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, + const bool internal) const ; }; #endif //RAY_TRACER_PHONG_H diff --git a/lib/geometry/point.cpp b/lib/geometry/point.cpp index 2f63099..246b006 100644 --- a/lib/geometry/point.cpp +++ b/lib/geometry/point.cpp @@ -23,4 +23,8 @@ point &operator+=(point &lhs, const direction &rhs) { direction operator-(const point &lhs, const point &rhs) { return direction(dynamic_cast(lhs)-dynamic_cast(rhs)); -} \ No newline at end of file +} + +point operator-(const point &lhs, const direction &rhs) { + return point(dynamic_cast(lhs)-dynamic_cast(rhs)); +} diff --git a/lib/geometry/point.h b/lib/geometry/point.h index edbb8fd..9c612f6 100644 --- a/lib/geometry/point.h +++ b/lib/geometry/point.h @@ -96,4 +96,14 @@ point &operator+=(point &lhs, const direction &rhs); */ direction operator-(const point &lhs, const point &rhs); +/** + * @brief Addition operator + * Subtracts a direction vector from a point vector. The result is a point vector again. This is equivalent to + * lhs+(-rhs). + * @param &lhs the point vector + * @param &rhs the direction vector + * @return the resulting point vector + */ +point operator-(const point &lhs, const direction &rhs); + #endif //RAY_TRACER_POINT_H \ No newline at end of file diff --git a/lib/geometry/shapes/mesh.cpp b/lib/geometry/shapes/mesh.cpp new file mode 100644 index 0000000..2e1e2f0 --- /dev/null +++ b/lib/geometry/shapes/mesh.cpp @@ -0,0 +1,38 @@ +// +// Created by chais on 18.09.15. +// + +#include "mesh.h" + +mesh::mesh(const direction *offset, const material *matrl, std::vector *faces) : shape(offset, matrl), + faces(faces) { } + +intersection mesh::intersect_full(const ray &r) { + intersection out = intersection(); + ray tr = this->world_to_object(r); + tr.o = tr.o-*this->offset; + float dist = std::numeric_limits::max(); + for (triangle *t : *this->faces) { + if (dot(*t->get_avg_normal(), tr.d) > 0) + continue; + intersection cur = t->intersect_full(tr); + if (cur.pos == nullptr) + continue; + float new_dist = length(tr.o-*cur.pos); + if (new_dist < dist) { + dist = new_dist; + out = cur; + out.object = this; + *out.pos = this->object_to_world(*out.pos+*this->offset); + *out.norm = normalise(this->object_to_world(*out.norm)); + } + } + return out; +} + +bool mesh::intersect_shadow(const ray &r) { + for (triangle *t : *this->faces) + if (t->intersect_shadow(r)) + return true; + return false; +} diff --git a/lib/geometry/shapes/mesh.h b/lib/geometry/shapes/mesh.h new file mode 100644 index 0000000..eb4266f --- /dev/null +++ b/lib/geometry/shapes/mesh.h @@ -0,0 +1,22 @@ +// +// Created by chais on 18.09.15. +// + +#ifndef RAY_TRACER_MESH_H +#define RAY_TRACER_MESH_H + +#include "shape.h" +#include "triangle.h" + +class mesh : public shape { +protected: + std::vector *faces; +public: + mesh(const direction *offset, const material *matrl, std::vector *faces); + + virtual intersection intersect_full(const ray &r); + + virtual bool intersect_shadow(const ray &r); +}; + +#endif //RAY_TRACER_MESH_H diff --git a/lib/geometry/shape.cpp b/lib/geometry/shapes/shape.cpp similarity index 73% rename from lib/geometry/shape.cpp rename to lib/geometry/shapes/shape.cpp index c06d827..9dda718 100644 --- a/lib/geometry/shape.cpp +++ b/lib/geometry/shapes/shape.cpp @@ -4,7 +4,10 @@ #include "shape.h" -shape::shape(material *matrl) : object_to_world(transform()), world_to_object(object_to_world.inv_trans, object_to_world.trans), matrl(matrl) { } +shape::shape(const direction *offset, const material *matrl) : object_to_world(transform()), + world_to_object(object_to_world.inv_trans, + object_to_world.trans), offset(offset), + matrl(matrl) { } color shape::shade(const color &lcol, const direction &l, const normal &n, const direction &v, const vec2 &pos, const bool internal) { diff --git a/lib/geometry/shape.h b/lib/geometry/shapes/shape.h similarity index 77% rename from lib/geometry/shape.h rename to lib/geometry/shapes/shape.h index 990c047..2ca158f 100644 --- a/lib/geometry/shape.h +++ b/lib/geometry/shapes/shape.h @@ -5,11 +5,11 @@ #ifndef RAY_TRACER_SHAPE_H #define RAY_TRACER_SHAPE_H -#include "ray.h" -#include "material/material.h" -#include "transform.h" -#include "intersection.h" -#include "color.h" +#include "../ray.h" +#include "../material/material.h" +#include "../transform.h" +#include "../intersection.h" +#include "../color.h" class shape { private: @@ -17,9 +17,10 @@ class shape { protected: transform object_to_world; transform world_to_object; - material *matrl; + const direction *offset; + const material *matrl; - shape(material *matrl); + shape(const direction *offset, const material *matrl); public: virtual intersection intersect_full(const ray &r) = 0; diff --git a/lib/geometry/sphere.cpp b/lib/geometry/shapes/sphere.cpp similarity index 74% rename from lib/geometry/sphere.cpp rename to lib/geometry/shapes/sphere.cpp index 1d287fb..c63cd87 100644 --- a/lib/geometry/sphere.cpp +++ b/lib/geometry/shapes/sphere.cpp @@ -4,7 +4,8 @@ #include "sphere.h" -sphere::sphere(float radius, material *matrl) : radius(radius), shape(matrl) { +sphere::sphere(const float &radius, const direction *position, material *matrl) : radius(radius), + shape(position, matrl) { assert(radius > 0); } @@ -15,7 +16,7 @@ std::ostream &operator<<(std::ostream &out, const sphere &a) { intersection sphere::intersect_full(const ray &r) { ray tr = this->world_to_object(r); - direction c = -tr.o; + direction c = point(-tr.o)+*this->offset; float a = std::max(float(0.0), dot(tr.d, c)); float b = std::sqrt(std::pow(length(c), 2)-std::pow(a, 2)); intersection out = intersection(); @@ -26,9 +27,9 @@ intersection sphere::intersect_full(const ray &r) { x = new point(tr.o+direction(tr.d*(a-d))); else x = new point(tr.o+direction(tr.d*(a+d))); - normal *norm = new normal(*x); - float local_len = length(direction(*x)); - vec2 *local_pos = new vec2((*x)[0]/local_len, (*x)[1]/local_len); + normal *norm = new normal(*x-*this->offset); + float local_len = length(*norm); + vec2 *local_pos = new vec2((*norm)[0]/local_len, (*norm)[1]/local_len); *x = this->object_to_world(*x); *norm = normalise(this->object_to_world(*norm)); out.object = this; @@ -41,7 +42,7 @@ intersection sphere::intersect_full(const ray &r) { bool sphere::intersect_shadow(const ray &r) { ray tr = this->world_to_object(r); - direction c = -tr.o; + direction c = point(-tr.o)+*this->offset; float a = std::max(float(0.0), dot(tr.d, c)); float b = std::sqrt(std::pow(length(c), 2)-std::pow(a, 2)); return (a > 0 && b < this->radius); diff --git a/lib/geometry/sphere.h b/lib/geometry/shapes/sphere.h similarity index 81% rename from lib/geometry/sphere.h rename to lib/geometry/shapes/sphere.h index f0e8510..a5be154 100644 --- a/lib/geometry/sphere.h +++ b/lib/geometry/shapes/sphere.h @@ -7,12 +7,13 @@ #include "shape.h" #include +#include class sphere : public shape { private: float radius; public: - sphere(float radius, material *matrl); + sphere(const float &radius, const direction *offset, material *matrl); friend std::ostream &operator<<(std::ostream &out, const sphere &a); diff --git a/lib/geometry/shapes/triangle.cpp b/lib/geometry/shapes/triangle.cpp new file mode 100644 index 0000000..4324d54 --- /dev/null +++ b/lib/geometry/shapes/triangle.cpp @@ -0,0 +1,41 @@ +// +// Created by chais on 18.09.15. +// + +#include "triangle.h" + +triangle::triangle(std::array *vertices, std::array *normals, std::array *texture_coords) + : vertices(vertices), normals(normals), texture_coords(texture_coords) { } + +intersection triangle::intersect_full(const ray &r) { + direction E1 = (*this->vertices)[1]-(*this->vertices)[0]; + direction E2 = (*this->vertices)[2]-(*this->vertices)[0]; + direction T = r.o-(*this->vertices)[0]; + direction P = cross(r.d, E2); + direction Q = cross(T, E1); + float s = 1/(dot(P, E1)); + float t = dot(Q, E2)*s; + float x = dot(P, T)*s; + float y = dot(Q, r.d)*s; + intersection out = intersection(); + if (t < 0 || x < 0 || y < 0 || x+y > 1) + return out; + out.pos = new point(r.o+r.d*t); + out.local_pos = new vec2( + (1-x-y)*(*this->texture_coords)[0][0]+x*(*this->texture_coords)[1][0]+y*(*this->texture_coords)[2][0], + (1-x-y)*(*this->texture_coords)[0][1]+x*(*this->texture_coords)[1][1]+y*(*this->texture_coords)[2][1]); + out.norm = this->get_barycentric_normal(x, y, 1-x-y); + return out; +} + +bool triangle::intersect_shadow(const ray &r) { + return false; +} + +normal *triangle::get_avg_normal() { + return new normal(normalise((*this->normals)[0]+(*this->normals)[1]+(*this->normals)[2])); +} + +normal *triangle::get_barycentric_normal(const float &a, const float &b, const float &c) { + return new normal(normalise((*this->normals)[0]*a+(*this->normals)[1]*b+(*this->normals)[2]*c)); +} \ No newline at end of file diff --git a/lib/geometry/shapes/triangle.h b/lib/geometry/shapes/triangle.h new file mode 100644 index 0000000..247ad48 --- /dev/null +++ b/lib/geometry/shapes/triangle.h @@ -0,0 +1,27 @@ +// +// Created by chais on 18.09.15. +// + +#ifndef RAY_TRACER_TRIANGLE_H +#define RAY_TRACER_TRIANGLE_H + +#include "shape.h" + +class triangle { +protected: + std::array *vertices; + std::array *normals; + std::array *texture_coords; +public: + triangle(std::array *vertices, std::array *normals, std::array *texture_coords); + + virtual intersection intersect_full(const ray &r); + + virtual bool intersect_shadow(const ray &r); + + virtual normal * get_avg_normal(); + + virtual normal * get_barycentric_normal(const float &a, const float &b, const float &c); +}; + +#endif //RAY_TRACER_TRIANGLE_H diff --git a/lib/light/ambient_light.cpp b/lib/light/ambient_light.cpp index 49fcfe6..9ea2f30 100644 --- a/lib/light/ambient_light.cpp +++ b/lib/light/ambient_light.cpp @@ -4,17 +4,17 @@ #include "ambient_light.h" -ambient_light::ambient_light(color col) : light(col) { } +ambient_light::ambient_light(const color *col) : light(col) { } std::ostream &operator<<(std::ostream &out, const ambient_light &a) { - out << "Ambient light: color [r, g, b]: " << a.col; + out << "Ambient light: color [r, g, b]: " << *a.col; return out; } -direction &ambient_light::get_direction(const point &pos) { - return this->dir; +direction *ambient_light::get_direction(const point &pos) { + return new direction(*this->dir); } -color &ambient_light::emit(const direction &dir) { - return this->col; +color *ambient_light::emit(const direction &dir) { + return new color(*this->col); } diff --git a/lib/light/ambient_light.h b/lib/light/ambient_light.h index cdf3c8d..e75067e 100644 --- a/lib/light/ambient_light.h +++ b/lib/light/ambient_light.h @@ -11,12 +11,13 @@ class ambient_light : public light { public: - ambient_light(color col); + ambient_light(const color *col); + friend std::ostream &operator<<(std::ostream &out, const ambient_light &a); - virtual direction &get_direction(const point &pos); + virtual direction *get_direction(const point &pos); - virtual color &emit(const direction &dir); + virtual color *emit(const direction &dir); }; #endif //RAY_TRACER_AMBIENT_LIGHT_H diff --git a/lib/light/light.cpp b/lib/light/light.cpp index b719b5b..d31c53a 100644 --- a/lib/light/light.cpp +++ b/lib/light/light.cpp @@ -3,8 +3,9 @@ // #include "light.h" -#include "../geometry/direction.h" -light::light(const color &col) : col(col), dir(direction()) { } +light::light(const color *col) : col(col), dir(new direction()) { } -light::light(const color &col, const direction &dir) : col(col), dir(normalise(dir)) { } +light::light(const color *col, const direction *dir) : col(col), dir(new direction(normalise(*dir))) { + +} diff --git a/lib/light/light.h b/lib/light/light.h index eb2c8cc..64b7d9f 100644 --- a/lib/light/light.h +++ b/lib/light/light.h @@ -8,20 +8,21 @@ #include #include "../geometry/color.h" #include "../geometry/point.h" +#include "../geometry/transform.h" class light { protected: - color col; - direction dir; + const color *col; + const direction *dir; - light(const color &col); + light(const color *col); - light(const color &col, const direction &dir); + light(const color *col, const direction *dir); public: - virtual direction &get_direction(const point &pos) = 0; + virtual direction *get_direction(const point &pos) = 0; - virtual color &emit(const direction &dir) = 0; + virtual color *emit(const direction &dir) = 0; }; #endif //RAY_TRACER_LIGHT_H diff --git a/lib/light/parallel_light.cpp b/lib/light/parallel_light.cpp index 754ccf9..2166986 100644 --- a/lib/light/parallel_light.cpp +++ b/lib/light/parallel_light.cpp @@ -4,17 +4,17 @@ #include "parallel_light.h" -parallel_light::parallel_light(const color &col, const direction &dir) : light(col, dir) { } +parallel_light::parallel_light(const color *col, const direction *dir) : light(col, dir) { } std::ostream &operator<<(std::ostream &out, const parallel_light &a) { - out << "Parallel light: color [r, g, b]: " << a.col << ", direction: " << a.dir; + out << "Parallel light: color [r, g, b]: " << *a.col << ", direction: " << *a.dir; return out; } -direction ¶llel_light::get_direction(const point &pos) { - return this->dir; +direction *parallel_light::get_direction(const point &pos) { + return new direction(*this->dir); } -color ¶llel_light::emit(const direction &dir) { - return this->col; +color *parallel_light::emit(const direction &dir) { + return new color(*this->col); } diff --git a/lib/light/parallel_light.h b/lib/light/parallel_light.h index 3151255..7772ffc 100644 --- a/lib/light/parallel_light.h +++ b/lib/light/parallel_light.h @@ -9,13 +9,13 @@ class parallel_light : public light { public: - parallel_light(const color &col, const direction &dir); + parallel_light(const color *col, const direction *dir); friend std::ostream &operator<<(std::ostream &out, const parallel_light &a); - virtual direction &get_direction(const point &pos); + virtual direction *get_direction(const point &pos); - virtual color &emit(const direction &dir); + virtual color *emit(const direction &dir); }; #endif //RAY_TRACER_PARALLEL_LIGHT_H diff --git a/lib/light/point_light.cpp b/lib/light/point_light.cpp new file mode 100644 index 0000000..68b6078 --- /dev/null +++ b/lib/light/point_light.cpp @@ -0,0 +1,15 @@ +// +// Created by chais on 15.09.15. +// + +#include "point_light.h" + +point_light::point_light(const color *col, const point *pos) : light(col), position(pos) { } + +direction *point_light::get_direction(const point &pos) { + return new direction(normalise(pos-*this->position)); +} + +color *point_light::emit(const direction &dir) { + return new color(*this->col); +} diff --git a/lib/light/point_light.h b/lib/light/point_light.h new file mode 100644 index 0000000..f5f8a13 --- /dev/null +++ b/lib/light/point_light.h @@ -0,0 +1,21 @@ +// +// Created by chais on 15.09.15. +// + +#ifndef RAY_TRACER_POINT_LIGHT_H +#define RAY_TRACER_POINT_LIGHT_H + +#include "light.h" + +class point_light : public light { +protected: + const point *position; +public: + point_light(const color *col, const point *pos); + + virtual direction *get_direction(const point &pos); + + virtual color *emit(const direction &dir); +}; + +#endif //RAY_TRACER_POINT_LIGHT_H \ No newline at end of file diff --git a/lib/math/helper.cpp b/lib/math/helper.cpp index 9e0f5f6..7a9aad5 100644 --- a/lib/math/helper.cpp +++ b/lib/math/helper.cpp @@ -11,4 +11,4 @@ float helper::to_radians(float angle) { bool helper::almost_equal(const float &x, const float &y, const int ulp) { return std::abs(x-y) < std::numeric_limits::epsilon()*std::abs(x-y)*ulp || std::abs(x-y) < std::numeric_limits::min(); -} +} \ No newline at end of file diff --git a/lib/math/helper.h b/lib/math/helper.h index 4c9baab..646c03b 100644 --- a/lib/math/helper.h +++ b/lib/math/helper.h @@ -11,6 +11,7 @@ class helper { public: static float to_radians(float angle); + static bool almost_equal(const float &x, const float &y, const int ulp); }; diff --git a/lib/math/mat4.cpp b/lib/math/mat4.cpp index 120328e..dc1c3c7 100644 --- a/lib/math/mat4.cpp +++ b/lib/math/mat4.cpp @@ -24,12 +24,12 @@ mat4 &mat4::operator=(const mat4 &in) { } std::array mat4::operator[](const unsigned long i) { - assert(0 <= i <= 3); + assert(0 <= i && i <= 3); return this->m[i]; } const std::array mat4::operator[](const unsigned long i) const { - assert(0 <= i <= 3); + assert(0 <= i && i <= 3); return this->m[i]; } diff --git a/lib/math/mat4.h b/lib/math/mat4.h index 9449dd6..9bc023e 100644 --- a/lib/math/mat4.h +++ b/lib/math/mat4.h @@ -215,7 +215,6 @@ vec4 operator*(const vec4 &a, const mat4 &b); mat4 operator*(const mat4 &a, const mat4 &b); /** - * /** * @brief Multiplication assignment * * Mulitplies the matrix lhs with the scalar rhs. The result is assigned to lhs. This is equivalent to lhs=lhs*rhs. diff --git a/lib/math/vec2.cpp b/lib/math/vec2.cpp index a60287a..5f8554d 100644 --- a/lib/math/vec2.cpp +++ b/lib/math/vec2.cpp @@ -23,12 +23,12 @@ vec2 &vec2::operator=(const vec2 &in) { } float &vec2::operator[](const unsigned long i) { - assert(0 <= i <= 1); + assert(0 <= i && i <= 1); return this->a[i]; } const float &vec2::operator[](const unsigned long i) const { - assert(0 <= i <= 1); + assert(0 <= i && i <= 1); return this->a[i]; } diff --git a/lib/math/vec4.cpp b/lib/math/vec4.cpp index 98aecee..b7178af 100644 --- a/lib/math/vec4.cpp +++ b/lib/math/vec4.cpp @@ -25,12 +25,12 @@ vec4 &vec4::operator=(const vec4 &in) { } float &vec4::operator[](const unsigned long i) { - assert(0 <= i <= 3); + assert(0 <= i && i <= 3); return this->a[i]; } const float &vec4::operator[](const unsigned long i) const { - assert(0 <= i <= 3); + assert(0 <= i && i <= 3); return this->a[i]; } diff --git a/lib/parser.cpp b/lib/parser.cpp new file mode 100644 index 0000000..9503eb6 --- /dev/null +++ b/lib/parser.cpp @@ -0,0 +1,181 @@ +// +// Created by chais on 15.09.15. +// + +#include "parser.h" +#include "geometry/material/lambertian_material.h" + +whitted_rt *parser::parse(const char *in_path, std::string &out_path) { + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(in_path, pugi::parse_default | pugi::parse_doctype); + assert(result); + pugi::xml_node scene = doc.child("scene"); + out_path = scene.attribute("output_file").value(); + color *background_color = parse_color(scene.child("background_color")); + camera *cam = parse_camera(scene.child("camera")); + std::vector *lights = parse_lights(scene.child("lights")); + std::vector *geometry = parse_surfaces(scene.child("surfaces")); + return new whitted_rt(background_color, cam, lights, geometry); +} + +camera *parser::parse_camera(const pugi::xml_node &cam) { + if (std::string(cam.attribute("type").value()) == "perspective") { + point *position = parse_position(cam.child("position")); + point *lookat = parse_position(cam.child("lookat")); + direction *up = parse_direction(cam.child("up")); + std::array resolution = {std::strtoul(cam.child("resolution").attribute("horizontal").value(), + nullptr, 10), + std::strtoul(cam.child("resolution").attribute("vertical").value(), + nullptr, 10)}; + perspective_camera *out = new perspective_camera(*position, + *lookat, + *up, + resolution, + std::strtoul(cam.child("max_bounces").attribute("n").value(), + nullptr, 10), + 1, // Number of samples per pixel + std::strtof( + cam.child("horizontal_fov").attribute("angle").value(), + nullptr) + ); + delete position; + delete lookat; + delete up; + return out; + } + return nullptr; +} + +std::vector *parser::parse_lights(const pugi::xml_node &lights) { + std::vector *out = new std::vector(); + out->push_back(parse_ambient_light(lights.child("ambient_light"))); + for (pugi::xml_node l : lights.children("parallel_light")) + out->push_back(parse_parallel_light(l)); + for (pugi::xml_node l : lights.children("point_light")) + out->push_back(parse_point_light(l)); + return out; +} + +ambient_light *parser::parse_ambient_light(const pugi::xml_node &l) { + return new ambient_light(parse_color(l.child("color"))); +} + +parallel_light *parser::parse_parallel_light(const pugi::xml_node &l) { + return new parallel_light(parse_color(l.child("color")), + parse_direction(l.child("direction"))); +} + +point_light *parser::parse_point_light(const pugi::xml_node &l) { + return new point_light(parse_color(l.child("color")), + parse_position(l.child("position"))); +} + +std::vector *parser::parse_surfaces(const pugi::xml_node &surfaces) { + std::vector *out = new std::vector(); + for (pugi::xml_node s : surfaces.children("sphere")) + out->push_back(parse_sphere(s)); + for (pugi::xml_node m : surfaces.children("mesh")) + out->push_back(parse_mesh(m)); + return out; +} + +sphere *parser::parse_sphere(pugi::xml_node &s) { + material *matrl = parse_material(s.child("material")); + sphere *out = new sphere(std::strtof(s.attribute("radius").value(), nullptr), parse_direction(s.child("position")), + matrl); + for (pugi::xml_node t : s.children("transforms")) + parse_transform(t, out); + return out; +} + +mesh *parser::parse_mesh(const pugi::xml_node &m) { + std::vector shapes; + std::vector materials; + std::string err = tinyobj::LoadObj(shapes, materials, m.attribute("name").value()); + if (!err.empty()) { + std::cerr << err << std::endl; + exit(1); + } + std::vector *triangles = new std::vector(); + for (tinyobj::shape_t s : shapes) + for (int i = 0; i < s.mesh.indices.size()/3; i++) { + std::array *vertices = new std::array(); + std::array *normals = new std::array(); + std::array *texture_coords = new std::array(); + for (int ii = 0; ii < 3; ii++) { + vertices->at(ii) = point(s.mesh.positions[s.mesh.indices[i*3+ii]*3], + s.mesh.positions[s.mesh.indices[i*3+ii]*3+1], + s.mesh.positions[s.mesh.indices[i*3+ii]*3+2]); + normals->at(ii) = normal(s.mesh.normals[s.mesh.indices[i*3+ii]*3], + s.mesh.normals[s.mesh.indices[i*3+ii]*3+1], + s.mesh.normals[s.mesh.indices[i*3+ii]*3+2]); + texture_coords->at(ii) = vec2(s.mesh.texcoords[s.mesh.indices[i*3+ii]*2], + s.mesh.texcoords[s.mesh.indices[i*3+ii]*2+1]); + } + triangles->push_back(new triangle(vertices, normals, texture_coords)); + } + material *matrl; + if (materials.empty()) { + matrl = parse_material(m.child("material")); + } + return new mesh(new direction(), matrl, triangles); +} + +void parser::parse_transform(const pugi::xml_node &t, sphere *s) { + std::array sf = { + std::strtof(t.child("scale").attribute("x").value(), nullptr), + std::strtof(t.child("scale").attribute("y").value(), nullptr), + std::strtof(t.child("scale").attribute("z").value(), nullptr) + }; + s->scale(sf); + for (pugi::xml_node r : t.children()) { + if (std::string(r.name()) == "rotateX") + s->rotateX(std::strtof(r.attribute("theta").value(), nullptr)); + else if (std::string(r.name()) == "rotateY") + s->rotateY(std::strtof(r.attribute("theta").value(), nullptr)); + else if (std::string(r.name()) == "rotateZ") + s->rotateZ(std::strtof(r.attribute("theta").value(), nullptr)); + } + direction *d = parse_direction(t.child("translate")); + s->translate(*d); + delete d; +} + +material *parser::parse_material(const pugi::xml_node &m) { + if (std::string(m.attribute("type").value()) == "phong") { + color *col = parse_color(m.child("color")); + phong_material *out = new phong_material(*col, + std::strtof(m.child("phong").attribute("ka").value(), nullptr), + std::strtof(m.child("phong").attribute("kd").value(), nullptr), + std::strtof(m.child("phong").attribute("ks").value(), nullptr), + std::strtof(m.child("phong").attribute("exponent").value(), nullptr)); + delete col; + return out; + } else if (std::string(m.attribute("type").value()) == "lambertian") { + color *col = parse_color(m.child("color")); + lambertian_material *out = new lambertian_material(*col, + std::strtof(m.child("lambertian").attribute("ka").value(), nullptr), + std::strtof(m.child("lambertian").attribute("kd").value(), nullptr)); + delete col; + return out; + } + return nullptr; +} + +color *parser::parse_color(const pugi::xml_node &col) { + return new color(std::strtof(col.attribute("r").value(), nullptr), + std::strtof(col.attribute("g").value(), nullptr), + std::strtof(col.attribute("b").value(), nullptr)); +} + +point *parser::parse_position(const pugi::xml_node &p) { + return new point(std::strtof(p.attribute("x").value(), nullptr), + std::strtof(p.attribute("y").value(), nullptr), + std::strtof(p.attribute("z").value(), nullptr)); +} + +direction *parser::parse_direction(const pugi::xml_node &d) { + return new direction(std::strtof(d.attribute("x").value(), nullptr), + std::strtof(d.attribute("y").value(), nullptr), + std::strtof(d.attribute("z").value(), nullptr)); +} diff --git a/lib/parser.h b/lib/parser.h new file mode 100644 index 0000000..990bc96 --- /dev/null +++ b/lib/parser.h @@ -0,0 +1,54 @@ +// +// Created by chais on 15.09.15. +// + +#ifndef RAY_TRACER_PARSER_H +#define RAY_TRACER_PARSER_H + +#include "whitted_rt.h" +#include "camera/perspective_camera.h" +#include "light/ambient_light.h" +#include "light/parallel_light.h" +#include "light/point_light.h" +#include "geometry/shapes/sphere.h" +#include "geometry/shapes/mesh.h" +#include "geometry/material/phong_material.h" +#include "tiny_obj_loader.h" +#include +#include +#include +#include + +class parser { +private: + static camera *parse_camera(const pugi::xml_node &cam); + + static std::vector *parse_lights(const pugi::xml_node &lights); + + static ambient_light *parse_ambient_light(const pugi::xml_node &l); + + static parallel_light *parse_parallel_light(const pugi::xml_node &l); + + static point_light *parse_point_light(const pugi::xml_node &l); + + static std::vector *parse_surfaces(const pugi::xml_node &surfaces); + + static sphere *parse_sphere(pugi::xml_node &s); + + static mesh *parse_mesh(const pugi::xml_node &m); + + static material *parse_material(const pugi::xml_node &m); + + static void parse_transform(const pugi::xml_node &t, sphere *s); + + static color *parse_color(const pugi::xml_node &col); + + static point *parse_position(const pugi::xml_node &p); + + static direction *parse_direction(const pugi::xml_node &d); + +public: + static whitted_rt *parse(const char *in_path, std::string &out_path); +}; + +#endif //RAY_TRACER_PARSER_H \ No newline at end of file diff --git a/lib/tiny_obj_loader.cpp b/lib/tiny_obj_loader.cpp new file mode 100644 index 0000000..6d6842a --- /dev/null +++ b/lib/tiny_obj_loader.cpp @@ -0,0 +1,912 @@ +// +// Copyright 2012-2015, Syoyo Fujita. +// +// Licensed under 2-clause BSD liecense. +// + +// +// version 0.9.14: Support specular highlight, bump, displacement and alpha map(#53) +// version 0.9.13: Report "Material file not found message" in `err`(#46) +// version 0.9.12: Fix groups being ignored if they have 'usemtl' just before 'g' (#44) +// version 0.9.11: Invert `Tr` parameter(#43) +// version 0.9.10: Fix seg fault on windows. +// version 0.9.9 : Replace atof() with custom parser. +// version 0.9.8 : Fix multi-materials(per-face material ID). +// version 0.9.7 : Support multi-materials(per-face material ID) per +// object/group. +// version 0.9.6 : Support Ni(index of refraction) mtl parameter. +// Parse transmittance material parameter correctly. +// version 0.9.5 : Parse multiple group name. +// Add support of specifying the base path to load material file. +// version 0.9.4 : Initial suupport of group tag(g) +// version 0.9.3 : Fix parsing triple 'x/y/z' +// version 0.9.2 : Add more .mtl load support +// version 0.9.1 : Add initial .mtl load support +// version 0.9.0 : Initial +// + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tiny_obj_loader.h" + +namespace tinyobj { + +#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) + + struct vertex_index { + int v_idx, vt_idx, vn_idx; + + vertex_index() { }; + + vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) { }; + + vertex_index(int vidx, int vtidx, int vnidx) : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) { }; + }; + +// for std::map + static inline bool operator<(const vertex_index &a, const vertex_index &b) { + if (a.v_idx != b.v_idx) + return (a.v_idx < b.v_idx); + if (a.vn_idx != b.vn_idx) + return (a.vn_idx < b.vn_idx); + if (a.vt_idx != b.vt_idx) + return (a.vt_idx < b.vt_idx); + + return false; + } + + struct obj_shape { + std::vector v; + std::vector vn; + std::vector vt; + }; + + static inline bool isSpace(const char c) { return (c == ' ') || (c == '\t'); } + + static inline bool isNewLine(const char c) { + return (c == '\r') || (c == '\n') || (c == '\0'); + } + +// Make index zero-base, and also support relative index. + static inline int fixIndex(int idx, int n) { + if (idx > 0) return idx-1; + if (idx == 0) return 0; + return n+idx; // negative value = relative + } + + static inline std::string parseString(const char *&token) { + std::string s; + token += strspn(token, " \t"); + size_t e = strcspn(token, " \t\r"); + s = std::string(token, &token[e]); + token += e; + return s; + } + + static inline int parseInt(const char *&token) { + token += strspn(token, " \t"); + int i = atoi(token); + token += strcspn(token, " \t\r"); + return i; + } + +// Tries to parse a floating point number located at s. +// +// s_end should be a location in the string where reading should absolutely +// stop. For example at the end of the string, to prevent buffer overflows. +// +// Parses the following EBNF grammar: +// sign = "+" | "-" ; +// END = ? anything not in digit ? +// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +// integer = [sign] , digit , {digit} ; +// decimal = integer , ["." , integer] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; +// +// Valid strings are for example: +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 +// +// If the parsing is a success, result is set to the parsed value and true +// is returned. +// +// The function is greedy and will parse until any of the following happens: +// - a non-conforming character is encountered. +// - s_end is reached. +// +// The following situations triggers a failure: +// - s >= s_end. +// - parse failure. +// + static bool tryParseDouble(const char *s, const char *s_end, double *result) { + if (s >= s_end) { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') { + sign = *curr; + curr++; + } + else if (isdigit(*curr)) { /* Pass through. */ } + else { + goto fail; + } + + // Read the integer part. + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { + mantissa *= 10; + mantissa += static_cast(*curr-0x30); + curr++; + read++; + } + + // We must make sure we actually got something. + if (read == 0) + goto fail; + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) + goto assemble; + + // Read the decimal part. + if (*curr == '.') { + curr++; + read = 1; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr-0x30)*pow(10.0, -read); + read++; + curr++; + } + } + else if (*curr == 'e' || *curr == 'E') { } + else { + goto assemble; + } + + if (!end_not_reached) + goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') { + curr++; + // Figure out if a sign is present and if it is. + if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) { + exp_sign = *curr; + curr++; + } + else if (isdigit(*curr)) { /* Pass through. */ } + else { + // Empty E is not allowed. + goto fail; + } + + read = 0; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { + exponent *= 10; + exponent += static_cast(*curr-0x30); + curr++; + read++; + } + exponent *= (exp_sign == '+' ? 1 : -1); + if (read == 0) + goto fail; + } + + assemble: + *result = (sign == '+' ? 1 : -1)*ldexp(mantissa*pow(5.0, exponent), exponent); + return true; + fail: + return false; + } + + static inline float parseFloat(const char *&token) { + token += strspn(token, " \t"); +#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER + float f = (float)atof(token); + token += strcspn(token, " \t\r"); +#else + const char *end = token+strcspn(token, " \t\r"); + double val = 0.0; + tryParseDouble(token, end, &val); + float f = static_cast(val); + token = end; +#endif + return f; + } + + static inline void parseFloat2(float &x, float &y, const char *&token) { + x = parseFloat(token); + y = parseFloat(token); + } + + static inline void parseFloat3(float &x, float &y, float &z, + const char *&token) { + x = parseFloat(token); + y = parseFloat(token); + z = parseFloat(token); + } + +// Parse triples: i, i/j/k, i//k, i/j + static vertex_index parseTriple(const char *&token, int vsize, int vnsize, + int vtsize) { + vertex_index vi(-1); + + vi.v_idx = fixIndex(atoi(token), vsize); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return vi; + } + token++; + + // i//k + if (token[0] == '/') { + token++; + vi.vn_idx = fixIndex(atoi(token), vnsize); + token += strcspn(token, "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = fixIndex(atoi(token), vtsize); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return vi; + } + + // i/j/k + token++; // skip '/' + vi.vn_idx = fixIndex(atoi(token), vnsize); + token += strcspn(token, "/ \t\r"); + return vi; + } + + static unsigned int + updateVertex(std::map &vertexCache, + std::vector &positions, std::vector &normals, + std::vector &texcoords, + const std::vector &in_positions, + const std::vector &in_normals, + const std::vector &in_texcoords, const vertex_index &i) { + const std::map::iterator it = vertexCache.find(i); + + if (it != vertexCache.end()) { + // found cache + return it->second; + } + + assert(in_positions.size() > (unsigned int) (3*i.v_idx+2)); + + positions.push_back(in_positions[3*i.v_idx+0]); + positions.push_back(in_positions[3*i.v_idx+1]); + positions.push_back(in_positions[3*i.v_idx+2]); + + if (i.vn_idx >= 0) { + normals.push_back(in_normals[3*i.vn_idx+0]); + normals.push_back(in_normals[3*i.vn_idx+1]); + normals.push_back(in_normals[3*i.vn_idx+2]); + } + + if (i.vt_idx >= 0) { + texcoords.push_back(in_texcoords[2*i.vt_idx+0]); + texcoords.push_back(in_texcoords[2*i.vt_idx+1]); + } + + unsigned int idx = static_cast(positions.size()/3-1); + vertexCache[i] = idx; + + return idx; + } + + void InitMaterial(material_t &material) { + material.name = ""; + material.ambient_texname = ""; + material.diffuse_texname = ""; + material.specular_texname = ""; + material.specular_highlight_texname = ""; + material.bump_texname = ""; + material.displacement_texname = ""; + material.alpha_texname = ""; + for (int i = 0; i < 3; i++) { + material.ambient[i] = 0.f; + material.diffuse[i] = 0.f; + material.specular[i] = 0.f; + material.transmittance[i] = 0.f; + material.emission[i] = 0.f; + } + material.illum = 0; + material.dissolve = 1.f; + material.shininess = 1.f; + material.ior = 1.f; + material.unknown_parameter.clear(); + } + + static bool exportFaceGroupToShape( + shape_t &shape, std::map vertexCache, + const std::vector &in_positions, + const std::vector &in_normals, + const std::vector &in_texcoords, + const std::vector > &faceGroup, + const int material_id, const std::string &name, bool clearCache) { + if (faceGroup.empty()) { + return false; + } + + // Flatten vertices and indices + for (size_t i = 0; i < faceGroup.size(); i++) { + const std::vector &face = faceGroup[i]; + + vertex_index i0 = face[0]; + vertex_index i1(-1); + vertex_index i2 = face[1]; + + size_t npolys = face.size(); + + // Polygon -> triangle fan conversion + for (size_t k = 2; k < npolys; k++) { + i1 = i2; + i2 = face[k]; + + unsigned int v0 = updateVertex( + vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0); + unsigned int v1 = updateVertex( + vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1); + unsigned int v2 = updateVertex( + vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2); + + shape.mesh.indices.push_back(v0); + shape.mesh.indices.push_back(v1); + shape.mesh.indices.push_back(v2); + + shape.mesh.material_ids.push_back(material_id); + } + } + + shape.name = name; + + if (clearCache) + vertexCache.clear(); + + return true; + } + + std::string LoadMtl(std::map &material_map, + std::vector &materials, + std::istream &inStream) { + std::stringstream err; + + // Create a default material anyway. + material_t material; + InitMaterial(material); + + int maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. + while (inStream.peek() != -1) { + inStream.getline(&buf[0], maxchars); + + std::string linebuf(&buf[0]); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size()-1] == '\n') + linebuf.erase(linebuf.size()-1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size()-1] == '\r') + linebuf.erase(linebuf.size()-1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') + continue; // empty line + + if (token[0] == '#') + continue; // comment line + + // new mtl + if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { + // flush previous material. + if (!material.name.empty()) { + material_map.insert( + std::pair(material.name, static_cast(materials.size()))); + materials.push_back(material); + } + + // initial temporary material + InitMaterial(material); + + // set new mtl name + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + material.name = namebuf; + continue; + } + + // ambient + if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + // diffuse + if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + // specular + if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + // transmittance + if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) { + token += 2; + material.ior = parseFloat(token); + continue; + } + + // emission + if (token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + // shininess + if (token[0] == 'N' && token[1] == 's' && isSpace(token[2])) { + token += 2; + material.shininess = parseFloat(token); + continue; + } + + // illum model + if (0 == strncmp(token, "illum", 5) && isSpace(token[5])) { + token += 6; + material.illum = parseInt(token); + continue; + } + + // dissolve + if ((token[0] == 'd' && isSpace(token[1]))) { + token += 1; + material.dissolve = parseFloat(token); + continue; + } + if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) { + token += 2; + // Invert value of Tr(assume Tr is in range [0, 1]) + material.dissolve = 1.0f-parseFloat(token); + continue; + } + + // ambient texture + if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6])) { + token += 7; + material.ambient_texname = token; + continue; + } + + // diffuse texture + if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6])) { + token += 7; + material.diffuse_texname = token; + continue; + } + + // specular texture + if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6])) { + token += 7; + material.specular_texname = token; + continue; + } + + // specular highlight texture + if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) { + token += 7; + material.specular_highlight_texname = token; + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_bump", 8)) && isSpace(token[8])) { + token += 9; + material.bump_texname = token; + continue; + } + + // alpha texture + if ((0 == strncmp(token, "map_d", 5)) && isSpace(token[5])) { + token += 6; + material.alpha_texname = token; + continue; + } + + // bump texture + if ((0 == strncmp(token, "bump", 4)) && isSpace(token[4])) { + token += 5; + material.bump_texname = token; + continue; + } + + // displacement texture + if ((0 == strncmp(token, "disp", 4)) && isSpace(token[4])) { + token += 5; + material.displacement_texname = token; + continue; + } + + // unknown parameter + const char *_space = strchr(token, ' '); + if (!_space) { + _space = strchr(token, '\t'); + } + if (_space) { + std::ptrdiff_t len = _space-token; + std::string key(token, len); + std::string value = _space+1; + material.unknown_parameter.insert( + std::pair(key, value)); + } + } + // flush last material. + material_map.insert( + std::pair(material.name, static_cast(materials.size()))); + materials.push_back(material); + + return err.str(); + } + + std::string MaterialFileReader::operator()(const std::string &matId, + std::vector &materials, + std::map &matMap) { + std::string filepath; + + if (!m_mtlBasePath.empty()) { + filepath = std::string(m_mtlBasePath)+matId; + } else { + filepath = matId; + } + + std::ifstream matIStream(filepath.c_str()); + std::string err = LoadMtl(matMap, materials, matIStream); + if (!matIStream) { + std::stringstream ss; + ss << "WARN: Material file [ " << filepath << " ] not found. Created a default material."; + err += ss.str(); + } + return err; + } + + std::string LoadObj(std::vector &shapes, + std::vector &materials, // [output] + const char *filename, const char *mtl_basepath) { + + shapes.clear(); + + std::stringstream err; + + std::ifstream ifs(filename); + if (!ifs) { + err << "Cannot open file [" << filename << "]" << std::endl; + return err.str(); + } + + std::string basePath; + if (mtl_basepath) { + basePath = mtl_basepath; + } + MaterialFileReader matFileReader(basePath); + + return LoadObj(shapes, materials, ifs, matFileReader); + } + + std::string LoadObj(std::vector &shapes, + std::vector &materials, // [output] + std::istream &inStream, MaterialReader &readMatFn) { + std::stringstream err; + + std::vector v; + std::vector vn; + std::vector vt; + std::vector > faceGroup; + std::string name; + + // material + std::map material_map; + std::map vertexCache; + int material = -1; + + shape_t shape; + + int maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. + while (inStream.peek() != -1) { + inStream.getline(&buf[0], maxchars); + + std::string linebuf(&buf[0]); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size()-1] == '\n') + linebuf.erase(linebuf.size()-1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size()-1] == '\r') + linebuf.erase(linebuf.size()-1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') + continue; // empty line + + if (token[0] == '#') + continue; // comment line + + // vertex + if (token[0] == 'v' && isSpace((token[1]))) { + token += 2; + float x, y, z; + parseFloat3(x, y, z, token); + v.push_back(x); + v.push_back(y); + v.push_back(z); + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2]))) { + token += 3; + float x, y, z; + parseFloat3(x, y, z, token); + vn.push_back(x); + vn.push_back(y); + vn.push_back(z); + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && isSpace((token[2]))) { + token += 3; + float x, y; + parseFloat2(x, y, token); + vt.push_back(x); + vt.push_back(y); + continue; + } + + // face + if (token[0] == 'f' && isSpace((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + std::vector face; + while (!isNewLine(token[0])) { + vertex_index vi = + parseTriple(token, static_cast(v.size()/3), static_cast(vn.size()/3), + static_cast(vt.size()/2)); + face.push_back(vi); + size_t n = strspn(token, " \t\r"); + token += n; + } + + faceGroup.push_back(face); + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) { + + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + + // Create face group per material. + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, + faceGroup, material, name, true); + if (ret) { + shapes.push_back(shape); + } + shape = shape_t(); + faceGroup.clear(); + + if (material_map.find(namebuf) != material_map.end()) { + material = material_map[namebuf]; + } else { + // { error!! material not found } + material = -1; + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + + std::string err_mtl = readMatFn(namebuf, materials, material_map); + if (!err_mtl.empty()) { + faceGroup.clear(); // for safety + return err_mtl; + } + + continue; + } + + // group name + if (token[0] == 'g' && isSpace((token[1]))) { + + // flush previous face group. + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, + faceGroup, material, name, true); + if (ret) { + shapes.push_back(shape); + } + + shape = shape_t(); + + // material = -1; + faceGroup.clear(); + + std::vector names; + while (!isNewLine(token[0])) { + std::string str = parseString(token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + // names[0] must be 'g', so skip the 0th element. + if (names.size() > 1) { + name = names[1]; + } else { + name = ""; + } + + continue; + } + + // object name + if (token[0] == 'o' && isSpace((token[1]))) { + + // flush previous face group. + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, + faceGroup, material, name, true); + if (ret) { + shapes.push_back(shape); + } + + // material = -1; + faceGroup.clear(); + shape = shape_t(); + + // @todo { multiple object name? } + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + name = std::string(namebuf); + + continue; + } + + // Ignore unknown command. + } + + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, + material, name, true); + if (ret) { + shapes.push_back(shape); + } + faceGroup.clear(); // for safety + + return err.str(); + } +} diff --git a/lib/tiny_obj_loader.h b/lib/tiny_obj_loader.h new file mode 100644 index 0000000..a2f0f89 --- /dev/null +++ b/lib/tiny_obj_loader.h @@ -0,0 +1,100 @@ +// +// Copyright 2012-2015, Syoyo Fujita. +// +// Licensed under 2-clause BSD liecense. +// +#ifndef _TINY_OBJ_LOADER_H +#define _TINY_OBJ_LOADER_H + +#include +#include +#include + +namespace tinyobj { + + typedef struct { + std::string name; + + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; // index of refraction + float dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; + + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d + std::map unknown_parameter; + } material_t; + + typedef struct { + std::vector positions; + std::vector normals; + std::vector texcoords; + std::vector indices; + std::vector material_ids; // per-mesh material ID + } mesh_t; + + typedef struct { + std::string name; + mesh_t mesh; + } shape_t; + + class MaterialReader { + public: + MaterialReader() { } + + virtual ~MaterialReader() { } + + virtual std::string operator()(const std::string &matId, + std::vector &materials, + std::map &matMap) = 0; + }; + + class MaterialFileReader : public MaterialReader { + public: + MaterialFileReader(const std::string &mtl_basepath) + : m_mtlBasePath(mtl_basepath) { } + + virtual ~MaterialFileReader() { } + + virtual std::string operator()(const std::string &matId, + std::vector &materials, + std::map &matMap); + + private: + std::string m_mtlBasePath; + }; + +/// Loads .obj from a file. +/// 'shapes' will be filled with parsed shape data +/// The function returns error string. +/// Returns empty string when loading .obj success. +/// 'mtl_basepath' is optional, and used for base path for .mtl file. + std::string LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + const char *filename, const char *mtl_basepath = NULL); + +/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve +/// std::istream for materials. +/// Returns empty string when loading .obj success. + std::string LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + std::istream &inStream, MaterialReader &readMatFn); + +/// Loads materials into std::map +/// Returns an empty string if successful + std::string LoadMtl(std::map &material_map, + std::vector &materials, std::istream &inStream); +} + +#endif // _TINY_OBJ_LOADER_H diff --git a/lib/whitted_rt.cpp b/lib/whitted_rt.cpp index 7d42c12..6f24f0d 100644 --- a/lib/whitted_rt.cpp +++ b/lib/whitted_rt.cpp @@ -4,19 +4,20 @@ #include "whitted_rt.h" -whitted_rt::whitted_rt(const color &background_color, camera *cam, const std::vector &lights, - const std::vector &scene, const unsigned long &max_bounces) : background_color( - background_color), cam(cam), lights(lights), scene(scene), max_bounces(max_bounces) { } +whitted_rt::whitted_rt(const color *background_color, camera *cam, std::vector *lights, + std::vector *scene) : background_color( + background_color), cam(cam), lights(lights), scene(scene) { } color whitted_rt::cast_ray(ray r, int step, bool internal) { intersection is = this->find_nearest(r); if (is.object == nullptr) // No intersection - return this->background_color; + return *this->background_color; color out = color(); - for (light *l : lights) { - direction light_dir = l->get_direction(*is.pos); - if (!this->cast_shadow(ray (*is.pos, -light_dir))) - out += is.object->shade(l->emit(light_dir), -light_dir, *is.norm, -r.d, *is.local_pos, internal); + for (light *l : *lights) { + direction *light_dir = l->get_direction(*is.pos); + if (!this->cast_shadow(ray (*is.pos, -*light_dir))) + out += is.object->shade(*l->emit(*light_dir), -*light_dir, *is.norm, -r.d, *is.local_pos, internal); + delete light_dir; } return out; /*try { @@ -93,7 +94,7 @@ void whitted_rt::render() { } bool whitted_rt::cast_shadow(const ray &r) { - for (shape *obstacle : this->scene) + for (shape *obstacle : *this->scene) if (obstacle->intersect_shadow(r)) return true; return false; @@ -102,7 +103,7 @@ bool whitted_rt::cast_shadow(const ray &r) { intersection whitted_rt::find_nearest(ray r) { intersection out = intersection(); float dist = std::numeric_limits::max(); - for (shape *object : scene) { + for (shape *object : *this->scene) { intersection cur = object->intersect_full(r); if (cur.object == nullptr) continue; @@ -114,3 +115,11 @@ intersection whitted_rt::find_nearest(ray r) { } return out; } + +color *whitted_rt::get_pixel(const unsigned long &x, const unsigned long &y) const { + return this->cam->get_pixel(x, y); +} + +const std::array &whitted_rt::get_resolution() const { + return this->cam->get_resolution(); +} diff --git a/lib/whitted_rt.h b/lib/whitted_rt.h index 5331e70..d703f4e 100644 --- a/lib/whitted_rt.h +++ b/lib/whitted_rt.h @@ -9,16 +9,15 @@ #include "camera/camera.h" #include "light/light.h" -#include "geometry/shape.h" +#include "geometry/shapes/shape.h" #include class whitted_rt { private: - const color background_color; + const color *background_color; camera *cam; - std::vector lights; - std::vector scene; - const unsigned long max_bounces; + std::vector *lights; + std::vector *scene; color cast_ray(ray r, int step, bool internal); @@ -27,10 +26,12 @@ class whitted_rt { intersection find_nearest(ray r); public: - whitted_rt(const color &background_color, camera *cam, const std::vector &lights, - const std::vector &scene, const unsigned long &max_bounces); + whitted_rt(const color *background_color, camera *cam, std::vector *lights, + std::vector *scene); void render(); + color *get_pixel(const unsigned long &x, const unsigned long &y) const; + const std::array &get_resolution() const; }; #endif //RAY_TRACER_WHITTEDRT_H \ No newline at end of file diff --git a/main.cpp b/main.cpp index 14ac7ab..c4fb7df 100644 --- a/main.cpp +++ b/main.cpp @@ -1,47 +1,31 @@ -#include #include "lib/whitted_rt.h" -#include "lib/geometry/sphere.h" -#include "lib/geometry/material/solid_material.h" -#include "lib/light/parallel_light.h" -#include "lib/camera/perspective_camera.h" -#include "lib/geometry/material/phong_material.h" -#include "lib/light/ambient_light.h" +#include "lib/parser.h" +#include #include using namespace std; -int main() { - camera *cam = new perspective_camera(); - color bgcolor = color(); - std::vector lights; - lights.push_back(new ambient_light(color{1, 1, 1})); - lights.push_back(new parallel_light(color{1, 1, 1}, direction{0, -1, 0})); - phong_material *red_lack = new phong_material(color{1, 0, 0}, 0.1, 0.6, 0.7, 200); - std::vector scene; - scene.push_back(new sphere(1.0, red_lack)); - std::array sf = {2, 1, 1}; - scene[0]->scale(sf); - scene[0]->rotateZ(30); - scene[0]->translate(direction{0, -1, -7}); - scene.push_back(new sphere(1.0, red_lack)); - sf[0] = 100; - sf[1] = 0.1; - sf[2] = 100; - scene[1]->scale(sf); - scene[1]->translate(direction{0, -4, -1}); - whitted_rt rt = whitted_rt(bgcolor, cam, lights, scene, 10); - rt.render(); - png::image img(1024, 768); +int main(int argc, char *argv[]) { + if (argc == 1) { + cout << "Usage: " << argv[0] << " " << endl + << " - Path of the XML file specifying the camera, lights and geometry." << endl; + return 0; + } + std::string out_file; + whitted_rt *rt = parser::parse(argv[1], out_file); + rt->render(); + std::array res = rt->get_resolution(); + png::image img(res[0], res[1]); png::rgb_pixel *pPtr; - for (unsigned long y = 0; y < 768; y++) { + for (unsigned long y = 0; y < res[1]; y++) { pPtr = &img[y][0]; - for (unsigned long x = 0; x < 1024; x++) { - std::array tmp = rgb(*cam->get_pixel(x, y)); + for (unsigned long x = 0; x < res[0]; x++) { + std::array tmp = rgb(*rt->get_pixel(x, y)); (*pPtr).red = tmp[0]; (*pPtr).green = tmp[1]; (*pPtr).blue = tmp[2]; pPtr++; } } - img.write("test.png"); + img.write(out_file); } \ No newline at end of file diff --git a/open_room.obj b/open_room.obj new file mode 100644 index 0000000..85b06f9 --- /dev/null +++ b/open_room.obj @@ -0,0 +1,41 @@ +# Blender3D v249 OBJ File: +# www.blender3d.org +v -5.000000 7.500000 -10.000000 +v -5.000000 -2.500000 -10.000000 +v -5.000000 -2.500000 0.000000 +v -5.000000 7.500001 -0.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vn 1.000000 0.000000 0.000000 +usemtl (null) +s 1 +f 1/1/1 4/2/1 3/3/1 +f 1/1/1 3/3/1 2/4/1 +v 5.000000 7.500000 -10.000000 +v 5.000000 -2.500000 -10.000000 +v -5.000000 -2.500000 -10.000000 +v -5.000000 7.500000 -10.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vn 0.000000 0.000000 1.000000 +usemtl (null) +s 1 +f 5/5/2 8/6/2 7/7/2 +f 5/5/2 7/7/2 6/8/2 +v 5.000000 -2.500000 -0.000000 +v 5.000000 -2.500000 -10.000000 +v -5.000000 -2.500000 -10.000000 +v -5.000000 -2.500000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +usemtl (null) +s 1 +f 9/9/3 10/10/3 11/11/3 +f 9/9/3 11/11/3 12/12/3