Sfoglia il codice sorgente

Updated meshoptimizer.

Бранимир Караџић 1 anno fa
parent
commit
85b33a0f63

+ 1 - 1
3rdparty/meshoptimizer/src/allocator.cpp

@@ -1,7 +1,7 @@
 // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
 // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
 #include "meshoptimizer.h"
 #include "meshoptimizer.h"
 
 
-void meshopt_setAllocator(void* (MESHOPTIMIZER_ALLOC_CALLCONV *allocate)(size_t), void (MESHOPTIMIZER_ALLOC_CALLCONV *deallocate)(void*))
+void meshopt_setAllocator(void*(MESHOPTIMIZER_ALLOC_CALLCONV* allocate)(size_t), void(MESHOPTIMIZER_ALLOC_CALLCONV* deallocate)(void*))
 {
 {
 	meshopt_Allocator::Storage::allocate = allocate;
 	meshopt_Allocator::Storage::allocate = allocate;
 	meshopt_Allocator::Storage::deallocate = deallocate;
 	meshopt_Allocator::Storage::deallocate = deallocate;

+ 1 - 1
3rdparty/meshoptimizer/src/clusterizer.cpp

@@ -441,7 +441,7 @@ static size_t kdtreeBuild(size_t offset, KDNode* nodes, size_t node_count, const
 	}
 	}
 
 
 	// split axis is one where the variance is largest
 	// split axis is one where the variance is largest
-	unsigned int axis = vars[0] >= vars[1] && vars[0] >= vars[2] ? 0 : vars[1] >= vars[2] ? 1 : 2;
+	unsigned int axis = (vars[0] >= vars[1] && vars[0] >= vars[2]) ? 0 : (vars[1] >= vars[2] ? 1 : 2);
 
 
 	float split = mean[axis];
 	float split = mean[axis];
 	size_t middle = kdtreePartition(indices, count, points, stride, axis, split);
 	size_t middle = kdtreePartition(indices, count, points, stride, axis, split);

+ 4 - 4
3rdparty/meshoptimizer/src/indexcodec.cpp

@@ -33,7 +33,7 @@ static int rotateTriangle(unsigned int a, unsigned int b, unsigned int c, unsign
 {
 {
 	(void)a;
 	(void)a;
 
 
-	return (b == next) ? 1 : (c == next) ? 2 : 0;
+	return (b == next) ? 1 : (c == next ? 2 : 0);
 }
 }
 
 
 static int getEdgeFifo(EdgeFifo fifo, unsigned int a, unsigned int b, unsigned int c, size_t offset)
 static int getEdgeFifo(EdgeFifo fifo, unsigned int a, unsigned int b, unsigned int c, size_t offset)
@@ -217,7 +217,7 @@ size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, cons
 			int fe = fer >> 2;
 			int fe = fer >> 2;
 			int fc = getVertexFifo(vertexfifo, c, vertexfifooffset);
 			int fc = getVertexFifo(vertexfifo, c, vertexfifooffset);
 
 
-			int fec = (fc >= 1 && fc < fecmax) ? fc : (c == next) ? (next++, 0) : 15;
+			int fec = (fc >= 1 && fc < fecmax) ? fc : (c == next ? (next++, 0) : 15);
 
 
 			if (fec == 15 && version >= 1)
 			if (fec == 15 && version >= 1)
 			{
 			{
@@ -267,8 +267,8 @@ size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, cons
 
 
 			// after rotation, a is almost always equal to next, so we don't waste bits on FIFO encoding for a
 			// after rotation, a is almost always equal to next, so we don't waste bits on FIFO encoding for a
 			int fea = (a == next) ? (next++, 0) : 15;
 			int fea = (a == next) ? (next++, 0) : 15;
-			int feb = (fb >= 0 && fb < 14) ? (fb + 1) : (b == next) ? (next++, 0) : 15;
-			int fec = (fc >= 0 && fc < 14) ? (fc + 1) : (c == next) ? (next++, 0) : 15;
+			int feb = (fb >= 0 && fb < 14) ? fb + 1 : (b == next ? (next++, 0) : 15);
+			int fec = (fc >= 0 && fc < 14) ? fc + 1 : (c == next ? (next++, 0) : 15);
 
 
 			// we encode feb & fec in 4 bits using a table if possible, and as a full byte otherwise
 			// we encode feb & fec in 4 bits using a table if possible, and as a full byte otherwise
 			unsigned char codeaux = (unsigned char)((feb << 4) | fec);
 			unsigned char codeaux = (unsigned char)((feb << 4) | fec);

+ 17 - 13
3rdparty/meshoptimizer/src/meshoptimizer.h

@@ -1,5 +1,5 @@
 /**
 /**
- * meshoptimizer - version 0.20
+ * meshoptimizer - version 0.21
  *
  *
  * Copyright (C) 2016-2024, by Arseny Kapoulkine ([email protected])
  * Copyright (C) 2016-2024, by Arseny Kapoulkine ([email protected])
  * Report bugs and download new versions at https://github.com/zeux/meshoptimizer
  * Report bugs and download new versions at https://github.com/zeux/meshoptimizer
@@ -12,7 +12,7 @@
 #include <stddef.h>
 #include <stddef.h>
 
 
 /* Version macro; major * 1000 + minor * 10 + patch */
 /* Version macro; major * 1000 + minor * 10 + patch */
-#define MESHOPTIMIZER_VERSION 200 /* 0.20 */
+#define MESHOPTIMIZER_VERSION 210 /* 0.21 */
 
 
 /* If no API is defined, assume default */
 /* If no API is defined, assume default */
 #ifndef MESHOPTIMIZER_API
 #ifndef MESHOPTIMIZER_API
@@ -311,12 +311,12 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_decodeFilterExp(void* buffer, size_t cou
  */
  */
 enum meshopt_EncodeExpMode
 enum meshopt_EncodeExpMode
 {
 {
-    /* When encoding exponents, use separate values for each component (maximum quality) */
-    meshopt_EncodeExpSeparate,
-    /* When encoding exponents, use shared value for all components of each vector (better compression) */
-    meshopt_EncodeExpSharedVector,
-    /* When encoding exponents, use shared value for each component of all vectors (best compression) */
-    meshopt_EncodeExpSharedComponent,
+	/* When encoding exponents, use separate values for each component (maximum quality) */
+	meshopt_EncodeExpSeparate,
+	/* When encoding exponents, use shared value for all components of each vector (better compression) */
+	meshopt_EncodeExpSharedVector,
+	/* When encoding exponents, use shared value for each component of all vectors (best compression) */
+	meshopt_EncodeExpSharedComponent,
 };
 };
 
 
 MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterOct(void* destination, size_t count, size_t stride, int bits, const float* data);
 MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterOct(void* destination, size_t count, size_t stride, int bits, const float* data);
@@ -328,8 +328,12 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterExp(void* destination, size_
  */
  */
 enum
 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,
+	/* 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,
 };
 };
 
 
 /**
 /**
@@ -969,10 +973,10 @@ inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_co
 template <typename T>
 template <typename T>
 inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* result_error)
 inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* result_error)
 {
 {
-    meshopt_IndexAdapter<T> in(NULL, indices, index_count);
-    meshopt_IndexAdapter<T> out(destination, NULL, index_count);
+	meshopt_IndexAdapter<T> in(NULL, indices, index_count);
+	meshopt_IndexAdapter<T> out(destination, NULL, index_count);
 
 
-    return meshopt_simplifyWithAttributes(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, vertex_attributes, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, target_index_count, target_error, options, result_error);
+	return meshopt_simplifyWithAttributes(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, vertex_attributes, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, target_index_count, target_error, options, result_error);
 }
 }
 
 
 template <typename T>
 template <typename T>

+ 120 - 29
3rdparty/meshoptimizer/src/simplifier.cpp

@@ -111,10 +111,12 @@ struct PositionHasher
 {
 {
 	const float* vertex_positions;
 	const float* vertex_positions;
 	size_t vertex_stride_float;
 	size_t vertex_stride_float;
+	const unsigned int* sparse_remap;
 
 
 	size_t hash(unsigned int index) const
 	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
 		// scramble bits to make sure that integer coordinates have entropy in lower bits
 		unsigned int x = key[0] ^ (key[0] >> 17);
 		unsigned int x = key[0] ^ (key[0] >> 17);
@@ -127,7 +129,25 @@ struct PositionHasher
 
 
 	bool equal(unsigned int lhs, unsigned int rhs) const
 	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;
 	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);
 	size_t table_size = hashBuckets2(vertex_count);
 	unsigned int* table = allocator.allocate<unsigned int>(table_size);
 	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);
 	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
 enum VertexKind
 {
 {
 	Kind_Manifold, // not on an attribute seam, not on any boundary
 	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;
 	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(loop, -1, vertex_count * sizeof(unsigned int));
 	memset(loopback, -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 (remap[i] == i)
 		{
 		{
-			if (vertex_lock && vertex_lock[i])
+			if (vertex_lock && vertex_lock[sparse_remap ? sparse_remap[i] : i])
 			{
 			{
 				// vertex is explicitly locked
 				// vertex is explicitly locked
 				result[i] = Kind_Locked;
 				result[i] = Kind_Locked;
@@ -383,7 +454,7 @@ struct Vector3
 	float x, y, z;
 	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);
 	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)
 	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)
 		if (result)
 		{
 		{
@@ -431,15 +503,17 @@ static float rescalePositions(Vector3* result, const float* vertex_positions_dat
 	return extent;
 	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);
 	size_t vertex_attributes_stride_float = vertex_attributes_stride / sizeof(float);
 
 
 	for (size_t i = 0; i < vertex_count; ++i)
 	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)
 		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];
 			result[i * attribute_count + k] = a * attribute_weights[k];
 		}
 		}
@@ -818,7 +892,13 @@ static bool hasTriangleFlip(const Vector3& a, const Vector3& b, const Vector3& c
 	Vector3 nbc = {eb.y * ec.z - eb.z * ec.y, eb.z * ec.x - eb.x * ec.z, eb.x * ec.y - eb.y * ec.x};
 	Vector3 nbc = {eb.y * ec.z - eb.z * ec.y, eb.z * ec.x - eb.x * ec.z, eb.x * ec.y - eb.y * ec.x};
 	Vector3 nbd = {eb.y * ed.z - eb.z * ed.y, eb.z * ed.x - eb.x * ed.z, eb.x * ed.y - eb.y * ed.x};
 	Vector3 nbd = {eb.y * ed.z - eb.z * ed.y, eb.z * ed.x - eb.x * ed.z, eb.x * ed.y - eb.y * ed.x};
 
 
-	return nbc.x * nbd.x + nbc.y * nbd.y + nbc.z * nbd.z <= 0;
+	float ndp = nbc.x * nbd.x + nbc.y * nbd.y + nbc.z * nbd.z;
+	float abc = nbc.x * nbc.x + nbc.y * nbc.y + nbc.z * nbc.z;
+	float abd = nbd.x * nbd.x + nbd.y * nbd.y + nbd.z * nbd.z;
+
+	// scale is cos(angle); somewhat arbitrarily set to ~75 degrees
+	// note that the "pure" check is ndp <= 0 (90 degree cutoff) but that allows flipping through a series of close-to-90 collapses
+	return ndp <= 0.25f * sqrtf(abc * abd);
 }
 }
 
 
 static bool hasTriangleFlips(const EdgeAdjacency& adjacency, const Vector3* vertex_positions, const unsigned int* collapse_remap, unsigned int i0, unsigned int i1)
 static bool hasTriangleFlips(const EdgeAdjacency& adjacency, const Vector3* vertex_positions, const unsigned int* collapse_remap, unsigned int i0, unsigned int i1)
@@ -1481,7 +1561,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 >= 12 && vertex_positions_stride <= 256);
 	assert(vertex_positions_stride % sizeof(float) == 0);
 	assert(vertex_positions_stride % sizeof(float) == 0);
 	assert(target_index_count <= index_count);
 	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 >= attribute_count * sizeof(float) && vertex_attributes_stride <= 256);
 	assert(vertex_attributes_stride % sizeof(float) == 0);
 	assert(vertex_attributes_stride % sizeof(float) == 0);
 	assert(attribute_count <= kMaxAttributes);
 	assert(attribute_count <= kMaxAttributes);
@@ -1489,22 +1569,30 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
 	meshopt_Allocator allocator;
 	meshopt_Allocator allocator;
 
 
 	unsigned int* result = destination;
 	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
 	// build adjacency information
 	EdgeAdjacency adjacency = {};
 	EdgeAdjacency adjacency = {};
 	prepareEdgeAdjacency(adjacency, index_count, vertex_count, allocator);
 	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
 	// build position remap that maps each vertex to the one with identical position
 	unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
 	unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
 	unsigned int* wedge = 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
 	// classify vertices; vertex kind determines collapse rules, see kCanCollapse
 	unsigned char* vertex_kind = allocator.allocate<unsigned char>(vertex_count);
 	unsigned char* vertex_kind = allocator.allocate<unsigned char>(vertex_count);
 	unsigned int* loop = allocator.allocate<unsigned int>(vertex_count);
 	unsigned int* loop = allocator.allocate<unsigned int>(vertex_count);
 	unsigned int* loopback = 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
 #if TRACE
 	size_t unique_positions = 0;
 	size_t unique_positions = 0;
@@ -1522,14 +1610,14 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
 #endif
 #endif
 
 
 	Vector3* vertex_positions = allocator.allocate<Vector3>(vertex_count);
 	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;
 	float* vertex_attributes = NULL;
 
 
 	if (attribute_count)
 	if (attribute_count)
 	{
 	{
 		vertex_attributes = allocator.allocate<float>(vertex_count * 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);
 	Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
@@ -1547,14 +1635,11 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
 		memset(attribute_gradients, 0, vertex_count * attribute_count * sizeof(QuadricGrad));
 		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)
 	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
 #if TRACE
 	size_t pass_count = 0;
 	size_t pass_count = 0;
@@ -1571,7 +1656,8 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
 	float result_error = 0;
 	float result_error = 0;
 
 
 	// target_error input is linear; we need to adjust it to match quadricError units
 	// 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)
 	while (result_count > target_index_count)
 	{
 	{
@@ -1630,9 +1716,14 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
 		memcpy(meshopt_simplifyDebugLoopBack, loopback, vertex_count * sizeof(unsigned int));
 		memcpy(meshopt_simplifyDebugLoopBack, loopback, vertex_count * sizeof(unsigned int));
 #endif
 #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
 	// result_error is quadratic; we need to remap it back to linear
 	if (out_result_error)
 	if (out_result_error)
-		*out_result_error = sqrtf(result_error);
+		*out_result_error = sqrtf(result_error) * error_scale;
 
 
 	return result_count;
 	return result_count;
 }
 }
@@ -1697,14 +1788,14 @@ size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* ind
 
 
 		// we clamp the prediction of the grid size to make sure that the search converges
 		// we clamp the prediction of the grid size to make sure that the search converges
 		int grid_size = next_grid_size;
 		int grid_size = next_grid_size;
-		grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid) ? max_grid - 1 : grid_size;
+		grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid ? max_grid - 1 : grid_size);
 
 
 		computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
 		computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
 		size_t triangles = countTriangles(vertex_ids, indices, index_count);
 		size_t triangles = countTriangles(vertex_ids, indices, index_count);
 
 
 #if TRACE
 #if TRACE
 		printf("pass %d (%s): grid size %d, triangles %d, %s\n",
 		printf("pass %d (%s): grid size %d, triangles %d, %s\n",
-		    pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses) ? "lerp" : "binary",
+		    pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses ? "lerp" : "binary"),
 		    grid_size, int(triangles),
 		    grid_size, int(triangles),
 		    (triangles <= target_index_count / 3) ? "under" : "over");
 		    (triangles <= target_index_count / 3) ? "under" : "over");
 #endif
 #endif
@@ -1829,14 +1920,14 @@ size_t meshopt_simplifyPoints(unsigned int* destination, const float* vertex_pos
 
 
 		// we clamp the prediction of the grid size to make sure that the search converges
 		// we clamp the prediction of the grid size to make sure that the search converges
 		int grid_size = next_grid_size;
 		int grid_size = next_grid_size;
-		grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid) ? max_grid - 1 : grid_size;
+		grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid ? max_grid - 1 : grid_size);
 
 
 		computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
 		computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
 		size_t vertices = countVertexCells(table, table_size, vertex_ids, vertex_count);
 		size_t vertices = countVertexCells(table, table_size, vertex_ids, vertex_count);
 
 
 #if TRACE
 #if TRACE
 		printf("pass %d (%s): grid size %d, vertices %d, %s\n",
 		printf("pass %d (%s): grid size %d, vertices %d, %s\n",
-		    pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses) ? "lerp" : "binary",
+		    pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses ? "lerp" : "binary"),
 		    grid_size, int(vertices),
 		    grid_size, int(vertices),
 		    (vertices <= target_vertex_count) ? "under" : "over");
 		    (vertices <= target_vertex_count) ? "under" : "over");
 #endif
 #endif

+ 1 - 1
3rdparty/meshoptimizer/src/stripifier.cpp

@@ -18,7 +18,7 @@ static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int
 	for (size_t i = 0; i < buffer_size; ++i)
 	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 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;
+		unsigned int v = (va < vb && va < vc) ? va : (vb < vc ? vb : vc);
 
 
 		if (v < iv)
 		if (v < iv)
 		{
 		{

+ 1 - 1
3rdparty/meshoptimizer/src/vertexcodec.cpp

@@ -245,7 +245,7 @@ static unsigned char* encodeBytes(unsigned char* data, unsigned char* data_end,
 			}
 			}
 		}
 		}
 
 
-		int bitslog2 = (best_bits == 1) ? 0 : (best_bits == 2) ? 1 : (best_bits == 4) ? 2 : 3;
+		int bitslog2 = (best_bits == 1) ? 0 : (best_bits == 2 ? 1 : (best_bits == 4 ? 2 : 3));
 		assert((1 << bitslog2) == best_bits);
 		assert((1 << bitslog2) == best_bits);
 
 
 		size_t header_offset = i / kByteGroupSize;
 		size_t header_offset = i / kByteGroupSize;