diff --git a/3rdparty/meshoptimizer/src/indexcodec.cpp b/3rdparty/meshoptimizer/src/indexcodec.cpp index 409e5d43e..eeb541e5b 100644 --- a/3rdparty/meshoptimizer/src/indexcodec.cpp +++ b/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(destination)[i] = (unsigned short)(index); + } + else + { + static_cast(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; +} diff --git a/3rdparty/meshoptimizer/src/meshoptimizer.h b/3rdparty/meshoptimizer/src/meshoptimizer.h index d75fa1417..5b2a851fe 100644 --- a/3rdparty/meshoptimizer/src/meshoptimizer.h +++ b/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 inline int meshopt_decodeIndexBuffer(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size); template +inline size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const T* indices, size_t index_count); +template +inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size); +template 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 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 +inline size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const T* indices, size_t index_count) +{ + meshopt_IndexAdapter in(0, indices, index_count); + + return meshopt_encodeIndexSequence(buffer, buffer_size, in.data, index_count); +} + +template +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 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) { diff --git a/3rdparty/meshoptimizer/src/vertexcodec.cpp b/3rdparty/meshoptimizer/src/vertexcodec.cpp index 0c5f79c55..eefcaf7bb 100644 --- a/3rdparty/meshoptimizer/src/vertexcodec.cpp +++ b/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); } diff --git a/3rdparty/meshoptimizer/src/vertexfilter.cpp b/3rdparty/meshoptimizer/src/vertexfilter.cpp index cedc270b8..f5364326f 100644 --- a/3rdparty/meshoptimizer/src/vertexfilter.cpp +++ b/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);