-
Notifications
You must be signed in to change notification settings - Fork 1
/
complexObject.cpp
184 lines (159 loc) · 6.03 KB
/
complexObject.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// System headers
#include <limits>
#include <algorithm>
// Our own headers
#include "./complexObject.h"
#include "./hit.h"
ComplexObject::ComplexObject(Mesh mesh) {
this->mesh = mesh;
Material defaultMaterial = Material();
nullVector = Vec3Df(0, 0, 0);
noHit = Hit(0, nullVector, nullVector, defaultMaterial);
initBoundingBox();
}
void ComplexObject::initBoundingBox() {
// Where we keep track of the following bounds
float xMin = std::numeric_limits<float>::max();
float yMin = std::numeric_limits<float>::max();
float zMin = std::numeric_limits<float>::max();
float xMax = -std::numeric_limits<float>::max();
float yMax = -std::numeric_limits<float>::max();
float zMax = -std::numeric_limits<float>::max();
for (int triangle = 0; triangle < mesh.triangles.size(); triangle++) {
Triangle T = mesh.triangles[triangle];
for (int vertex = 0; vertex < 3; vertex++) {
Vertex V = mesh.vertices[T.v[vertex]];
Vec3Df vertexPosition = V.p;
// X axis
xMin = std::min(xMin, vertexPosition[0]);
xMax = std::max(xMax, vertexPosition[0]);
// Y axis
yMin = std::min(yMin, vertexPosition[1]);
yMax = std::max(yMax, vertexPosition[1]);
// Z axis
zMin = std::min(zMin, vertexPosition[2]);
zMax = std::max(zMax, vertexPosition[2]);
}
}
bounds[0] = Vec3Df(xMin, yMin, zMin);
bounds[1] = Vec3Df(xMax, yMax, zMax);
}
Hit ComplexObject::intersectBoundingBox(Vec3Df origin, Vec3Df dest) {
// Our implementation is based on the ray-box intersection algorithm
// as proposed here: http://people.csail.mit.edu/amy/papers/box-jgt.pdf
// This section should be stored in a ray datastructure where it's cached
Vec3Df direction = dest - origin;
Vec3Df inverseDirection = Vec3Df(1/direction[0], 1/direction[1], 1/direction[2]);
int sign[3];
sign[0] = (inverseDirection[0] < 0);
sign[1] = (inverseDirection[1] < 0);
sign[2] = (inverseDirection[2] < 0);
// Intersection algorithm
float xMin, yMin, zMin, xMax, yMax, zMax;
xMin = (bounds[ sign[0] ][0] - origin[0]) * inverseDirection[0];
xMax = (bounds[ 1-sign[0] ][0] - origin[0]) * inverseDirection[0];
yMin = (bounds[ sign[1] ][1] - origin[1]) * inverseDirection[1];
yMax = (bounds[ 1-sign[1] ][1] - origin[1]) * inverseDirection[1];
zMin = (bounds[ sign[2] ][2] - origin[2]) * inverseDirection[2];
zMax = (bounds[ 1-sign[2] ][2] - origin[2]) * inverseDirection[2];
if ( (xMin > yMax) || (yMin > xMax) ) return noHit;
if (yMin > xMin) xMin = yMin;
if (yMax < xMax) xMax = yMax;
if ( (xMin > zMax) || (zMin > xMax) ) return noHit;
if (zMin > xMin) xMin = zMin;
if (zMax < xMax) xMax = zMax;
return Hit(1, Vec3Df(xMin, yMin, zMin), nullVector, defaultMaterial);
}
Hit ComplexObject::intersectMesh(Vec3Df origin, Vec3Df dest) {
// hit is is where we keep track of hits with backfaces
// For the moment we use noHit as a symbol
Hit hit = noHit;
//Material that is displayed if no material is found in Mesh
Material errorMat = Material();
errorMat.set_Kd(1,1,0);
errorMat.set_Ka(1,1,0);
errorMat.set_Ks(1,1,0);
errorMat.set_Ns(96.7f);
errorMat.set_illum(2);
Material actualMat = errorMat;
for (int i = 0; i < mesh.triangles.size(); i++) {
Triangle T = mesh.triangles[i];
//now return the actual material which is defined in the mesh
int materialIndex = 999;
if(mesh.triangleMaterials.size()>0){
materialIndex = mesh.triangleMaterials[i];
}
if(0 <= materialIndex && materialIndex < mesh.materials.size()){
actualMat = mesh.materials[materialIndex];
}
// Our implementation is based on the proposed algorithm of Dan Sunday at: http://geomalgorithms.com/a06-_intersect-2.html
Vertex v0 = mesh.vertices[T.v[0]];
Vertex v1 = mesh.vertices[T.v[1]];
Vertex v2 = mesh.vertices[T.v[2]];
// Edge vectors
Vec3Df u = v1.p-v0.p;
Vec3Df v = v2.p-v0.p;
Vec3Df n = Vec3Df::crossProduct(u, v); // Normal of the triangle
Vec3Df w0 = origin - v0.p;
float a = -Vec3Df::dotProduct(n, w0);
float b = Vec3Df::dotProduct(n, dest);
// Use this as a threshold to avoid division overflow
if (fabs(b) < 0.0000001) {
// ray is parallel to triangle plane (either precisely on or disjoint from plane)
continue;
}
// Get intersection point of ray with triangle plane
float r = a / b;
if (r < 0.01) // ray goes away from triangle
{
continue;
}
// intersect point of ray and plane
Vec3Df I = origin + (r * dest);
// Next up; triangle text; is I inside T?
float uu, uv, vv, wu, wv, D;
uu = Vec3Df::dotProduct(u, u);
uv = Vec3Df::dotProduct(u, v);
vv = Vec3Df::dotProduct(v, v);
Vec3Df w = I - v0.p;
wu = Vec3Df::dotProduct(w, u);
wv = Vec3Df::dotProduct(w, v);
D = uv * uv - uu * vv;
// get and test parametric coords
float s, t;
s = (uv * wv - vv * wu) / D;
if (s < 0.0 || s > 1.0) // I is outside T
continue;
t = (uv * wu - uu * wv) / D;
if (t < 0.0 || (s + t) > 1.0) // I is outside T
continue;
n.normalize(); // We can now truncate the normal to length of 1
// If we get here I (the ray hitpoint) is in T (the triangle):
// We check if we already found a hit before
if (hit.isHit == 0) {
// In that case we can just assign the current hit as the first one
hit = Hit(1, I, n, actualMat);
} else {
// If so, we check whether this one is closer to the origin
float previousDistance = (hit.hitPoint - origin).getLength();
float currentDistance = (I - origin).getLength();
// Now check if it's closer
if (currentDistance < previousDistance) {
// If it is then we save the current hit
hit = Hit(1, I, n, actualMat);
} else {
// If not we discard this hit and continue looking for one which is
continue;
}
}
}
return hit;
}
Hit ComplexObject::intersect(Vec3Df origin, Vec3Df dest) {
Hit hit;
hit = this->intersectBoundingBox(origin, dest);
if (!hit.isHit)
return noHit;
hit = this->intersectMesh(origin, dest);
return hit;
}