mirror of
https://github.com/openharmony/third_party_meshoptimizer.git
synced 2026-07-19 12:03:07 -04:00
Merge pull request #704 from zeux/simplify
Improve support for cluster simplification
This commit is contained in:
@@ -522,6 +522,8 @@ void simplifyPoints(const Mesh& mesh, float threshold = 0.2f)
|
||||
double start = timestamp();
|
||||
|
||||
size_t target_vertex_count = size_t(mesh.vertices.size() * threshold);
|
||||
if (target_vertex_count == 0)
|
||||
return;
|
||||
|
||||
std::vector<unsigned int> indices(target_vertex_count);
|
||||
indices.resize(meshopt_simplifyPoints(&indices[0], &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), NULL, 0, 0, target_vertex_count));
|
||||
@@ -641,6 +643,61 @@ void simplifyComplete(const Mesh& mesh)
|
||||
}
|
||||
}
|
||||
|
||||
void simplifyClusters(const Mesh& mesh, float threshold = 0.2f)
|
||||
{
|
||||
// note: we use clusters that are larger than normal to give simplifier room to work; in practice you'd use cluster groups merged from smaller clusters and build a cluster DAG
|
||||
const size_t max_vertices = 255;
|
||||
const size_t max_triangles = 512;
|
||||
|
||||
double start = timestamp();
|
||||
|
||||
size_t max_meshlets = meshopt_buildMeshletsBound(mesh.indices.size(), max_vertices, max_triangles);
|
||||
std::vector<meshopt_Meshlet> meshlets(max_meshlets);
|
||||
std::vector<unsigned int> meshlet_vertices(max_meshlets * max_vertices);
|
||||
std::vector<unsigned char> meshlet_triangles(max_meshlets * max_triangles * 3);
|
||||
|
||||
meshlets.resize(meshopt_buildMeshlets(&meshlets[0], &meshlet_vertices[0], &meshlet_triangles[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), max_vertices, max_triangles, 0.f));
|
||||
|
||||
double middle = timestamp();
|
||||
|
||||
float scale = meshopt_simplifyScale(&mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex));
|
||||
|
||||
std::vector<unsigned int> lod;
|
||||
lod.reserve(mesh.indices.size());
|
||||
|
||||
float error = 0.f;
|
||||
|
||||
for (size_t i = 0; i < meshlets.size(); ++i)
|
||||
{
|
||||
const meshopt_Meshlet& m = meshlets[i];
|
||||
|
||||
size_t cluster_offset = lod.size();
|
||||
|
||||
for (size_t j = 0; j < m.triangle_count * 3; ++j)
|
||||
lod.push_back(meshlet_vertices[m.vertex_offset + meshlet_triangles[m.triangle_offset + j]]);
|
||||
|
||||
unsigned int options = meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute;
|
||||
|
||||
float cluster_target_error = 1e-2f * scale;
|
||||
size_t cluster_target = size_t(float(m.triangle_count) * threshold) * 3;
|
||||
float cluster_error = 0.f;
|
||||
size_t cluster_size = meshopt_simplify(&lod[cluster_offset], &lod[cluster_offset], m.triangle_count * 3, &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), cluster_target, cluster_target_error, options, &cluster_error);
|
||||
|
||||
error = cluster_error > error ? cluster_error : error;
|
||||
|
||||
// simplified cluster is available in lod[cluster_offset..cluster_offset + cluster_size]
|
||||
lod.resize(cluster_offset + cluster_size);
|
||||
}
|
||||
|
||||
double end = timestamp();
|
||||
|
||||
printf("%-9s: %d triangles => %d triangles (%.2f%% deviation) in %.2f msec, clusterized in %.2f msec\n",
|
||||
"SimplifyN", // N for Nanite
|
||||
int(mesh.indices.size() / 3), int(lod.size() / 3),
|
||||
error / scale * 100,
|
||||
(end - middle) * 1000, (middle - start) * 1000);
|
||||
}
|
||||
|
||||
void optimize(const Mesh& mesh, const char* name, void (*optf)(Mesh& mesh))
|
||||
{
|
||||
Mesh copy = mesh;
|
||||
@@ -1231,6 +1288,7 @@ void process(const char* path)
|
||||
simplifySloppy(mesh);
|
||||
simplifyComplete(mesh);
|
||||
simplifyPoints(mesh);
|
||||
simplifyClusters(mesh);
|
||||
|
||||
spatialSort(mesh);
|
||||
spatialSortTriangles(mesh);
|
||||
@@ -1246,6 +1304,7 @@ void processDev(const char* path)
|
||||
return;
|
||||
|
||||
simplify(mesh);
|
||||
simplifyClusters(mesh);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
||||
+116
-9
@@ -1188,15 +1188,15 @@ static void simplifyAttr()
|
||||
static void simplifyLockFlags()
|
||||
{
|
||||
float vb[] = {
|
||||
0.000000f, 0.000000f, 0.000000f,
|
||||
0.000000f, 1.000000f, 0.000000f,
|
||||
0.000000f, 2.000000f, 0.000000f,
|
||||
1.000000f, 0.000000f, 0.000000f,
|
||||
1.000000f, 1.000000f, 0.000000f,
|
||||
1.000000f, 2.000000f, 0.000000f,
|
||||
2.000000f, 0.000000f, 0.000000f,
|
||||
2.000000f, 1.000000f, 0.000000f,
|
||||
2.000000f, 2.000000f, 0.000000f, // clang-format :-/
|
||||
0, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 2, 0,
|
||||
1, 0, 0,
|
||||
1, 1, 0,
|
||||
1, 2, 0,
|
||||
2, 0, 0,
|
||||
2, 1, 0,
|
||||
2, 2, 0, // clang-format :-/
|
||||
};
|
||||
|
||||
unsigned char lock[9] = {
|
||||
@@ -1233,6 +1233,111 @@ static void simplifyLockFlags()
|
||||
assert(memcmp(ib, expected, sizeof(expected)) == 0);
|
||||
}
|
||||
|
||||
static void simplifySparse()
|
||||
{
|
||||
float vb[] = {
|
||||
0, 0, 100,
|
||||
0, 1, 0,
|
||||
0, 2, 100,
|
||||
1, 0, 0.1f,
|
||||
1, 1, 0.1f,
|
||||
1, 2, 0.1f,
|
||||
2, 0, 100,
|
||||
2, 1, 0,
|
||||
2, 2, 100, // clang-format :-/
|
||||
};
|
||||
|
||||
float vba[] = {
|
||||
100,
|
||||
0.5f,
|
||||
100,
|
||||
0.5f,
|
||||
0.5f,
|
||||
0,
|
||||
100,
|
||||
0.5f,
|
||||
100, // clang-format :-/
|
||||
};
|
||||
|
||||
float aw[] = {
|
||||
0.2f};
|
||||
|
||||
unsigned char lock[9] = {
|
||||
8, 1, 8,
|
||||
1, 0, 1,
|
||||
8, 1, 8, // clang-format :-/
|
||||
};
|
||||
|
||||
// 1
|
||||
// 3 4 5
|
||||
// 7
|
||||
|
||||
unsigned int ib[] = {
|
||||
3, 1, 4,
|
||||
1, 5, 4,
|
||||
3, 4, 7,
|
||||
4, 5, 7, // clang-format :-/
|
||||
};
|
||||
|
||||
unsigned int res[12];
|
||||
|
||||
// vertices 3-4-5 are slightly elevated along Z which guides the collapses when only using geometry
|
||||
unsigned int expected[] = {
|
||||
1, 5, 3,
|
||||
3, 5, 7, // clang-format :-/
|
||||
};
|
||||
|
||||
assert(meshopt_simplify(res, ib, 12, vb, 9, 12, 6, 1e-3f, meshopt_SimplifySparse) == 6);
|
||||
assert(memcmp(res, expected, sizeof(expected)) == 0);
|
||||
|
||||
// vertices 1-4-7 have a crease in the attribute value which guides the collapses the opposite way when weighing attributes sufficiently
|
||||
unsigned int expecteda[] = {
|
||||
3, 1, 7,
|
||||
1, 5, 7, // clang-format :-/
|
||||
};
|
||||
|
||||
assert(meshopt_simplifyWithAttributes(res, ib, 12, vb, 9, 12, vba, sizeof(float), aw, 1, lock, 6, 1e-1f, meshopt_SimplifySparse) == 6);
|
||||
assert(memcmp(res, expecteda, sizeof(expecteda)) == 0);
|
||||
|
||||
// a final test validates that destination can alias when using sparsity
|
||||
assert(meshopt_simplify(ib, ib, 12, vb, 9, 12, 6, 1e-3f, meshopt_SimplifySparse) == 6);
|
||||
assert(memcmp(ib, expected, sizeof(expected)) == 0);
|
||||
}
|
||||
|
||||
static void simplifyErrorAbsolute()
|
||||
{
|
||||
float vb[] = {
|
||||
0, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 2, 0,
|
||||
1, 0, 0,
|
||||
1, 1, 1,
|
||||
1, 2, 0,
|
||||
2, 0, 0,
|
||||
2, 1, 0,
|
||||
2, 2, 0, // clang-format :-/
|
||||
};
|
||||
|
||||
// 0 1 2
|
||||
// 3 4 5
|
||||
// 6 7 8
|
||||
|
||||
unsigned int ib[] = {
|
||||
0, 1, 3,
|
||||
3, 1, 4,
|
||||
1, 2, 4,
|
||||
4, 2, 5,
|
||||
3, 4, 6,
|
||||
6, 4, 7,
|
||||
4, 5, 7,
|
||||
7, 5, 8, // clang-format :-/
|
||||
};
|
||||
|
||||
float error = 0.f;
|
||||
assert(meshopt_simplify(ib, ib, 24, vb, 9, 12, 18, 2.f, meshopt_SimplifyLockBorder | meshopt_SimplifyErrorAbsolute, &error) == 18);
|
||||
assert(fabsf(error - 0.85f) < 0.01f);
|
||||
}
|
||||
|
||||
static void adjacency()
|
||||
{
|
||||
// 0 1/4
|
||||
@@ -1448,6 +1553,8 @@ void runTests()
|
||||
simplifyLockBorder();
|
||||
simplifyAttr();
|
||||
simplifyLockFlags();
|
||||
simplifySparse();
|
||||
simplifyErrorAbsolute();
|
||||
|
||||
adjacency();
|
||||
tessellation();
|
||||
|
||||
@@ -155,6 +155,8 @@ var MeshoptSimplifier = (function() {
|
||||
|
||||
var simplifyOptions = {
|
||||
LockBorder: 1,
|
||||
Sparse: 2,
|
||||
ErrorAbsolute: 4,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
// This file is part of meshoptimizer library and is distributed under the terms of MIT License.
|
||||
// Copyright (C) 2016-2024, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
||||
export type Flags = "LockBorder";
|
||||
export type Flags = "LockBorder" | "Sparse" | "ErrorAbsolute";
|
||||
|
||||
export const MeshoptSimplifier: {
|
||||
supported: boolean;
|
||||
|
||||
@@ -154,6 +154,8 @@ var MeshoptSimplifier = (function() {
|
||||
|
||||
var simplifyOptions = {
|
||||
LockBorder: 1,
|
||||
Sparse: 2,
|
||||
ErrorAbsolute: 4,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -330,6 +330,10 @@ enum
|
||||
{
|
||||
/* Do not move vertices that are located on the topological border (vertices on triangle edges that don't have a paired triangle). Useful for simplifying portions of the larger mesh. */
|
||||
meshopt_SimplifyLockBorder = 1 << 0,
|
||||
/* Improve simplification performance assuming input indices are a sparse subset of the mesh. Note that error becomes relative to subset extents. */
|
||||
meshopt_SimplifySparse = 1 << 1,
|
||||
/* Treat error limit and resulting error as absolute instead of relative to mesh extents. */
|
||||
meshopt_SimplifyErrorAbsolute = 1 << 2,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+109
-24
@@ -111,10 +111,12 @@ struct PositionHasher
|
||||
{
|
||||
const float* vertex_positions;
|
||||
size_t vertex_stride_float;
|
||||
const unsigned int* sparse_remap;
|
||||
|
||||
size_t hash(unsigned int index) const
|
||||
{
|
||||
const unsigned int* key = reinterpret_cast<const unsigned int*>(vertex_positions + index * vertex_stride_float);
|
||||
unsigned int ri = sparse_remap ? sparse_remap[index] : index;
|
||||
const unsigned int* key = reinterpret_cast<const unsigned int*>(vertex_positions + ri * vertex_stride_float);
|
||||
|
||||
// scramble bits to make sure that integer coordinates have entropy in lower bits
|
||||
unsigned int x = key[0] ^ (key[0] >> 17);
|
||||
@@ -127,7 +129,25 @@ struct PositionHasher
|
||||
|
||||
bool equal(unsigned int lhs, unsigned int rhs) const
|
||||
{
|
||||
return memcmp(vertex_positions + lhs * vertex_stride_float, vertex_positions + rhs * vertex_stride_float, sizeof(float) * 3) == 0;
|
||||
unsigned int li = sparse_remap ? sparse_remap[lhs] : lhs;
|
||||
unsigned int ri = sparse_remap ? sparse_remap[rhs] : rhs;
|
||||
|
||||
return memcmp(vertex_positions + li * vertex_stride_float, vertex_positions + ri * vertex_stride_float, sizeof(float) * 3) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct RemapHasher
|
||||
{
|
||||
unsigned int* remap;
|
||||
|
||||
size_t hash(unsigned int id) const
|
||||
{
|
||||
return id * 0x5bd1e995;
|
||||
}
|
||||
|
||||
bool equal(unsigned int lhs, unsigned int rhs) const
|
||||
{
|
||||
return remap[lhs] == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -167,9 +187,9 @@ static T* hashLookup2(T* table, size_t buckets, const Hash& hash, const T& key,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void buildPositionRemap(unsigned int* remap, unsigned int* wedge, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, meshopt_Allocator& allocator)
|
||||
static void buildPositionRemap(unsigned int* remap, unsigned int* wedge, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const unsigned int* sparse_remap, meshopt_Allocator& allocator)
|
||||
{
|
||||
PositionHasher hasher = {vertex_positions_data, vertex_positions_stride / sizeof(float)};
|
||||
PositionHasher hasher = {vertex_positions_data, vertex_positions_stride / sizeof(float), sparse_remap};
|
||||
|
||||
size_t table_size = hashBuckets2(vertex_count);
|
||||
unsigned int* table = allocator.allocate<unsigned int>(table_size);
|
||||
@@ -205,6 +225,57 @@ static void buildPositionRemap(unsigned int* remap, unsigned int* wedge, const f
|
||||
allocator.deallocate(table);
|
||||
}
|
||||
|
||||
static unsigned int* buildSparseRemap(unsigned int* indices, size_t index_count, size_t vertex_count, size_t* out_vertex_count, meshopt_Allocator& allocator)
|
||||
{
|
||||
// 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
unsigned int* remap = allocator.allocate<unsigned int>(unique);
|
||||
size_t offset = 0;
|
||||
|
||||
// temporary map dense => sparse; we allocate it last so that we can deallocate it
|
||||
size_t revremap_size = hashBuckets2(unique);
|
||||
unsigned int* revremap = allocator.allocate<unsigned int>(revremap_size);
|
||||
memset(revremap, -1, revremap_size * sizeof(unsigned int));
|
||||
|
||||
// fill remap, using revremap as a helper, and rewrite indices in the same pass
|
||||
RemapHasher hasher = {remap};
|
||||
|
||||
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)
|
||||
{
|
||||
remap[offset] = index;
|
||||
*entry = unsigned(offset);
|
||||
offset++;
|
||||
}
|
||||
|
||||
indices[i] = *entry;
|
||||
}
|
||||
|
||||
allocator.deallocate(revremap);
|
||||
|
||||
assert(offset == unique);
|
||||
*out_vertex_count = unique;
|
||||
|
||||
return remap;
|
||||
}
|
||||
|
||||
enum VertexKind
|
||||
{
|
||||
Kind_Manifold, // not on an attribute seam, not on any boundary
|
||||
@@ -252,7 +323,7 @@ static bool hasEdge(const EdgeAdjacency& adjacency, unsigned int a, unsigned int
|
||||
return false;
|
||||
}
|
||||
|
||||
static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned int* loopback, size_t vertex_count, const EdgeAdjacency& adjacency, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_lock, unsigned int options)
|
||||
static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned int* loopback, size_t vertex_count, const EdgeAdjacency& adjacency, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_lock, const unsigned int* sparse_remap, unsigned int options)
|
||||
{
|
||||
memset(loop, -1, vertex_count * sizeof(unsigned int));
|
||||
memset(loopback, -1, vertex_count * sizeof(unsigned int));
|
||||
@@ -298,7 +369,7 @@ static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned
|
||||
{
|
||||
if (remap[i] == i)
|
||||
{
|
||||
if (vertex_lock && vertex_lock[i])
|
||||
if (vertex_lock && vertex_lock[sparse_remap ? sparse_remap[i] : i])
|
||||
{
|
||||
// vertex is explicitly locked
|
||||
result[i] = Kind_Locked;
|
||||
@@ -383,7 +454,7 @@ struct Vector3
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
static float rescalePositions(Vector3* result, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride)
|
||||
static float rescalePositions(Vector3* result, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const unsigned int* sparse_remap = NULL)
|
||||
{
|
||||
size_t vertex_stride_float = vertex_positions_stride / sizeof(float);
|
||||
|
||||
@@ -392,7 +463,8 @@ static float rescalePositions(Vector3* result, const float* vertex_positions_dat
|
||||
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
{
|
||||
const float* v = vertex_positions_data + i * vertex_stride_float;
|
||||
unsigned int ri = sparse_remap ? sparse_remap[i] : unsigned(i);
|
||||
const float* v = vertex_positions_data + ri * vertex_stride_float;
|
||||
|
||||
if (result)
|
||||
{
|
||||
@@ -431,15 +503,17 @@ static float rescalePositions(Vector3* result, const float* vertex_positions_dat
|
||||
return extent;
|
||||
}
|
||||
|
||||
static void rescaleAttributes(float* result, const float* vertex_attributes_data, size_t vertex_count, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count)
|
||||
static void rescaleAttributes(float* result, const float* vertex_attributes_data, size_t vertex_count, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned int* sparse_remap)
|
||||
{
|
||||
size_t vertex_attributes_stride_float = vertex_attributes_stride / sizeof(float);
|
||||
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
{
|
||||
unsigned int ri = sparse_remap ? sparse_remap[i] : unsigned(i);
|
||||
|
||||
for (size_t k = 0; k < attribute_count; ++k)
|
||||
{
|
||||
float a = vertex_attributes_data[i * vertex_attributes_stride_float + k];
|
||||
float a = vertex_attributes_data[ri * vertex_attributes_stride_float + k];
|
||||
|
||||
result[i * attribute_count + k] = a * attribute_weights[k];
|
||||
}
|
||||
@@ -1481,7 +1555,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256);
|
||||
assert(vertex_positions_stride % sizeof(float) == 0);
|
||||
assert(target_index_count <= index_count);
|
||||
assert((options & ~(meshopt_SimplifyLockBorder)) == 0);
|
||||
assert((options & ~(meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute)) == 0);
|
||||
assert(vertex_attributes_stride >= attribute_count * sizeof(float) && vertex_attributes_stride <= 256);
|
||||
assert(vertex_attributes_stride % sizeof(float) == 0);
|
||||
assert(attribute_count <= kMaxAttributes);
|
||||
@@ -1489,22 +1563,30 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
meshopt_Allocator allocator;
|
||||
|
||||
unsigned int* result = destination;
|
||||
if (result != indices)
|
||||
memcpy(result, indices, index_count * sizeof(unsigned int));
|
||||
|
||||
// build an index remap and update indices/vertex_count to minimize the subsequent work
|
||||
// note: as a consequence, errors will be computed relative to the subset extent
|
||||
unsigned int* sparse_remap = NULL;
|
||||
if (options & meshopt_SimplifySparse)
|
||||
sparse_remap = buildSparseRemap(result, index_count, vertex_count, &vertex_count, allocator);
|
||||
|
||||
// build adjacency information
|
||||
EdgeAdjacency adjacency = {};
|
||||
prepareEdgeAdjacency(adjacency, index_count, vertex_count, allocator);
|
||||
updateEdgeAdjacency(adjacency, indices, index_count, vertex_count, NULL);
|
||||
updateEdgeAdjacency(adjacency, result, index_count, vertex_count, NULL);
|
||||
|
||||
// build position remap that maps each vertex to the one with identical position
|
||||
unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
|
||||
unsigned int* wedge = allocator.allocate<unsigned int>(vertex_count);
|
||||
buildPositionRemap(remap, wedge, vertex_positions_data, vertex_count, vertex_positions_stride, allocator);
|
||||
buildPositionRemap(remap, wedge, vertex_positions_data, vertex_count, vertex_positions_stride, sparse_remap, allocator);
|
||||
|
||||
// classify vertices; vertex kind determines collapse rules, see kCanCollapse
|
||||
unsigned char* vertex_kind = allocator.allocate<unsigned char>(vertex_count);
|
||||
unsigned int* loop = allocator.allocate<unsigned int>(vertex_count);
|
||||
unsigned int* loopback = allocator.allocate<unsigned int>(vertex_count);
|
||||
classifyVertices(vertex_kind, loop, loopback, vertex_count, adjacency, remap, wedge, vertex_lock, options);
|
||||
classifyVertices(vertex_kind, loop, loopback, vertex_count, adjacency, remap, wedge, vertex_lock, sparse_remap, options);
|
||||
|
||||
#if TRACE
|
||||
size_t unique_positions = 0;
|
||||
@@ -1522,14 +1604,14 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
#endif
|
||||
|
||||
Vector3* vertex_positions = allocator.allocate<Vector3>(vertex_count);
|
||||
rescalePositions(vertex_positions, vertex_positions_data, vertex_count, vertex_positions_stride);
|
||||
float vertex_scale = rescalePositions(vertex_positions, vertex_positions_data, vertex_count, vertex_positions_stride, sparse_remap);
|
||||
|
||||
float* vertex_attributes = NULL;
|
||||
|
||||
if (attribute_count)
|
||||
{
|
||||
vertex_attributes = allocator.allocate<float>(vertex_count * attribute_count);
|
||||
rescaleAttributes(vertex_attributes, vertex_attributes_data, vertex_count, vertex_attributes_stride, attribute_weights, attribute_count);
|
||||
rescaleAttributes(vertex_attributes, vertex_attributes_data, vertex_count, vertex_attributes_stride, attribute_weights, attribute_count, sparse_remap);
|
||||
}
|
||||
|
||||
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
|
||||
@@ -1547,14 +1629,11 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
memset(attribute_gradients, 0, vertex_count * attribute_count * sizeof(QuadricGrad));
|
||||
}
|
||||
|
||||
fillFaceQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap);
|
||||
fillEdgeQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
|
||||
fillFaceQuadrics(vertex_quadrics, result, index_count, vertex_positions, remap);
|
||||
fillEdgeQuadrics(vertex_quadrics, result, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
|
||||
|
||||
if (attribute_count)
|
||||
fillAttributeQuadrics(attribute_quadrics, attribute_gradients, indices, index_count, vertex_positions, vertex_attributes, attribute_count, remap);
|
||||
|
||||
if (result != indices)
|
||||
memcpy(result, indices, index_count * sizeof(unsigned int));
|
||||
fillAttributeQuadrics(attribute_quadrics, attribute_gradients, result, index_count, vertex_positions, vertex_attributes, attribute_count, remap);
|
||||
|
||||
#if TRACE
|
||||
size_t pass_count = 0;
|
||||
@@ -1571,7 +1650,8 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
float result_error = 0;
|
||||
|
||||
// target_error input is linear; we need to adjust it to match quadricError units
|
||||
float error_limit = target_error * target_error;
|
||||
float error_scale = (options & meshopt_SimplifyErrorAbsolute) ? vertex_scale : 1.f;
|
||||
float error_limit = (target_error * target_error) / (error_scale * error_scale);
|
||||
|
||||
while (result_count > target_index_count)
|
||||
{
|
||||
@@ -1630,9 +1710,14 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
memcpy(meshopt_simplifyDebugLoopBack, loopback, vertex_count * sizeof(unsigned int));
|
||||
#endif
|
||||
|
||||
// convert resulting indices back into the dense space of the larger mesh
|
||||
if (sparse_remap)
|
||||
for (size_t i = 0; i < result_count; ++i)
|
||||
result[i] = sparse_remap[result[i]];
|
||||
|
||||
// result_error is quadratic; we need to remap it back to linear
|
||||
if (out_result_error)
|
||||
*out_result_error = sqrtf(result_error);
|
||||
*out_result_error = sqrtf(result_error) * error_scale;
|
||||
|
||||
return result_count;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user