-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBVHNode.cpp
More file actions
231 lines (179 loc) · 7.66 KB
/
BVHNode.cpp
File metadata and controls
231 lines (179 loc) · 7.66 KB
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#include "BVHNode.h"
#include "Utils.h"
#include "Scene.h"
namespace dae
{
bool BVH::IntersectBVH(const Ray& ray, const int nodeIdx)
{
//If the tree is not build we can not hit
if(!isBuild) return false;
BVHNode& node = bvhNode[nodeIdx];
HitRecord hit_record{};
//if the AABB is not hit, return false
const bool AABB = IntersectAABB(ray, node.aabbMin, node.aabbMax, hit_record);
//If the ray does not hit the bounding box we won't hit the mesh
if(!AABB) return false;
//If nod is an ending node, check if we hit the triangle
if (node.isLeaf())
{
bool didHit = false;
//Hit-test every triangle in the node
for (int i{}; i < node.triangleCount; ++i )
{
didHit = GeometryUtils::HitTest_Triangle(GetTriangleByIndex(triangleIndex[node.firstPrim + i]), ray, hit_record);
//If we hit, return true
if(didHit) {return true;}
}
}
//Go deeper in the tree
else
{
const bool next = IntersectBVH( ray, node.firstPrim);
const bool nextR = IntersectBVH( ray, node.firstPrim + 1);
if(next || nextR) return true;
}
//Should this false be here?
return false;
}
void BVH::IntersectBVH(const Ray& ray, const int nodeIdx, HitRecord& hitRecord)
{
if(!isBuild) return;
BVHNode& node = bvhNode[nodeIdx];
if (!IntersectAABB( ray, node.aabbMin, node.aabbMax, hitRecord)) return;
if (node.isLeaf())
{
for (int i{}; i < node.triangleCount; ++i )
{
GeometryUtils::HitTest_Triangle(GetTriangleByIndex(triangleIndex[node.firstPrim + i]), ray, hitRecord);
}
}
else
{
IntersectBVH( ray, node.firstPrim, hitRecord );
IntersectBVH( ray, node.firstPrim + 1 , hitRecord);
}
}
void BVH::BuildBVH(const std::vector<TriangleMesh>& triangleMeshes)
{
if(triangleMeshes.empty()) return;
//Get the mesh
Meshes = triangleMeshes;
//Add the triangle count
for(const auto& mesh : Meshes)
{
amountOfTriangles += static_cast<int>(mesh.GetAmountOfTriangles());
}
//Resize nodes and index of triangles
bvhNode.resize(static_cast<size_t>(amountOfTriangles) * 2);
triangleIndex.resize(amountOfTriangles);
// populate triangle index array
for (int i{}; i < amountOfTriangles; ++i)
{
triangleIndex[i] = i;
}
// assign all triangles to root node
BVHNode& root = bvhNode[rootNodeIdx];
root.firstPrim = 0;
root.triangleCount = amountOfTriangles;
UpdateNodeBounds( rootNodeIdx );
// subdivide recursively
Subdivide( rootNodeIdx );
isBuild = true;
}
void BVH::UpdateNodeBounds( int nodeIdx )
{
BVHNode& node = bvhNode[nodeIdx];
const Triangle firstTriangle = GetTriangleByIndex(triangleIndex[node.firstPrim]);
node.aabbMin = node.aabbMax = firstTriangle.v0;
for (int first = node.firstPrim, i = 0; i < node.triangleCount; ++i)
{
const int leafTriIdx = triangleIndex[first + i];
Triangle leafTri = GetTriangleByIndex(leafTriIdx);
node.aabbMin = Vector3::Min(node.aabbMin, leafTri.v0);
node.aabbMin = Vector3::Min(node.aabbMin, leafTri.v1);
node.aabbMin = Vector3::Min(node.aabbMin, leafTri.v2);
node.aabbMax = Vector3::Max(node.aabbMax, leafTri.v0);
node.aabbMax = Vector3::Max(node.aabbMax, leafTri.v1);
node.aabbMax = Vector3::Max(node.aabbMax, leafTri.v2);
}
}
void BVH::Subdivide( int nodeIdx )
{
// terminate recursion when there is nothing more to split
BVHNode& node = bvhNode[nodeIdx];
if (node.triangleCount <= 2) return;
// determine split axis and position
Vector3 extent = node.aabbMax - node.aabbMin;
int axis = 0;
if (extent.y > extent.x) axis = 1;
if (extent.z > extent[axis]) axis = 2;
// Calculate the position of the splitting plane (median of the chosen axis)
const float splitPos = node.aabbMin[axis] + extent[axis] * 0.5f;
//Left pointer starts at the beginning of the array
//Right pointer starts at the end of the array
int leftPointer = node.firstPrim;
int rightPointer = leftPointer + node.triangleCount - 1;
while (leftPointer <= rightPointer)
{
// Check if the center coordinate of the current triangle is on the left side of the splitting plane
if (GetTriangleByIndex(triangleIndex[leftPointer]).center[axis] < splitPos)
{
++leftPointer;
}
else
{
// If the center coordinate is on the right side of the splitting plane, swap the triangles
// This moves the right-side pointer to the left while maintaining order
std::swap( triangleIndex[leftPointer], triangleIndex[rightPointer--] );
}
}
//Abort split if one of the sides is empty
const int leftCount = leftPointer - node.firstPrim;
if (leftCount == 0 || leftCount == node.triangleCount) return;
// Create child nodes for the left and right sub-nodes
const int leftChildIdx = nodesUsed++;
const int rightChildIdx = nodesUsed++;
// Update child node properties
bvhNode[leftChildIdx].firstPrim = node.firstPrim;
bvhNode[leftChildIdx].triangleCount = leftCount;
bvhNode[rightChildIdx].firstPrim = leftPointer;
bvhNode[rightChildIdx].triangleCount = node.triangleCount - leftCount;
node.firstPrim = leftChildIdx;
node.triangleCount = 0;
// Update the bounding boxes of child nodes
UpdateNodeBounds( leftChildIdx );
UpdateNodeBounds( rightChildIdx );
// Subdivide teh left and right children
Subdivide( leftChildIdx );
Subdivide( rightChildIdx );
}
Triangle BVH::GetTriangleByIndex(int index) const
{
for (const auto& mesh : Meshes)
{
const int numTrianglesInMesh = static_cast<int>(mesh.GetAmountOfTriangles());
if (index < numTrianglesInMesh)
{
return mesh.GetTriangleByIndex(index);
}
index -= numTrianglesInMesh;
}
return {};
}
bool BVH::IntersectAABB( const Ray& ray, const Vector3 bmin, const Vector3 bmax, const HitRecord& hitRecord)
{
const float tx1 = (bmin.x - ray.origin.x) / ray.direction.x;
const float tx2 = (bmax.x - ray.origin.x) / ray.direction.x;
float tMin = std::min( tx1, tx2 );
float tMax = std::max( tx1, tx2 );
const float ty1 = (bmin.y - ray.origin.y) / ray.direction.y;
const float ty2 = (bmax.y - ray.origin.y) / ray.direction.y;
tMin = std::max( tMin, std::min( ty1, ty2 ) );
tMax = std::min( tMax, std::max( ty1, ty2 ) );
const float tz1 = (bmin.z - ray.origin.z) / ray.direction.z;
const float tz2 = (bmax.z - ray.origin.z) / ray.direction.z;
tMin = std::max( tMin, std::min( tz1, tz2 ) );
tMax = std::min( tMax, std::max( tz1, tz2 ) );
return tMax >= tMin && tMin < hitRecord.t && tMax > 0;
}
}