Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/meshoptimizer/stripifier.cpp
9898 views
1
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
2
#include "meshoptimizer.h"
3
4
#include <assert.h>
5
#include <limits.h>
6
#include <string.h>
7
8
// This work is based on:
9
// Francine Evans, Steven Skiena and Amitabh Varshney. Optimizing Triangle Strips for Fast Rendering. 1996
10
namespace meshopt
11
{
12
13
static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned char* valence)
14
{
15
unsigned int index = 0;
16
unsigned int iv = ~0u;
17
18
for (size_t i = 0; i < buffer_size; ++i)
19
{
20
unsigned char va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]];
21
unsigned int v = (va < vb && va < vc) ? va : (vb < vc ? vb : vc);
22
23
if (v < iv)
24
{
25
index = unsigned(i);
26
iv = v;
27
}
28
}
29
30
return index;
31
}
32
33
static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1)
34
{
35
for (size_t i = 0; i < buffer_size; ++i)
36
{
37
unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
38
39
if (e0 == a && e1 == b)
40
return (int(i) << 2) | 2;
41
else if (e0 == b && e1 == c)
42
return (int(i) << 2) | 0;
43
else if (e0 == c && e1 == a)
44
return (int(i) << 2) | 1;
45
}
46
47
return -1;
48
}
49
50
} // namespace meshopt
51
52
size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int restart_index)
53
{
54
assert(destination != indices);
55
assert(index_count % 3 == 0);
56
57
using namespace meshopt;
58
59
meshopt_Allocator allocator;
60
61
const size_t buffer_capacity = 8;
62
63
unsigned int buffer[buffer_capacity][3] = {};
64
unsigned int buffer_size = 0;
65
66
size_t index_offset = 0;
67
68
unsigned int strip[2] = {};
69
unsigned int parity = 0;
70
71
size_t strip_size = 0;
72
73
// compute vertex valence; this is used to prioritize starting triangle for strips
74
// note: we use 8-bit counters for performance; for outlier vertices the valence is incorrect but that just affects the heuristic
75
unsigned char* valence = allocator.allocate<unsigned char>(vertex_count);
76
memset(valence, 0, vertex_count);
77
78
for (size_t i = 0; i < index_count; ++i)
79
{
80
unsigned int index = indices[i];
81
assert(index < vertex_count);
82
83
valence[index]++;
84
}
85
86
int next = -1;
87
88
while (buffer_size > 0 || index_offset < index_count)
89
{
90
assert(next < 0 || (size_t(next >> 2) < buffer_size && (next & 3) < 3));
91
92
// fill triangle buffer
93
while (buffer_size < buffer_capacity && index_offset < index_count)
94
{
95
buffer[buffer_size][0] = indices[index_offset + 0];
96
buffer[buffer_size][1] = indices[index_offset + 1];
97
buffer[buffer_size][2] = indices[index_offset + 2];
98
99
buffer_size++;
100
index_offset += 3;
101
}
102
103
assert(buffer_size > 0);
104
105
if (next >= 0)
106
{
107
unsigned int i = next >> 2;
108
unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
109
unsigned int v = buffer[i][next & 3];
110
111
// ordered removal from the buffer
112
memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0]));
113
buffer_size--;
114
115
// update vertex valences for strip start heuristic
116
valence[a]--;
117
valence[b]--;
118
valence[c]--;
119
120
// find next triangle (note that edge order flips on every iteration)
121
// in some cases we need to perform a swap to pick a different outgoing triangle edge
122
// for [a b c], the default strip edge is [b c], but we might want to use [a c]
123
int cont = findStripNext(buffer, buffer_size, parity ? strip[1] : v, parity ? v : strip[1]);
124
int swap = cont < 0 ? findStripNext(buffer, buffer_size, parity ? v : strip[0], parity ? strip[0] : v) : -1;
125
126
if (cont < 0 && swap >= 0)
127
{
128
// [a b c] => [a b a c]
129
destination[strip_size++] = strip[0];
130
destination[strip_size++] = v;
131
132
// next strip has same winding
133
// ? a b => b a v
134
strip[1] = v;
135
136
next = swap;
137
}
138
else
139
{
140
// emit the next vertex in the strip
141
destination[strip_size++] = v;
142
143
// next strip has flipped winding
144
strip[0] = strip[1];
145
strip[1] = v;
146
parity ^= 1;
147
148
next = cont;
149
}
150
}
151
else
152
{
153
// if we didn't find anything, we need to find the next new triangle
154
// we use a heuristic to maximize the strip length
155
unsigned int i = findStripFirst(buffer, buffer_size, valence);
156
unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
157
158
// ordered removal from the buffer
159
memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0]));
160
buffer_size--;
161
162
// update vertex valences for strip start heuristic
163
valence[a]--;
164
valence[b]--;
165
valence[c]--;
166
167
// we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration
168
int ea = findStripNext(buffer, buffer_size, c, b);
169
int eb = findStripNext(buffer, buffer_size, a, c);
170
int ec = findStripNext(buffer, buffer_size, b, a);
171
172
// in some cases we can have several matching edges; since we can pick any edge, we pick the one with the smallest
173
// triangle index in the buffer. this reduces the effect of stripification on ACMR and additionally - for unclear
174
// reasons - slightly improves the stripification efficiency
175
int mine = INT_MAX;
176
mine = (ea >= 0 && mine > ea) ? ea : mine;
177
mine = (eb >= 0 && mine > eb) ? eb : mine;
178
mine = (ec >= 0 && mine > ec) ? ec : mine;
179
180
if (ea == mine)
181
{
182
// keep abc
183
next = ea;
184
}
185
else if (eb == mine)
186
{
187
// abc -> bca
188
unsigned int t = a;
189
a = b, b = c, c = t;
190
191
next = eb;
192
}
193
else if (ec == mine)
194
{
195
// abc -> cab
196
unsigned int t = c;
197
c = b, b = a, a = t;
198
199
next = ec;
200
}
201
202
if (restart_index)
203
{
204
if (strip_size)
205
destination[strip_size++] = restart_index;
206
207
destination[strip_size++] = a;
208
destination[strip_size++] = b;
209
destination[strip_size++] = c;
210
211
// new strip always starts with the same edge winding
212
strip[0] = b;
213
strip[1] = c;
214
parity = 1;
215
}
216
else
217
{
218
if (strip_size)
219
{
220
// connect last strip using degenerate triangles
221
destination[strip_size++] = strip[1];
222
destination[strip_size++] = a;
223
}
224
225
// note that we may need to flip the emitted triangle based on parity
226
// we always end up with outgoing edge "cb" in the end
227
unsigned int e0 = parity ? c : b;
228
unsigned int e1 = parity ? b : c;
229
230
destination[strip_size++] = a;
231
destination[strip_size++] = e0;
232
destination[strip_size++] = e1;
233
234
strip[0] = e0;
235
strip[1] = e1;
236
parity ^= 1;
237
}
238
}
239
}
240
241
return strip_size;
242
}
243
244
size_t meshopt_stripifyBound(size_t index_count)
245
{
246
assert(index_count % 3 == 0);
247
248
// worst case without restarts is 2 degenerate indices and 3 indices per triangle
249
// worst case with restarts is 1 restart index and 3 indices per triangle
250
return (index_count / 3) * 5;
251
}
252
253
size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count, unsigned int restart_index)
254
{
255
assert(destination != indices);
256
257
size_t offset = 0;
258
size_t start = 0;
259
260
for (size_t i = 0; i < index_count; ++i)
261
{
262
if (restart_index && indices[i] == restart_index)
263
{
264
start = i + 1;
265
}
266
else if (i - start >= 2)
267
{
268
unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i];
269
270
// flip winding for odd triangles
271
if ((i - start) & 1)
272
{
273
unsigned int t = a;
274
a = b, b = t;
275
}
276
277
// although we use restart indices, strip swaps still produce degenerate triangles, so skip them
278
if (a != b && a != c && b != c)
279
{
280
destination[offset + 0] = a;
281
destination[offset + 1] = b;
282
destination[offset + 2] = c;
283
offset += 3;
284
}
285
}
286
}
287
288
return offset;
289
}
290
291
size_t meshopt_unstripifyBound(size_t index_count)
292
{
293
assert(index_count == 0 || index_count >= 3);
294
295
return (index_count == 0) ? 0 : (index_count - 2) * 3;
296
}
297
298