Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Geometry/Indexify.cpp
9913 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Geometry/Indexify.h>
8
#include <Jolt/Geometry/AABox.h>
9
10
JPH_NAMESPACE_BEGIN
11
12
static JPH_INLINE const Float3 &sIndexifyGetFloat3(const TriangleList &inTriangles, uint32 inVertexIndex)
13
{
14
return inTriangles[inVertexIndex / 3].mV[inVertexIndex % 3];
15
}
16
17
static JPH_INLINE Vec3 sIndexifyGetVec3(const TriangleList &inTriangles, uint32 inVertexIndex)
18
{
19
return Vec3::sLoadFloat3Unsafe(sIndexifyGetFloat3(inTriangles, inVertexIndex));
20
}
21
22
static void sIndexifyVerticesBruteForce(const TriangleList &inTriangles, const uint32 *inVertexIndices, const uint32 *inVertexIndicesEnd, Array<uint32> &ioWeldedVertices, float inVertexWeldDistance)
23
{
24
float weld_dist_sq = Square(inVertexWeldDistance);
25
26
// Compare every vertex
27
for (const uint32 *v1_idx = inVertexIndices; v1_idx < inVertexIndicesEnd; ++v1_idx)
28
{
29
Vec3 v1 = sIndexifyGetVec3(inTriangles, *v1_idx);
30
31
// with every other vertex...
32
for (const uint32 *v2_idx = v1_idx + 1; v2_idx < inVertexIndicesEnd; ++v2_idx)
33
{
34
Vec3 v2 = sIndexifyGetVec3(inTriangles, *v2_idx);
35
36
// If they're weldable
37
if ((v2 - v1).LengthSq() <= weld_dist_sq)
38
{
39
// Find the lowest indices both indices link to
40
uint32 idx1 = *v1_idx;
41
for (;;)
42
{
43
uint32 new_idx1 = ioWeldedVertices[idx1];
44
if (new_idx1 >= idx1)
45
break;
46
idx1 = new_idx1;
47
}
48
uint32 idx2 = *v2_idx;
49
for (;;)
50
{
51
uint32 new_idx2 = ioWeldedVertices[idx2];
52
if (new_idx2 >= idx2)
53
break;
54
idx2 = new_idx2;
55
}
56
57
// Order the vertices
58
uint32 lowest = min(idx1, idx2);
59
uint32 highest = max(idx1, idx2);
60
61
// Link highest to lowest
62
ioWeldedVertices[highest] = lowest;
63
64
// Also update the vertices we started from to avoid creating long chains
65
ioWeldedVertices[*v1_idx] = lowest;
66
ioWeldedVertices[*v2_idx] = lowest;
67
break;
68
}
69
}
70
}
71
}
72
73
static void sIndexifyVerticesRecursively(const TriangleList &inTriangles, uint32 *ioVertexIndices, uint inNumVertices, uint32 *ioScratch, Array<uint32> &ioWeldedVertices, float inVertexWeldDistance, uint inMaxRecursion)
74
{
75
// Check if we have few enough vertices to do a brute force search
76
// Or if we've recursed too deep (this means we chipped off a few vertices each iteration because all points are very close)
77
if (inNumVertices <= 8 || inMaxRecursion == 0)
78
{
79
sIndexifyVerticesBruteForce(inTriangles, ioVertexIndices, ioVertexIndices + inNumVertices, ioWeldedVertices, inVertexWeldDistance);
80
return;
81
}
82
83
// Calculate bounds
84
AABox bounds;
85
for (const uint32 *v = ioVertexIndices, *v_end = ioVertexIndices + inNumVertices; v < v_end; ++v)
86
bounds.Encapsulate(sIndexifyGetVec3(inTriangles, *v));
87
88
// Determine split plane
89
int split_axis = bounds.GetExtent().GetHighestComponentIndex();
90
float split_value = bounds.GetCenter()[split_axis];
91
92
// Partition vertices
93
uint32 *v_read = ioVertexIndices, *v_write = ioVertexIndices, *v_end = ioVertexIndices + inNumVertices;
94
uint32 *scratch = ioScratch;
95
while (v_read < v_end)
96
{
97
// Calculate distance to plane
98
float distance_to_split_plane = sIndexifyGetFloat3(inTriangles, *v_read)[split_axis] - split_value;
99
if (distance_to_split_plane < -inVertexWeldDistance)
100
{
101
// Vertex is on the right side
102
*v_write = *v_read;
103
++v_read;
104
++v_write;
105
}
106
else if (distance_to_split_plane > inVertexWeldDistance)
107
{
108
// Vertex is on the wrong side, swap with the last vertex
109
--v_end;
110
std::swap(*v_read, *v_end);
111
}
112
else
113
{
114
// Vertex is too close to the split plane, it goes on both sides
115
*scratch++ = *v_read++;
116
}
117
}
118
119
// Check if we made any progress
120
uint num_vertices_on_both_sides = (uint)(scratch - ioScratch);
121
if (num_vertices_on_both_sides == inNumVertices)
122
{
123
sIndexifyVerticesBruteForce(inTriangles, ioVertexIndices, ioVertexIndices + inNumVertices, ioWeldedVertices, inVertexWeldDistance);
124
return;
125
}
126
127
// Calculate how we classified the vertices
128
uint num_vertices_left = (uint)(v_write - ioVertexIndices);
129
uint num_vertices_right = (uint)(ioVertexIndices + inNumVertices - v_end);
130
JPH_ASSERT(num_vertices_left + num_vertices_right + num_vertices_on_both_sides == inNumVertices);
131
memcpy(v_write, ioScratch, num_vertices_on_both_sides * sizeof(uint32));
132
133
// Recurse
134
uint max_recursion = inMaxRecursion - 1;
135
sIndexifyVerticesRecursively(inTriangles, ioVertexIndices, num_vertices_left + num_vertices_on_both_sides, ioScratch, ioWeldedVertices, inVertexWeldDistance, max_recursion);
136
sIndexifyVerticesRecursively(inTriangles, ioVertexIndices + num_vertices_left, num_vertices_right + num_vertices_on_both_sides, ioScratch, ioWeldedVertices, inVertexWeldDistance, max_recursion);
137
}
138
139
void Indexify(const TriangleList &inTriangles, VertexList &outVertices, IndexedTriangleList &outTriangles, float inVertexWeldDistance)
140
{
141
uint num_triangles = (uint)inTriangles.size();
142
uint num_vertices = num_triangles * 3;
143
144
// Create a list of all vertex indices
145
Array<uint32> vertex_indices;
146
vertex_indices.resize(num_vertices);
147
for (uint i = 0; i < num_vertices; ++i)
148
vertex_indices[i] = i;
149
150
// Link each vertex to itself
151
Array<uint32> welded_vertices;
152
welded_vertices.resize(num_vertices);
153
for (uint i = 0; i < num_vertices; ++i)
154
welded_vertices[i] = i;
155
156
// A scope to free memory used by the scratch array
157
{
158
// Some scratch memory, used for the vertices that fall in both partitions
159
Array<uint32> scratch;
160
scratch.resize(num_vertices);
161
162
// Recursively split the vertices
163
sIndexifyVerticesRecursively(inTriangles, vertex_indices.data(), num_vertices, scratch.data(), welded_vertices, inVertexWeldDistance, 32);
164
}
165
166
// Do a pass to complete the welding, linking each vertex to the vertex it is welded to
167
// (and since we're going from 0 to N we can be sure that the vertex we're linking to is already linked to the lowest vertex)
168
uint num_resulting_vertices = 0;
169
for (uint i = 0; i < num_vertices; ++i)
170
{
171
JPH_ASSERT(welded_vertices[welded_vertices[i]] <= welded_vertices[i]);
172
welded_vertices[i] = welded_vertices[welded_vertices[i]];
173
if (welded_vertices[i] == i)
174
++num_resulting_vertices;
175
}
176
177
// Collect the vertices
178
outVertices.clear();
179
outVertices.reserve(num_resulting_vertices);
180
for (uint i = 0; i < num_vertices; ++i)
181
if (welded_vertices[i] == i)
182
{
183
// New vertex
184
welded_vertices[i] = (uint32)outVertices.size();
185
outVertices.push_back(sIndexifyGetFloat3(inTriangles, i));
186
}
187
else
188
{
189
// Reused vertex, remap index
190
welded_vertices[i] = welded_vertices[welded_vertices[i]];
191
}
192
193
// Create indexed triangles
194
outTriangles.clear();
195
outTriangles.reserve(num_triangles);
196
for (uint t = 0; t < num_triangles; ++t)
197
{
198
IndexedTriangle it;
199
it.mMaterialIndex = inTriangles[t].mMaterialIndex;
200
it.mUserData = inTriangles[t].mUserData;
201
for (int v = 0; v < 3; ++v)
202
it.mIdx[v] = welded_vertices[t * 3 + v];
203
if (!it.IsDegenerate(outVertices))
204
outTriangles.push_back(it);
205
}
206
}
207
208
void Deindexify(const VertexList &inVertices, const IndexedTriangleList &inTriangles, TriangleList &outTriangles)
209
{
210
outTriangles.resize(inTriangles.size());
211
for (size_t t = 0; t < inTriangles.size(); ++t)
212
{
213
const IndexedTriangle &in = inTriangles[t];
214
Triangle &out = outTriangles[t];
215
out.mMaterialIndex = in.mMaterialIndex;
216
out.mUserData = in.mUserData;
217
for (int v = 0; v < 3; ++v)
218
out.mV[v] = inVertices[in.mIdx[v]];
219
}
220
}
221
222
JPH_NAMESPACE_END
223
224