77a045e902
-Reworked how meshes are treated by importer by using EditorSceneImporterMesh and EditorSceneImporterMeshNode. Instead of Mesh and MeshInstance, this allows more efficient processing of meshes before they are actually registered in the RenderingServer. -Integrated MeshOptimizer -Reworked internals of SurfaceTool to use arrays, making it more performant and easy to run optimizatons on.
296 lines
7.5 KiB
C++
296 lines
7.5 KiB
C++
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
|
|
#include "meshoptimizer.h"
|
|
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
|
|
// This work is based on:
|
|
// Francine Evans, Steven Skiena and Amitabh Varshney. Optimizing Triangle Strips for Fast Rendering. 1996
|
|
namespace meshopt
|
|
{
|
|
|
|
static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned int* valence)
|
|
{
|
|
unsigned int index = 0;
|
|
unsigned int iv = ~0u;
|
|
|
|
for (size_t i = 0; i < buffer_size; ++i)
|
|
{
|
|
unsigned int va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]];
|
|
unsigned int v = (va < vb && va < vc) ? va : (vb < vc) ? vb : vc;
|
|
|
|
if (v < iv)
|
|
{
|
|
index = unsigned(i);
|
|
iv = v;
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1)
|
|
{
|
|
for (size_t i = 0; i < buffer_size; ++i)
|
|
{
|
|
unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
|
|
|
|
if (e0 == a && e1 == b)
|
|
return (int(i) << 2) | 2;
|
|
else if (e0 == b && e1 == c)
|
|
return (int(i) << 2) | 0;
|
|
else if (e0 == c && e1 == a)
|
|
return (int(i) << 2) | 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
} // namespace meshopt
|
|
|
|
size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int restart_index)
|
|
{
|
|
assert(destination != indices);
|
|
assert(index_count % 3 == 0);
|
|
|
|
using namespace meshopt;
|
|
|
|
meshopt_Allocator allocator;
|
|
|
|
const size_t buffer_capacity = 8;
|
|
|
|
unsigned int buffer[buffer_capacity][3] = {};
|
|
unsigned int buffer_size = 0;
|
|
|
|
size_t index_offset = 0;
|
|
|
|
unsigned int strip[2] = {};
|
|
unsigned int parity = 0;
|
|
|
|
size_t strip_size = 0;
|
|
|
|
// compute vertex valence; this is used to prioritize starting triangle for strips
|
|
unsigned int* valence = allocator.allocate<unsigned int>(vertex_count);
|
|
memset(valence, 0, vertex_count * sizeof(unsigned int));
|
|
|
|
for (size_t i = 0; i < index_count; ++i)
|
|
{
|
|
unsigned int index = indices[i];
|
|
assert(index < vertex_count);
|
|
|
|
valence[index]++;
|
|
}
|
|
|
|
int next = -1;
|
|
|
|
while (buffer_size > 0 || index_offset < index_count)
|
|
{
|
|
assert(next < 0 || (size_t(next >> 2) < buffer_size && (next & 3) < 3));
|
|
|
|
// fill triangle buffer
|
|
while (buffer_size < buffer_capacity && index_offset < index_count)
|
|
{
|
|
buffer[buffer_size][0] = indices[index_offset + 0];
|
|
buffer[buffer_size][1] = indices[index_offset + 1];
|
|
buffer[buffer_size][2] = indices[index_offset + 2];
|
|
|
|
buffer_size++;
|
|
index_offset += 3;
|
|
}
|
|
|
|
assert(buffer_size > 0);
|
|
|
|
if (next >= 0)
|
|
{
|
|
unsigned int i = next >> 2;
|
|
unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
|
|
unsigned int v = buffer[i][next & 3];
|
|
|
|
// ordered removal from the buffer
|
|
memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0]));
|
|
buffer_size--;
|
|
|
|
// update vertex valences for strip start heuristic
|
|
valence[a]--;
|
|
valence[b]--;
|
|
valence[c]--;
|
|
|
|
// find next triangle (note that edge order flips on every iteration)
|
|
// in some cases we need to perform a swap to pick a different outgoing triangle edge
|
|
// for [a b c], the default strip edge is [b c], but we might want to use [a c]
|
|
int cont = findStripNext(buffer, buffer_size, parity ? strip[1] : v, parity ? v : strip[1]);
|
|
int swap = cont < 0 ? findStripNext(buffer, buffer_size, parity ? v : strip[0], parity ? strip[0] : v) : -1;
|
|
|
|
if (cont < 0 && swap >= 0)
|
|
{
|
|
// [a b c] => [a b a c]
|
|
destination[strip_size++] = strip[0];
|
|
destination[strip_size++] = v;
|
|
|
|
// next strip has same winding
|
|
// ? a b => b a v
|
|
strip[1] = v;
|
|
|
|
next = swap;
|
|
}
|
|
else
|
|
{
|
|
// emit the next vertex in the strip
|
|
destination[strip_size++] = v;
|
|
|
|
// next strip has flipped winding
|
|
strip[0] = strip[1];
|
|
strip[1] = v;
|
|
parity ^= 1;
|
|
|
|
next = cont;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we didn't find anything, we need to find the next new triangle
|
|
// we use a heuristic to maximize the strip length
|
|
unsigned int i = findStripFirst(buffer, buffer_size, &valence[0]);
|
|
unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
|
|
|
|
// ordered removal from the buffer
|
|
memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0]));
|
|
buffer_size--;
|
|
|
|
// update vertex valences for strip start heuristic
|
|
valence[a]--;
|
|
valence[b]--;
|
|
valence[c]--;
|
|
|
|
// we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration
|
|
int ea = findStripNext(buffer, buffer_size, c, b);
|
|
int eb = findStripNext(buffer, buffer_size, a, c);
|
|
int ec = findStripNext(buffer, buffer_size, b, a);
|
|
|
|
// in some cases we can have several matching edges; since we can pick any edge, we pick the one with the smallest
|
|
// triangle index in the buffer. this reduces the effect of stripification on ACMR and additionally - for unclear
|
|
// reasons - slightly improves the stripification efficiency
|
|
int mine = INT_MAX;
|
|
mine = (ea >= 0 && mine > ea) ? ea : mine;
|
|
mine = (eb >= 0 && mine > eb) ? eb : mine;
|
|
mine = (ec >= 0 && mine > ec) ? ec : mine;
|
|
|
|
if (ea == mine)
|
|
{
|
|
// keep abc
|
|
next = ea;
|
|
}
|
|
else if (eb == mine)
|
|
{
|
|
// abc -> bca
|
|
unsigned int t = a;
|
|
a = b, b = c, c = t;
|
|
|
|
next = eb;
|
|
}
|
|
else if (ec == mine)
|
|
{
|
|
// abc -> cab
|
|
unsigned int t = c;
|
|
c = b, b = a, a = t;
|
|
|
|
next = ec;
|
|
}
|
|
|
|
if (restart_index)
|
|
{
|
|
if (strip_size)
|
|
destination[strip_size++] = restart_index;
|
|
|
|
destination[strip_size++] = a;
|
|
destination[strip_size++] = b;
|
|
destination[strip_size++] = c;
|
|
|
|
// new strip always starts with the same edge winding
|
|
strip[0] = b;
|
|
strip[1] = c;
|
|
parity = 1;
|
|
}
|
|
else
|
|
{
|
|
if (strip_size)
|
|
{
|
|
// connect last strip using degenerate triangles
|
|
destination[strip_size++] = strip[1];
|
|
destination[strip_size++] = a;
|
|
}
|
|
|
|
// note that we may need to flip the emitted triangle based on parity
|
|
// we always end up with outgoing edge "cb" in the end
|
|
unsigned int e0 = parity ? c : b;
|
|
unsigned int e1 = parity ? b : c;
|
|
|
|
destination[strip_size++] = a;
|
|
destination[strip_size++] = e0;
|
|
destination[strip_size++] = e1;
|
|
|
|
strip[0] = e0;
|
|
strip[1] = e1;
|
|
parity ^= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return strip_size;
|
|
}
|
|
|
|
size_t meshopt_stripifyBound(size_t index_count)
|
|
{
|
|
assert(index_count % 3 == 0);
|
|
|
|
// worst case without restarts is 2 degenerate indices and 3 indices per triangle
|
|
// worst case with restarts is 1 restart index and 3 indices per triangle
|
|
return (index_count / 3) * 5;
|
|
}
|
|
|
|
size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count, unsigned int restart_index)
|
|
{
|
|
assert(destination != indices);
|
|
|
|
size_t offset = 0;
|
|
size_t start = 0;
|
|
|
|
for (size_t i = 0; i < index_count; ++i)
|
|
{
|
|
if (restart_index && indices[i] == restart_index)
|
|
{
|
|
start = i + 1;
|
|
}
|
|
else if (i - start >= 2)
|
|
{
|
|
unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i];
|
|
|
|
// flip winding for odd triangles
|
|
if ((i - start) & 1)
|
|
{
|
|
unsigned int t = a;
|
|
a = b, b = t;
|
|
}
|
|
|
|
// although we use restart indices, strip swaps still produce degenerate triangles, so skip them
|
|
if (a != b && a != c && b != c)
|
|
{
|
|
destination[offset + 0] = a;
|
|
destination[offset + 1] = b;
|
|
destination[offset + 2] = c;
|
|
offset += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
size_t meshopt_unstripifyBound(size_t index_count)
|
|
{
|
|
assert(index_count == 0 || index_count >= 3);
|
|
|
|
return (index_count == 0) ? 0 : (index_count - 2) * 3;
|
|
}
|