using System; using System.Runtime.CompilerServices; using Unity.Collections; using Unity.Jobs; using UnityEngine; using UnityEngine.Assertions; [assembly: InternalsVisibleTo("Unity.Meshopt.Decompress.Tests")] namespace Meshoptimizer { /// /// Vertex attribute filter to be applied /// public enum Filter { /// /// Don't use this value as parameter directly! /// It's for deserialization purpose only. /// Undefined, /// /// No filter should be applied /// None, /// /// Apply octahedral filter, usually for normals /// Octahedral, /// /// Apply quaternion filter, usually for rotations /// Quaternion, /// /// Apply exponential filter, usually for positional data /// Exponential } /// /// Mode defines the type of buffer to decode /// public enum Mode { /// /// Don't use this value as parameter directly! /// It's for deserialization purpose only. /// Undefined, /// /// Vertex attributes /// Attributes, /// /// Triangle indices buffer /// Triangles, /// /// Index sequence /// Indices, } /// /// The Decode class provides static methods for decoding/decompressing meshoptimizer compressed /// vertex and index buffers. /// public static class Decode { #region Constants internal const byte indexHeader = 0xe0; internal const byte sequenceHeader = 0xd0; internal const uint kVertexBlockSizeBytes = 8192; internal const uint kVertexBlockMaxSize = 256; internal const uint kByteGroupSize = 16; internal const uint kByteGroupDecodeLimit = 24; #endregion Constants /// /// Creates a C# job that decompresses the provided source buffer into destination /// /// An array with a length of one. The job's return code will end up at index 0 /// Destination buffer where the source will be decompressed into /// Number of elements (vertices/indices) to decode /// Size of elements (vertex/index) in bytes /// Source buffer /// Compression mode /// In case of mode, filter to be applied /// JobHandle for the created C# job /// Thrown upon invalid mode/filter public static JobHandle DecodeGltfBuffer( NativeSlice returnCode, NativeArray destination, int count, int size, NativeSlice source, Mode mode, Filter filter = Filter.None ) { Assert.AreEqual(1, returnCode.Length); returnCode[0] = int.MinValue; switch (mode) { case Mode.Attributes: { var job = new DecodeVertexJob { destination = destination, vertexCount = (uint)count, vertexSize = (uint)size, source = source, filter = filter, returnCode = returnCode }; return job.Schedule(); } case Mode.Triangles: { var job = new DecodeIndexTrianglesJob { destination = destination, indexCount = count, indexSize = size, source = source, returnCode = returnCode, triangleWriter = DecodeIndexTrianglesJob.GetTriangleWriter(size) }; return job.Schedule(); } case Mode.Indices: { var job = new DecodeIndexSequenceJob { destination = destination, indexCount = count, indexSize = size, source = source, returnCode = returnCode }; return job.Schedule(); } default: throw new ArgumentOutOfRangeException(nameof(mode), mode, null); } } /// /// Synchronous variant of (decodes on the current thread) /// /// Destination buffer where the source will be decompressed into /// Number of elements (vertices/indices) to decode /// Size of elements (vertex/index) in bytes /// Source buffer /// Compression mode /// In case of mode, filter to be applied /// Return code that is 0 in case of success public static int DecodeGltfBufferSync( NativeArray destination, int count, int size, NativeSlice source, Mode mode, Filter filter = Filter.None ) { using (var returnCode = new NativeArray(1, Allocator.TempJob)) { var jobHandle = DecodeGltfBuffer( returnCode, destination, count, size, source, mode, filter ); jobHandle.Complete(); return returnCode[0]; } } internal static sbyte UnZigZag8(byte v) { return (sbyte)(-(v & 1) ^ (v >> 1)); } internal static unsafe uint DecodeVByte(ref byte* data) { var lead = *data++; // fast path: single byte if (lead < 128) return lead; // slow path: up to 4 extra bytes // note that this loop always terminates, which is important for malformed data var result = (uint)lead & 127; var shift = 7; for (var i = 0; i < 4; ++i) { var group = *data++; result |= (uint)((group & 127) << shift); shift += 7; if (group < 128) break; } return result; } } }