Răsfoiți Sursa

Updated meshoptimizer.

Бранимир Караџић 5 ani în urmă
părinte
comite
4879111fe9

+ 138 - 14
3rdparty/meshoptimizer/src/indexcodec.cpp

@@ -19,6 +19,7 @@ namespace meshopt
 {
 
 const unsigned char kIndexHeader = 0xe0;
+const unsigned char kSequenceHeader = 0xd0;
 
 static int gEncodeIndexVersion = 0;
 
@@ -125,20 +126,16 @@ static unsigned int decodeVByte(const unsigned char*& data)
 	return result;
 }
 
-static void encodeIndex(unsigned char*& data, unsigned int index, unsigned int next, unsigned int last)
+static void encodeIndex(unsigned char*& data, unsigned int index, unsigned int last)
 {
-	(void)next;
-
 	unsigned int d = index - last;
 	unsigned int v = (d << 1) ^ (int(d) >> 31);
 
 	encodeVByte(data, v);
 }
 
-static unsigned int decodeIndex(const unsigned char*& data, unsigned int next, unsigned int last)
+static unsigned int decodeIndex(const unsigned char*& data, unsigned int last)
 {
-	(void)next;
-
 	unsigned int v = decodeVByte(data);
 	unsigned int d = (v >> 1) ^ -int(v & 1);
 
@@ -284,7 +281,7 @@ size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, cons
 
 			// note that we need to update the last index since free indices are delta-encoded
 			if (fec == 15)
-				encodeIndex(data, c, next, last), last = c;
+				encodeIndex(data, c, last), last = c;
 
 			// we only need to push third vertex since first two are likely already in the vertex fifo
 			if (fec == 0 || fec >= fecmax)
@@ -344,13 +341,13 @@ size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, cons
 
 			// note that we need to update the last index since free indices are delta-encoded
 			if (fea == 15)
-				encodeIndex(data, a, next, last), last = a;
+				encodeIndex(data, a, last), last = a;
 
 			if (feb == 15)
-				encodeIndex(data, b, next, last), last = b;
+				encodeIndex(data, b, last), last = b;
 
 			if (fec == 15)
-				encodeIndex(data, c, next, last), last = c;
+				encodeIndex(data, c, last), last = c;
 
 			// only push vertices that weren't already in fifo
 			if (fea == 0 || fea == 15)
@@ -525,7 +522,7 @@ int meshopt_decodeIndexBuffer(void* destination, size_t index_count, size_t inde
 
 				// fec - (fec ^ 3) decodes 13, 14 into -1, 1
 				// note that we need to update the last index since free indices are delta-encoded
-				last = c = (fec != 15) ? last + (fec - (fec ^ 3)) : decodeIndex(data, next, last);
+				last = c = (fec != 15) ? last + (fec - (fec ^ 3)) : decodeIndex(data, last);
 
 				// output triangle
 				writeTriangle(destination, i, index_size, a, b, c);
@@ -597,13 +594,13 @@ int meshopt_decodeIndexBuffer(void* destination, size_t index_count, size_t inde
 
 				// note that we need to update the last index since free indices are delta-encoded
 				if (fea == 15)
-					last = a = decodeIndex(data, next, last);
+					last = a = decodeIndex(data, last);
 
 				if (feb == 15)
-					last = b = decodeIndex(data, next, last);
+					last = b = decodeIndex(data, last);
 
 				if (fec == 15)
-					last = c = decodeIndex(data, next, last);
+					last = c = decodeIndex(data, last);
 
 				// output triangle
 				writeTriangle(destination, i, index_size, a, b, c);
@@ -626,3 +623,130 @@ int meshopt_decodeIndexBuffer(void* destination, size_t index_count, size_t inde
 
 	return 0;
 }
+
+size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const unsigned int* indices, size_t index_count)
+{
+	using namespace meshopt;
+
+	// the minimum valid encoding is header, 1 byte per index and a 4-byte tail
+	if (buffer_size < 1 + index_count + 4)
+		return 0;
+
+	int version = gEncodeIndexVersion;
+
+	buffer[0] = (unsigned char)(kSequenceHeader | version);
+
+	unsigned int last[2] = {};
+	unsigned int current = 0;
+
+	unsigned char* data = buffer + 1;
+	unsigned char* data_safe_end = buffer + buffer_size - 4;
+
+	for (size_t i = 0; i < index_count; ++i)
+	{
+		// make sure we have enough data to write
+		// each index writes at most 5 bytes of data; there's a 4 byte tail after data_safe_end
+		// after this we can be sure we can write without extra bounds checks
+		if (data >= data_safe_end)
+			return 0;
+
+		unsigned int index = indices[i];
+
+		// this is a heuristic that switches between baselines when the delta grows too large
+		// we want the encoded delta to fit into one byte (7 bits), but 2 bits are used for sign and baseline index
+		// for now we immediately switch the baseline when delta grows too large - this can be adjusted arbitrarily
+		int cd = int(index - last[current]);
+		current ^= ((cd < 0 ? -cd : cd) >= 30);
+
+		// encode delta from the last index
+		unsigned int d = index - last[current];
+		unsigned int v = (d << 1) ^ (int(d) >> 31);
+
+		// note: low bit encodes the index of the last baseline which will be used for reconstruction
+		encodeVByte(data, (v << 1) | current);
+
+		// update last for the next iteration that uses it
+		last[current] = index;
+	}
+
+	// make sure we have enough space to write tail
+	if (data > data_safe_end)
+		return 0;
+
+	for (int k = 0; k < 4; ++k)
+		*data++ = 0;
+
+	return data - buffer;
+}
+
+size_t meshopt_encodeIndexSequenceBound(size_t index_count, size_t vertex_count)
+{
+	// compute number of bits required for each index
+	unsigned int vertex_bits = 1;
+
+	while (vertex_bits < 32 && vertex_count > size_t(1) << vertex_bits)
+		vertex_bits++;
+
+	// worst-case encoding is 1 varint-7 encoded index delta for a K bit value and an extra bit
+	unsigned int vertex_groups = (vertex_bits + 1 + 1 + 6) / 7;
+
+	return 1 + index_count * vertex_groups + 4;
+}
+
+int meshopt_decodeIndexSequence(void* destination, size_t index_count, size_t index_size, const unsigned char* buffer, size_t buffer_size)
+{
+	using namespace meshopt;
+
+	// the minimum valid encoding is header, 1 byte per index and a 4-byte tail
+	if (buffer_size < 1 + index_count + 4)
+		return -2;
+
+	if ((buffer[0] & 0xf0) != kSequenceHeader)
+		return -1;
+
+	int version = buffer[0] & 0x0f;
+	if (version > 1)
+		return -1;
+
+	const unsigned char* data = buffer + 1;
+	const unsigned char* data_safe_end = buffer + buffer_size - 4;
+
+	unsigned int last[2] = {};
+
+	for (size_t i = 0; i < index_count; ++i)
+	{
+		// make sure we have enough data to read
+		// each index reads at most 5 bytes of data; there's a 4 byte tail after data_safe_end
+		// after this we can be sure we can read without extra bounds checks
+		if (data >= data_safe_end)
+			return -2;
+
+		unsigned int v = decodeVByte(data);
+
+		// decode the index of the last baseline
+		unsigned int current = v & 1;
+		v >>= 1;
+
+		// reconstruct index as a delta
+		unsigned int d = (v >> 1) ^ -int(v & 1);
+		unsigned int index = last[current] + d;
+
+		// update last for the next iteration that uses it
+		last[current] = index;
+
+		if (index_size == 2)
+		{
+			static_cast<unsigned short*>(destination)[i] = (unsigned short)(index);
+		}
+		else
+		{
+			static_cast<unsigned int*>(destination)[i] = index;
+		}
+	}
+
+	// we should've read all data bytes and stopped at the boundary between data and tail
+	if (data != data_safe_end)
+		return -3;
+
+	return 0;
+}

+ 45 - 2
3rdparty/meshoptimizer/src/meshoptimizer.h

@@ -158,6 +158,7 @@ MESHOPTIMIZER_API size_t meshopt_optimizeVertexFetchRemap(unsigned int* destinat
 /**
  * Index buffer encoder
  * Encodes index data into an array of bytes that is generally much smaller (<1.5 bytes/triangle) and compresses better (<1 bytes/triangle) compared to original.
+ * Input index buffer must represent a triangle list.
  * Returns encoded data size on success, 0 on error; the only error condition is if buffer doesn't have enough space
  * For maximum efficiency the index buffer being encoded has to be optimized for vertex cache and vertex fetch first.
  *
@@ -167,7 +168,7 @@ MESHOPTIMIZER_API size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t
 MESHOPTIMIZER_API size_t meshopt_encodeIndexBufferBound(size_t index_count, size_t vertex_count);
 
 /**
- * Experimental: Set index buffer encoder format version
+ * Experimental: Set index encoder format version
  * version must specify the data format version to encode; valid values are 0 (decodable by all library versions) and 1 (decodable by 0.14+)
  */
 MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeIndexVersion(int version);
@@ -182,6 +183,27 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeIndexVersion(int version);
  */
 MESHOPTIMIZER_API int meshopt_decodeIndexBuffer(void* destination, size_t index_count, size_t index_size, const unsigned char* buffer, size_t buffer_size);
 
+/**
+ * Experimental: Index sequence encoder
+ * Encodes index sequence into an array of bytes that is generally smaller and compresses better compared to original.
+ * Input index sequence can represent arbitrary topology; for triangle lists meshopt_encodeIndexBuffer is likely to be better.
+ * Returns encoded data size on success, 0 on error; the only error condition is if buffer doesn't have enough space
+ *
+ * buffer must contain enough space for the encoded index sequence (use meshopt_encodeIndexSequenceBound to compute worst case size)
+ */
+MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const unsigned int* indices, size_t index_count);
+MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_encodeIndexSequenceBound(size_t index_count, size_t vertex_count);
+
+/**
+ * Index sequence decoder
+ * Decodes index data from an array of bytes generated by meshopt_encodeIndexSequence
+ * Returns 0 if decoding was successful, and an error code otherwise
+ * The decoder is safe to use for untrusted input, but it may produce garbage data (e.g. out of range indices).
+ *
+ * destination must contain enough space for the resulting index sequence (index_count elements)
+ */
+MESHOPTIMIZER_EXPERIMENTAL int meshopt_decodeIndexSequence(void* destination, size_t index_count, size_t index_size, const unsigned char* buffer, size_t buffer_size);
+
 /**
  * Vertex buffer encoder
  * Encodes vertex data into an array of bytes that is generally smaller and compresses better compared to original.
@@ -194,7 +216,7 @@ MESHOPTIMIZER_API size_t meshopt_encodeVertexBuffer(unsigned char* buffer, size_
 MESHOPTIMIZER_API size_t meshopt_encodeVertexBufferBound(size_t vertex_count, size_t vertex_size);
 
 /**
- * Experimental: Set vertex buffer encoder format version
+ * Experimental: Set vertex encoder format version
  * version must specify the data format version to encode; valid values are 0 (decodable by all library versions)
  */
 MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeVertexVersion(int version);
@@ -491,6 +513,10 @@ inline size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_siz
 template <typename T>
 inline int meshopt_decodeIndexBuffer(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size);
 template <typename T>
+inline size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const T* indices, size_t index_count);
+template <typename T>
+inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size);
+template <typename T>
 inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error);
 template <typename T>
 inline size_t meshopt_simplifySloppy(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count);
@@ -788,6 +814,23 @@ inline int meshopt_decodeIndexBuffer(T* destination, size_t index_count, const u
 	return meshopt_decodeIndexBuffer(destination, index_count, sizeof(T), buffer, buffer_size);
 }
 
+template <typename T>
+inline size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const T* indices, size_t index_count)
+{
+	meshopt_IndexAdapter<T> in(0, indices, index_count);
+
+	return meshopt_encodeIndexSequence(buffer, buffer_size, in.data, index_count);
+}
+
+template <typename T>
+inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size)
+{
+	char index_size_valid[sizeof(T) == 2 || sizeof(T) == 4 ? 1 : -1];
+	(void)index_size_valid;
+
+	return meshopt_decodeIndexSequence(destination, index_count, sizeof(T), buffer, buffer_size);
+}
+
 template <typename T>
 inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error)
 {

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

@@ -85,7 +85,7 @@
 #if defined(SIMD_WASM)
 // v128_t wasm_v8x16_swizzle(v128_t a, v128_t b)
 SIMD_TARGET
-static __inline__ v128_t __DEFAULT_FN_ATTRS wasm_v8x16_swizzle(v128_t a, v128_t b)
+static __inline__ v128_t wasm_v8x16_swizzle(v128_t a, v128_t b)
 {
 	return (v128_t)__builtin_wasm_swizzle_v8x16((__i8x16)a, (__i8x16)b);
 }

+ 3 - 3
3rdparty/meshoptimizer/src/vertexfilter.cpp

@@ -145,7 +145,7 @@ static void decodeFilterOctSimd(signed char* data, size_t count)
 
 		// fixup octahedral coordinates for z<0
 		// note: i32x4_min_s with 0 is equvalent to f32x4_min
-		v128_t t = wasm_i32x4_min_s(z, wasm_i32x4_splat(0));
+		v128_t t = wasm_i32x4_min(z, wasm_i32x4_splat(0));
 
 		x = wasm_f32x4_add(x, wasm_v128_xor(t, wasm_v128_and(x, sign)));
 		y = wasm_f32x4_add(y, wasm_v128_xor(t, wasm_v128_and(y, sign)));
@@ -201,7 +201,7 @@ static void decodeFilterOctSimd(short* data, size_t count)
 
 		// fixup octahedral coordinates for z<0
 		// note: i32x4_min_s with 0 is equvalent to f32x4_min
-		v128_t t = wasm_i32x4_min_s(z, wasm_i32x4_splat(0));
+		v128_t t = wasm_i32x4_min(z, wasm_i32x4_splat(0));
 
 		x = wasm_f32x4_add(x, wasm_v128_xor(t, wasm_v128_and(x, sign)));
 		y = wasm_f32x4_add(y, wasm_v128_xor(t, wasm_v128_and(y, sign)));
@@ -267,7 +267,7 @@ static void decodeFilterQuatSimd(short* data, size_t count)
 		// reconstruct w as a square root; we clamp to 0.f to avoid NaN due to precision errors
 		// note: i32x4_max_s with 0 is equivalent to f32x4_max
 		v128_t ww = wasm_f32x4_sub(wasm_f32x4_splat(1.f), wasm_f32x4_add(wasm_f32x4_mul(x, x), wasm_f32x4_add(wasm_f32x4_mul(y, y), wasm_f32x4_mul(z, z))));
-		v128_t w = wasm_f32x4_sqrt(wasm_i32x4_max_s(ww, wasm_i32x4_splat(0)));
+		v128_t w = wasm_f32x4_sqrt(wasm_i32x4_max(ww, wasm_i32x4_splat(0)));
 
 		v128_t s = wasm_f32x4_splat(32767.f);