diff --git a/ChangeLog.md b/ChangeLog.md
index 5ac1d657..dea0d12a 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unrelease] -
### Added
- Experimental glTF Editor Export (under main menu `File > Export` and via API `GLTFast.Export.GameObjectExport`; #249)
+- Support for meshopt compressed glTFs (EXT_meshopt_compression; #106)
- *Generate Lightmap UVs* option in the glTF import inspector lets you create a secondary texture coordinate set (similar to the Model Import Settings from other formats; #238)
- Generic `ICodeLogger` methods that don't require a `LogCode`
### Changed
diff --git a/Documentation~/features.md b/Documentation~/features.md
index 6dbb1372..fb500646 100644
--- a/Documentation~/features.md
+++ b/Documentation~/features.md
@@ -49,10 +49,11 @@ The glTF 2.0 specification is fully supported, with only a few minor remarks.
|glTF (.gltf) | ✅ | ✅
|glTF-Binary (.glb) | ✅ | ✅
| | |
-| **Buffer type**
+| **Buffer**
| External URIs | ✅ | ✅
| GLB main buffer | ✅ | ✅
-| Embed buffers or textures (base-64 encoded within JSON) | ✅ |
+| Embed buffers or textures (base-64 encoded within JSON) | ✅ |
+| [meshoptimizer compression][MeshOpt] (via [package][MeshOptPkg])| ✅ |
| | |
| **Basics**
| Scenes | ✅ | ✅
@@ -136,7 +137,7 @@ The glTF 2.0 specification is fully supported, with only a few minor remarks.
| | |
| **Vendor**
| 1EXT_mesh_gpu_instancing | ✅ |
-| EXT_meshopt_compression | [ℹ️][MeshOpt] |
+| EXT_meshopt_compression | ✅ |
| EXT_lights_image_based | [ℹ️][IBL] |
1: Without support for custom vertex attributes (e.g. `_ID`)
@@ -262,7 +263,8 @@ Possibly incomplete list of things that are known to not work with Entities yet:
[HDRP]: https://unity.com/srp/High-Definition-Render-Pipeline
[IBL]: https://github.com/atteneder/glTFast/issues/108
[IOR]: https://github.com/atteneder/glTFast/issues/207
-[MeshOpt]: https://github.com/atteneder/glTFast/issues/106
+[MeshOpt]: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_meshopt_compression
+[MeshOptPkg]: https://docs.unity3d.com/Packages/com.unity.meshopt.decompress@0.1/manual/index.html
[newIssue]: https://github.com/atteneder/glTFast/issues/new
[PointLights]: https://github.com/atteneder/glTFast/issues/17
[RuntimeExport]: https://github.com/atteneder/glTFast/issues/259
diff --git a/README.md b/README.md
index ea1355ae..bf0d8f00 100644
--- a/README.md
+++ b/README.md
@@ -48,10 +48,13 @@ It runs a script that installs *glTFast* via a [scoped registry](https://docs.un
Afterwards *glTFast* and further, optional packages are listed in the *Package Manager* (under *My Registries*) and can be installed and updated from there.
-### Optional dependencies
+### Optional Packages
-- [Draco 3D Data Compression Unity Package](https://github.com/atteneder/DracoUnity) (provides support for [KHR_draco_mesh_compression](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression))
-- [KTX/Basis Texture Unity Package](https://github.com/atteneder/KtxUnity) (in Beta; provides support for [KHR_texture_basisu](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu))
+There are some related package that improve *glTFast* by extending its feature set.
+
+- [Draco 3D Data Compression Unity Package][DracoUnity] (provides support for [KHR_draco_mesh_compression][ExtDraco])
+- [KTX/Basis Texture Unity Package][KtxUnity] (in Beta; provides support for [KHR_texture_basisu][ExtBasisU])
+- [*meshoptimizer decompression for Unity*][Meshopt] (provides support for [EXT_meshopt_compression][ExtMeshopt])
Alternative: Install via GIT URL
@@ -63,9 +66,10 @@ Enter the following URL:
`https://github.com/atteneder/glTFast.git`
-To add support for Draco mesh compression, repeat the last step and also add the DracoUnity packages using this URL:
+To add more functionality, repeat the last step and also add related packages using these URLs:
-`https://gitlab.com/atteneder/DracoUnity.git`
+- `https://gitlab.com/atteneder/DracoUnity.git` for Draco mesh compression
+- `https://github.com/atteneder/KtxUnity.git` for KTX texture compression
> Note: You have to have a GIT LFS client (large file support) installed on your system. Otherwise you will get an error that the native library file (dll on Windows) is corrupt!
@@ -149,13 +153,19 @@ limitations under the License.
*Khronos®* is a registered trademark and *glTF™* is a trademark of [The Khronos Group Inc][khronos].
-[unity]: https://unity.com
-[gltf]: https://www.khronos.org/gltf
-[gltf-spec]: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html
-[gltfast-web-demo]: https://gltf.pixel.engineer
-[khronos]: https://www.khronos.org
[embibe]: https://www.embibe.com
+[DracoUnity]: https://github.com/atteneder/DracoUnity
+[ExtBasisU]: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu
+[ExtDraco]: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
+[ExtMeshopt]: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_meshopt_compression
+[gltf-spec]: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html
+[gltf]: https://www.khronos.org/gltf
[gltfasset_component]: ./Documentation~/img/gltfasset_component.png "Inspector showing a GltfAsset component added to a GameObject"
+[gltfast-web-demo]: https://gltf.pixel.engineer
[import-gif]: ./Documentation~/img/import.gif "Video showing glTF files being copied into the Assets folder and imported"
+[khronos]: https://www.khronos.org
+[KtxUnity]: https://github.com/atteneder/KtxUnity
+[Meshopt]: https://docs.unity3d.com/Packages/com.unity.meshopt.decompress@0.1/manual/index.html
+[unity]: https://unity.com
[upm_install]: ./Documentation~/img/upm_install.png "Unity Package Manager add menu"
[workflows]: ./Documentation~/glTFast.md#workflows
\ No newline at end of file
diff --git a/Runtime/Scripts/Extensions.cs b/Runtime/Scripts/Extensions.cs
index 07849384..16cb1cbe 100644
--- a/Runtime/Scripts/Extensions.cs
+++ b/Runtime/Scripts/Extensions.cs
@@ -35,6 +35,7 @@ public static class Extensions {
public const string MaterialsTransmission = "KHR_materials_transmission";
public const string MaterialsUnlit = "KHR_materials_unlit";
public const string MeshGPUInstancing = "EXT_mesh_gpu_instancing";
+ public const string MeshoptCompression = "EXT_meshopt_compression";
public const string MeshQuantization = "KHR_mesh_quantization";
public const string TextureBasisUniversal = "KHR_texture_basisu";
public const string TextureTransform = "KHR_texture_transform";
diff --git a/Runtime/Scripts/GltfImport.cs b/Runtime/Scripts/GltfImport.cs
index bfff49ba..6c934ea8 100644
--- a/Runtime/Scripts/GltfImport.cs
+++ b/Runtime/Scripts/GltfImport.cs
@@ -35,6 +35,9 @@
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using Debug = UnityEngine.Debug;
+#if MESHOPT
+using Meshoptimizer;
+#endif
#if MEASURE_TIMINGS
using GLTFast.Tests;
#endif
@@ -83,6 +86,9 @@ public class GltfImport : IGltfReadable, IGltfBuffers {
#if KTX_UNITY
Extensions.TextureBasisUniversal,
#endif // KTX_UNITY
+#if MESHOPT
+ Extensions.MeshoptCompression,
+#endif
Extensions.MaterialsPbrSpecularGlossiness,
Extensions.MaterialsUnlit,
Extensions.TextureTransform,
@@ -164,6 +170,12 @@ public class GltfImport : IGltfReadable, IGltfBuffers {
/// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#binary-buffer
GlbBinChunk? glbBinChunk;
+#if MESHOPT
+ Dictionary> meshoptBufferViews;
+ NativeArray meshoptReturnValues;
+ JobHandle meshoptJobHandle;
+#endif
+
///
/// Unity's animation system addresses target GameObjects by hierarchical name.
/// To make sure names are consistent and have no conflicts they are precalculated
@@ -537,6 +549,12 @@ async Task LoadContent() {
var success = await WaitForBufferDownloads();
downloadTasks?.Clear();
+#if MESHOPT
+ if (success) {
+ MeshoptDecode();
+ }
+#endif
+
if (textureDownloadTasks != null) {
success = success && await WaitForTextureDownloads();
textureDownloadTasks.Clear();
@@ -825,7 +843,9 @@ async Task WaitForBufferDownloads() {
continue;
}
var b = buffers[i];
- binChunks[i] = new GlbBinChunk(0,(uint) b.Length);
+ if (b != null) {
+ binChunks[i] = new GlbBinChunk(0,(uint) b.Length);
+ }
}
Profiler.EndSample();
}
@@ -1027,8 +1047,38 @@ byte[] GetBuffer(int index) {
return buffers[index];
}
- unsafe NativeSlice GetBufferView(BufferView bufferView) {
- int bufferIndex = bufferView.buffer;
+ NativeSlice GetBufferView(int bufferViewIndex,int offset = 0, int length = 0) {
+ var bufferView = gltfRoot.bufferViews[bufferViewIndex];
+#if MESHOPT
+ if (bufferView.extensions?.EXT_meshopt_compression != null) {
+ var fullSlice = meshoptBufferViews[bufferViewIndex];
+ if (offset == 0 && length <= 0) {
+ return fullSlice;
+ }
+ Assert.IsTrue(offset >= 0);
+ if (length <= 0) {
+ length = fullSlice.Length - offset;
+ }
+ Assert.IsTrue(offset+length <= fullSlice.Length);
+ return new NativeSlice(fullSlice,offset,length);
+ }
+#endif
+ return GetBufferViewSlice(bufferView,offset,length);
+ }
+
+ unsafe NativeSlice GetBufferViewSlice(
+ BufferViewBase bufferView,
+ int offset = 0,
+ int length = 0
+ )
+ {
+ Assert.IsTrue(offset >= 0);
+ if (length <= 0) {
+ length = bufferView.byteLength - offset;
+ }
+ Assert.IsTrue( offset+length <= bufferView.byteLength);
+
+ var bufferIndex = bufferView.buffer;
if(!nativeBuffers[bufferIndex].IsCreated) {
Profiler.BeginSample("ConvertToNativeArray");
var buffer = GetBuffer(bufferIndex);
@@ -1043,9 +1093,71 @@ unsafe NativeSlice GetBufferView(BufferView bufferView) {
Profiler.EndSample();
}
var chunk = binChunks[bufferIndex];
- return new NativeSlice(nativeBuffers[bufferIndex],chunk.start+bufferView.byteOffset,bufferView.byteLength);
+ return new NativeSlice(
+ nativeBuffers[bufferIndex],
+ chunk.start + bufferView.byteOffset + offset,
+ length
+ );
}
+#if MESHOPT
+ void MeshoptDecode() {
+ if(gltfRoot.bufferViews!=null) {
+ List jobHandlesList = null;
+ for (var i = 0; i < gltfRoot.bufferViews.Length; i++) {
+ var bufferView = gltfRoot.bufferViews[i];
+ if (bufferView.extensions?.EXT_meshopt_compression != null) {
+ var meshopt = bufferView.extensions?.EXT_meshopt_compression;
+ if (jobHandlesList == null) {
+ meshoptBufferViews = new Dictionary>();
+ jobHandlesList = new List(gltfRoot.bufferViews.Length);
+ meshoptReturnValues = new NativeArray(gltfRoot.bufferViews.Length, Allocator.TempJob);
+ }
+
+ var arr = new NativeArray(meshopt.count * meshopt.byteStride, Allocator.Persistent);
+
+ var origBufferView = GetBufferViewSlice(meshopt);
+
+ var jobHandle = Decode.DecodeGltfBuffer(
+ new NativeSlice(meshoptReturnValues,i,1),
+ arr,
+ meshopt.count,
+ meshopt.byteStride,
+ origBufferView,
+ meshopt.modeEnum,
+ meshopt.filterEnum
+ );
+ jobHandlesList.Add(jobHandle);
+ meshoptBufferViews[i] = arr;
+ }
+ }
+
+ if (jobHandlesList != null) {
+ using (var jobHandles = new NativeArray(jobHandlesList.ToArray(), Allocator.Temp)) {
+ meshoptJobHandle = JobHandle.CombineDependencies(jobHandles);
+ }
+ }
+ }
+ }
+
+ async Task WaitForMeshoptDecode() {
+ var success = true;
+ if (meshoptBufferViews != null) {
+ while (!meshoptJobHandle.IsCompleted) {
+ await Task.Yield();
+ }
+ meshoptJobHandle.Complete();
+
+ foreach (var returnValue in meshoptReturnValues) {
+ success &= returnValue == 0;
+ }
+ meshoptReturnValues.Dispose();
+ }
+ return success;
+ }
+
+#endif // MESHOPT
+
async Task Prepare() {
if(gltfRoot.meshes!=null) {
meshPrimitiveIndex = new int[gltfRoot.meshes.Length+1];
@@ -1066,9 +1178,14 @@ async Task Prepare() {
CreateTexturesFromBuffers(gltfRoot.images,gltfRoot.bufferViews,imageCreateContexts);
}
await deferAgent.BreakPoint();
-
+
var success = true;
-
+
+#if MESHOPT
+ success = await WaitForMeshoptDecode();
+ if (!success) return false;
+#endif
+
if(gltfRoot.accessors!=null) {
success = await LoadAccessorData(gltfRoot);
await deferAgent.BreakPoint();
@@ -1446,6 +1563,18 @@ void DisposeVolatileData() {
imageReadable = null;
imageGamma = null;
glbBinChunk = null;
+
+#if MESHOPT
+ if(meshoptBufferViews!=null) {
+ foreach (var nativeBuffer in meshoptBufferViews.Values) {
+ nativeBuffer.Dispose();
+ }
+ meshoptBufferViews = null;
+ }
+ if (meshoptReturnValues.IsCreated) {
+ meshoptReturnValues.Dispose();
+ }
+#endif
}
void InstantiateSceneInternal( Root gltf, IInstantiator instantiator, int sceneId ) {
@@ -1654,7 +1783,6 @@ async Task
if (imgFormat!=ImageFormat.Unknown) {
if (img.bufferView >= 0) {
- var bufferView = bufferViews[img.bufferView];
if(imgFormat == ImageFormat.KTX) {
#if KTX_UNITY
@@ -1662,7 +1790,7 @@ async Task
if(ktxLoadContextsBuffer==null) {
ktxLoadContextsBuffer = new List();
}
- var ktxContext = new KtxLoadNativeContext(i,GetBufferView(bufferView));
+ var ktxContext = new KtxLoadNativeContext(i,GetBufferView(img.bufferView));
ktxLoadContextsBuffer.Add(ktxContext);
Profiler.EndSample();
await deferAgent.BreakPoint();
@@ -1671,6 +1799,7 @@ async Task
#endif // KTX_UNITY
} else {
Profiler.BeginSample("CreateTexturesFromBuffers.ExtractBuffer");
+ var bufferView = bufferViews[img.bufferView];
var buffer = GetBuffer(bufferView.buffer);
var chunk = binChunks[bufferView.buffer];
@@ -2253,7 +2382,7 @@ void PreparePrimitiveDraco( Root gltf, Mesh mesh, MeshPrimitive primitive, ref P
var draco_ext = primitive.extensions.KHR_draco_mesh_compression;
var bufferView = gltf.bufferViews[draco_ext.bufferView];
- var buffer = GetBufferView(bufferView);
+ var buffer = GetBufferViewSlice(bufferView);
c.StartDecode(buffer, draco_ext.attributes.WEIGHTS_0, draco_ext.attributes.JOINTS_0);
}
@@ -2290,9 +2419,7 @@ unsafe void GetIndicesJob(Root gltf, int accessorIndex, out int[] indices, out J
Profiler.BeginSample("PrepareGetIndicesJob");
// index
var accessor = gltf.accessors[accessorIndex];
- var bufferView = gltf.bufferViews[accessor.bufferView];
- int bufferIndex = bufferView.buffer;
- var buffer = GetBuffer(bufferIndex);
+ var bufferView = GetBufferView(accessor.bufferView,accessor.byteOffset);
Profiler.BeginSample("Alloc");
indices = new int[accessor.count];
@@ -2301,28 +2428,26 @@ unsafe void GetIndicesJob(Root gltf, int accessorIndex, out int[] indices, out J
resultHandle = GCHandle.Alloc(indices, GCHandleType.Pinned);
Profiler.EndSample();
- var chunk = binChunks[bufferIndex];
Assert.AreEqual(accessor.typeEnum, GLTFAccessorAttributeType.SCALAR);
//Assert.AreEqual(accessor.count * GetLength(accessor.typeEnum) * 4 , (int) chunk.length);
if (accessor.isSparse) {
logger.Error(LogCode.SparseAccessor,"indices");
}
- var start = accessor.byteOffset + bufferView.byteOffset + chunk.start;
Profiler.BeginSample("CreateJob");
switch( accessor.componentType ) {
case GLTFComponentType.UnsignedByte:
if(flip) {
var job8 = new Jobs.ConvertIndicesUInt8ToInt32FlippedJob();
- fixed( void* src = &(buffer[start]), dst = &(indices[0]) ) {
- job8.input = (byte*)src;
+ fixed( void* dst = &(indices[0]) ) {
+ job8.input = (byte*)bufferView.GetUnsafeReadOnlyPtr();
job8.result = (int3*)dst;
}
jobHandle = job8.Schedule(accessor.count/3,DefaultBatchCount);
} else {
var job8 = new Jobs.ConvertIndicesUInt8ToInt32Job();
- fixed( void* src = &(buffer[start]), dst = &(indices[0]) ) {
- job8.input = (byte*)src;
+ fixed( void* dst = &(indices[0]) ) {
+ job8.input = (byte*)bufferView.GetUnsafeReadOnlyPtr();
job8.result = (int*)dst;
}
jobHandle = job8.Schedule(accessor.count,DefaultBatchCount);
@@ -2331,15 +2456,15 @@ unsafe void GetIndicesJob(Root gltf, int accessorIndex, out int[] indices, out J
case GLTFComponentType.UnsignedShort:
if(flip) {
var job16 = new Jobs.ConvertIndicesUInt16ToInt32FlippedJob();
- fixed( void* src = &(buffer[start]), dst = &(indices[0]) ) {
- job16.input = (ushort*) src;
+ fixed( void* dst = &(indices[0]) ) {
+ job16.input = (ushort*) bufferView.GetUnsafeReadOnlyPtr();
job16.result = (int3*) dst;
}
jobHandle = job16.Schedule(accessor.count/3,DefaultBatchCount);
} else {
var job16 = new Jobs.ConvertIndicesUInt16ToInt32Job();
- fixed( void* src = &(buffer[start]), dst = &(indices[0]) ) {
- job16.input = (ushort*) src;
+ fixed( void* dst = &(indices[0]) ) {
+ job16.input = (ushort*) bufferView.GetUnsafeReadOnlyPtr();
job16.result = (int*) dst;
}
jobHandle = job16.Schedule(accessor.count,DefaultBatchCount);
@@ -2348,15 +2473,15 @@ unsafe void GetIndicesJob(Root gltf, int accessorIndex, out int[] indices, out J
case GLTFComponentType.UnsignedInt:
if(flip) {
var job32 = new Jobs.ConvertIndicesUInt32ToInt32FlippedJob();
- fixed( void* src = &(buffer[start]), dst = &(indices[0]) ) {
- job32.input = (uint*) src;
+ fixed( void* dst = &(indices[0]) ) {
+ job32.input = (uint*) bufferView.GetUnsafeReadOnlyPtr();
job32.result = (int3*) dst;
}
jobHandle = job32.Schedule(accessor.count/3,DefaultBatchCount);
} else {
var job32 = new Jobs.ConvertIndicesUInt32ToInt32Job();
- fixed( void* src = &(buffer[start]), dst = &(indices[0]) ) {
- job32.input = (uint*) src;
+ fixed( void* dst = &(indices[0]) ) {
+ job32.input = (uint*) bufferView.GetUnsafeReadOnlyPtr();
job32.result = (int*) dst;
}
jobHandle = job32.Schedule(accessor.count,DefaultBatchCount);
@@ -2375,30 +2500,25 @@ unsafe void GetMatricesJob(Root gltf, int accessorIndex, out NativeArray(accessor.count,Allocator.Persistent);
Profiler.EndSample();
- var chunk = binChunks[bufferIndex];
Assert.AreEqual(accessor.typeEnum, GLTFAccessorAttributeType.MAT4);
//Assert.AreEqual(accessor.count * GetLength(accessor.typeEnum) * 4 , (int) chunk.length);
if (accessor.isSparse) {
logger.Error(LogCode.SparseAccessor,"Matrix");
}
- var start = accessor.byteOffset + bufferView.byteOffset + chunk.start;
Profiler.BeginSample("CreateJob");
switch( accessor.componentType ) {
case GLTFComponentType.Float:
- var job32 = new Jobs.ConvertMatricesJob();
- job32.result = (float4x4*)matrices.GetUnsafePtr();
- fixed( void* src = &(buffer[start]) ) {
- job32.input = (float4x4*) src;
- }
+ var job32 = new Jobs.ConvertMatricesJob {
+ input = (float4x4*)bufferView.GetUnsafeReadOnlyPtr(),
+ result = (float4x4*)matrices.GetUnsafePtr()
+ };
jobHandle = job32.Schedule(accessor.count,DefaultBatchCount);
break;
default:
@@ -2413,39 +2533,33 @@ unsafe void GetMatricesJob(Root gltf, int accessorIndex, out NativeArray vectors, out JobHandle? jobHandle, bool flip) {
Profiler.BeginSample("GetVector3Job");
var accessor = gltf.accessors[accessorIndex];
- var bufferView = gltf.bufferViews[accessor.bufferView];
- var bufferIndex = bufferView.buffer;
- var buffer = GetBuffer(bufferIndex);
+ var bufferView = GetBufferView(accessor.bufferView,accessor.byteOffset);
Profiler.BeginSample("Alloc");
vectors = new NativeArray(accessor.count,Allocator.Persistent);
Profiler.EndSample();
- var chunk = binChunks[bufferIndex];
Assert.AreEqual(accessor.typeEnum, GLTFAccessorAttributeType.VEC3);
if (accessor.isSparse) {
logger.Error(LogCode.SparseAccessor,"Vector3");
}
- var start = accessor.byteOffset + bufferView.byteOffset + chunk.start;
Profiler.BeginSample("CreateJob");
switch( accessor.componentType ) {
case GLTFComponentType.Float when flip: {
- var job = new ConvertVector3FloatToFloatJob { result = (float3*)vectors.GetUnsafePtr() };
- fixed( void* src = &(buffer[start]) ) {
- job.input = (float3*) src;
- }
+ var job = new ConvertVector3FloatToFloatJob {
+ input = (float3*)bufferView.GetUnsafeReadOnlyPtr(),
+ result = (float3*)vectors.GetUnsafePtr()
+ };
jobHandle = job.Schedule(accessor.count,DefaultBatchCount);
break;
}
case GLTFComponentType.Float when !flip: {
var job = new MemCopyJob {
+ input = (float*)bufferView.GetUnsafeReadOnlyPtr(),
bufferSize = accessor.count * 12,
result = (float*)vectors.GetUnsafePtr()
};
- fixed( void* src = &(buffer[start]) ) {
- job.input = (float*) src;
- }
jobHandle = job.Schedule();
break;
}
@@ -2462,50 +2576,40 @@ unsafe void GetVector4Job(Root gltf, int accessorIndex, out NativeArray(accessor.count,Allocator.Persistent);
Profiler.EndSample();
- var chunk = binChunks[bufferIndex];
Assert.AreEqual(accessor.typeEnum, GLTFAccessorAttributeType.VEC4);
if (accessor.isSparse) {
logger.Error(LogCode.SparseAccessor,"Vector4");
}
- var start = accessor.byteOffset + bufferView.byteOffset + chunk.start;
Profiler.BeginSample("CreateJob");
switch( accessor.componentType ) {
case GLTFComponentType.Float: {
var job = new ConvertRotationsFloatToFloatJob {
+ input = (float4*)bufferView.GetUnsafeReadOnlyPtr(),
result = (float4*)vectors.GetUnsafePtr()
};
- fixed( void* src = &(buffer[start]) ) {
- job.input = (float4*) src;
- }
jobHandle = job.Schedule(accessor.count,DefaultBatchCount);
break;
}
case GLTFComponentType.Short: {
var job = new ConvertRotationsInt16ToFloatJob {
+ input = (short*)bufferView.GetUnsafeReadOnlyPtr(),
result = (float*)vectors.GetUnsafePtr()
};
- fixed( void* src = &(buffer[start]) ) {
- job.input = (short*) src;
- }
jobHandle = job.Schedule(accessor.count,DefaultBatchCount);
break;
}
case GLTFComponentType.Byte: {
var job = new ConvertRotationsInt8ToFloatJob {
+ input = (sbyte*)bufferView.GetUnsafeReadOnlyPtr(),
result = (float*)vectors.GetUnsafePtr()
};
- fixed( void* src = &(buffer[start]) ) {
- job.input = (sbyte*) src;
- }
jobHandle = job.Schedule(accessor.count,DefaultBatchCount);
break;
}
@@ -2524,8 +2628,7 @@ unsafe void GetScalarJob(Root gltf, int accessorIndex, out NativeArray? s
scalars = null;
jobHandle = null;
var accessor = gltf.accessors[accessorIndex];
- var bufferView = gltf.bufferViews[accessor.bufferView];
- var buffer = GetBufferView(bufferView);
+ var buffer = GetBufferView(accessor.bufferView,accessor.byteOffset);
Assert.AreEqual(accessor.typeEnum, GLTFAccessorAttributeType.SCALAR);
if (accessor.isSparse) {
@@ -2601,11 +2704,20 @@ public unsafe void GetAccessor(int index, out Accessor accessor, out void* data,
return;
}
var bufferView = gltfRoot.bufferViews[accessor.bufferView];
- byteStride = bufferView.byteStride;
- var bufferIndex = bufferView.buffer;
- var buffer = GetBuffer(bufferIndex);
- fixed(void* src = &(buffer[accessor.byteOffset + bufferView.byteOffset + binChunks[bufferIndex].start])) {
- data = src;
+#if MESHOPT
+ var meshopt = bufferView.extensions?.EXT_meshopt_compression;
+ if (meshopt != null) {
+ byteStride = meshopt.byteStride;
+ data = (byte*)meshoptBufferViews[accessor.bufferView].GetUnsafeReadOnlyPtr() + accessor.byteOffset;
+ } else
+#endif
+ {
+ byteStride = bufferView.byteStride;
+ var bufferIndex = bufferView.buffer;
+ var buffer = GetBuffer(bufferIndex);
+ fixed(void* src = &(buffer[accessor.byteOffset + bufferView.byteOffset + binChunks[bufferIndex].start])) {
+ data = src;
+ }
}
// // Alternative that uses NativeArray/Slice
@@ -2615,19 +2727,37 @@ public unsafe void GetAccessor(int index, out Accessor accessor, out void* data,
public unsafe void GetAccessorSparseIndices(AccessorSparseIndices sparseIndices, out void* data) {
var bufferView = gltfRoot.bufferViews[sparseIndices.bufferView];
- var bufferIndex = bufferView.buffer;
- var buffer = GetBuffer(bufferIndex);
- fixed (void* src = &(buffer[sparseIndices.byteOffset + bufferView.byteOffset + binChunks[bufferIndex].start])) {
- data = src;
+#if MESHOPT
+ var meshopt = bufferView.extensions?.EXT_meshopt_compression;
+ if (meshopt != null) {
+ data = (byte*)meshoptBufferViews[(int)sparseIndices.bufferView].GetUnsafeReadOnlyPtr() + sparseIndices.byteOffset;
+ }
+ else
+#endif
+ {
+ var bufferIndex = bufferView.buffer;
+ var buffer = GetBuffer(bufferIndex);
+ fixed (void* src = &(buffer[sparseIndices.byteOffset + bufferView.byteOffset + binChunks[bufferIndex].start])) {
+ data = src;
+ }
}
}
public unsafe void GetAccessorSparseValues(AccessorSparseValues sparseValues, out void* data) {
var bufferView = gltfRoot.bufferViews[sparseValues.bufferView];
- var bufferIndex = bufferView.buffer;
- var buffer = GetBuffer(bufferIndex);
- fixed (void* src = &(buffer[sparseValues.byteOffset + bufferView.byteOffset + binChunks[bufferIndex].start])) {
- data = src;
+#if MESHOPT
+ var meshopt = bufferView.extensions?.EXT_meshopt_compression;
+ if (meshopt != null) {
+ data = (byte*)meshoptBufferViews[(int)sparseValues.bufferView].GetUnsafeReadOnlyPtr() + sparseValues.byteOffset;
+ }
+ else
+#endif
+ {
+ var bufferIndex = bufferView.buffer;
+ var buffer = GetBuffer(bufferIndex);
+ fixed (void* src = &(buffer[sparseValues.byteOffset + bufferView.byteOffset + binChunks[bufferIndex].start])) {
+ data = src;
+ }
}
}
#endregion IGltfBuffers
diff --git a/Runtime/Scripts/Schema/BufferView.cs b/Runtime/Scripts/Schema/BufferView.cs
index 6531d7c3..db715a7c 100644
--- a/Runtime/Scripts/Schema/BufferView.cs
+++ b/Runtime/Scripts/Schema/BufferView.cs
@@ -13,14 +13,18 @@
// limitations under the License.
//
+#if MESHOPT
+using Meshoptimizer;
+#endif
+
namespace GLTFast.Schema {
- public enum BufferViewTarget
- {
- None = 0,
- ArrayBuffer = 34962,
- ElementArrayBuffer = 34963,
- }
+ // public enum BufferViewTarget
+ // {
+ // None = 0,
+ // ArrayBuffer = 34962,
+ // ElementArrayBuffer = 34963,
+ // }
[System.Serializable]
public class BufferSlice {
@@ -38,7 +42,7 @@ public class BufferSlice {
}
[System.Serializable]
- public class BufferView : BufferSlice {
+ public class BufferViewBase : BufferSlice {
///
/// The index of the buffer.
///
@@ -51,7 +55,11 @@ public class BufferView : BufferSlice {
/// 255
///
public int byteStride = -1;
+ }
+
+ [System.Serializable]
+ public class BufferView : BufferViewBase {
///
/// The target that the WebGL buffer should be bound to.
/// All valid values correspond to WebGL enums.
@@ -74,5 +82,54 @@ public void GltfSerialize(JsonWriter writer) {
}
writer.Close();
}
+
+#if MESHOPT
+ public BufferViewExtensions extensions;
+#endif
+ }
+
+#if MESHOPT
+ [System.Serializable]
+ public class BufferViewExtensions {
+ public BufferViewMeshoptExtension EXT_meshopt_compression;
+ }
+
+ [System.Serializable]
+ public class BufferViewMeshoptExtension : BufferViewBase {
+
+ public int count;
+ public string mode;
+ public string filter;
+
+ Mode _modeEnum = Mode.Undefined;
+ public Mode modeEnum {
+ get {
+ if (_modeEnum != Mode.Undefined) {
+ return _modeEnum;
+ }
+ if (!string.IsNullOrEmpty (mode)) {
+ _modeEnum = (Mode)System.Enum.Parse (typeof(Mode), mode, true);
+ mode = null;
+ return _modeEnum;
+ }
+ return Mode.Undefined;
+ }
+ }
+
+ Filter _filterEnum = Filter.Undefined;
+ public Filter filterEnum {
+ get {
+ if (_filterEnum != Filter.Undefined) {
+ return _filterEnum;
+ }
+ if (!string.IsNullOrEmpty (filter)) {
+ _filterEnum = (Filter)System.Enum.Parse (typeof(Filter), filter, true);
+ filter = null;
+ return _filterEnum;
+ }
+ return Filter.None;
+ }
+ }
}
+#endif
}
\ No newline at end of file
diff --git a/Runtime/Scripts/Schema/glTFastSchema.asmdef b/Runtime/Scripts/Schema/glTFastSchema.asmdef
index ce3e2cfa..3007f21a 100644
--- a/Runtime/Scripts/Schema/glTFastSchema.asmdef
+++ b/Runtime/Scripts/Schema/glTFastSchema.asmdef
@@ -2,7 +2,8 @@
"name": "glTFastSchema",
"rootNamespace": "",
"references": [
- "Unity.Mathematics"
+ "Unity.Mathematics",
+ "Unity.Meshopt.Decompress"
],
"includePlatforms": [],
"excludePlatforms": [],
@@ -22,6 +23,11 @@
"expression": "1.1.0",
"define": "KTX_UNITY"
},
+ {
+ "name": "com.unity.meshopt.decompress",
+ "expression": "",
+ "define": "MESHOPT"
+ },
{
"name": "com.unity.modules.animation",
"expression": "1.0.0",
diff --git a/Runtime/Scripts/glTFast.asmdef b/Runtime/Scripts/glTFast.asmdef
index d2dd6a48..24fb0e1a 100644
--- a/Runtime/Scripts/glTFast.asmdef
+++ b/Runtime/Scripts/glTFast.asmdef
@@ -1,14 +1,15 @@
{
"name": "glTFast",
"references": [
- "Draco",
"glTFastSchema",
"glTFastFakeSchema",
- "Ktx",
"Unity.Mathematics",
"Unity.Burst",
"Unity.Collections",
"Unity.Jobs",
+ "Draco",
+ "Ktx",
+ "Unity.Meshopt.Decompress",
"Unity.RenderPipelines.Universal.Runtime",
"Unity.RenderPipelines.HighDefinition.Runtime"
],
@@ -54,6 +55,11 @@
"name": "com.unity.jobs",
"expression": "0.2.0",
"define": "UNITY_JOBS"
+ },
+ {
+ "name": "com.unity.meshopt.decompress",
+ "expression": "",
+ "define": "MESHOPT"
}
],
"noEngineReferences": false
diff --git a/package.json b/package.json
index b8ff4c26..adf28146 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "com.atteneder.gltfast",
"version": "4.4.0",
"displayName": "glTFast",
- "description": "Load glTF 3D files fast at runtime or import them into the asset database in the Editor",
+ "description": "Use glTFast to import and export glTF 3D files efficiently at runtime or in the Editor",
"unity": "2019.4",
"keywords": [
"mesh",