Merge pull request #1196 from ousttrue/fix10/oldmeta_when_migrate

[1.0] RuntimeLoad と meta 取得 を実装
This commit is contained in:
ousttrue 2021-09-08 17:50:15 +09:00 committed by GitHub
commit a2307b42b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 712 additions and 510 deletions

View File

@ -52,7 +52,7 @@ namespace UniGLTF
//
// Parse(parse glb, parser gltf json)
//
var data = new AmbiguousGltfFileParser(scriptedImporter.assetPath).Parse();
var data = new AutoGltfFileParser(scriptedImporter.assetPath).Parse();
//

View File

@ -24,7 +24,7 @@ namespace UniGLTF
base.OnEnable();
m_importer = target as GltfScriptedImporter;
m_data = new AmbiguousGltfFileParser(m_importer.assetPath).Parse();
m_data = new AutoGltfFileParser(m_importer.assetPath).Parse();
var materialGenerator = new GltfMaterialDescriptorGenerator();
var materialKeys = m_data.GLTF.materials.Select((_, i) => materialGenerator.Get(m_data, i).SubAssetKey);

View File

@ -24,7 +24,7 @@ namespace UniGLTF
base.OnEnable();
m_importer = target as ZipArchivedGltfScriptedImporter;
m_data = new AmbiguousGltfFileParser(m_importer.assetPath).Parse();
m_data = new AutoGltfFileParser(m_importer.assetPath).Parse();
var materialGenerator = new GltfMaterialDescriptorGenerator();
var materialKeys = m_data.GLTF.materials.Select((_, i) => materialGenerator.Get(m_data, i).SubAssetKey);

View File

@ -79,7 +79,7 @@ namespace UniGLTF
{
if (awaitCaller == null)
{
awaitCaller = new TaskCaller();
awaitCaller = new ImmediateCaller();
}
if (MeasureTime == null)

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3a36dcb5732b467598919c0ed66f0c5a
timeCreated: 1624802689

View File

@ -3,14 +3,15 @@
namespace UniGLTF
{
/// <summary>
/// Ambiguous file parser.
/// Auto detection file parser.
/// Determine parsing method from the file extension.
/// Detects `gltf`` zip`, others as` glb`
/// </summary>
public sealed class AmbiguousGltfFileParser
public sealed class AutoGltfFileParser
{
private readonly string _path;
public AmbiguousGltfFileParser(string path)
public AutoGltfFileParser(string path)
{
_path = path;
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bc9d157827565c84f9ad41c907fd12e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -81,7 +81,7 @@ namespace UniGLTF
GltfData data = null;
try
{
data = new AmbiguousGltfFileParser(gltf.FullName).Parse();
data = new AutoGltfFileParser(gltf.FullName).Parse();
}
catch (Exception ex)
{
@ -127,7 +127,7 @@ namespace UniGLTF
GltfData data = null;
try
{
data = new AmbiguousGltfFileParser(gltf.FullName).Parse();
data = new AutoGltfFileParser(gltf.FullName).Parse();
}
catch (Exception ex)
{
@ -205,7 +205,7 @@ namespace UniGLTF
{
var path = Path.Combine(root.FullName, "DamagedHelmet/glTF-Binary/DamagedHelmet.glb");
var data = new AmbiguousGltfFileParser(path).Parse();
var data = new AutoGltfFileParser(path).Parse();
var matDesc = new GltfMaterialDescriptorGenerator().Get(data, 0);
Assert.AreEqual("Standard", matDesc.ShaderName);

View File

@ -106,6 +106,7 @@ namespace VRM
private void OnDisable()
{
Debug.Log("StopCoroutine");
if (m_coroutine != null)
{
StopCoroutine(m_coroutine);

View File

@ -67,6 +67,7 @@ namespace VRM.SimpleViewer
private void OnDisable()
{
Debug.Log("StopCoroutine");
StopCoroutine(m_coroutine);
}
}

View File

@ -99,7 +99,7 @@ namespace VRM.SimpleViewer
public async Task UpdateMetaAsync(VRMImporterContext context)
{
var meta = await context.ReadMetaAsync(new TaskCaller(), true);
var meta = await context.ReadMetaAsync(new ImmediateCaller(), true);
m_textModelTitle.text = meta.Title;
m_textModelVersion.text = meta.Version;
@ -183,38 +183,115 @@ namespace VRM.SimpleViewer
m_target = GameObject.FindObjectOfType<TargetMover>().gameObject;
}
HumanPoseTransfer m_loaded;
VRMBlendShapeProxy m_proxy;
AIUEO m_lipSync;
bool m_enableLipSyncValue;
bool EnableLipSyncValue
class Loaded : IDisposable
{
set
RuntimeGltfInstance _instance;
HumanPoseTransfer _pose;
VRMBlendShapeProxy m_proxy;
Blinker m_blink;
bool m_enableBlinkValue;
public bool EnableBlinkValue
{
if (m_enableLipSyncValue == value) return;
m_enableLipSyncValue = value;
if (m_lipSync != null)
set
{
m_lipSync.enabled = m_enableLipSyncValue;
if (m_blink == value) return;
m_enableBlinkValue = value;
if (m_blink != null)
{
m_blink.enabled = m_enableBlinkValue;
}
}
}
AIUEO m_lipSync;
bool m_enableLipSyncValue;
public bool EnableLipSyncValue
{
set
{
if (m_enableLipSyncValue == value) return;
m_enableLipSyncValue = value;
if (m_lipSync != null)
{
m_lipSync.enabled = m_enableLipSyncValue;
}
}
}
public Loaded(RuntimeGltfInstance instance, HumanPoseTransfer src, Transform lookAtTarget)
{
_instance = instance;
var lookAt = instance.GetComponent<VRMLookAtHead>();
if (lookAt != null)
{
_pose = _instance.gameObject.AddComponent<HumanPoseTransfer>();
_pose.Source = src;
_pose.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
m_lipSync = instance.gameObject.AddComponent<AIUEO>();
m_blink = instance.gameObject.AddComponent<Blinker>();
lookAt.Target = lookAtTarget;
lookAt.UpdateType = UpdateType.LateUpdate; // after HumanPoseTransfer's setPose
var animation = instance.GetComponent<Animation>();
if (animation && animation.clip != null)
{
animation.Play(animation.clip.name);
}
m_proxy = instance.GetComponent<VRMBlendShapeProxy>();
}
}
public void Dispose()
{
// Destroy game object. not RuntimeGltfInstance
GameObject.Destroy(_instance.gameObject);
}
public void EnableBvh(HumanPoseTransfer src)
{
if (_pose != null)
{
_pose.Source = src;
_pose.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
}
}
public void EnableTPose(HumanPoseClip pose)
{
if (_pose != null)
{
_pose.PoseClip = pose;
_pose.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseClip;
}
}
public void OnResetClicked()
{
if (_pose != null)
{
foreach (var spring in _pose.GetComponentsInChildren<VRMSpringBone>())
{
spring.Setup();
}
}
}
public void Update()
{
if (m_proxy != null)
{
m_proxy.Apply();
}
}
}
Loaded m_loaded;
Blinker m_blink;
bool m_enableBlinkValue;
bool EnableBlinkValue
{
set
{
if (m_blink == value) return;
m_enableBlinkValue = value;
if (m_blink != null)
{
m_blink.enabled = m_enableBlinkValue;
}
}
}
private void Start()
{
@ -222,7 +299,7 @@ namespace VRM.SimpleViewer
VRMVersion.MAJOR, VRMVersion.MINOR);
m_open.onClick.AddListener(OnOpenClicked);
m_reset.onClick.AddListener(OnResetClicked);
m_reset.onClick.AddListener(() => m_loaded.OnResetClicked());
// load initial bvh
if (m_motion != null)
@ -249,49 +326,18 @@ namespace VRM.SimpleViewer
private void Update()
{
EnableLipSyncValue = m_enableLipSync.isOn;
EnableBlinkValue = m_enableAutoBlink.isOn;
if (Input.GetKeyDown(KeyCode.Tab))
{
if (Root != null) Root.SetActive(!Root.activeSelf);
}
m_ui.UpdateToggle(EnableBvh, EnableTPose);
m_ui.UpdateToggle(() => m_loaded?.EnableBvh(m_src), () => m_loaded?.EnableTPose(m_pose));
if (m_proxy != null)
{
m_proxy.Apply();
}
}
void EnableBvh()
{
if (m_loaded != null)
{
m_loaded.Source = m_src;
m_loaded.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
}
}
void EnableTPose()
{
if (m_loaded != null)
{
m_loaded.PoseClip = m_pose;
m_loaded.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseClip;
}
}
void OnResetClicked()
{
if (m_loaded == null)
{
return;
}
foreach (var spring in m_loaded.GetComponentsInChildren<VRMSpringBone>())
{
spring.Setup();
m_loaded.EnableLipSyncValue = m_enableLipSync.isOn;
m_loaded.EnableBlinkValue = m_enableAutoBlink.isOn;
m_loaded.Update();
}
}
@ -357,109 +403,61 @@ namespace VRM.SimpleViewer
}
Debug.LogFormat("{0}", path);
var ext = Path.GetExtension(path).ToLower();
switch (ext)
GltfData data;
try
{
case ".vrm":
{
var data = new GlbFileParser(path).Parse();
var vrm = new VRMData(data);
using (var context = new VRMImporterContext(vrm, materialGenerator: GetVrmMaterialGenerator(m_useUrpMaterial.isOn, vrm.VrmExtension)))
{
await m_texts.UpdateMetaAsync(context);
var loaded = await context.LoadAsync();
loaded.EnableUpdateWhenOffscreen();
loaded.ShowMeshes();
SetModel(loaded.gameObject);
}
break;
}
data = new AutoGltfFileParser(path).Parse();
}
catch (Exception ex)
{
Debug.LogWarningFormat("parse error: {0}", ex);
return;
}
case ".glb":
{
var data = new GlbFileParser(path).Parse();
var context = new UniGLTF.ImporterContext(data, materialGenerator: GetGltfMaterialGenerator(m_useUrpMaterial.isOn));
var loaded = context.Load();
loaded.EnableUpdateWhenOffscreen();
loaded.ShowMeshes();
SetModel(loaded.gameObject);
break;
}
case ".gltf":
{
var data = new GltfFileWithResourceFilesParser(path).Parse();
var context = new UniGLTF.ImporterContext(data, materialGenerator: GetGltfMaterialGenerator(m_useUrpMaterial.isOn));
var loaded = context.Load();
loaded.EnableUpdateWhenOffscreen();
loaded.ShowMeshes();
SetModel(loaded.gameObject);
break;
}
case ".zip":
{
var data = new ZipArchivedGltfFileParser(path).Parse();
var context = new UniGLTF.ImporterContext(data, materialGenerator: GetGltfMaterialGenerator(m_useUrpMaterial.isOn));
var loaded = context.Load();
loaded.EnableUpdateWhenOffscreen();
loaded.ShowMeshes();
SetModel(loaded.gameObject);
break;
}
default:
Debug.LogWarningFormat("unknown file type: {0}", path);
break;
try
{
var vrm = new VRMData(data);
using (var loader = new VRMImporterContext(vrm, materialGenerator: GetVrmMaterialGenerator(m_useUrpMaterial.isOn, vrm.VrmExtension)))
{
await m_texts.UpdateMetaAsync(loader);
var instance = await loader.LoadAsync();
SetModel(instance);
}
}
catch (NotVrm0Exception)
{
using (var loader = new UniGLTF.ImporterContext(data, materialGenerator: GetGltfMaterialGenerator(m_useUrpMaterial.isOn)))
{
var instance = await loader.LoadAsync();
SetModel(instance);
}
}
}
void SetModel(GameObject go)
void SetModel(RuntimeGltfInstance instance)
{
// cleanup
var loaded = m_loaded;
m_loaded = null;
if (loaded != null)
if (m_loaded != null)
{
Debug.LogFormat("destroy {0}", loaded);
GameObject.Destroy(loaded.gameObject);
m_loaded.Dispose();
m_loaded = null;
}
if (go != null)
{
var lookAt = go.GetComponent<VRMLookAtHead>();
if (lookAt != null)
{
m_loaded = go.AddComponent<HumanPoseTransfer>();
m_loaded.Source = m_src;
m_loaded.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
instance.EnableUpdateWhenOffscreen();
instance.ShowMeshes();
m_lipSync = go.AddComponent<AIUEO>();
m_blink = go.AddComponent<Blinker>();
lookAt.Target = m_target.transform;
lookAt.UpdateType = UpdateType.LateUpdate; // after HumanPoseTransfer's setPose
}
var animation = go.GetComponent<Animation>();
if (animation && animation.clip != null)
{
animation.Play(animation.clip.name);
}
m_proxy = go.GetComponent<VRMBlendShapeProxy>();
}
m_loaded = new Loaded(instance, m_src, m_target.transform);
}
void SetMotion(HumanPoseTransfer src)
{
m_src = src;
src.GetComponent<Renderer>().enabled = false;
EnableBvh();
if (m_loaded != null)
{
m_loaded.EnableBvh(src);
}
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Linq;
using UniGLTF;
using UniGLTF.Extensions.VRMC_vrm;
using UniJSON;
@ -17,15 +18,17 @@ namespace UniVRM10
{
public GltfData Data { get; }
public UniGLTF.Extensions.VRMC_vrm.VRMC_vrm VrmExtension { get; }
public readonly Migration.Vrm0Meta OriginalMetaBeforeMigration;
public readonly Vrm10FileType FileType;
public readonly String Message;
public Vrm10Data(GltfData data, VRMC_vrm vrm, Vrm10FileType fileType, string message)
public Vrm10Data(GltfData data, VRMC_vrm vrm, Vrm10FileType fileType, string message, Migration.Vrm0Meta oldMeta = null)
{
Data = data;
VrmExtension = vrm;
FileType = fileType;
Message = message;
OriginalMetaBeforeMigration = oldMeta;
}
public static bool TryParseOrMigrate(string path, bool doMigrate, out Vrm10Data result)
@ -33,19 +36,24 @@ namespace UniVRM10
return TryParseOrMigrate(path, File.ReadAllBytes(path), doMigrate, out result);
}
public static bool TryParseOrMigrate(string path, byte[] bytes, bool doMigrate, out Vrm10Data result)
{
var data = new GlbLowLevelParser(path, bytes).Parse();
return TryParseOrMigrate(data, doMigrate, out result);
}
/// <summary>
/// VRM1 でパースし、失敗したら Migration してから VRM1 でパースする
/// </summary>
/// <param name="path"></param>
/// <param name="doMigrate"></param>
/// <returns></returns>
public static bool TryParseOrMigrate(string path, byte[] bytes, bool doMigrate, out Vrm10Data result)
public static bool TryParseOrMigrate(GltfData data, bool doMigrate, out Vrm10Data result)
{
//
// Parse(parse glb, parser gltf json)
//
{
var data = new GlbLowLevelParser(path, bytes).Parse();
if (UniGLTF.Extensions.VRMC_vrm.GltfDeserializer.TryGet(data.GLTF.extensions, out UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm))
{
// success
@ -56,10 +64,11 @@ namespace UniVRM10
// try migrateion
byte[] migrated = default;
Migration.Vrm0Meta oldMeta = default;
try
{
var glb = UniGLTF.Glb.Parse(bytes);
var json = glb.Json.Bytes.ParseAsJson();
var json = data.Json.ParseAsJson();
var bin = data.Chunks.First(x => x.ChunkType == GlbChunkType.BIN);
try
{
@ -86,12 +95,14 @@ namespace UniVRM10
return false;
}
migrated = MigrationVrm.Migrate(json, glb.Binary.Bytes);
migrated = MigrationVrm.Migrate(json, bin.Bytes);
if (migrated == null)
{
result = new Vrm10Data(default, default, Vrm10FileType.Vrm0, "vrm0: cannot migrate");
return false;
}
oldMeta = Migration.Vrm0Meta.FromJsonBytes(json);
}
catch (Exception ex)
{
@ -100,11 +111,15 @@ namespace UniVRM10
}
{
var data = new GlbLowLevelParser(path, migrated).Parse();
if (UniGLTF.Extensions.VRMC_vrm.GltfDeserializer.TryGet(data.GLTF.extensions, out VRMC_vrm vrm))
var migratedData = new GlbLowLevelParser(data.TargetPath, migrated).Parse();
if (UniGLTF.Extensions.VRMC_vrm.GltfDeserializer.TryGet(migratedData.GLTF.extensions, out VRMC_vrm vrm))
{
// success
result = new Vrm10Data(data, vrm, Vrm10FileType.Vrm0, "vrm0: migrated");
if (oldMeta == null)
{
throw new NullReferenceException("oldMeta");
}
result = new Vrm10Data(migratedData, vrm, Vrm10FileType.Vrm0, "vrm0: migrated", oldMeta);
return true;
}

View File

@ -306,6 +306,24 @@ namespace UniVRM10
return clip;
}
public async Task<Texture2D> LoadVrmThumbnailAsync(IAwaitCaller awaitCaller = null)
{
if (awaitCaller == null)
{
awaitCaller = new ImmediateCaller();
}
if (Vrm10TextureDescriptorGenerator.TryGetMetaThumbnailTextureImportParam(Data, m_vrm.VrmExtension, out (SubAssetKey, VRMShaders.TextureDescriptor Param) kv))
{
var texture = await TextureFactory.GetTextureAsync(kv.Param, awaitCaller);
return texture as Texture2D;
}
else
{
return null;
}
}
async Task<VRM10Object> LoadVrmAsync(IAwaitCaller awaitCaller, UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrmExtension)
{
if (m_externalMap.TryGetValue(VRM10Object.SubAssetKey, out UnityEngine.Object obj) && obj is VRM10Object vrm)
@ -347,13 +365,11 @@ namespace UniVRM10
{
meta.Authors.AddRange(src.Authors);
}
if (Vrm10TextureDescriptorGenerator.TryGetMetaThumbnailTextureImportParam(Data, vrmExtension, out (SubAssetKey, VRMShaders.TextureDescriptor Param) kv))
var tex2D = await LoadVrmThumbnailAsync(awaitCaller);
if (tex2D != null)
{
var texture = await TextureFactory.GetTextureAsync(kv.Param, awaitCaller);
if (texture is Texture2D tex2D)
{
meta.Thumbnail = tex2D;
}
meta.Thumbnail = tex2D;
}
}

View File

@ -0,0 +1,109 @@
using System;
using UniJSON;
namespace UniVRM10.Migration
{
public enum AllowedUser
{
OnlyAuthor,
ExplicitlyLicensedPerson,
Everyone,
}
public enum LicenseType
{
Redistribution_Prohibited,
CC0,
CC_BY,
CC_BY_NC,
CC_BY_SA,
CC_BY_NC_SA,
CC_BY_ND,
CC_BY_NC_ND,
Other
}
public enum UssageLicense
{
Disallow,
Allow,
}
/// <summary>
/// VRM0.X version meta. This class has meta before migrate.
/// </summary>
public class Vrm0Meta
{
public string title;
public string version;
public string author;
public string contactInformation;
public string reference;
public int texture = -1;
public AllowedUser allowedUser = AllowedUser.OnlyAuthor;
public bool violentUsage = false;
public bool sexualUsage = false;
public bool commercialUsage = false;
public string otherPermissionUrl;
public LicenseType licenseType = LicenseType.Redistribution_Prohibited;
public string otherLicenseUrl;
public static Vrm0Meta FromJsonBytes(UniJSON.JsonNode glTF)
{
var oldMeta = new Vrm0Meta();
var extensions = glTF["extensions"];
var vrm = extensions["VRM"];
var meta = vrm["meta"];
foreach (var kv in meta.ObjectItems())
{
var key = kv.Key.GetString();
switch (key)
{
case "title":
oldMeta.title = kv.Value.GetString();
break;
case "version":
oldMeta.version = kv.Value.GetString();
break;
case "author":
oldMeta.author = kv.Value.GetString();
break;
case "contactInformation":
oldMeta.contactInformation = kv.Value.GetString();
break;
case "reference":
oldMeta.reference = kv.Value.GetString();
break;
case "texture":
oldMeta.texture = kv.Value.GetInt32();
break;
case "allowedUserName":
oldMeta.allowedUser = (AllowedUser)Enum.Parse(typeof(AllowedUser), kv.Value.GetString(), true);
break;
case "violentUssageName":
oldMeta.violentUsage = kv.Value.GetString() == "Allow";
break;
case "sexualUssageName":
oldMeta.sexualUsage = kv.Value.GetString() == "Allow";
break;
case "commercialUssageName":
oldMeta.commercialUsage = kv.Value.GetString() == "Allow";
break;
case "otherPermissionUrl":
oldMeta.otherPermissionUrl = kv.Value.GetString();
break;
case "licenseName":
oldMeta.licenseType = (LicenseType)Enum.Parse(typeof(LicenseType), kv.Value.GetString(), true);
break;
case "otherLicenseUrl":
oldMeta.otherLicenseUrl = kv.Value.GetString();
break;
default:
UnityEngine.Debug.Log($"{key}");
break;
}
}
return oldMeta;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57e782f0f79ef4d4abcd4f1c5438e9e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -5,7 +5,7 @@ using UniGLTF;
using UniHumanoid;
using UnityEngine;
using UnityEngine.UI;
using VRMShaders;
namespace UniVRM10.VRM10Viewer
{
@ -93,7 +93,31 @@ namespace UniVRM10.VRM10Viewer
m_textDistributionOther.text = "";
}
public void UpdateMeta(VRM10ObjectMeta meta)
public void UpdateMeta(Migration.Vrm0Meta meta, Texture2D thumbnail)
{
if (meta == null)
{
return;
}
m_textModelTitle.text = meta.title;
m_textModelVersion.text = meta.version;
m_textModelAuthor.text = meta.author;
m_textModelContact.text = meta.contactInformation;
m_textModelReference.text = meta.reference;
m_textPermissionAllowed.text = meta.allowedUser.ToString();
m_textPermissionViolent.text = meta.violentUsage.ToString();
m_textPermissionSexual.text = meta.sexualUsage.ToString();
m_textPermissionCommercial.text = meta.commercialUsage.ToString();
m_textPermissionOther.text = meta.otherPermissionUrl;
// m_textDistributionLicense.text = meta.ModificationLicense.ToString();
m_textDistributionOther.text = meta.otherLicenseUrl;
m_thumbnail.texture = thumbnail;
}
public void UpdateMeta(UniGLTF.Extensions.VRMC_vrm.Meta meta, Texture2D thumbnail)
{
if (meta == null)
{
@ -108,16 +132,16 @@ namespace UniVRM10.VRM10Viewer
{
m_textModelReference.text = meta.References[0];
}
m_textPermissionAllowed.text = meta.AllowedUser.ToString();
m_textPermissionViolent.text = meta.ViolentUsage.ToString();
m_textPermissionSexual.text = meta.SexualUsage.ToString();
// m_textPermissionAllowed.text = meta.AllowedUser.ToString();
m_textPermissionViolent.text = meta.AllowExcessivelyViolentUsage.ToString();
m_textPermissionSexual.text = meta.AllowExcessivelySexualUsage.ToString();
m_textPermissionCommercial.text = meta.CommercialUsage.ToString();
m_textPermissionOther.text = meta.OtherPermissionUrl;
// m_textPermissionOther.text = meta.OtherPermissionUrl;
m_textDistributionLicense.text = meta.ModificationLicense.ToString();
// m_textDistributionLicense.text = meta.ModificationLicense.ToString();
m_textDistributionOther.text = meta.OtherLicenseUrl;
m_thumbnail.texture = meta.Thumbnail;
m_thumbnail.texture = thumbnail;
}
}
[SerializeField]
@ -182,53 +206,113 @@ namespace UniVRM10.VRM10Viewer
m_target = GameObject.FindObjectOfType<VRM10TargetMover>().gameObject;
}
HumanPoseTransfer m_loaded;
VRM10Controller m_controller;
VRM10AIUEO m_lipSync;
bool m_enableLipSyncValue;
bool EnableLipSyncValue
class Loaded : IDisposable
{
set
RuntimeGltfInstance m_instance;
HumanPoseTransfer m_pose;
VRM10Controller m_controller;
VRM10AIUEO m_lipSync;
bool m_enableLipSyncValue;
public bool EnableLipSyncValue
{
if (m_enableLipSyncValue == value) return;
m_enableLipSyncValue = value;
if (m_lipSync != null)
set
{
m_lipSync.enabled = m_enableLipSyncValue;
}
}
}
VRM10AutoExpression m_autoExpression;
bool m_enableAutoExpressionValue;
bool EnableAutoExpressionValue
{
set
{
if (m_enableAutoExpressionValue == value) return;
m_enableAutoExpressionValue = value;
if (m_autoExpression != null)
{
m_autoExpression.enabled = m_enableAutoExpressionValue;
}
}
}
VRM10Blinker m_blink;
bool m_enableBlinkValue;
bool EnableBlinkValue
{
set
{
if (m_blink == value) return;
m_enableBlinkValue = value;
if (m_blink != null)
{
m_blink.enabled = m_enableBlinkValue;
if (m_enableLipSyncValue == value) return;
m_enableLipSyncValue = value;
if (m_lipSync != null)
{
m_lipSync.enabled = m_enableLipSyncValue;
}
}
}
VRM10AutoExpression m_autoExpression;
bool m_enableAutoExpressionValue;
public bool EnableAutoExpressionValue
{
set
{
if (m_enableAutoExpressionValue == value) return;
m_enableAutoExpressionValue = value;
if (m_autoExpression != null)
{
m_autoExpression.enabled = m_enableAutoExpressionValue;
}
}
}
VRM10Blinker m_blink;
bool m_enableBlinkValue;
public bool EnableBlinkValue
{
set
{
if (m_blink == value) return;
m_enableBlinkValue = value;
if (m_blink != null)
{
m_blink.enabled = m_enableBlinkValue;
}
}
}
public Loaded(RuntimeGltfInstance instance, HumanPoseTransfer src, Transform lookAtTarget)
{
m_instance = instance;
m_controller = instance.GetComponent<VRM10Controller>();
if (m_controller != null)
{
// VRM
m_controller.UpdateType = VRM10Controller.UpdateTypes.LateUpdate; // after HumanPoseTransfer's setPose
{
m_pose = instance.gameObject.AddComponent<HumanPoseTransfer>();
m_pose.Source = src;
m_pose.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
m_lipSync = instance.gameObject.AddComponent<VRM10AIUEO>();
m_blink = instance.gameObject.AddComponent<VRM10Blinker>();
m_autoExpression = instance.gameObject.AddComponent<VRM10AutoExpression>();
m_controller.LookAtTargetType = VRM10ObjectLookAt.LookAtTargetTypes.CalcYawPitchToGaze;
m_controller.Gaze = lookAtTarget;
}
}
var animation = instance.GetComponent<Animation>();
if (animation && animation.clip != null)
{
// GLTF animation
animation.Play(animation.clip.name);
}
}
public void Dispose()
{
// destroy GameObject
GameObject.Destroy(m_instance.gameObject);
}
public void EnableBvh(HumanPoseTransfer src)
{
if (m_pose != null)
{
m_pose.Source = src;
m_pose.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
}
}
public void EnableTPose(HumanPoseClip pose)
{
if (m_pose != null)
{
m_pose.PoseClip = pose;
m_pose.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseClip;
}
}
}
Loaded m_loaded;
private void Start()
{
@ -261,39 +345,19 @@ namespace UniVRM10.VRM10Viewer
private void Update()
{
EnableLipSyncValue = m_enableLipSync.isOn;
EnableBlinkValue = m_enableAutoBlink.isOn;
EnableAutoExpressionValue = m_enableAutoExpression.isOn;
if (m_loaded != null)
{
m_loaded.EnableLipSyncValue = m_enableLipSync.isOn;
m_loaded.EnableBlinkValue = m_enableAutoBlink.isOn;
m_loaded.EnableAutoExpressionValue = m_enableAutoExpression.isOn;
}
if (Input.GetKeyDown(KeyCode.Tab))
{
if (Root != null) Root.SetActive(!Root.activeSelf);
}
m_ui.UpdateToggle(EnableBvh, EnableTPose);
// if (m_controller != null)
// {
// m_controller.Expression.Apply();
// }
}
void EnableBvh()
{
if (m_loaded != null)
{
m_loaded.Source = m_src;
m_loaded.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
}
}
void EnableTPose()
{
if (m_loaded != null)
{
m_loaded.PoseClip = m_pose;
m_loaded.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseClip;
}
m_ui.UpdateToggle(() => m_loaded?.EnableBvh(m_src), () => m_loaded?.EnableTPose(m_pose));
}
void OnOpenClicked()
@ -350,7 +414,7 @@ namespace UniVRM10.VRM10Viewer
}
}
void LoadModel(string path)
async void LoadModel(string path)
{
if (!File.Exists(path))
{
@ -358,115 +422,63 @@ namespace UniVRM10.VRM10Viewer
}
Debug.LogFormat("{0}", path);
var ext = Path.GetExtension(path).ToLower();
switch (ext)
GltfData data;
try
{
case ".vrm":
data = new AutoGltfFileParser(path).Parse();
}
catch (Exception ex)
{
Debug.LogWarning(ex);
return;
}
if (Vrm10Data.TryParseOrMigrate(data, doMigrate: true, out Vrm10Data vrm))
{
// vrm
using (var loader = new Vrm10Importer(vrm, materialGenerator: GetVrmMaterialDescriptorGenerator(m_useUrpMaterial.isOn)))
{
// migrate しても thumbnail は同じ
var thumbnail = await loader.LoadVrmThumbnailAsync();
if (vrm.OriginalMetaBeforeMigration != null)
{
if (!Vrm10Data.TryParseOrMigrate(path, doMigrate: true, out Vrm10Data result))
{
Debug.LogError(result.Message);
return;
}
using (var loader = new Vrm10Importer(result, materialGenerator: GetVrmMaterialDescriptorGenerator(m_useUrpMaterial.isOn)))
{
var loaded = loader.Load();
loaded.ShowMeshes();
loaded.EnableUpdateWhenOffscreen();
SetModel(loaded.gameObject);
}
break;
// migrated from vrm-0.x. use OldMeta
m_texts.UpdateMeta(vrm.OriginalMetaBeforeMigration, thumbnail);
}
else
{
// load vrm-1.0. use newMeta
m_texts.UpdateMeta(vrm.VrmExtension.Meta, thumbnail);
}
case ".glb":
{
var data = new GlbFileParser(path).Parse();
using (var loader = new UniGLTF.ImporterContext(data, materialGenerator: GetMaterialDescriptorGenerator(m_useUrpMaterial.isOn)))
{
var loaded = loader.Load();
loaded.ShowMeshes();
loaded.EnableUpdateWhenOffscreen();
SetModel(loaded.gameObject);
}
break;
}
case ".gltf":
{
var data = new GltfFileWithResourceFilesParser(path).Parse();
using (var loader = new UniGLTF.ImporterContext(data, materialGenerator: GetMaterialDescriptorGenerator(m_useUrpMaterial.isOn)))
{
var loaded = loader.Load();
loaded.ShowMeshes();
loaded.EnableUpdateWhenOffscreen();
SetModel(loaded.gameObject);
}
break;
}
case ".zip":
{
var data = new ZipArchivedGltfFileParser(path).Parse();
using (var loader = new UniGLTF.ImporterContext(data, materialGenerator: GetMaterialDescriptorGenerator(m_useUrpMaterial.isOn)))
{
var loaded = loader.Load();
loaded.ShowMeshes();
loaded.EnableUpdateWhenOffscreen();
SetModel(loaded.gameObject);
}
break;
}
default:
Debug.LogWarningFormat("unknown file type: {0}", path);
break;
var instance = await loader.LoadAsync();
SetModel(instance);
}
}
else
{
// gltf
using (var loader = new UniGLTF.ImporterContext(data, materialGenerator: GetMaterialDescriptorGenerator(m_useUrpMaterial.isOn)))
{
var instance = await loader.LoadAsync();
SetModel(instance);
}
}
}
void SetModel(GameObject go)
void SetModel(RuntimeGltfInstance instance)
{
// cleanup
var loaded = m_loaded;
m_loaded = null;
if (loaded != null)
if (m_loaded != null)
{
Debug.LogFormat("destroy {0}", loaded);
GameObject.Destroy(loaded.gameObject);
m_loaded.Dispose();
m_loaded = null;
}
if (go != null)
{
m_controller = go.GetComponent<VRM10Controller>();
if (m_controller != null)
{
m_texts.UpdateMeta(m_controller.Vrm.Meta);
m_controller.UpdateType = VRM10Controller.UpdateTypes.LateUpdate; // after HumanPoseTransfer's setPose
{
m_loaded = go.AddComponent<HumanPoseTransfer>();
m_loaded.Source = m_src;
m_loaded.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
m_lipSync = go.AddComponent<VRM10AIUEO>();
m_blink = go.AddComponent<VRM10Blinker>();
m_autoExpression = go.AddComponent<VRM10AutoExpression>();
m_controller.LookAtTargetType = VRM10ObjectLookAt.LookAtTargetTypes.CalcYawPitchToGaze;
m_controller.Gaze = m_target.transform;
}
}
var animation = go.GetComponent<Animation>();
if (animation && animation.clip != null)
{
animation.Play(animation.clip.name);
}
}
instance.ShowMeshes();
instance.EnableUpdateWhenOffscreen();
m_loaded = new Loaded(instance, m_src, m_target.transform);
}
void SetMotion(HumanPoseTransfer src)
@ -474,7 +486,10 @@ namespace UniVRM10.VRM10Viewer
m_src = src;
src.GetComponent<Renderer>().enabled = false;
EnableBvh();
if (m_loaded != null)
{
m_loaded.EnableBvh(m_src);
}
}
}
}

View File

@ -254,5 +254,12 @@ namespace UniVRM10
Assert.AreEqual(VALUE.y, springBone.Colliders[colliderIndex].Shape.Sphere.Offset[1]);
Assert.AreEqual(VALUE.z, springBone.Colliders[colliderIndex].Shape.Sphere.Offset[2]);
}
[Test]
public void MigrateMeta()
{
Assert.True(Vrm10Data.TryParseOrMigrate(AliciaPath, true, out Vrm10Data vrm));
var a = 0;
}
}
}

View File

@ -54,28 +54,4 @@ namespace VRMShaders
return Task.FromResult(action());
}
}
/// <summary>
/// 非同期実行
/// </summary>
public sealed class TaskCaller : IAwaitCaller
{
public Task NextFrame()
{
return Task.Run(() =>
{
// Thread.Sleep(10);
});
}
public Task Run(Action action)
{
return Task.Run(action);
}
public Task<T> Run<T>(Func<T> action)
{
return Task.Run(action);
}
}
}

View File

@ -0,0 +1,32 @@
# v0.36
## Changed Storage Position of Texture Name
Conforming to the GLTF specification.
```json
json.images[i].extra.name
```
After the change
```json
json.images[i].name
```
## Changed Storage Position BlendShape Name
Conforming to the GLTF specification.
* `extras` is not allowed in target
* https://github.com/KhronosGroup/glTF/issues/1036#issuecomment-314078356
```json
json.meshes[i].primitives[j].targets[k].extra.name
```
After the change
```json
json.meshes[i].primitives[j].extras.targetNames[k]
```

View File

@ -58,14 +58,8 @@ See `SimpleViewer` sample.
```cs
GltfData Load(string path)
{
var ext = Path.GetExtension(path).ToLower();
switch(ext)
{
case ".glb": return new GlbFileParser(path).Parse();
case ".gltf": return new GltfFileWithResourceFilesParser(path).Parse();
case ".zip": return new ZipArchivedGltfFileParser(path).Parse();
default: throw new Exception($"unknown: {ext}");
}
// Detect type by file extension automatically
return new AutoGltfFileParser(path).Parse();
}
```

View File

@ -1,45 +1,10 @@
# Version
| version | |
|---------|---------------------------------------|
| v0.82.1 | Material replacement when(for URP) |
| v0.82.0 | Material replacement when(for URP) |
| v0.77 | Reorganize ImporterContext |
| v0.68 | Reorganize ImporterContext |
| v0.63.2 | Update gltf extensions implementation |
| v0.56 | Update BlendShapeKey spec |
[Rework BlendShapeKey's Interface](https://github.com/vrm-c/UniVRM/wiki/ReleaseNote-v0.56.0%28en%29#reworks-blendshapekeys-interface)
## v0.36
### Changed Storage Position of Texture Name
Conforming to the GLTF specification.
```json
json.images[i].extra.name
```
After the change
```json
json.images[i].name
```
### Changed Storage Position BlendShape Name
Conforming to the GLTF specification.
* `extras` is not allowed in target
* https://github.com/KhronosGroup/glTF/issues/1036#issuecomment-314078356
```json
json.meshes[i].primitives[j].targets[k].extra.name
```
After the change
```json
json.meshes[i].primitives[j].extras.targetNames[k]
```
| version | |
|---------|-----------------------------------------------------|
| v0.82.1 | `materialGenerator(for URP)` argument and `VrmData` |
| v0.79 | `GltfData` |
| v0.77 | `RuntimeGltfInstance` |
| v0.68 | `GltfParser` |
| v0.63.2 | Update gltf extensions implementation |
| v0.56 | Update BlendShapeKey spec |

View File

@ -12,6 +12,8 @@
href: gltf/0_82_glb_import.md
- name: glTF extensions implementation(0.63.2)
href: gltf/how_to_impl_extension.md
- name: GltfUpdate(0.36)
href: gltf/0_36_update.md
- name: VRM
items:
@ -37,7 +39,7 @@
- name: VRM-1.0(β)
items:
- name: 🚧Samples/VRM10Viewer
- name: 🚧RuntimeImport
- name: RuntimeImport
href: vrm1/vrm1_runtime_load.md
- name: 🚧Humanoid
href: vrm1/vrm1_get_humanoid.md

View File

@ -10,6 +10,8 @@ tags: ["api"]
## Environment
UniVRM v0.58.0
[Rework BlendShapeKey's Interface](https://github.com/vrm-c/UniVRM/wiki/ReleaseNote-v0.56.0%28en%29#reworks-blendshapekeys-interface)
## Methods
* [Recommended] `SetValues`

View File

@ -10,6 +10,8 @@ Below step is needed.
1. Load a `RuntimeGltfInstance` from `VRMData`.
1. Use the `RuntimeGltfInstance`
See `Assets\VRM\Samples\SimpleViewer\ViewerUI.cs`
# 1. Get `GltfData`
```cs

View File

@ -1,19 +1,44 @@
---
title: 🚧Runtime ロード
weight: 10
---
# RuntimeLoad
VRM-1.0 を使うアプリケーションは、 `VRM-0.x` の資産もロードしたいはずです。
`VRM-1.0` can load `VRM-0.x`.
In that case, incompatible migrated meta properties are not allowed.
Therefore, we provide an API that allows you to access the original meta before migration.
`VRM-1.0``VRM-0.X` の meta の非互換に [Metaの自動的なマイグレーションは禁止する方針](https://github.com/vrm-c/vrm-specification/issues/181) となりました。
See `Assets\VRM10\Samples\VRM10Viewer\VRM10ViewerUI.cs`.
## VRM-0.X を VRM-1.0 コンポーネントに対してロードする
```cs
async Task<RuntimeGltfInstance> LoadAsync(string path)
{
GltfData data = new GltfZipOrGlbFileParser(path).Parse();
`UniVRM-1.0` では、
`VRM-0.X` のライセンスを保持したまま、`VRM-1.0` のコンポーネントにロードする機能を提供します(予定)。
// The doMigrate argument allows you to load that older version of the vrm.
if (Vrm10Data.TryParseOrMigrate(data, doMigrate: true, out Vrm10Data vrm))
{
// vrm
using (var loader = new Vrm10Importer(vrm,
materialGenerator: GetVrmMaterialDescriptorGenerator(m_useUrpMaterial.isOn)))
{
// It has been migrated, but it is the same thumbnail
var thumbnail = await loader.LoadVrmThumbnailAsync();
アプリケーションは、この方法でロードしたモデルは、`VRM-0.X` ライセンスとして扱ってください。
if (vrm.OldMeta != null)
{
// migrated from vrm-0.x. use OldMeta
UpdateMeta(vrm.OldMeta, thumbnail);
}
else
{
// load vrm-1.0. use newMeta
UpdateMeta(vrm.VrmExtension.Meta, thumbnail);
}
## RuntimeLoad 例
`vrm-0.x``vrm-1.0` 両方をロードしてライセンスを取得する例。
// load model
RuntimeGltfInstance instance = await loader.LoadAsync();
return instance;
}
}
else{
throw new Exception("not vrm");
}
}
```

View File

@ -0,0 +1,36 @@
# v0.36
## テクスチャ名の格納位置の修正
GLTFの仕様に準拠しました。
* extraはextrasの間違い
* imageはnameを持っていた
```json
json.images[i].extra.name
```
変更後
```json
json.images[i].name
```
## ブレンドシェイプ名の格納位置の修正
GLTFの仕様に準拠しました。
* extraはextrasの間違い
* targetにextrasは不許可
* https://github.com/KhronosGroup/glTF/issues/1036#issuecomment-314078356
```json
json.meshes[i].primitives[j].targets[k].extra.name
```
変更後
```json
json.meshes[i].primitives[j].extras.targetNames[k]
```

View File

@ -58,14 +58,8 @@ GltfData Load(string path)
```cs
GltfData Load(string path)
{
var ext = Path.GetExtension(path).ToLower();
switch(ext)
{
case ".glb": return new GlbFileParser(path).Parse();
case ".gltf": return new GltfFileWithResourceFilesParser(path).Parse();
case ".zip": return new ZipArchivedGltfFileParser(path).Parse();
default: throw new Exception($"unknown: {ext}");
}
// ファイル拡張子で自動判定します
return new AutoGltfFileParser(path).Parse();
}
```

View File

@ -1,49 +1,10 @@
# Version
| version | |
|---------|-------------------------------------|
| v0.82.1 | Import時のMaterial差し替え(URP対応) |
| v0.82.0 | Import時のMaterial差し替え(URP対応) |
| v0.77 | ImporterContext の整理 |
| v0.68 | ImporterContext の整理 |
| v0.63.2 | gltf の extension の実装方法を変更 |
| v0.56 | BlendShapeKey の仕様変更 |
[BlendShapeKeyのインタフェースを厳格化、整理](https://github.com/vrm-c/UniVRM/wiki/ReleaseNote-v0.56.0%28ja%29#blendshapekey%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%82%92%E5%8E%B3%E6%A0%BC%E5%8C%96%E6%95%B4%E7%90%86)
## v0.36
### テクスチャ名の格納位置の修正
GLTFの仕様に準拠しました。
* extraはextrasの間違い
* imageはnameを持っていた
```json
json.images[i].extra.name
```
変更後
```json
json.images[i].name
```
### ブレンドシェイプ名の格納位置の修正
GLTFの仕様に準拠しました。
* extraはextrasの間違い
* targetにextrasは不許可
* https://github.com/KhronosGroup/glTF/issues/1036#issuecomment-314078356
```json
json.meshes[i].primitives[j].targets[k].extra.name
```
変更後
```json
json.meshes[i].primitives[j].extras.targetNames[k]
```
| version | |
|---------|-------------------------------------------------|
| v0.82.1 | `materialGenerator(URP対応)` 引き数と `VrmData` |
| v0.79 | `GltfData` |
| v0.77 | `RuntimeGltfInstance` |
| v0.68 | `GltfParser` |
| v0.63.2 | gltf の extension の実装方法を変更 |
| v0.56 | BlendShapeKey の仕様変更 |

View File

@ -12,6 +12,8 @@
href: gltf/0_82_glb_import.md
- name: glTF拡張の実装(0.63.2)
href: gltf/how_to_impl_extension.md
- name: GltfUpdate(0.36)
href: gltf/0_36_update.md
- name: VRM
items:
@ -37,7 +39,7 @@
- name: VRM-1.0(β)
items:
- name: 🚧Samples/VRM10Viewer
- name: 🚧RuntimeImport
- name: RuntimeImport
href: vrm1/vrm1_runtime_load.md
- name: 🚧Humanoid
href: vrm1/vrm1_get_humanoid.md

View File

@ -11,6 +11,8 @@ tags: ["api"]
## 環境
UniVRM v0.58.0
[BlendShapeKeyのインタフェースを厳格化、整理](https://github.com/vrm-c/UniVRM/wiki/ReleaseNote-v0.56.0%28ja%29#blendshapekey%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%82%92%E5%8E%B3%E6%A0%BC%E5%8C%96%E6%95%B4%E7%90%86)
## 使用するメソッド
* [推奨] `SetValues`

View File

@ -1,4 +1,4 @@
* `Version 0.82.0` `0.82.1` 以降を使ってください。
* `Version 0.82.0` `0.82.1` 以降を使ってください。
* `Version 0.82.1~`
以下の手順で import します。
@ -8,6 +8,8 @@
1. `VrmData` から `RuntimeGltfInstance` をロードする。
1. `RuntimeGltfInstance` を使う。
サンプルの `Assets\VRM\Samples\SimpleViewer\ViewerUI.cs` も参照してください。
# 1. `GltfData` を得る
```cs

View File

@ -1,19 +1,44 @@
---
title: 🚧Runtime ロード
weight: 10
---
# RuntimeLoad
VRM-1.0 を使うアプリケーションは、 `VRM-0.x` の資産もロードしたいはずです。
`VRM-1.0` は、 `VRM-0.x` もロードできます。
その場合、あたらしい meta への変換が発生し互換性の無い部分はすべて `不許可` の値になります。
このため、変換前のライセンスにアクセスする API を提供します。
`VRM-1.0``VRM-0.X` の meta の非互換に [Metaの自動的なマイグレーションは禁止する方針](https://github.com/vrm-c/vrm-specification/issues/181) となりました。
サンプルの `Assets\VRM10\Samples\VRM10Viewer\VRM10ViewerUI.cs` も参照してください
## VRM-0.X を VRM-1.0 コンポーネントに対してロードする
```cs
async Task<RuntimeGltfInstance> LoadAsync(string path)
{
GltfData data = new GltfZipOrGlbFileParser(path).Parse();
`UniVRM-1.0` では、
`VRM-0.X` のライセンスを保持したまま、`VRM-1.0` のコンポーネントにロードする機能を提供します(予定)。
// doMigrate: true で旧バージョンの vrm をロードできます。
if (Vrm10Data.TryParseOrMigrate(data, doMigrate: true, out Vrm10Data vrm))
{
// vrm
using (var loader = new Vrm10Importer(vrm,
materialGenerator: GetVrmMaterialDescriptorGenerator(m_useUrpMaterial.isOn)))
{
// migrate しても thumbnail は同じ
var thumbnail = await loader.LoadVrmThumbnailAsync();
アプリケーションは、この方法でロードしたモデルは、`VRM-0.X` ライセンスとして扱ってください。
if (vrm.OldMeta != null)
{
// migrated from vrm-0.x. use OldMeta
UpdateMeta(vrm.OldMeta, thumbnail);
}
else
{
// load vrm-1.0. use newMeta
UpdateMeta(vrm.VrmExtension.Meta, thumbnail);
}
## RuntimeLoad 例
`vrm-0.x``vrm-1.0` 両方をロードしてライセンスを取得する例。
// モデルをロード
RuntimeGltfInstance instance = await loader.LoadAsync();
return instance;
}
}
else{
throw new Exception("not vrm");
}
}
```