diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/NativeFile.cs b/Packages/UniGLTF/Runtime/UniGLTF/IO/NativeFile.cs
new file mode 100644
index 000000000..a5c327e06
--- /dev/null
+++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/NativeFile.cs
@@ -0,0 +1,59 @@
+using System.IO;
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+using Unity.IO.LowLevel.Unsafe;
+
+namespace UniGLTF
+{
+ ///
+ /// Read a file directly into a NativeArray via AsyncReadManager without a managed byte[].
+ /// Ported from Unio (https://github.com/hadashiA/Unio) MIT License.
+ ///
+ public static class NativeFile
+ {
+ public static unsafe NativeArray ReadAllBytes(string filePath)
+ {
+ FileInfoResult fileInfoResult;
+ var fileInfoHandle = AsyncReadManager.GetFileInfo(filePath, &fileInfoResult);
+ fileInfoHandle.JobHandle.Complete();
+
+ if (fileInfoResult.FileState == FileState.Absent)
+ {
+ throw new FileNotFoundException(filePath);
+ }
+
+ var size = fileInfoResult.FileSize;
+ var buffer = (byte*)UnsafeUtility.Malloc(size, 16, Allocator.Persistent);
+ var readCommand = new ReadCommand
+ {
+ Offset = 0,
+ Size = size,
+ Buffer = buffer,
+ };
+
+ var readHandle = AsyncReadManager.Read(filePath, &readCommand, 1);
+ try
+ {
+ readHandle.JobHandle.Complete();
+ if (readHandle.Status != ReadStatus.Complete)
+ {
+ UnsafeUtility.Free(buffer, Allocator.Persistent);
+ throw new IOException($"Read operation failed ({readHandle.Status}). {filePath}");
+ }
+ }
+ finally
+ {
+ if (readHandle.IsValid())
+ {
+ readHandle.Dispose();
+ }
+ }
+
+ var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(buffer, (int)size, Allocator.Persistent);
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+ NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, AtomicSafetyHandle.Create());
+#endif
+ return array;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/NativeFile.cs.meta b/Packages/UniGLTF/Runtime/UniGLTF/IO/NativeFile.cs.meta
new file mode 100644
index 000000000..958a58db4
--- /dev/null
+++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/NativeFile.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b5b957f0aa4fb4d05b57882560f2d778
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/VRM10/Runtime/IO/Vrm10.cs b/Packages/VRM10/Runtime/IO/Vrm10.cs
index 1008602fa..6bc6c9052 100644
--- a/Packages/VRM10/Runtime/IO/Vrm10.cs
+++ b/Packages/VRM10/Runtime/IO/Vrm10.cs
@@ -1,8 +1,8 @@
using System;
-using System.IO;
using System.Threading;
using System.Threading.Tasks;
using UniGLTF;
+using Unity.Collections;
using UnityEngine;
namespace UniVRM10
@@ -53,23 +53,34 @@ namespace UniVRM10
? new RuntimeOnlyAwaitCaller()
: new ImmediateCaller();
- using var gltfData = await awaitCaller.Run(() =>
+ NativeArray bytes = default;
+ try
{
- var bytes = File.ReadAllBytes(path);
- return new GlbLowLevelParser(path, bytes).Parse();
- });
- return await LoadAsync(
- gltfData,
- canLoadVrm0X,
- controlRigGenerationOption,
- showMeshes,
- awaitCaller,
- textureDeserializer,
- materialGenerator,
- vrmMetaInformationCallback,
- ct,
- importerContextSettings,
- springboneRuntime);
+ using var gltfData = await awaitCaller.Run(() =>
+ {
+ bytes = NativeFile.ReadAllBytes(path);
+ return GlbLowLevelParser.Parse(path, bytes);
+ });
+ return await LoadAsync(
+ gltfData,
+ canLoadVrm0X,
+ controlRigGenerationOption,
+ showMeshes,
+ awaitCaller,
+ textureDeserializer,
+ materialGenerator,
+ vrmMetaInformationCallback,
+ ct,
+ importerContextSettings,
+ springboneRuntime);
+ }
+ finally
+ {
+ if (bytes.IsCreated)
+ {
+ bytes.Dispose();
+ }
+ }
}
///
@@ -89,7 +100,7 @@ namespace UniVRM10
/// CancellationToken
/// Importer context settings.
/// vrm-1.0 instance. Maybe return null if unexpected error was raised.
- public static async Task LoadBytesAsync(
+ public static Task LoadBytesAsync(
byte[] bytes,
bool canLoadVrm0X = true,
ControlRigGenerationOption controlRigGenerationOption = ControlRigGenerationOption.Generate,
@@ -101,12 +112,80 @@ namespace UniVRM10
CancellationToken ct = default,
ImporterContextSettings importerContextSettings = null,
IVrm10SpringBoneRuntime springboneRuntime = null)
+ {
+ return LoadBytesAsync(
+ bytes.AsMemory(),
+ canLoadVrm0X,
+ controlRigGenerationOption,
+ showMeshes,
+ awaitCaller,
+ textureDeserializer,
+ materialGenerator,
+ vrmMetaInformationCallback,
+ ct,
+ importerContextSettings,
+ springboneRuntime);
+ }
+
+ ///
+ /// Load the VRM file from the NativeArray.
+ ///
+ /// The caller is responsible for disposing `bytes`. Do not dispose it until this task completes.
+ /// You should call this on Unity main thread.
+ /// This will throw Exceptions (include OperationCanceledException).
+ ///
+ public static Task LoadBytesAsync(
+ NativeArray bytes,
+ bool canLoadVrm0X = true,
+ ControlRigGenerationOption controlRigGenerationOption = ControlRigGenerationOption.Generate,
+ bool showMeshes = true,
+ IAwaitCaller awaitCaller = null,
+ ITextureDeserializer textureDeserializer = null,
+ IMaterialDescriptorGenerator materialGenerator = null,
+ VrmMetaInformationCallback vrmMetaInformationCallback = null,
+ CancellationToken ct = default,
+ ImporterContextSettings importerContextSettings = null,
+ IVrm10SpringBoneRuntime springboneRuntime = null)
+ {
+ return LoadBytesAsync(
+ bytes.AsMemory(),
+ canLoadVrm0X,
+ controlRigGenerationOption,
+ showMeshes,
+ awaitCaller,
+ textureDeserializer,
+ materialGenerator,
+ vrmMetaInformationCallback,
+ ct,
+ importerContextSettings,
+ springboneRuntime);
+ }
+
+ ///
+ /// Load the VRM file from the memory.
+ ///
+ /// The caller is responsible for keeping the underlying memory alive until this task completes.
+ /// You should call this on Unity main thread.
+ /// This will throw Exceptions (include OperationCanceledException).
+ ///
+ public static async Task LoadBytesAsync(
+ ReadOnlyMemory bytes,
+ bool canLoadVrm0X = true,
+ ControlRigGenerationOption controlRigGenerationOption = ControlRigGenerationOption.Generate,
+ bool showMeshes = true,
+ IAwaitCaller awaitCaller = null,
+ ITextureDeserializer textureDeserializer = null,
+ IMaterialDescriptorGenerator materialGenerator = null,
+ VrmMetaInformationCallback vrmMetaInformationCallback = null,
+ CancellationToken ct = default,
+ ImporterContextSettings importerContextSettings = null,
+ IVrm10SpringBoneRuntime springboneRuntime = null)
{
awaitCaller ??= Application.isPlaying
? new RuntimeOnlyAwaitCaller()
: new ImmediateCaller();
- using var gltfData = await awaitCaller.Run(() => new GlbLowLevelParser(string.Empty, bytes).Parse());
+ using var gltfData = await awaitCaller.Run(() => GlbLowLevelParser.Parse("", bytes));
return await LoadAsync(
gltfData,
canLoadVrm0X,