mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-07-22 02:00:59 -05:00
VRM10ViewerController.cs
This commit is contained in:
parent
9495ebfe53
commit
5ebaa1e3ec
344
Assets/VRM10_Samples/VRM10Viewer/UI/VRM10ViewerController.cs
Normal file
344
Assets/VRM10_Samples/VRM10Viewer/UI/VRM10ViewerController.cs
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UniGLTF;
|
||||
using UniGLTF.SpringBoneJobs.Blittables;
|
||||
using UnityEngine;
|
||||
using static UniVRM10.Vrm10;
|
||||
|
||||
|
||||
namespace UniVRM10.VRM10Viewer
|
||||
{
|
||||
class VRM10ViewerController : IDisposable
|
||||
{
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource?.Dispose();
|
||||
}
|
||||
public void Cancel()
|
||||
{
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private TinyMToonrMaterialImporter m_mtoonImporter;
|
||||
private TinyPbrMaterialImporter m_pbrImporter;
|
||||
|
||||
private Loaded m_loaded;
|
||||
Loaded Loaded
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_loaded;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (m_loaded != null)
|
||||
{
|
||||
m_loaded.Dispose();
|
||||
m_loaded = null;
|
||||
}
|
||||
m_loaded = value;
|
||||
m_onLoaded(m_loaded);
|
||||
}
|
||||
}
|
||||
|
||||
IVrm10Animation m_src = default;
|
||||
public IVrm10Animation TPose;
|
||||
public IVrm10Animation Motion
|
||||
{
|
||||
get { return m_src; }
|
||||
set
|
||||
{
|
||||
if (m_src != null)
|
||||
{
|
||||
m_src.Dispose();
|
||||
}
|
||||
m_src = value;
|
||||
|
||||
TPose = new Vrm10TPose(m_src.ControlRig.Item1.GetRawHipsPosition());
|
||||
}
|
||||
}
|
||||
Action<Loaded> m_onLoaded;
|
||||
VrmMetaInformationCallback m_updateMeta;
|
||||
public VRM10ViewerController(
|
||||
TinyMToonrMaterialImporter tinyMToon,
|
||||
TinyPbrMaterialImporter tinyPbr,
|
||||
VrmMetaInformationCallback updateMeta,
|
||||
Action<Loaded> onLoaded
|
||||
)
|
||||
{
|
||||
m_mtoonImporter = tinyMToon;
|
||||
m_pbrImporter = tinyPbr;
|
||||
m_onLoaded = onLoaded;
|
||||
m_updateMeta = updateMeta;
|
||||
}
|
||||
|
||||
public void ShowBoxMan(bool show)
|
||||
{
|
||||
if (Motion != null)
|
||||
{
|
||||
Motion.ShowBoxMan(show);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern void WebGL_VRM10_VRM10Viewer_FileDialog(string target, string message);
|
||||
|
||||
string FileDialog()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return UnityEditor.EditorUtility.OpenFilePanel("Open VRM", "", "vrm,glb,gltf,zip");
|
||||
#elif UNITY_STANDALONE_WIN
|
||||
return VRM10FileDialogForWindows.FileDialog("open VRM", "vrm", "glb", "gltf", "zip");
|
||||
#elif UNITY_WEBGL
|
||||
// Open WebGL_VRM10_VRM10Viewer_FileDialog
|
||||
// see: Assets/UniGLTF/Runtime/Utils/Plugins/OpenFile.jslib
|
||||
WebGL_VRM10_VRM10Viewer_FileDialog("Canvas", "FileSelected");
|
||||
// Control flow does not return here. return empty string with dummy
|
||||
return null;
|
||||
#else
|
||||
return Application.dataPath + "/default.vrm";
|
||||
#endif
|
||||
}
|
||||
|
||||
public IEnumerator LoadCoroutine(string url, LoadOptions opts)
|
||||
{
|
||||
var www = new WWW(url);
|
||||
yield return www;
|
||||
var _ = LoadModelBytes("WebGL.vrm", www.bytes, opts);
|
||||
}
|
||||
|
||||
public void OnOpenModelClicked(LoadOptions opts)
|
||||
{
|
||||
var path = FileDialog();
|
||||
_ = LoadModelPath(path, opts);
|
||||
}
|
||||
|
||||
public async void OnOpenMotionClicked()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var path = UnityEditor.EditorUtility.OpenFilePanel("Open Motion", "", "bvh,gltf,glb,vrma");
|
||||
#elif UNITY_STANDALONE_WIN
|
||||
var path = VRM10FileDialogForWindows.FileDialog("open Motion", "bvh", "gltf", "glb", "vrma");
|
||||
#else
|
||||
var path = Application.dataPath + "/default.bvh";
|
||||
#endif
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(path).ToLower();
|
||||
if (ext == ".bvh")
|
||||
{
|
||||
Motion = BvhMotion.LoadBvhFromPath(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// gltf, glb etc...
|
||||
using GltfData data = new AutoGltfFileParser(path).Parse();
|
||||
using var loader = new VrmAnimationImporter(data);
|
||||
var instance = await loader.LoadAsync(new ImmediateCaller());
|
||||
Motion = instance.GetComponent<Vrm10AnimationInstance>();
|
||||
instance.GetComponent<Animation>().Play();
|
||||
}
|
||||
|
||||
public async void OnPastePoseClicked()
|
||||
{
|
||||
var text = GUIUtility.systemCopyBuffer;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Motion = await Vrm10PoseLoader.LoadVrmAnimationPose(text);
|
||||
}
|
||||
catch (UniJSON.ParserException)
|
||||
{
|
||||
UniGLTFLogger.Warning("UniJSON.ParserException");
|
||||
}
|
||||
catch (UniJSON.DeserializationException)
|
||||
{
|
||||
UniGLTFLogger.Warning("UniJSON.DeserializationException");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnResetSpringBoneClicked()
|
||||
{
|
||||
if (Loaded != null)
|
||||
{
|
||||
if (Loaded.Runtime != null)
|
||||
{
|
||||
Loaded.Runtime.SpringBone.RestoreInitialTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReconstructSpringBoneClicked()
|
||||
{
|
||||
if (Loaded != null)
|
||||
{
|
||||
if (Loaded.Runtime != null)
|
||||
{
|
||||
Loaded.Runtime.SpringBone.ReconstructSpringBone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LoadModelPath(string path, LoadOptions opts)
|
||||
{
|
||||
var bytes = await File.ReadAllBytesAsync(path);
|
||||
await LoadModelBytes(path, bytes, opts);
|
||||
}
|
||||
|
||||
IMaterialDescriptorGenerator GetMaterialDescriptorGenerator(LoadOptions opts)
|
||||
{
|
||||
var useCustomPbr = opts.UseCustomPbrMaterial && m_pbrImporter != null;
|
||||
var useCustomMToon = opts.UseCustomMToonMaterial && m_mtoonImporter != null;
|
||||
if (!useCustomPbr && !useCustomMToon)
|
||||
{
|
||||
// カスタムしない。デフォルトのローダーを使う
|
||||
return default;
|
||||
}
|
||||
|
||||
return OrderedMaterialDescriptorGenerator.CreateCustomGenerator(
|
||||
useCustomPbr ? m_pbrImporter : null,
|
||||
useCustomMToon ? m_mtoonImporter : null);
|
||||
}
|
||||
|
||||
IAwaitCaller GetIAwaitCaller(bool useAsync)
|
||||
{
|
||||
if (useAsync)
|
||||
{
|
||||
if (Application.platform == RuntimePlatform.WebGLPlayer)
|
||||
{
|
||||
return new RuntimeOnlyNoThreadAwaitCaller();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new RuntimeOnlyAwaitCaller();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ImmediateCaller();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task LoadModelBytes(string path, byte[] bytes, LoadOptions opts)
|
||||
{
|
||||
// cleanup
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
var cancellationToken = _cancellationTokenSource.Token;
|
||||
|
||||
try
|
||||
{
|
||||
//
|
||||
// try VRM
|
||||
//
|
||||
UniGLTFLogger.Log($"{path}");
|
||||
var vrm10Instance = await Vrm10.LoadBytesAsync(bytes,
|
||||
canLoadVrm0X: true,
|
||||
showMeshes: false,
|
||||
awaitCaller: GetIAwaitCaller(opts.UseAsync),
|
||||
materialGenerator: GetMaterialDescriptorGenerator(opts),
|
||||
vrmMetaInformationCallback: m_updateMeta,
|
||||
ct: cancellationToken,
|
||||
springboneRuntime: opts.UseSpringboneSingelton ? new Vrm10FastSpringboneRuntime() : new Vrm10FastSpringboneRuntimeStandalone());
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
UnityObjectDestroyer.DestroyRuntimeOrEditor(vrm10Instance.gameObject);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
if (vrm10Instance == null)
|
||||
{
|
||||
UniGLTFLogger.Warning("LoadPathAsync is null");
|
||||
return;
|
||||
}
|
||||
|
||||
var instance = vrm10Instance.GetComponent<RuntimeGltfInstance>();
|
||||
instance.ShowMeshes();
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
Loaded = new Loaded(instance);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
//
|
||||
// fallback gltf
|
||||
//
|
||||
var instance = await GltfUtility.LoadBytesAsync(path, bytes,
|
||||
awaitCaller: GetIAwaitCaller(opts.UseAsync),
|
||||
materialGenerator: GetMaterialDescriptorGenerator(opts)
|
||||
);
|
||||
instance.ShowMeshes();
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
Loaded = new Loaded(instance);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is OperationCanceledException)
|
||||
{
|
||||
UniGLTFLogger.Warning($"Canceled to Load: {path}");
|
||||
}
|
||||
else
|
||||
{
|
||||
UniGLTFLogger.Error($"Failed to Load: {path}");
|
||||
UniGLTFLogger.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryUpdate(
|
||||
bool useTPose,
|
||||
BlittableModelLevel blittableModelLevel,
|
||||
out Loaded loaded)
|
||||
{
|
||||
loaded = Loaded;
|
||||
if (loaded == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (loaded.Runtime == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (useTPose || Motion == null)
|
||||
{
|
||||
loaded.Runtime.VrmAnimation = TPose;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Automatically retarget in Vrm10Runtime.Process
|
||||
loaded.Runtime.VrmAnimation = Motion;
|
||||
}
|
||||
|
||||
loaded.Runtime.SpringBone.SetModelLevel(Loaded.Instance.transform, blittableModelLevel);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class LoadOptions
|
||||
{
|
||||
public bool UseAsync;
|
||||
public bool UseSpringboneSingelton;
|
||||
public bool UseCustomPbrMaterial;
|
||||
public bool UseCustomMToonMaterial;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37ba766f2a14fb54690b1d62e2ccf450
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -3,15 +3,13 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UniGLTF;
|
||||
using UniGLTF.SpringBoneJobs.Blittables;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.UI;
|
||||
|
||||
|
||||
namespace UniVRM10.VRM10Viewer
|
||||
{
|
||||
public class VRM10ViewerUI : MonoBehaviour
|
||||
|
|
@ -182,26 +180,6 @@ namespace UniVRM10.VRM10Viewer
|
|||
[SerializeField]
|
||||
Slider m_pitch = default;
|
||||
|
||||
IVrm10Animation m_src = default;
|
||||
public IVrm10Animation Motion
|
||||
{
|
||||
get { return m_src; }
|
||||
set
|
||||
{
|
||||
if (m_src != null)
|
||||
{
|
||||
m_src.Dispose();
|
||||
}
|
||||
m_src = value;
|
||||
|
||||
TPose = new Vrm10TPose(m_src.ControlRig.Item1.GetRawHipsPosition());
|
||||
}
|
||||
}
|
||||
|
||||
public IVrm10Animation TPose;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
[Serializable]
|
||||
class TextFields
|
||||
{
|
||||
|
|
@ -428,8 +406,6 @@ namespace UniVRM10.VRM10Viewer
|
|||
#endif
|
||||
}
|
||||
|
||||
Loaded m_loaded;
|
||||
|
||||
static class ArgumentChecker
|
||||
{
|
||||
static string[] Supported = {
|
||||
|
|
@ -485,23 +461,45 @@ namespace UniVRM10.VRM10Viewer
|
|||
}
|
||||
}
|
||||
|
||||
VRM10ViewerController m_controller;
|
||||
VRM10AutoExpression m_autoEmotion;
|
||||
VRM10Blinker m_autoBlink;
|
||||
VRM10AIUEO m_autoLipsync;
|
||||
|
||||
private TinyMToonrMaterialImporter m_mtoonImporter;
|
||||
private TinyPbrMaterialImporter m_pbrImporter;
|
||||
void OnLoaded(Loaded loaded)
|
||||
{
|
||||
m_showBoxMan.isOn = false;
|
||||
m_happy.OnLoad(loaded.Instance.Vrm.Expression.Happy);
|
||||
m_angry.OnLoad(loaded.Instance.Vrm.Expression.Angry);
|
||||
m_sad.OnLoad(loaded.Instance.Vrm.Expression.Sad);
|
||||
m_relaxed.OnLoad(loaded.Instance.Vrm.Expression.Relaxed);
|
||||
m_surprised.OnLoad(loaded.Instance.Vrm.Expression.Surprised);
|
||||
m_lipAa.OnLoad(loaded.Instance.Vrm.Expression.Aa);
|
||||
m_lipIh.OnLoad(loaded.Instance.Vrm.Expression.Ih);
|
||||
m_lipOu.OnLoad(loaded.Instance.Vrm.Expression.Ou);
|
||||
m_lipEe.OnLoad(loaded.Instance.Vrm.Expression.Ee);
|
||||
m_lipOh.OnLoad(loaded.Instance.Vrm.Expression.Oh);
|
||||
m_blink.OnLoad(loaded.Instance.Vrm.Expression.Blink);
|
||||
}
|
||||
|
||||
LoadOptions MakeLoadOptions()
|
||||
{
|
||||
return new LoadOptions()
|
||||
{
|
||||
UseAsync = m_useAsync.isOn,
|
||||
UseSpringboneSingelton = m_useSpringboneSingelton.isOn,
|
||||
UseCustomPbrMaterial = m_useCustomPbrMaterial.isOn,
|
||||
UseCustomMToonMaterial = m_useCustomMToonMaterial.isOn,
|
||||
};
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (m_mtoonMaterialOpaque != null && m_mtoonMaterialAlphaBlend != null)
|
||||
{
|
||||
m_mtoonImporter = new(m_mtoonMaterialOpaque, m_mtoonMaterialAlphaBlend);
|
||||
}
|
||||
if (m_pbrOpaqueMaterial != null && m_pbrAlphaBlendMaterial != null)
|
||||
{
|
||||
m_pbrImporter = new(m_pbrOpaqueMaterial, m_pbrAlphaBlendMaterial);
|
||||
}
|
||||
m_controller = new(
|
||||
(m_mtoonMaterialOpaque != null && m_mtoonMaterialAlphaBlend != null) ? new TinyMToonrMaterialImporter(m_mtoonMaterialOpaque, m_mtoonMaterialAlphaBlend) : null,
|
||||
(m_pbrOpaqueMaterial != null && m_pbrAlphaBlendMaterial != null) ? new TinyPbrMaterialImporter(m_pbrOpaqueMaterial, m_pbrAlphaBlendMaterial) : null,
|
||||
m_texts.UpdateMeta,
|
||||
OnLoaded);
|
||||
|
||||
// URP かつ WebGL で有効にする
|
||||
m_useCustomMToonMaterial.isOn = Application.platform == RuntimePlatform.WebGLPlayer && GraphicsSettings.renderPipelineAsset != null;
|
||||
|
|
@ -512,26 +510,26 @@ namespace UniVRM10.VRM10Viewer
|
|||
|
||||
m_version.text = string.Format("VRM10ViewerUI {0}", PackageVersion.VERSION);
|
||||
|
||||
m_openModel.onClick.AddListener(OnOpenModelClicked);
|
||||
m_openMotion.onClick.AddListener(OnOpenMotionClicked);
|
||||
m_pastePose.onClick.AddListener(OnPastePoseClicked);
|
||||
m_resetSpringBone.onClick.AddListener(OnResetSpringBoneClicked);
|
||||
m_reconstructSpringBone.onClick.AddListener(OnReconstructSpringBoneClicked);
|
||||
m_openModel.onClick.AddListener(() => m_controller.OnOpenModelClicked(MakeLoadOptions()));
|
||||
m_openMotion.onClick.AddListener(m_controller.OnOpenMotionClicked);
|
||||
m_pastePose.onClick.AddListener(m_controller.OnPastePoseClicked);
|
||||
m_resetSpringBone.onClick.AddListener(m_controller.OnResetSpringBoneClicked);
|
||||
m_reconstructSpringBone.onClick.AddListener(m_controller.OnReconstructSpringBoneClicked);
|
||||
|
||||
// load initial bvh
|
||||
if (m_motion != null)
|
||||
{
|
||||
Motion = BvhMotion.LoadBvhFromText(m_motion.text);
|
||||
m_controller.Motion = BvhMotion.LoadBvhFromText(m_motion.text);
|
||||
if (GraphicsSettings.renderPipelineAsset != null
|
||||
&& m_pbrAlphaBlendMaterial != null)
|
||||
{
|
||||
Motion.SetBoxManMaterial(Instantiate(m_pbrOpaqueMaterial));
|
||||
m_controller.Motion.SetBoxManMaterial(Instantiate(m_pbrOpaqueMaterial));
|
||||
}
|
||||
}
|
||||
|
||||
if (ArgumentChecker.TryGetFirstLoadable(out var cmd))
|
||||
{
|
||||
var _ = LoadModelPath(cmd);
|
||||
var _ = m_controller.LoadModelPath(cmd, MakeLoadOptions());
|
||||
}
|
||||
|
||||
m_texts.Start();
|
||||
|
|
@ -539,7 +537,7 @@ namespace UniVRM10.VRM10Viewer
|
|||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_cancellationTokenSource?.Dispose();
|
||||
m_controller.Dispose();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
|
|
@ -551,158 +549,112 @@ namespace UniVRM10.VRM10Viewer
|
|||
|
||||
if (Input.GetKeyDown(KeyCode.Escape))
|
||||
{
|
||||
if (_cancellationTokenSource != null)
|
||||
m_controller.Cancel();
|
||||
}
|
||||
|
||||
m_controller.ShowBoxMan(m_showBoxMan.isOn);
|
||||
if (m_controller.TryUpdate(
|
||||
m_ui.IsTPose,
|
||||
new BlittableModelLevel
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
ExternalForce = new Vector3(m_springboneExternalX.value, m_springboneExternalY.value, m_springboneExternalZ.value),
|
||||
StopSpringBoneWriteback = m_springbonePause.isOn,
|
||||
SupportsScalingAtRuntime = m_springboneScaling.isOn,
|
||||
},
|
||||
out var loaded
|
||||
))
|
||||
{
|
||||
m_happy.ApplyRuntime(loaded.Instance.Vrm.Expression.Happy);
|
||||
m_angry.ApplyRuntime(loaded.Instance.Vrm.Expression.Angry);
|
||||
m_sad.ApplyRuntime(loaded.Instance.Vrm.Expression.Sad);
|
||||
m_relaxed.ApplyRuntime(loaded.Instance.Vrm.Expression.Relaxed);
|
||||
m_surprised.ApplyRuntime(loaded.Instance.Vrm.Expression.Surprised);
|
||||
m_lipAa.ApplyRuntime(loaded.Instance.Vrm.Expression.Aa);
|
||||
m_lipIh.ApplyRuntime(loaded.Instance.Vrm.Expression.Ih);
|
||||
m_lipOu.ApplyRuntime(loaded.Instance.Vrm.Expression.Ou);
|
||||
m_lipEe.ApplyRuntime(loaded.Instance.Vrm.Expression.Ee);
|
||||
m_lipOh.ApplyRuntime(loaded.Instance.Vrm.Expression.Oh);
|
||||
m_blink.ApplyRuntime(loaded.Instance.Vrm.Expression.Blink);
|
||||
|
||||
var vrm = loaded.Instance;
|
||||
if (m_enableAutoExpression.isOn)
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Happy, m_autoEmotion.Happy);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Angry, m_autoEmotion.Angry);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Sad, m_autoEmotion.Sad);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Relaxed, m_autoEmotion.Relaxed);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Surprised, m_autoEmotion.Surprised);
|
||||
m_happy.m_expression.SetValueWithoutNotify(m_autoEmotion.Happy);
|
||||
m_angry.m_expression.SetValueWithoutNotify(m_autoEmotion.Angry);
|
||||
m_sad.m_expression.SetValueWithoutNotify(m_autoEmotion.Sad);
|
||||
m_relaxed.m_expression.SetValueWithoutNotify(m_autoEmotion.Relaxed);
|
||||
m_surprised.m_expression.SetValueWithoutNotify(m_autoEmotion.Surprised);
|
||||
}
|
||||
else
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Happy, m_happy.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Angry, m_angry.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Sad, m_sad.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Relaxed, m_relaxed.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Surprised, m_surprised.m_expression.value);
|
||||
}
|
||||
|
||||
if (m_enableLipSync.isOn)
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Aa, m_autoLipsync.Aa);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ih, m_autoLipsync.Ih);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ou, m_autoLipsync.Ou);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ee, m_autoLipsync.Ee);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Oh, m_autoLipsync.Oh);
|
||||
m_lipAa.m_expression.SetValueWithoutNotify(m_autoLipsync.Aa);
|
||||
m_lipIh.m_expression.SetValueWithoutNotify(m_autoLipsync.Ih);
|
||||
m_lipOu.m_expression.SetValueWithoutNotify(m_autoLipsync.Ou);
|
||||
m_lipEe.m_expression.SetValueWithoutNotify(m_autoLipsync.Ee);
|
||||
m_lipOh.m_expression.SetValueWithoutNotify(m_autoLipsync.Oh);
|
||||
}
|
||||
else
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Aa, m_lipAa.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ih, m_lipIh.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ou, m_lipOu.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ee, m_lipEe.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Oh, m_lipOh.m_expression.value);
|
||||
}
|
||||
|
||||
if (m_enableAutoBlink.isOn)
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Blink, m_autoBlink.BlinkValue);
|
||||
m_blink.m_expression.SetValueWithoutNotify(m_autoBlink.BlinkValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Blink, m_blink.m_expression.value);
|
||||
}
|
||||
|
||||
if (m_useLookAtTarget.isOn)
|
||||
{
|
||||
var (yaw, pitch) = vrm.Runtime.LookAt.CalculateYawPitchFromLookAtPosition(m_lookAtTarget.transform.position);
|
||||
vrm.Runtime.LookAt.SetYawPitchManually(yaw, pitch);
|
||||
m_yaw.value = yaw;
|
||||
m_pitch.value = pitch;
|
||||
}
|
||||
else
|
||||
{
|
||||
vrm.Runtime.LookAt.SetYawPitchManually(m_yaw.value, m_pitch.value);
|
||||
}
|
||||
|
||||
if (vrm.TryGetBoneTransform(HumanBodyBones.Head, out var head))
|
||||
{
|
||||
var initLocarlRotation = vrm.DefaultTransformStates[head].LocalRotation;
|
||||
var r = head.rotation * Quaternion.Inverse(initLocarlRotation);
|
||||
var pos = head.position
|
||||
+ (r * Vector3.forward * 0.7f)
|
||||
+ (r * Vector3.up * 0.07f)
|
||||
;
|
||||
m_faceCamera.position = pos;
|
||||
m_faceCamera.rotation = r;
|
||||
}
|
||||
}
|
||||
|
||||
if (Motion != null)
|
||||
{
|
||||
Motion.ShowBoxMan(m_showBoxMan.isOn);
|
||||
}
|
||||
|
||||
if (m_loaded != null)
|
||||
{
|
||||
var vrm = m_loaded.Instance;
|
||||
|
||||
if (m_loaded.Runtime != null)
|
||||
{
|
||||
if (m_ui.IsTPose)
|
||||
{
|
||||
m_loaded.Runtime.VrmAnimation = TPose;
|
||||
}
|
||||
else if (Motion != null)
|
||||
{
|
||||
// Automatically retarget in Vrm10Runtime.Process
|
||||
m_loaded.Runtime.VrmAnimation = Motion;
|
||||
}
|
||||
|
||||
m_loaded.Runtime.SpringBone.SetModelLevel(vrm.transform, new BlittableModelLevel
|
||||
{
|
||||
ExternalForce = new Vector3(m_springboneExternalX.value, m_springboneExternalY.value, m_springboneExternalZ.value),
|
||||
StopSpringBoneWriteback = m_springbonePause.isOn,
|
||||
SupportsScalingAtRuntime = m_springboneScaling.isOn,
|
||||
});
|
||||
|
||||
|
||||
m_happy.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Happy);
|
||||
m_angry.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Angry);
|
||||
m_sad.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Sad);
|
||||
m_relaxed.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Relaxed);
|
||||
m_surprised.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Surprised);
|
||||
m_lipAa.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Aa);
|
||||
m_lipIh.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Ih);
|
||||
m_lipOu.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Ou);
|
||||
m_lipEe.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Ee);
|
||||
m_lipOh.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Oh);
|
||||
m_blink.ApplyRuntime(m_loaded.Instance.Vrm.Expression.Blink);
|
||||
|
||||
if (m_enableAutoExpression.isOn)
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Happy, m_autoEmotion.Happy);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Angry, m_autoEmotion.Angry);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Sad, m_autoEmotion.Sad);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Relaxed, m_autoEmotion.Relaxed);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Surprised, m_autoEmotion.Surprised);
|
||||
m_happy.m_expression.SetValueWithoutNotify(m_autoEmotion.Happy);
|
||||
m_angry.m_expression.SetValueWithoutNotify(m_autoEmotion.Angry);
|
||||
m_sad.m_expression.SetValueWithoutNotify(m_autoEmotion.Sad);
|
||||
m_relaxed.m_expression.SetValueWithoutNotify(m_autoEmotion.Relaxed);
|
||||
m_surprised.m_expression.SetValueWithoutNotify(m_autoEmotion.Surprised);
|
||||
}
|
||||
else
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Happy, m_happy.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Angry, m_angry.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Sad, m_sad.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Relaxed, m_relaxed.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Surprised, m_surprised.m_expression.value);
|
||||
}
|
||||
|
||||
if (m_enableLipSync.isOn)
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Aa, m_autoLipsync.Aa);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ih, m_autoLipsync.Ih);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ou, m_autoLipsync.Ou);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ee, m_autoLipsync.Ee);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Oh, m_autoLipsync.Oh);
|
||||
m_lipAa.m_expression.SetValueWithoutNotify(m_autoLipsync.Aa);
|
||||
m_lipIh.m_expression.SetValueWithoutNotify(m_autoLipsync.Ih);
|
||||
m_lipOu.m_expression.SetValueWithoutNotify(m_autoLipsync.Ou);
|
||||
m_lipEe.m_expression.SetValueWithoutNotify(m_autoLipsync.Ee);
|
||||
m_lipOh.m_expression.SetValueWithoutNotify(m_autoLipsync.Oh);
|
||||
}
|
||||
else
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Aa, m_lipAa.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ih, m_lipIh.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ou, m_lipOu.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Ee, m_lipEe.m_expression.value);
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Oh, m_lipOh.m_expression.value);
|
||||
}
|
||||
|
||||
if (m_enableAutoBlink.isOn)
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Blink, m_autoBlink.BlinkValue);
|
||||
m_blink.m_expression.SetValueWithoutNotify(m_autoBlink.BlinkValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
vrm.Runtime.Expression.SetWeight(ExpressionKey.Blink, m_blink.m_expression.value);
|
||||
}
|
||||
|
||||
if (m_useLookAtTarget.isOn)
|
||||
{
|
||||
var (yaw, pitch) = vrm.Runtime.LookAt.CalculateYawPitchFromLookAtPosition(m_lookAtTarget.transform.position);
|
||||
vrm.Runtime.LookAt.SetYawPitchManually(yaw, pitch);
|
||||
m_yaw.value = yaw;
|
||||
m_pitch.value = pitch;
|
||||
}
|
||||
else
|
||||
{
|
||||
vrm.Runtime.LookAt.SetYawPitchManually(m_yaw.value, m_pitch.value);
|
||||
}
|
||||
|
||||
if (vrm.TryGetBoneTransform(HumanBodyBones.Head, out var head))
|
||||
{
|
||||
var initLocarlRotation = vrm.DefaultTransformStates[head].LocalRotation;
|
||||
var r = head.rotation * Quaternion.Inverse(initLocarlRotation);
|
||||
var pos = head.position
|
||||
+ (r * Vector3.forward * 0.7f)
|
||||
+ (r * Vector3.up * 0.07f)
|
||||
;
|
||||
m_faceCamera.position = pos;
|
||||
m_faceCamera.rotation = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern void WebGL_VRM10_VRM10Viewer_FileDialog(string target, string message);
|
||||
|
||||
string FileDialog()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return UnityEditor.EditorUtility.OpenFilePanel("Open VRM", "", "vrm,glb,gltf,zip");
|
||||
#elif UNITY_STANDALONE_WIN
|
||||
return VRM10FileDialogForWindows.FileDialog("open VRM", "vrm", "glb", "gltf", "zip");
|
||||
#elif UNITY_WEBGL
|
||||
// Open WebGL_VRM10_VRM10Viewer_FileDialog
|
||||
// see: Assets/UniGLTF/Runtime/Utils/Plugins/OpenFile.jslib
|
||||
WebGL_VRM10_VRM10Viewer_FileDialog("Canvas", "FileSelected");
|
||||
// Control flow does not return here. return empty string with dummy
|
||||
return null;
|
||||
#else
|
||||
return Application.dataPath + "/default.vrm";
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnOpenModelClicked()
|
||||
{
|
||||
var path = FileDialog();
|
||||
_ = LoadModelPath(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -712,212 +664,7 @@ namespace UniVRM10.VRM10Viewer
|
|||
public void FileSelected(string url)
|
||||
{
|
||||
UniGLTFLogger.Log($"FileSelected: {url}");
|
||||
StartCoroutine(LoadCoroutine(url));
|
||||
}
|
||||
|
||||
IEnumerator LoadCoroutine(string url)
|
||||
{
|
||||
var www = new WWW(url);
|
||||
yield return www;
|
||||
var _ = LoadModelBytes("WebGL.vrm", www.bytes);
|
||||
}
|
||||
|
||||
async void OnOpenMotionClicked()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var path = UnityEditor.EditorUtility.OpenFilePanel("Open Motion", "", "bvh,gltf,glb,vrma");
|
||||
#elif UNITY_STANDALONE_WIN
|
||||
var path = VRM10FileDialogForWindows.FileDialog("open Motion", "bvh", "gltf", "glb", "vrma");
|
||||
#else
|
||||
var path = Application.dataPath + "/default.bvh";
|
||||
#endif
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(path).ToLower();
|
||||
if (ext == ".bvh")
|
||||
{
|
||||
Motion = BvhMotion.LoadBvhFromPath(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// gltf, glb etc...
|
||||
using GltfData data = new AutoGltfFileParser(path).Parse();
|
||||
using var loader = new VrmAnimationImporter(data);
|
||||
var instance = await loader.LoadAsync(new ImmediateCaller());
|
||||
Motion = instance.GetComponent<Vrm10AnimationInstance>();
|
||||
instance.GetComponent<Animation>().Play();
|
||||
}
|
||||
|
||||
async void OnPastePoseClicked()
|
||||
{
|
||||
var text = GUIUtility.systemCopyBuffer;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Motion = await Vrm10PoseLoader.LoadVrmAnimationPose(text);
|
||||
}
|
||||
catch (UniJSON.ParserException)
|
||||
{
|
||||
UniGLTFLogger.Warning("UniJSON.ParserException");
|
||||
}
|
||||
catch (UniJSON.DeserializationException)
|
||||
{
|
||||
UniGLTFLogger.Warning("UniJSON.DeserializationException");
|
||||
}
|
||||
}
|
||||
|
||||
void OnResetSpringBoneClicked()
|
||||
{
|
||||
if (m_loaded != null)
|
||||
{
|
||||
if (m_loaded.Runtime != null)
|
||||
{
|
||||
m_loaded.Runtime.SpringBone.RestoreInitialTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnReconstructSpringBoneClicked()
|
||||
{
|
||||
if (m_loaded != null)
|
||||
{
|
||||
if (m_loaded.Runtime != null)
|
||||
{
|
||||
m_loaded.Runtime.SpringBone.ReconstructSpringBone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IMaterialDescriptorGenerator GetMaterialDescriptorGenerator()
|
||||
{
|
||||
var useCustomPbr = m_useCustomPbrMaterial.isOn && m_pbrImporter != null;
|
||||
var useCustomMToon = m_useCustomMToonMaterial.isOn && m_mtoonImporter != null;
|
||||
if (!useCustomPbr && !useCustomMToon)
|
||||
{
|
||||
// カスタムしない。デフォルトのローダーを使う
|
||||
return default;
|
||||
}
|
||||
|
||||
return OrderedMaterialDescriptorGenerator.CreateCustomGenerator(
|
||||
useCustomPbr ? m_pbrImporter : null,
|
||||
useCustomMToon ? m_mtoonImporter : null);
|
||||
}
|
||||
|
||||
IAwaitCaller GetIAwaitCaller()
|
||||
{
|
||||
if (m_useAsync.isOn)
|
||||
{
|
||||
if (Application.platform == RuntimePlatform.WebGLPlayer)
|
||||
{
|
||||
return new RuntimeOnlyNoThreadAwaitCaller();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new RuntimeOnlyAwaitCaller();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ImmediateCaller();
|
||||
}
|
||||
}
|
||||
|
||||
async Task LoadModelPath(string path)
|
||||
{
|
||||
var bytes = await File.ReadAllBytesAsync(path);
|
||||
await LoadModelBytes(path, bytes);
|
||||
}
|
||||
|
||||
async Task LoadModelBytes(string path, byte[] bytes)
|
||||
{
|
||||
// cleanup
|
||||
m_loaded?.Dispose();
|
||||
m_loaded = null;
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
var cancellationToken = _cancellationTokenSource.Token;
|
||||
|
||||
try
|
||||
{
|
||||
//
|
||||
// try VRM
|
||||
//
|
||||
UniGLTFLogger.Log($"{path}");
|
||||
var vrm10Instance = await Vrm10.LoadBytesAsync(bytes,
|
||||
canLoadVrm0X: true,
|
||||
showMeshes: false,
|
||||
awaitCaller: GetIAwaitCaller(),
|
||||
materialGenerator: GetMaterialDescriptorGenerator(),
|
||||
vrmMetaInformationCallback: m_texts.UpdateMeta,
|
||||
ct: cancellationToken,
|
||||
springboneRuntime: m_useSpringboneSingelton.isOn ? new Vrm10FastSpringboneRuntime() : new Vrm10FastSpringboneRuntimeStandalone());
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
UnityObjectDestroyer.DestroyRuntimeOrEditor(vrm10Instance.gameObject);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
if (vrm10Instance == null)
|
||||
{
|
||||
UniGLTFLogger.Warning("LoadPathAsync is null");
|
||||
return;
|
||||
}
|
||||
|
||||
var instance = vrm10Instance.GetComponent<RuntimeGltfInstance>();
|
||||
instance.ShowMeshes();
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
m_loaded = new Loaded(instance);
|
||||
m_showBoxMan.isOn = false;
|
||||
|
||||
m_happy.OnLoad(m_loaded.Instance.Vrm.Expression.Happy);
|
||||
m_angry.OnLoad(m_loaded.Instance.Vrm.Expression.Angry);
|
||||
m_sad.OnLoad(m_loaded.Instance.Vrm.Expression.Sad);
|
||||
m_relaxed.OnLoad(m_loaded.Instance.Vrm.Expression.Relaxed);
|
||||
m_surprised.OnLoad(m_loaded.Instance.Vrm.Expression.Surprised);
|
||||
m_lipAa.OnLoad(m_loaded.Instance.Vrm.Expression.Aa);
|
||||
m_lipIh.OnLoad(m_loaded.Instance.Vrm.Expression.Ih);
|
||||
m_lipOu.OnLoad(m_loaded.Instance.Vrm.Expression.Ou);
|
||||
m_lipEe.OnLoad(m_loaded.Instance.Vrm.Expression.Ee);
|
||||
m_lipOh.OnLoad(m_loaded.Instance.Vrm.Expression.Oh);
|
||||
m_blink.OnLoad(m_loaded.Instance.Vrm.Expression.Blink);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
//
|
||||
// fallback gltf
|
||||
//
|
||||
var instance = await GltfUtility.LoadBytesAsync(path, bytes,
|
||||
awaitCaller: GetIAwaitCaller(),
|
||||
materialGenerator: GetMaterialDescriptorGenerator()
|
||||
);
|
||||
|
||||
instance.ShowMeshes();
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
m_loaded = new Loaded(instance);
|
||||
m_showBoxMan.isOn = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is OperationCanceledException)
|
||||
{
|
||||
UniGLTFLogger.Warning($"Canceled to Load: {path}");
|
||||
}
|
||||
else
|
||||
{
|
||||
UniGLTFLogger.Error($"Failed to Load: {path}");
|
||||
UniGLTFLogger.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
StartCoroutine(m_controller.LoadCoroutine(url, MakeLoadOptions()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user