Skip to content

Commit 03f0b18

Browse files
committedJul 14, 2022
Fix raytracer from zip file
1 parent 059ea7b commit 03f0b18

8 files changed

+503
-291
lines changed
 

‎CMakeLists.txt

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@ project(render-sample)
22
cmake_minimum_required(VERSION 3.15)
33
set(CMAKE_CXX_STANDARD 17)
44

5-
add_executable(${PROJECT_NAME} main.cpp)
5+
add_executable(raytracer-tests raytracer-tests.cpp)
66

7+
add_executable(${PROJECT_NAME} main.cpp)
78
find_package(PNG REQUIRED)
89
find_package(JPEG REQUIRED)
910
target_link_libraries(${PROJECT_NAME} ${PNG_LIBRARY} ${JPEG_LIBRARIES})
11+
12+
13+
enable_testing()
14+
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
15+
target_link_libraries(raytracer-tests gtest gtest_main)
16+
target_link_libraries(raytracer-tests ${PNG_LIBRARY} ${JPEG_LIBRARIES})

‎builder.h

+87-33
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#pragma once
22

3-
#include <unordered_map>
4-
53
#include "object.h"
64
#include "datatypes.h"
75

@@ -11,46 +9,96 @@ class SceneBuilder {
119
_state.name2material.emplace(name, mtl);
1210
}
1311

14-
void AddVertex(const std::array<float, 3>& params) {
12+
void AddVertex(const std::array<double, 3>& params) {
1513
_state.vertex.emplace_back(params[0], params[1], params[2]);
1614
}
1715

18-
void AddLight(const std::array<float, 6>& params) {
16+
void AddVertexNormal(const std::array<double, 3>& params) {
17+
_state.vertex_normal.emplace_back(params[0], params[1], params[2]);
18+
}
19+
20+
void AddLight(const std::array<double, 6>& params) {
1921
_scene.lights.emplace_back(Vec3f{params[0], params[1], params[2]},
20-
Vec3f{params[3], params[4], params[5]});
22+
Vec3f{params[3], params[4], params[5]});
2123
}
2224

23-
void BuildSphere(const std::string& mtl, const std::array<float, 4>& params) {
25+
void BuildSphere(const std::string& mtl, const std::array<double, 4>& params) {
2426
auto&& material = FindMaterial(mtl);
25-
_scene.objects.push_back(std::make_shared<Sphere>(Vec3f{params[0], params[1], params[2]}, params[3], material));
27+
_scene.objects.push_back(
28+
std::make_shared<Sphere>(Vec3f{params[0], params[1], params[2]}, params[3], material));
2629
}
2730

28-
void BuildPolygon(const std::string& mtl, const std::vector<int>& params) {
31+
void BuildPolygon(const std::string& mtl, const PolygonInfo& pi) {
2932
auto&& material = FindMaterial(mtl);
3033

31-
if (params.size() == 3) {
32-
int idx0 = params[0] >= 0 ? params[0]-1 : _state.vertex.size() + params[0];
33-
int idx1 = params[1] >= 0 ? params[1]-1 : _state.vertex.size() + params[1];
34-
int idx2 = params[2] >= 0 ? params[2]-1 : _state.vertex.size() + params[2];
35-
_scene.objects.push_back(std::make_shared<Triangle>(std::array<Vec3f, 3>{_state.vertex[idx0],
36-
_state.vertex[idx1],
37-
_state.vertex[idx2]},
38-
material));
39-
} else if (params.size() == 4) {
40-
int idx0 = params[0] >= 0 ? params[0]-1 : _state.vertex.size() + params[0];
41-
int idx1 = params[1] >= 0 ? params[1]-1 : _state.vertex.size() + params[1];
42-
int idx2 = params[2] >= 0 ? params[2]-1 : _state.vertex.size() + params[2];
43-
int idx3 = params[3] >= 0 ? params[3]-1 : _state.vertex.size() + params[3];
44-
45-
_scene.objects.push_back(std::make_shared<Triangle>(std::array<Vec3f, 3>{_state.vertex[idx0],
46-
_state.vertex[idx1],
47-
_state.vertex[idx2]},
48-
material));
49-
50-
_scene.objects.push_back(std::make_shared<Triangle>(std::array<Vec3f, 3>{_state.vertex[idx2],
51-
_state.vertex[idx3],
52-
_state.vertex[idx0]},
53-
material));
34+
if (pi.vertex.size() == 3) {
35+
int idx0 = GetNormalizedIndex(pi.vertex[0].v, _state.vertex.size());
36+
int idx1 = GetNormalizedIndex(pi.vertex[1].v, _state.vertex.size());
37+
int idx2 = GetNormalizedIndex(pi.vertex[2].v, _state.vertex.size());
38+
39+
// FIXME: If one vertex has explicit normal others should have as well
40+
if (pi.vertex[0].vn) {
41+
int idxn0 =
42+
GetNormalizedIndex(pi.vertex[0].vn.value(), _state.vertex_normal.size());
43+
int idxn1 =
44+
GetNormalizedIndex(pi.vertex[1].vn.value(), _state.vertex_normal.size());
45+
int idxn2 =
46+
GetNormalizedIndex(pi.vertex[2].vn.value(), _state.vertex_normal.size());
47+
_scene.objects.push_back(std::make_shared<Triangle>(
48+
std::array<Vec3f, 3>{_state.vertex[idx0], _state.vertex[idx1],
49+
_state.vertex[idx2]},
50+
ExplicitNormals{_state.vertex_normal[idxn0], _state.vertex_normal[idxn1],
51+
_state.vertex_normal[idxn2]},
52+
material));
53+
} else {
54+
_scene.objects.push_back(std::make_shared<Triangle>(
55+
std::array<Vec3f, 3>{_state.vertex[idx0], _state.vertex[idx1],
56+
_state.vertex[idx2]},
57+
material));
58+
}
59+
60+
} else if (pi.vertex.size() == 4) {
61+
int idx0 = GetNormalizedIndex(pi.vertex[0].v, _state.vertex.size());
62+
int idx1 = GetNormalizedIndex(pi.vertex[1].v, _state.vertex.size());
63+
int idx2 = GetNormalizedIndex(pi.vertex[2].v, _state.vertex.size());
64+
int idx3 = GetNormalizedIndex(pi.vertex[3].v, _state.vertex.size());
65+
66+
if (pi.vertex[0].vn) {
67+
int idxn0 =
68+
GetNormalizedIndex(pi.vertex[0].vn.value(), _state.vertex_normal.size());
69+
int idxn1 =
70+
GetNormalizedIndex(pi.vertex[1].vn.value(), _state.vertex_normal.size());
71+
int idxn2 =
72+
GetNormalizedIndex(pi.vertex[2].vn.value(), _state.vertex_normal.size());
73+
int idxn3 =
74+
GetNormalizedIndex(pi.vertex[3].vn.value(), _state.vertex_normal.size());
75+
76+
_scene.objects.push_back(std::make_shared<Triangle>(
77+
std::array<Vec3f, 3>{_state.vertex[idx0], _state.vertex[idx1],
78+
_state.vertex[idx2]},
79+
ExplicitNormals{_state.vertex_normal[idxn0], _state.vertex_normal[idxn1],
80+
_state.vertex_normal[idxn2]},
81+
material));
82+
83+
_scene.objects.push_back(std::make_shared<Triangle>(
84+
std::array<Vec3f, 3>{_state.vertex[idx0], _state.vertex[idx2],
85+
_state.vertex[idx3]},
86+
ExplicitNormals{_state.vertex_normal[idxn0], _state.vertex_normal[idxn2],
87+
_state.vertex_normal[idxn3]},
88+
material));
89+
90+
} else {
91+
_scene.objects.push_back(std::make_shared<Triangle>(
92+
std::array<Vec3f, 3>{_state.vertex[idx0], _state.vertex[idx1],
93+
_state.vertex[idx2]},
94+
material));
95+
96+
_scene.objects.push_back(std::make_shared<Triangle>(
97+
std::array<Vec3f, 3>{_state.vertex[idx0], _state.vertex[idx2],
98+
_state.vertex[idx3]},
99+
material));
100+
}
101+
54102
} else {
55103
throw std::logic_error("Unsupported Polygon");
56104
}
@@ -62,17 +110,23 @@ class SceneBuilder {
62110

63111
private:
64112
Material FindMaterial(const std::string& name) {
65-
if (auto material = _state.name2material.find(name); material != _state.name2material.end()) {
113+
if (auto material = _state.name2material.find(name);
114+
material != _state.name2material.end()) {
66115
return material->second;
67116
} else {
68117
throw std::logic_error("Can't find material " + name + " for sphere ");
69118
}
70119
}
71120

121+
int GetNormalizedIndex(int idx, int size) {
122+
return idx >= 0 ? (idx - 1) : (size + idx);
123+
}
124+
72125
private:
73126
struct State {
74-
std::unordered_map<std::string, Material> name2material;
127+
std::unordered_map<std::string, Material> name2material;
75128
std::vector<Vec3f> vertex;
129+
std::vector<Vec3f> vertex_normal;
76130
};
77131

78132
State _state;

‎datatypes.h

+35-24
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
11
#pragma once
22

3-
#include <array>
4-
#include <vector>
5-
63
struct Vec3f {
74
Vec3f() = default;
85

9-
Vec3f(std::array<double, 3> arr) : x(arr[0]), y(arr[1]), z(arr[2]) {}
10-
Vec3f(std::array<float, 3> arr) : x(arr[0]), y(arr[1]), z(arr[2]) {}
6+
Vec3f(std::array<double, 3> arr) : x(arr[0]), y(arr[1]), z(arr[2]) {
7+
}
118

12-
Vec3f(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
9+
Vec3f(double _x, double _y, double _z) : x(_x), y(_y), z(_z) {
10+
}
1311

14-
Vec3f exp(float e) const {
12+
Vec3f exp(double e) const {
1513
return Vec3f{std::pow(x, e), std::pow(y, e), std::pow(z, e)};
1614
}
1715

18-
Vec3f operator*(float c) const {
16+
Vec3f operator*(double c) const {
1917
return Vec3f{x * c, y * c, z * c};
2018
};
2119

22-
Vec3f operator/(float c) const {
20+
Vec3f operator/(double c) const {
2321
return Vec3f{x / c, y / c, z / c};
2422
};
2523

26-
// FIXME: Compare float
24+
// FIXME: Compare double
2725
bool operator==(Vec3f rhs) {
2826
return x == rhs.x && y == rhs.y && z == rhs.z;
2927
}
@@ -36,11 +34,11 @@ struct Vec3f {
3634
return *this == Vec3f{0, 0, 0};
3735
}
3836

39-
float dot(Vec3f rhs) const {
37+
double dot(Vec3f rhs) const {
4038
return (x * rhs.x) + (y * rhs.y) + (z * rhs.z);
4139
};
4240

43-
float length() const {
41+
double length() const {
4442
return std::sqrt(x * x + y * y + z * z);
4543
}
4644

@@ -57,17 +55,17 @@ struct Vec3f {
5755
return *this;
5856
};
5957

60-
float norm() const {
58+
double norm() const {
6159
return std::sqrt(x * x + y * y + z * z);
6260
}
6361

64-
Vec3f normalize(float l = 1) const {
65-
return (*this)*(l / norm());
62+
Vec3f normalize(double l = 1) const {
63+
return (*this) * (l / norm());
6664
}
6765

68-
float x = 0.0;
69-
float y = 0.0;
70-
float z = 0.0;
66+
double x = 0.0;
67+
double y = 0.0;
68+
double z = 0.0;
7169
};
7270

7371
Vec3f operator-(Vec3f lhs, Vec3f rhs) {
@@ -82,7 +80,7 @@ Vec3f operator*(Vec3f lhs, Vec3f rhs) {
8280
return Vec3f{lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z};
8381
};
8482

85-
Vec3f operator*(float v, Vec3f rhs) {
83+
Vec3f operator*(double v, Vec3f rhs) {
8684
return rhs * v;
8785
};
8886

@@ -95,19 +93,32 @@ Vec3f operator+(int val, Vec3f rhs) {
9593
return {rhs.x + val, rhs.y + val, rhs.z + val};
9694
};
9795

96+
Vec3f operator+(Vec3f lhs, double val) {
97+
return val + lhs;
98+
};
99+
100+
Vec3f operator-(Vec3f rhs, double val) {
101+
return {rhs.x - val, rhs.y - val, rhs.z - val};
102+
};
103+
98104
std::ostream& operator<<(std::ostream& os, Vec3f vec) {
99105
os << "[ " << vec.x << " " << vec.y << " " << vec.z << " ]" << std::endl;
100106
return os;
101107
}
102108

103-
104109
class Matf {
105110
public:
106111
Matf() = default;
107-
Matf(size_t w, size_t h) : width(w), height(h), data(w, std::vector<Vec3f>(h)) {}
112+
Matf(size_t w, size_t h) : width(w), height(h), data(w, std::vector<Vec3f>(h)) {
113+
}
108114

109-
size_t GetW() const {return width;}
110-
size_t GetH() const {return height;}
115+
size_t GetW() const {
116+
return width;
117+
}
118+
119+
size_t GetH() const {
120+
return height;
121+
}
111122

112123
std::vector<Vec3f>& operator[](int i) {
113124
return data[i];
@@ -118,7 +129,7 @@ class Matf {
118129
}
119130

120131
private:
121-
size_t width = 0;
132+
size_t width = 0;
122133
size_t height = 0;
123134

124135
std::vector<std::vector<Vec3f>> data;

‎main.cpp

+22-10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
#include "camera_options.h"
44
#include "render_options.h"
55

6+
#include "image.h"
7+
#include "object.h"
8+
#include "parser.h"
9+
610
#include "raytracer.h"
711

812
const std::string kBasePath = "/home/atalaman/workspace/2022/petprojects/raytracer/";
@@ -23,15 +27,23 @@ int main() {
2327
//camera_opts.look_to = std::array<double, 3>{0.0, 0.0, 0.0};
2428
//RenderOptions render_opts{1};
2529
//CheckImage("triangle/scene.obj", "triangle/scene.png", camera_opts, render_opts, "tr.png");
26-
//CameraOptions camera_opts(500, 500);
27-
//camera_opts.look_from = std::array<double, 3>{100, 200, 150};
28-
//camera_opts.look_to = std::array<double, 3>{0.0, 100.0, 0.0};
29-
//RenderOptions render_opts{1};
30-
//CheckImage("deer/CERF_Free.obj", "deer/result.png", camera_opts, render_opts, "deer.png");
31-
CameraOptions camera_opts(640, 480);
32-
camera_opts.look_from = std::array<double, 3>{0.0, 1.0, 0.98};
33-
camera_opts.look_to = std::array<double, 3>{0.0, 1.0, 0.0};
34-
RenderOptions render_opts{4};
35-
CheckImage("box/cube.obj", "box/cube.png", camera_opts, render_opts);
30+
31+
CameraOptions camera_opts(500, 500);
32+
camera_opts.look_from = std::array<double, 3>{100, 200, 150};
33+
camera_opts.look_to = std::array<double, 3>{0.0, 100.0, 0.0};
34+
RenderOptions render_opts{1};
35+
CheckImage("deer/CERF_Free.obj", "deer/result.png", camera_opts, render_opts, "deer.png");
36+
37+
//CameraOptions camera_opts(640, 480);
38+
//camera_opts.look_from = std::array<double, 3>{0.0, 1.0, 0.98};
39+
//camera_opts.look_to = std::array<double, 3>{0.0, 1.0, 0.0};
40+
//RenderOptions render_opts{4};
41+
//CheckImage("box/cube.obj", "box/cube.png", camera_opts, render_opts, "cube.png");
42+
43+
//CameraOptions camera_opts(640, 480);
44+
//camera_opts.look_from = std::array<double, 3>{0.0, 1.0, 0.98};
45+
//camera_opts.look_to = std::array<double, 3>{0.0, 1.0, 0.0};
46+
//RenderOptions render_opts{4};
47+
//CheckImage("custom/vase.obj", "custom/vase.png", camera_opts, render_opts, "vase.png");
3648
return 0;
3749
}

‎object.h

+113-48
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,84 @@
11
#pragma once
22

3-
#include <memory>
43
#include <optional>
4+
#include <vector>
5+
#include <memory>
56

67
#include "datatypes.h"
78

9+
struct TriangleVertex {
10+
int v;
11+
std::optional<int> vt;
12+
std::optional<int> vn;
13+
};
14+
15+
struct ExplicitNormals {
16+
Vec3f v0n;
17+
Vec3f v1n;
18+
Vec3f v2n;
19+
};
20+
21+
std::ostream& operator<<(std::ostream& os, const TriangleVertex& tv) {
22+
// FIXME: Ugly handler
23+
if (tv.vt.has_value() && tv.vn.has_value()) {
24+
os << tv.v << " / " << tv.vt.has_value() << " / " << tv.vn.has_value() << std::endl;
25+
} else if (tv.vn.has_value()) {
26+
os << tv.v << " / / " << tv.vn.has_value() << std::endl;
27+
} else {
28+
os << tv.v << std::endl;
29+
}
30+
31+
return os;
32+
}
33+
34+
struct PolygonInfo {
35+
std::vector<TriangleVertex> vertex;
36+
};
37+
838
struct HitInfo {
939
Vec3f point;
1040
Vec3f N;
11-
float t;
41+
double t;
1242

1343
// FIXME: Triangle specific params
14-
float u, v;
44+
double u, v;
1545
};
1646

1747
struct Ray {
1848
Vec3f orig;
1949
Vec3f dir;
2050

2151
// Return point on ray
22-
Vec3f at(float t) const {
52+
Vec3f at(double t) const {
2353
return orig + dir * t;
2454
}
2555
};
2656

2757
struct Light {
28-
Light(const Vec3f& p,
29-
const Vec3f& i) : position(p), intensity(i) {}
58+
Light(const Vec3f& p, const Vec3f& i) : position(p), intensity(i) {
59+
}
3060

3161
Vec3f position;
3262
Vec3f intensity;
3363
};
3464
using Lights = std::vector<Light>;
3565

3666
struct Material {
67+
std::string name;
68+
3769
Vec3f Ka;
3870
Vec3f Ke;
3971
Vec3f Kd;
4072
Vec3f Ks;
4173

42-
float d = 1;
43-
float Tr = 0;
44-
float Ns = 0;
74+
Vec3f Tf;
75+
76+
double d = 1;
77+
double Tr = 0;
78+
79+
double Ns = 0;
4580
int illum = 0;
46-
float Ni = 1;
81+
double Ni = 1;
4782
};
4883

4984
class Object {
@@ -52,105 +87,135 @@ class Object {
5287

5388
// NB: With default material
5489
Object() = default;
55-
Object(Material m) : material(std::move(m)) { }
90+
Object(Material m) : material(std::move(m)) {
91+
}
5692

5793
virtual std::optional<HitInfo> intersect(const Ray& ray) = 0;
58-
const Material& GetMaterial() const { return material; }
94+
95+
const Material& GetMaterial() const {
96+
return material;
97+
}
5998

6099
private:
61100
Material material;
62101
};
63102
using Objects = std::vector<Object::Ptr>;
64103

65-
66104
class Sphere : public Object {
67105
public:
68-
Sphere(Vec3f center, float radius, Material m = {}) : Object(std::move(m)), c(center), r(radius) {};
106+
Sphere(Vec3f center, double radius, Material m = {})
107+
: Object(std::move(m)), c(center), r(radius){};
69108

70109
std::optional<HitInfo> intersect(const Ray& ray) override {
71110
// Geometric solution
72111
Vec3f L = c - ray.orig;
73112

74-
float tca = L.dot(ray.dir);
113+
double tca = L.dot(ray.dir);
75114

76115
if (tca < 0) {
77116
return {};
78117
}
79118

80-
float d2 = L.dot(L) - tca*tca;
119+
double d2 = L.dot(L) - tca * tca;
81120

82121
if (d2 < 0) {
83122
return {};
84123
}
85124

86-
if (d2 > r * r) return {};
125+
if (d2 > r * r) {
126+
return {};
127+
}
87128

88-
float thc = sqrtf(r * r - d2);
89-
float distance = tca - thc;
90-
float t1 = tca + thc;
129+
double thc = sqrtf(r * r - d2);
130+
double distance = tca - thc;
131+
double t1 = tca + thc;
91132

92-
if (distance < 0) distance = t1;
93-
if (distance < 0) return {};
133+
if (distance < 0) {
134+
distance = t1;
135+
}
136+
137+
if (distance < 0) {
138+
return {};
139+
}
94140

95141
Vec3f phit = ray.at(distance);
96-
Vec3f N = (phit - c).normalize();
142+
Vec3f N = (phit - c).normalize();
97143

98144
return HitInfo{phit, N, distance};
99145
}
100146

101147
private:
102148
Vec3f c;
103-
float r;
149+
double r;
104150
};
105151

106-
107152
class Triangle : public Object {
108153
public:
109154
// FIXME: Uncomment
110155
Triangle(const std::array<Vec3f, 3>& pts, Material m = {})
111-
: Object(std::move(m)), v0(pts[0]), v1(pts[1]), v2(pts[2]) {}
156+
: Object(std::move(m)), v0(pts[0]), v1(pts[1]), v2(pts[2]) {
157+
}
158+
159+
Triangle(const std::array<Vec3f, 3>& pts, const ExplicitNormals& normals, Material m = {})
160+
: Object(std::move(m)), v0(pts[0]), v1(pts[1]), v2(pts[2]), has_normals(normals) {
161+
}
112162

113163
std::optional<HitInfo> intersect(const Ray& ray) override {
114-
constexpr float kEpsilon = 1e-8;
115-
float u, v;
116-
Vec3f v0v1 = v1 - v0;
117-
Vec3f v0v2 = v2 - v0;
164+
constexpr double kEpsilon = 1e-8;
165+
double u, v, w;
118166

119-
Vec3f pvec = ray.dir.cross(v0v2);
167+
Vec3f v0v1 = v1 - v0;
168+
Vec3f v0v2 = v2 - v0;
120169

121-
Vec3f N = v0v1.cross(v0v2).normalize(); // N
170+
Vec3f pvec = ray.dir.cross(v0v2);
122171

123-
float det = v0v1.dot(pvec);
172+
double det = v0v1.dot(pvec);
124173

125174
// ray and triangle are parallel if det is close to 0
126-
if (fabs(det) < kEpsilon) return {};
127-
128-
float invDet = 1 / det;
129-
130-
Vec3f tvec = ray.orig - v0;
131-
u = tvec.dot(pvec) * invDet;
132-
if (u < 0 || u > 1) return {};
133-
134-
Vec3f qvec = tvec.cross(v0v1);
135-
v = ray.dir.dot(qvec) * invDet;
136-
if (v < 0 || u + v > 1) return {};
137-
138-
float t = v0v2.dot(qvec) * invDet;
175+
if (std::fabs(det) < kEpsilon) {
176+
return {};
177+
}
178+
179+
double invDet = 1 / det;
180+
181+
Vec3f tvec = ray.orig - v0;
182+
u = tvec.dot(pvec) * invDet;
183+
if (u < 0 || u > 1) {
184+
return {};
185+
}
186+
187+
Vec3f qvec = tvec.cross(v0v1);
188+
v = ray.dir.dot(qvec) * invDet;
189+
if (v < 0 || u + v > 1) {
190+
return {};
191+
}
192+
193+
double t = v0v2.dot(qvec) * invDet;
139194
auto P = ray.at(t);
140195

141196
if (t < 0) {
142197
return {};
143198
}
144199

200+
Vec3f N = v0v1.cross(v0v2).normalize();
201+
202+
w = (1 - u - v);
203+
if (has_normals) {
204+
auto normals = has_normals.value();
205+
N = (u * normals.v0n.normalize() + v * normals.v1n.normalize() +
206+
w * normals.v2n.normalize())
207+
.normalize();
208+
}
209+
145210
return HitInfo{P, N, t};
146211
}
147212

148-
149213
private:
150214
Vec3f v0, v1, v2;
215+
std::optional<ExplicitNormals> has_normals;
151216
};
152217

153218
struct Scene {
154219
Objects objects;
155-
Lights lights;
220+
Lights lights;
156221
};

‎parser.h

+109-44
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
#pragma once
22

3+
#include <unordered_map>
4+
35
#include "builder.h"
46
#include "tokenizer.h"
7+
#include "object.h"
58

69
#include <cassert>
710

11+
#include <optional>
12+
13+
std::ostream& operator<<(std::ostream& os, const PolygonInfo& pi) {
14+
os << "PolygonInfo: " << std::endl;
15+
for (auto&& v : pi.vertex) {
16+
os << v << std::endl;
17+
}
18+
return os;
19+
}
20+
821
class Parser {
922
public:
1023
const Scene& Parse(const std::string filename) {
@@ -27,7 +40,6 @@ class Parser {
2740
throw std::logic_error("Next token after mtllib should be Tokenizer::Word");
2841
}
2942
auto&& filename = std::get<Tokenizer::Word>(tok).word;
30-
std::cout << "mtllib path = " << filename << std::endl;
3143
ParseMtlFile(dir + "/" + filename);
3244
} else if (word == "usemtl") {
3345
tokenizer.Next();
@@ -37,38 +49,42 @@ class Parser {
3749
}
3850

3951
// Store mtl_name for object
40-
// FIXME: Should it be parsed together with object ?
52+
// FIXME: Should it be parsed together with object ?
4153
mtl_name = std::get<Tokenizer::Word>(tok).word;
4254
} else if (word == "S") {
4355
tokenizer.Next();
44-
auto params = ParseConstants<float, 4>(&tokenizer);
45-
std::cout << "Sphere params = " << params[0] << " " << params[1] << " " << params[2] << " " << params[3] << std::endl;
56+
auto params = ParseConstants<double, 4>(&tokenizer);
4657
_builder.BuildSphere(mtl_name, params);
4758
} else if (word == "v") {
48-
tokenizer.Next();
49-
auto coords = ParseConstants<float, 3>(&tokenizer);
50-
std::cout << "v : " << coords[0] << " " << coords[1] << " " << coords[2] << std::endl;
59+
tokenizer.Next();
60+
auto coords = ParseConstants<double, 3>(&tokenizer);
5161
_builder.AddVertex(coords);
62+
} else if (word == "vn") {
63+
tokenizer.Next();
64+
auto coords = ParseConstants<double, 3>(&tokenizer);
65+
_builder.AddVertexNormal(coords);
66+
} else if (word == "vt") {
67+
tokenizer.Next();
68+
auto coords = ParseConstants<double, 3>(&tokenizer);
69+
// FIXME: Isn't necessary for baseline
70+
//_builder.AddVertexTexture(coords);
5271
} else if (word == "f") {
53-
tokenizer.Next();
54-
auto params = ParseAllConstantInLine<int>(&tokenizer);
55-
std::cout << "NUM coords = " << params.size() << std::endl;
56-
_builder.BuildPolygon(mtl_name, params);
72+
tokenizer.Next();
73+
auto polygon = ParsePolygon(&tokenizer);
74+
_builder.BuildPolygon(mtl_name, polygon);
5775
// FIXME: After ParseAllConstantInLine() we are already on next token,
5876
// so just continue
5977
continue;
6078
} else if (word == "P") {
6179
tokenizer.Next();
62-
auto params = ParseConstants<float, 6>(&tokenizer);
63-
std::cout << "Ligh params = " << params[0] << " " << params[1] << " " << params[2] << " " << params[3] << " " << params[4] << " " << params[5] << std::endl;
80+
auto params = ParseConstants<double, 6>(&tokenizer);
6481
_builder.AddLight(params);
65-
}
66-
else {
67-
std::cout << "SKIP LINE STARTED WITH: " << word << std::endl;
82+
} else {
6883
tokenizer.NextLine();
6984
continue;
70-
//throw std::logic_error("Unrecognized value for Tokenizer::Word in *.obj file");
7185
}
86+
} else if (std::holds_alternative<Tokenizer::EndOfFile>(token)) {
87+
break;
7288
} else {
7389
throw std::logic_error("Line in *.obj file shouldn't start with this token");
7490
}
@@ -101,7 +117,6 @@ class Parser {
101117
throw std::logic_error("Next token after newmtl should be Tokenizer::Word");
102118
}
103119
std::string name = std::get<Tokenizer::Word>(tok).word;
104-
std::cout << "mtl name = " << name << std::endl;
105120

106121
// Init material
107122
Material mtl;
@@ -116,45 +131,49 @@ class Parser {
116131
throw std::logic_error("Unsupported option for newmtl");
117132
}
118133
auto optname = std::get<Tokenizer::Word>(tok).word;
119-
std::cout << "optname = " << optname << std::endl;
120134

121135
// NB: Go to params list
122136
tokenizer->Next();
123137

124138
if (optname == "Kd") {
125-
auto Kd_params = ParseConstants<float, 3>(tokenizer);
126-
std::cout << optname << ": " << Kd_params[0] << " " << Kd_params[1] << " " << Kd_params[2] << std::endl;
139+
auto Kd_params = ParseConstants<double, 3>(tokenizer);
127140
mtl.Kd = Vec3f(Kd_params);
128-
} else if(optname == "Ka") {
129-
auto Ka_params = ParseConstants<float, 3>(tokenizer);
130-
std::cout << optname << ": " << Ka_params[0] << " " << Ka_params[1] << " " << Ka_params[2] << std::endl;
141+
} else if (optname == "Ka") {
142+
auto Ka_params = ParseConstants<double, 3>(tokenizer);
131143
mtl.Ka = Vec3f(Ka_params);
132144
} else if (optname == "Ke") {
133-
auto Ke = ParseConstants<float, 3>(tokenizer);
134-
std::cout << optname << ": " << Ke[0] << " " << Ke[1] << " " << Ke[2] << std::endl;
145+
auto Ke = ParseConstants<double, 3>(tokenizer);
135146
mtl.Ke = Vec3f(Ke);
136147
} else if (optname == "Ks") {
137-
auto Ks_params = ParseConstants<float, 3>(tokenizer);
138-
std::cout << optname << ": " << Ks_params[0] << " " << Ks_params[1] << " " << Ks_params[2] << std::endl;
148+
auto Ks_params = ParseConstants<double, 3>(tokenizer);
139149
mtl.Ks = Vec3f(Ks_params);
140150
} else if (optname == "Ns") {
141-
auto Ns = ParseConstants<float, 1>(tokenizer);
142-
std::cout << optname << ": " << Ns[0] << std::endl;
151+
auto Ns = ParseConstants<double, 1>(tokenizer);
143152
mtl.Ns = Ns[0];
144153
} else if (optname == "illum") {
145154
auto illum = ParseConstants<int, 1>(tokenizer);
146-
std::cout << optname << ": " << illum[0] << std::endl;
147155
mtl.illum = illum[0];
148156
} else if (optname == "Ni") {
149-
auto Ni = ParseConstants<float, 1>(tokenizer);
150-
std::cout << optname << ": " << Ni[0] << std::endl;
157+
auto Ni = ParseConstants<double, 1>(tokenizer);
151158
mtl.Ni = Ni[0];
159+
} else if (optname == "Tf") {
160+
auto Tf = ParseConstants<double, 3>(tokenizer);
161+
mtl.Tf = Vec3f(Tf);
162+
} else if (optname == "d") {
163+
auto d = ParseConstants<double, 1>(tokenizer);
164+
mtl.d = d[0];
165+
mtl.Tr = 1 - mtl.d;
166+
} else if (optname == "Tr") {
167+
auto Tr = ParseConstants<double, 1>(tokenizer);
168+
mtl.Tr = Tr[0];
169+
mtl.d = 1 - mtl.Tr;
152170
} else {
153171
throw std::logic_error("Unsupported option name: " + optname);
154172
}
155173

156174
if (tokenizer->IsEnd()) {
157175
// Mtl file is over
176+
mtl.name = name;
158177
_builder.AddMaterial(name, mtl);
159178
return;
160179
}
@@ -166,40 +185,86 @@ class Parser {
166185

167186
if (std::get<Tokenizer::Word>(tok).word == "newmtl") {
168187
// New material section is started
188+
mtl.name = name;
169189
_builder.AddMaterial(name, mtl);
170190
return;
171191
}
172192
}
173193
}
174194

175-
template<typename T, size_t N>
195+
template <typename T, size_t N>
176196
std::array<T, N> ParseConstants(Tokenizer* tokenizer) {
177197
std::array<T, N> values;
178198
for (int i = 0; i < N; ++i) {
179199
auto tok = tokenizer->GetToken();
180200
if (!std::holds_alternative<Tokenizer::Constant>(tok)) {
181-
throw std::logic_error("Param list for option should contain float constants");
201+
throw std::logic_error("Param list for option should contain double constants");
182202
}
183-
// NB: Tokenizer alway return float
203+
// NB: Tokenizer alway return double
184204
values[i] = static_cast<T>(std::get<Tokenizer::Constant>(tok).val);
185205
tokenizer->Next();
186206
}
187207
return values;
188208
}
189209

190-
template<typename T>
191-
std::vector<T> ParseAllConstantInLine(Tokenizer* tokenizer) {
192-
std::vector<T> values;
210+
TriangleVertex ParseTriangleVertex(Tokenizer* tokenizer) {
211+
// Format: v/vt/vn
212+
TriangleVertex tv;
213+
214+
// Parse v
215+
auto tok = tokenizer->GetToken();
216+
if (!std::holds_alternative<Tokenizer::Constant>(tok)) {
217+
throw std::logic_error("Triangle vertex should contain constant in the beginning");
218+
}
219+
tv.v = std::get<Tokenizer::Constant>(tok).val;
220+
tokenizer->Next();
221+
222+
tok = tokenizer->GetToken();
223+
if (!std::holds_alternative<Tokenizer::Slash>(tok)) {
224+
return tv;
225+
}
226+
tokenizer->Next();
227+
228+
tok = tokenizer->GetToken();
229+
// Parse vt
230+
if (std::holds_alternative<Tokenizer::Constant>(tok)) {
231+
tv.vt = std::get<Tokenizer::Constant>(tok).val;
232+
// Skip next slash
233+
tokenizer->Next();
234+
tok = tokenizer->GetToken();
235+
if (!std::holds_alternative<Tokenizer::Slash>(tok)) {
236+
throw std::logic_error("Slash should follow after number");
237+
}
238+
}
239+
tokenizer->Next();
240+
241+
// Parse vn
242+
tok = tokenizer->GetToken();
243+
if (!std::holds_alternative<Tokenizer::Constant>(tok)) {
244+
throw std::logic_error("Triangle vertex should contain constant in the ending");
245+
}
246+
tv.vn = std::get<Tokenizer::Constant>(tok).val;
247+
248+
tokenizer->Next();
249+
return tv;
250+
}
251+
252+
PolygonInfo ParsePolygon(Tokenizer* tokenizer) {
253+
PolygonInfo pi;
254+
193255
while (true) {
194256
auto tok = tokenizer->GetToken();
195257
if (!std::holds_alternative<Tokenizer::Constant>(tok)) {
196-
return values;
258+
return pi;
259+
}
260+
261+
auto tv = ParseTriangleVertex(tokenizer);
262+
pi.vertex.push_back(tv);
263+
264+
if (tokenizer->IsEnd()) {
265+
return pi;
197266
}
198-
// NB: Tokenizer alway return float
199-
values.push_back(static_cast<T>(std::get<Tokenizer::Constant>(tok).val));
200-
tokenizer->Next();
201267
}
202-
// Code shouldn't be there
203268
assert(false);
204269
}
205270

‎raytracer.h

+109-122
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@
99
#include "image.h"
1010
#include "object.h"
1111
#include "parser.h"
12-
1312
#include "camera_options.h"
1413
#include "render_options.h"
1514

16-
using Arr44f = std::array<std::array<float, 4>, 4>;
15+
using Arr44f = std::array<std::array<double, 4>, 4>;
1716
using TransformVec = std::function<Vec3f(const Vec3f& vec)>;
1817
using TransformDir = std::function<Vec3f(const Vec3f& vec)>;
1918

@@ -22,169 +21,164 @@ struct Options {
2221
RenderOptions render_options;
2322
};
2423

25-
std::tuple<TransformVec, TransformDir>
26-
lookAt(const Vec3f& from,
27-
const Vec3f& to,
28-
const Vec3f& tmp = Vec3f{0.0, 0.0, -1.0})
29-
{
30-
Vec3f forward = (from-to).normalize();
31-
Vec3f right = (tmp.cross(forward)).normalize();
32-
Vec3f up = forward.cross(right).normalize();
33-
34-
Arr44f cam2world;
35-
36-
cam2world[0][0] = right.x;
37-
cam2world[0][1] = right.y;
38-
cam2world[0][2] = right.z;
39-
cam2world[0][3] = 0.0;
40-
41-
cam2world[1][0] = up.x;
42-
cam2world[1][1] = up.y;
43-
cam2world[1][2] = up.z;
44-
cam2world[1][3] = 0.0;
45-
46-
cam2world[2][0] = forward.x;
47-
cam2world[2][1] = forward.y;
48-
cam2world[2][2] = forward.z;
49-
cam2world[2][3] = 0.0;
50-
51-
cam2world[3][0] = from.x;
52-
cam2world[3][1] = from.y;
53-
cam2world[3][2] = from.z;
54-
cam2world[3][3] = 1.0;
55-
56-
// FIXME: Debug info
57-
#ifdef DEBUG
58-
std::cout << "mat : " << std::endl;
59-
for (int i = 0; i < 4; ++i) {
60-
for (int j = 0; j < 4; ++j) {
61-
std::cout << cam2world[i][j] << " ";
62-
}
63-
std::cout << std::endl;
64-
}
65-
#endif
66-
67-
auto transform_vec = [=](const Vec3f& src) {
68-
float a, b, c, w;
69-
a = src.x * cam2world[0][0] + src.y * cam2world[1][0] + src.z * cam2world[2][0] + cam2world[3][0];
70-
b = src.x * cam2world[0][1] + src.y * cam2world[1][1] + src.z * cam2world[2][1] + cam2world[3][1];
71-
c = src.x * cam2world[0][2] + src.y * cam2world[1][2] + src.z * cam2world[2][2] + cam2world[3][2];
72-
return Vec3f{a, b, c};
73-
};
74-
75-
auto transform_dir = [=](const Vec3f& src) {
76-
float a, b, c;
77-
a = src.x * cam2world[0][0] + src.y * cam2world[1][0] + src.z * cam2world[2][0];
78-
b = src.x * cam2world[0][1] + src.y * cam2world[1][1] + src.z * cam2world[2][1];
79-
c = src.x * cam2world[0][2] + src.y * cam2world[1][2] + src.z * cam2world[2][2];
80-
return Vec3f{a, b, c};
81-
};
82-
return std::make_tuple(transform_vec, transform_dir);
83-
}
84-
85-
Vec3f tone_mapping(Vec3f pixel, float C) {
86-
return pixel * ((1 + (pixel/(C*C)))) / (1 + pixel);
24+
Vec3f tone_mapping(Vec3f pixel, double C) {
25+
return pixel * ((1 + (pixel / (C * C)))) / (1 + pixel);
8726
}
8827

8928
Vec3f gamma_correction(Vec3f pixel) {
9029
return pixel.exp(1 / 2.2);
9130
}
9231

9332
RGB toRGB(Vec3f Vgamma) {
94-
RGB pixel{static_cast<int>(Vgamma.x * 255),
95-
static_cast<int>(Vgamma.y * 255),
96-
static_cast<int>(Vgamma.z * 255)
97-
};
33+
RGB pixel{static_cast<int>(Vgamma.x * 255), static_cast<int>(Vgamma.y * 255),
34+
static_cast<int>(Vgamma.z * 255)};
9835

9936
return pixel;
10037
}
10138

102-
Vec3f trace(const Ray& ray, const Scene& scene, const Options& options, int depth=0) {
39+
double clamp(double lower, double upper, double value) {
40+
if (value < lower) {
41+
return lower;
42+
}
43+
44+
if (value > upper) {
45+
return upper;
46+
}
47+
48+
return value;
49+
}
50+
51+
Vec3f refract(const Vec3f& I, const Vec3f& N, double ior) {
52+
double cosi = clamp(-1, 1, I.dot(N));
53+
double etai = 1, etat = ior;
54+
55+
Vec3f n = N;
56+
if (cosi < 0) {
57+
cosi = -cosi;
58+
} else {
59+
std::swap(etai, etat);
60+
n = -1 * N;
61+
}
62+
63+
double eta = etai / etat;
64+
double k = 1 - eta * eta * (1 - cosi * cosi);
65+
return k < 0 ? Vec3f{0, 0, 0} : (eta * I + (eta * cosi - sqrtf(k)) * n);
66+
}
67+
68+
Vec3f reflect(const Vec3f& I, const Vec3f& N) {
69+
return I - (N * 2.f * N.dot(I));
70+
}
71+
72+
Vec3f trace(const Ray& ray, const Scene& scene, const Options& options, int depth = 0,
73+
bool outside = true) {
10374
Vec3f background{0.0, 0.0, 0.0};
10475
if (depth == options.render_options.depth) {
10576
return background;
10677
}
10778

10879
std::shared_ptr<Object> nearest;
109-
float distance = std::numeric_limits<float>::max();
80+
double distance = std::numeric_limits<double>::max();
11081
HitInfo info;
11182

11283
for (auto&& obj : scene.objects) {
11384
auto has_hit = obj->intersect(ray);
11485
if (!has_hit) {
11586
continue;
11687
}
117-
11888
const auto& hi = has_hit.value();
89+
11990
if (hi.t < distance) {
12091
nearest = obj;
12192
info = hi;
93+
distance = hi.t;
12294
}
123-
12495
}
12596

12697
if (!nearest) {
12798
return background;
12899
}
129100

130101
const auto& material = nearest->GetMaterial();
131-
Vec3f diffuse {0.0, 0.0, 0.0};
102+
Vec3f diffuse{0.0, 0.0, 0.0};
132103
Vec3f specular{0.0, 0.0, 0.0};
133-
float bias = 1e-4;
134104

135-
for (auto&& light : scene.lights) {
136-
Vec3f new_p = info.point + ((ray.orig - info.point).normalize() * 0.0001);
105+
Vec3f Ibase{0.0, 0.0, 0.0};
106+
Vec3f Icomp{0.0, 0.0, 0.0};
137107

108+
Vec3f newN = info.N;
109+
110+
if (ray.dir.dot(newN) > 0) {
111+
newN = newN * -1;
112+
}
113+
114+
Vec3f vE = (ray.orig - info.point).normalize();
115+
116+
if (material.illum > 2) {
117+
double bias = 0.001;
118+
if (outside) {
119+
Vec3f refldir = reflect(ray.dir, newN).normalize();
120+
Vec3f vR = reflect(-1 * refldir, newN);
121+
122+
Vec3f shiftedP = info.point + bias * newN;
123+
Vec3f reflc = trace(Ray{shiftedP, refldir}, scene, options, depth + 1);
124+
125+
auto refldiffuse = reflc * std::max(0.0, newN.dot(refldir));
126+
auto reflspecular = reflc * std::pow(std::max(0.0, vR.dot(vE)), material.Ns);
127+
128+
Icomp += (material.Kd * refldiffuse) + (material.Ks * reflspecular);
129+
}
130+
// NB: Refraction
131+
double Tr = outside ? material.Tr : 1.0;
132+
double ior = outside ? material.Ni : 1 / material.Ni;
133+
Vec3f refrdir = refract(ray.dir, newN, ior).normalize();
134+
Vec3f refrorig = outside ? info.point - (bias * newN) : info.point + (bias * newN);
135+
auto refrc =
136+
trace(Ray{refrorig, refrdir}, scene, options, depth + 1, outside ? false : true);
137+
138+
Icomp += refrc * Tr;
139+
}
140+
141+
for (auto&& light : scene.lights) {
142+
Vec3f new_p = info.point + ((ray.orig - info.point).normalize() * 0.00001);
138143
Vec3f newp2light = (light.position - new_p).normalize();
139144

140145
bool no_intersect = false;
141-
for (auto&& obj : scene.objects) {
146+
for (const auto& obj : scene.objects) {
142147
auto has_hit = obj->intersect(Ray{new_p, newp2light});
143148
if (!has_hit) {
144149
continue;
145150
}
146151

147152
const auto& hi = has_hit.value();
148-
float len = (hi.point - new_p).length();
153+
double len = (hi.point - new_p).length();
149154
if (len < (light.position - new_p).length()) {
150155
no_intersect = true;
156+
break;
151157
}
152158
}
153159

154160
if (no_intersect) {
155161
continue;
156162
}
157163

158-
Vec3f Vl = (light.position - info.point).normalize();
159-
Vec3f newN = info.N;
164+
Vec3f vL = (light.position - info.point).normalize();
165+
Vec3f vR = reflect(-1 * vL, newN);
160166

161-
if (Vl.dot(info.N) < 0) {
162-
newN = info.N * -1;
163-
}
164-
165-
if (material.illum > 2) {
166-
Vec3f refldir = ray.dir - newN * 2 * ray.dir.dot(newN);
167-
Vec3f reflection = trace(Ray{info.point, refldir}, scene, options, depth+1);
168-
}
169-
170-
diffuse += light.intensity * std::max(0.f, Vl.dot(newN));
171-
172-
//Vec3f Ve = -1 * ray.dir;
173-
Vec3f Ve = (ray.orig - info.point).normalize();
174-
175-
Vec3f Vr = 2.0 * (newN.dot(Vl)) * newN - Vl;
176-
specular += (light.intensity * std::pow(std::max(0.f, Vr.dot(Ve)), material.Ns));
167+
diffuse += light.intensity * std::max(0.0, newN.dot(vL));
168+
specular += light.intensity * std::pow(std::max(0.0, vR.dot(vE)), material.Ns);
177169
}
178170

179-
return material.Ka + material.Ke + (material.Kd * diffuse) + (material.Ks * specular);
171+
Ibase += material.Ka + material.Ke + (material.Kd * diffuse) + (material.Ks * specular);
172+
173+
return Ibase + Icomp;
180174
}
181175

182176
void postprocessing(Matf& mat) {
183-
float C = std::numeric_limits<float>::min();
177+
double C = std::numeric_limits<double>::min();
184178
for (int i = 0; i < mat.GetW(); ++i) {
185179
for (int j = 0; j < mat.GetH(); ++j) {
186180
auto& vec = mat[i][j];
187-
float max = std::max({vec.x, vec.y, vec.z});
181+
double max = std::max({vec.x, vec.y, vec.z});
188182
if (max > C) {
189183
C = max;
190184
}
@@ -201,23 +195,18 @@ void postprocessing(Matf& mat) {
201195

202196
Image Render(const std::string& filename, const CameraOptions& camera_options,
203197
const RenderOptions& render_options) {
204-
int width = camera_options.screen_width;
198+
int width = camera_options.screen_width;
205199
int height = camera_options.screen_height;
206-
float fov = camera_options.fov;
200+
double fov = camera_options.fov;
207201

208202
Options options{camera_options, render_options};
209203

210204
Vec3f from = Vec3f{camera_options.look_from};
211-
Vec3f to = Vec3f{camera_options.look_to};
205+
Vec3f to = Vec3f{camera_options.look_to};
212206

213207
// FIXME: Ugly stub !!!!
214-
Vec3f tmp = (from - to).normalize().cross(Vec3f{0, 1, 0}).IsZero() ? Vec3f{0, 0, -1} : Vec3f{0, 1, 0} ;
215-
216-
// Get transformation from camera coords to world coords
217-
auto [vec2world, dir2world] = lookAt(from, to, tmp);
218-
219-
// Find orig in world coords
220-
auto orig = vec2world(Vec3f{0, 0, 0});
208+
Vec3f tmp =
209+
(from - to).normalize().cross(Vec3f{0, 1, 0}).IsZero() ? Vec3f{0, 0, -1} : Vec3f{0, 1, 0};
221210

222211
// Init image & Mat
223212
Image img(width, height);
@@ -226,34 +215,32 @@ Image Render(const std::string& filename, const CameraOptions& camera_options,
226215
Parser parser;
227216
const auto& scene = parser.Parse(filename);
228217

229-
float scale = std::tan(fov * 0.5);
218+
double scale = std::tan(fov * 0.5);
230219

231-
float ratio = width / static_cast<float>(height);
220+
double ratio = width / static_cast<double>(height);
232221

233-
Vec3f forward = (from-to).normalize();
234-
Vec3f right = tmp.cross(forward).normalize();
235-
//Vec3f up = right.cross(forward).normalize();
236-
Vec3f up = forward.cross(right).normalize();
222+
Vec3f forward = (from - to).normalize();
223+
Vec3f right = tmp.cross(forward).normalize();
224+
Vec3f up = forward.cross(right).normalize();
237225

238226
for (int j = 0; j < height; ++j) {
239227
for (int i = 0; i < width; ++i) {
240228
// FIXME: Should it be without static_cast ???
229+
double ps_x = (i + 0.5) / static_cast<double>(width);
230+
double ps_y = (j + 0.5) / static_cast<double>(height);
241231

242-
float ps_x = (i + 0.5) / static_cast<float>(width); // Pixel screen x
243-
float ps_y = (j + 0.5) / static_cast<float>(height); // Pixel screen y
244-
245-
float x = (2 * ps_x - 1) * scale * ratio; // Pixel camera x
246-
float y = (1 - 2 * ps_y ) * scale; // Pixel camera y
232+
double x = (2 * ps_x - 1) * scale * ratio;
233+
double y = (1 - 2 * ps_y) * scale;
247234

248235
Vec3f view{x, y, -1};
236+
Vec3f zero{0, 0, 0};
249237

250238
Ray ray;
251-
Vec3f zero{0,0,0};
252239

253240
ray.orig = Vec3f{zero.dot(Vec3f{right.x, up.x, forward.x}) + from.x,
254241
zero.dot(Vec3f{right.y, up.y, forward.y}) + from.y,
255242
zero.dot(Vec3f{right.z, up.z, forward.z}) + from.z};
256-
243+
257244
auto p = Vec3f{view.dot(Vec3f{right.x, up.x, forward.x}) + from.x,
258245
view.dot(Vec3f{right.y, up.y, forward.y}) + from.y,
259246
view.dot(Vec3f{right.z, up.z, forward.z}) + from.z};

‎tokenizer.h

+20-9
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@
77
class Tokenizer {
88
public:
99
struct Constant {
10-
float val;
10+
double val;
1111
};
1212

1313
struct Word {
1414
std::string word;
1515
};
1616

17-
using Token = std::variant<Constant, Word>;
17+
struct Slash {};
18+
19+
struct EndOfFile {};
20+
21+
using Token = std::variant<Constant, Word, Slash, EndOfFile>;
1822

1923
Tokenizer(std::istream* in) : _in(in) {
2024
SkipSpaces();
@@ -35,15 +39,22 @@ class Tokenizer {
3539
return *_ltoken;
3640
}
3741

38-
// NB: Only three entities are supported: numeric constant, string, comment
3942
char c = _in->peek();
40-
if (std::isdigit(c) || c == '-') {
41-
_ltoken.reset(new Token{Constant{ParseFloat()}});
43+
if (c == '/') {
44+
_ltoken.reset(new Token{Slash{}});
45+
// NB: Move cursor to the next symbol
46+
_in->get();
47+
} else if (std::isdigit(c) || c == '-') {
48+
_ltoken.reset(new Token{Constant{parseDouble()}});
4249
} else if (c == '#') {
4350
ParseComment();
4451
Next();
45-
auto token = GetToken();
46-
_ltoken.reset(new Token{token});
52+
if (IsEnd()) {
53+
_ltoken.reset(new Token{EndOfFile{}});
54+
} else {
55+
auto token = GetToken();
56+
_ltoken.reset(new Token{token});
57+
}
4758
} else if (std::isalpha(c)) {
4859
auto word = ParseString();
4960
_ltoken.reset(new Token{Word{word}});
@@ -78,8 +89,8 @@ class Tokenizer {
7889
}
7990
}
8091

81-
float ParseFloat() {
82-
float val;
92+
double parseDouble() {
93+
double val;
8394
(*_in) >> val;
8495
return val;
8596
}

0 commit comments

Comments
 (0)
Please sign in to comment.