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;
}
}
}