virtualx-engine/thirdparty/meshoptimizer/stripifier.cpp
reduz 77a045e902 Rework Mesh handling on scene importing.
-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.
2020-12-13 21:29:51 -03:00

295 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;
}