You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-12-31 18:41:20 +00:00
meshoptimizer: Update to 1.0
This commit is contained in:
140
thirdparty/meshoptimizer/simplifier.cpp
vendored
140
thirdparty/meshoptimizer/simplifier.cpp
vendored
@@ -243,14 +243,18 @@ static unsigned int* buildSparseRemap(unsigned int* indices, size_t index_count,
|
||||
{
|
||||
// use a bit set to compute the precise number of unique vertices
|
||||
unsigned char* filter = allocator.allocate<unsigned char>((vertex_count + 7) / 8);
|
||||
memset(filter, 0, (vertex_count + 7) / 8);
|
||||
|
||||
for (size_t i = 0; i < index_count; ++i)
|
||||
{
|
||||
unsigned int index = indices[i];
|
||||
assert(index < vertex_count);
|
||||
filter[index / 8] = 0;
|
||||
}
|
||||
|
||||
size_t unique = 0;
|
||||
for (size_t i = 0; i < index_count; ++i)
|
||||
{
|
||||
unsigned int index = indices[i];
|
||||
assert(index < vertex_count);
|
||||
|
||||
unique += (filter[index / 8] & (1 << (index % 8))) == 0;
|
||||
filter[index / 8] |= 1 << (index % 8);
|
||||
}
|
||||
@@ -269,7 +273,6 @@ static unsigned int* buildSparseRemap(unsigned int* indices, size_t index_count,
|
||||
for (size_t i = 0; i < index_count; ++i)
|
||||
{
|
||||
unsigned int index = indices[i];
|
||||
|
||||
unsigned int* entry = hashLookup2(revremap, revremap_size, hasher, index, ~0u);
|
||||
|
||||
if (*entry == ~0u)
|
||||
@@ -364,8 +367,8 @@ static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned
|
||||
memset(loopback, -1, vertex_count * sizeof(unsigned int));
|
||||
|
||||
// incoming & outgoing open edges: ~0u if no open edges, i if there are more than 1
|
||||
// note that this is the same data as required in loop[] arrays; loop[] data is only valid for border/seam
|
||||
// but here it's okay to fill the data out for other types of vertices as well
|
||||
// note that this is the same data as required in loop[] arrays; loop[] data is only used for border/seam by default
|
||||
// in permissive mode we also use it to guide complex-complex collapses, so we fill it for all vertices
|
||||
unsigned int* openinc = loopback;
|
||||
unsigned int* openout = loop;
|
||||
|
||||
@@ -617,7 +620,7 @@ static void rescaleAttributes(float* result, const float* vertex_attributes_data
|
||||
}
|
||||
}
|
||||
|
||||
static void finalizeVertices(float* vertex_positions_data, size_t vertex_positions_stride, float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t vertex_count, const Vector3* vertex_positions, const float* vertex_attributes, const unsigned int* sparse_remap, const unsigned int* attribute_remap, float vertex_scale, const float* vertex_offset, const unsigned char* vertex_update)
|
||||
static void finalizeVertices(float* vertex_positions_data, size_t vertex_positions_stride, float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t vertex_count, const Vector3* vertex_positions, const float* vertex_attributes, const unsigned int* sparse_remap, const unsigned int* attribute_remap, float vertex_scale, const float* vertex_offset, const unsigned char* vertex_kind, const unsigned char* vertex_update, const unsigned char* vertex_lock)
|
||||
{
|
||||
size_t vertex_positions_stride_float = vertex_positions_stride / sizeof(float);
|
||||
size_t vertex_attributes_stride_float = vertex_attributes_stride / sizeof(float);
|
||||
@@ -629,12 +632,20 @@ static void finalizeVertices(float* vertex_positions_data, size_t vertex_positio
|
||||
|
||||
unsigned int ri = sparse_remap ? sparse_remap[i] : unsigned(i);
|
||||
|
||||
const Vector3& p = vertex_positions[i];
|
||||
float* v = vertex_positions_data + ri * vertex_positions_stride_float;
|
||||
// updating externally locked vertices is not allowed
|
||||
if (vertex_lock && (vertex_lock[ri] & meshopt_SimplifyVertex_Lock) != 0)
|
||||
continue;
|
||||
|
||||
v[0] = p.x * vertex_scale + vertex_offset[0];
|
||||
v[1] = p.y * vertex_scale + vertex_offset[1];
|
||||
v[2] = p.z * vertex_scale + vertex_offset[2];
|
||||
// moving locked vertices may result in floating point drift
|
||||
if (vertex_kind[i] != Kind_Locked)
|
||||
{
|
||||
const Vector3& p = vertex_positions[i];
|
||||
float* v = vertex_positions_data + ri * vertex_positions_stride_float;
|
||||
|
||||
v[0] = p.x * vertex_scale + vertex_offset[0];
|
||||
v[1] = p.y * vertex_scale + vertex_offset[1];
|
||||
v[2] = p.z * vertex_scale + vertex_offset[2];
|
||||
}
|
||||
|
||||
if (attribute_count)
|
||||
{
|
||||
@@ -1260,6 +1271,20 @@ static float getNeighborhoodRadius(const EdgeAdjacency& adjacency, const Vector3
|
||||
return sqrtf(result);
|
||||
}
|
||||
|
||||
static unsigned int getComplexTarget(unsigned int v, unsigned int target, const unsigned int* remap, const unsigned int* loop, const unsigned int* loopback)
|
||||
{
|
||||
unsigned int r = remap[target];
|
||||
|
||||
// use loop metadata to guide complex collapses towards the correct wedge
|
||||
// this works for edges on attribute discontinuities because loop/loopback track the single half-edge without a pair, similar to seams
|
||||
if (loop[v] != ~0u && remap[loop[v]] == r)
|
||||
return loop[v];
|
||||
else if (loopback[v] != ~0u && remap[loopback[v]] == r)
|
||||
return loopback[v];
|
||||
else
|
||||
return target;
|
||||
}
|
||||
|
||||
static size_t boundEdgeCollapses(const EdgeAdjacency& adjacency, size_t vertex_count, size_t index_count, unsigned char* vertex_kind)
|
||||
{
|
||||
size_t dual_count = 0;
|
||||
@@ -1382,15 +1407,22 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
|
||||
}
|
||||
else
|
||||
{
|
||||
// complex edges can have multiple wedges, so we need to aggregate errors for all wedges
|
||||
// this is different from seams (where we aggregate pairwise) because all wedges collapse onto the same target
|
||||
// complex edges can have multiple wedges, so we need to aggregate errors for all wedges based on the selected target
|
||||
if (vertex_kind[i0] == Kind_Complex)
|
||||
for (unsigned int v = wedge[i0]; v != i0; v = wedge[v])
|
||||
ei += quadricError(attribute_quadrics[v], &attribute_gradients[v * attribute_count], attribute_count, vertex_positions[i1], &vertex_attributes[i1 * attribute_count]);
|
||||
{
|
||||
unsigned int t = getComplexTarget(v, i1, remap, loop, loopback);
|
||||
|
||||
ei += quadricError(attribute_quadrics[v], &attribute_gradients[v * attribute_count], attribute_count, vertex_positions[t], &vertex_attributes[t * attribute_count]);
|
||||
}
|
||||
|
||||
if (vertex_kind[i1] == Kind_Complex && bidi)
|
||||
for (unsigned int v = wedge[i1]; v != i1; v = wedge[v])
|
||||
ej += quadricError(attribute_quadrics[v], &attribute_gradients[v * attribute_count], attribute_count, vertex_positions[i0], &vertex_attributes[i0 * attribute_count]);
|
||||
{
|
||||
unsigned int t = getComplexTarget(v, i0, remap, loop, loopback);
|
||||
|
||||
ej += quadricError(attribute_quadrics[v], &attribute_gradients[v * attribute_count], attribute_count, vertex_positions[t], &vertex_attributes[t * attribute_count]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1542,7 +1574,9 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
|
||||
|
||||
do
|
||||
{
|
||||
collapse_remap[v] = i1;
|
||||
unsigned int t = getComplexTarget(v, i1, remap, loop, loopback);
|
||||
|
||||
collapse_remap[v] = t;
|
||||
v = wedge[v];
|
||||
} while (v != i0);
|
||||
}
|
||||
@@ -1634,10 +1668,10 @@ static void updateQuadrics(const unsigned int* collapse_remap, size_t vertex_cou
|
||||
}
|
||||
}
|
||||
|
||||
static void solveQuadrics(Vector3* vertex_positions, float* vertex_attributes, size_t vertex_count, const Quadric* vertex_quadrics, const QuadricGrad* volume_gradients, const Quadric* attribute_quadrics, const QuadricGrad* attribute_gradients, size_t attribute_count, const unsigned int* remap, const unsigned int* wedge, const EdgeAdjacency& adjacency, const unsigned char* vertex_kind, const unsigned char* vertex_update)
|
||||
static void solvePositions(Vector3* vertex_positions, size_t vertex_count, const Quadric* vertex_quadrics, const QuadricGrad* volume_gradients, const Quadric* attribute_quadrics, const QuadricGrad* attribute_gradients, size_t attribute_count, const unsigned int* remap, const unsigned int* wedge, const EdgeAdjacency& adjacency, const unsigned char* vertex_kind, const unsigned char* vertex_update)
|
||||
{
|
||||
#if TRACE
|
||||
size_t stats[5] = {};
|
||||
size_t stats[6] = {};
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
@@ -1645,7 +1679,6 @@ static void solveQuadrics(Vector3* vertex_positions, float* vertex_attributes, s
|
||||
if (!vertex_update[i])
|
||||
continue;
|
||||
|
||||
// moving externally locked vertices is prohibited
|
||||
// moving vertices on an attribute discontinuity may result in extrapolating UV outside of the chart bounds
|
||||
// moving vertices on a border requires a stronger edge quadric to preserve the border geometry
|
||||
if (vertex_kind[i] == Kind_Locked || vertex_kind[i] == Kind_Seam || vertex_kind[i] == Kind_Border)
|
||||
@@ -1709,36 +1742,64 @@ static void solveQuadrics(Vector3* vertex_positions, float* vertex_attributes, s
|
||||
continue;
|
||||
}
|
||||
|
||||
// reject updates that increase positional error too much; allow some tolerance to improve attribute quality
|
||||
if (quadricError(vertex_quadrics[i], p) > quadricError(vertex_quadrics[i], vp) * 1.5f + 1e-6f)
|
||||
{
|
||||
TRACESTATS(5);
|
||||
continue;
|
||||
}
|
||||
|
||||
TRACESTATS(1);
|
||||
vertex_positions[i] = p;
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
printf("updated %d/%d positions; failed solve %d bounds %d flip %d\n", int(stats[1]), int(stats[0]), int(stats[2]), int(stats[3]), int(stats[4]));
|
||||
printf("updated %d/%d positions; failed solve %d bounds %d flip %d error %d\n", int(stats[1]), int(stats[0]), int(stats[2]), int(stats[3]), int(stats[4]), int(stats[5]));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (attribute_count == 0)
|
||||
return;
|
||||
|
||||
static void solveAttributes(Vector3* vertex_positions, float* vertex_attributes, size_t vertex_count, const Quadric* attribute_quadrics, const QuadricGrad* attribute_gradients, size_t attribute_count, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const unsigned char* vertex_update)
|
||||
{
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
{
|
||||
if (!vertex_update[i])
|
||||
continue;
|
||||
|
||||
// updating externally locked vertices is prohibited
|
||||
if (vertex_kind[i] == Kind_Locked)
|
||||
if (remap[i] != i)
|
||||
continue;
|
||||
|
||||
const Vector3& p = vertex_positions[remap[i]];
|
||||
const Quadric& A = attribute_quadrics[i];
|
||||
|
||||
float iw = A.w == 0 ? 0.f : 1.f / A.w;
|
||||
|
||||
for (size_t k = 0; k < attribute_count; ++k)
|
||||
{
|
||||
const QuadricGrad& G = attribute_gradients[i * attribute_count + k];
|
||||
unsigned int shared = ~0u;
|
||||
|
||||
vertex_attributes[i * attribute_count + k] = (G.gx * p.x + G.gy * p.y + G.gz * p.z + G.gw) * iw;
|
||||
// for complex vertices, preserve attribute continuity and use highest weight wedge if values were shared
|
||||
if (vertex_kind[i] == Kind_Complex)
|
||||
{
|
||||
shared = unsigned(i);
|
||||
|
||||
for (unsigned int v = wedge[i]; v != i; v = wedge[v])
|
||||
if (vertex_attributes[v * attribute_count + k] != vertex_attributes[i * attribute_count + k])
|
||||
shared = ~0u;
|
||||
else if (shared != ~0u && attribute_quadrics[v].w > attribute_quadrics[shared].w)
|
||||
shared = v;
|
||||
}
|
||||
|
||||
// update attributes for all wedges
|
||||
unsigned int v = unsigned(i);
|
||||
do
|
||||
{
|
||||
unsigned int r = (shared == ~0u) ? v : shared;
|
||||
|
||||
const Vector3& p = vertex_positions[i]; // same for all wedges
|
||||
const Quadric& A = attribute_quadrics[r];
|
||||
const QuadricGrad& G = attribute_gradients[r * attribute_count + k];
|
||||
|
||||
float iw = A.w == 0 ? 0.f : 1.f / A.w;
|
||||
float av = (G.gx * p.x + G.gy * p.y + G.gz * p.z + G.gw) * iw;
|
||||
|
||||
vertex_attributes[v * attribute_count + k] = av;
|
||||
v = wedge[v];
|
||||
} while (v != i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2264,7 +2325,7 @@ static float interpolate(float y, float x0, float y0, float x1, float y1, float
|
||||
// three point interpolation from "revenge of interpolation search" paper
|
||||
float num = (y1 - y) * (x1 - x2) * (x1 - x0) * (y2 - y0);
|
||||
float den = (y2 - y) * (x1 - x2) * (y0 - y1) + (y0 - y) * (x1 - x0) * (y1 - y2);
|
||||
return x1 + num / den;
|
||||
return x1 + (den == 0.f ? 0.f : num / den);
|
||||
}
|
||||
|
||||
} // namespace meshopt
|
||||
@@ -2519,16 +2580,19 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
{
|
||||
unsigned int v = result[i];
|
||||
|
||||
// recomputing externally locked vertices may result in floating point drift
|
||||
vertex_update[v] = vertex_kind[v] != Kind_Locked;
|
||||
// mark the vertex for finalizeVertices and root vertex for solve*
|
||||
vertex_update[remap[v]] = vertex_update[v] = 1;
|
||||
}
|
||||
|
||||
// edge adjacency may be stale as we haven't updated it after last series of edge collapses
|
||||
updateEdgeAdjacency(adjacency, result, result_count, vertex_count, remap);
|
||||
|
||||
solveQuadrics(vertex_positions, vertex_attributes, vertex_count, vertex_quadrics, volume_gradients, attribute_quadrics, attribute_gradients, attribute_count, remap, wedge, adjacency, vertex_kind, vertex_update);
|
||||
solvePositions(vertex_positions, vertex_count, vertex_quadrics, volume_gradients, attribute_quadrics, attribute_gradients, attribute_count, remap, wedge, adjacency, vertex_kind, vertex_update);
|
||||
|
||||
finalizeVertices(const_cast<float*>(vertex_positions_data), vertex_positions_stride, const_cast<float*>(vertex_attributes_data), vertex_attributes_stride, attribute_weights, attribute_count, vertex_count, vertex_positions, vertex_attributes, sparse_remap, attribute_remap, vertex_scale, vertex_offset, vertex_update);
|
||||
if (attribute_count)
|
||||
solveAttributes(vertex_positions, vertex_attributes, vertex_count, attribute_quadrics, attribute_gradients, attribute_count, remap, wedge, vertex_kind, vertex_update);
|
||||
|
||||
finalizeVertices(const_cast<float*>(vertex_positions_data), vertex_positions_stride, const_cast<float*>(vertex_attributes_data), vertex_attributes_stride, attribute_weights, attribute_count, vertex_count, vertex_positions, vertex_attributes, sparse_remap, attribute_remap, vertex_scale, vertex_offset, vertex_kind, vertex_update, vertex_lock);
|
||||
}
|
||||
|
||||
// if debug visualization data is requested, fill it instead of index data; for simplicity, this doesn't work with sparsity
|
||||
|
||||
Reference in New Issue
Block a user