mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-14 06:19:47 -05:00
369 lines
12 KiB
C#
369 lines
12 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using UniGLTF;
|
|
using UniHumanoid;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using VRMShaders;
|
|
|
|
namespace UniVRM10.VRM10Viewer
|
|
{
|
|
public class VRM10ViewerUI : MonoBehaviour
|
|
{
|
|
[Header("UI")]
|
|
[SerializeField]
|
|
Text m_version = default;
|
|
|
|
[SerializeField]
|
|
Button m_open = default;
|
|
|
|
[SerializeField]
|
|
Toggle m_enableLipSync = default;
|
|
|
|
[SerializeField]
|
|
Toggle m_enableAutoBlink = default;
|
|
|
|
[SerializeField]
|
|
Toggle m_enableAutoExpression = default;
|
|
|
|
[SerializeField]
|
|
Toggle m_useUrpMaterial = default;
|
|
|
|
[SerializeField]
|
|
Toggle m_useAsync = default;
|
|
|
|
[Header("Runtime")]
|
|
[SerializeField]
|
|
Animator m_src = default;
|
|
|
|
[SerializeField]
|
|
GameObject m_target = default;
|
|
|
|
[SerializeField]
|
|
GameObject Root = default;
|
|
|
|
[SerializeField]
|
|
TextAsset m_motion;
|
|
|
|
private CancellationTokenSource _cancellationTokenSource;
|
|
|
|
[Serializable]
|
|
class TextFields
|
|
{
|
|
[SerializeField, Header("Info")]
|
|
Text m_textModelTitle = default;
|
|
[SerializeField]
|
|
Text m_textModelVersion = default;
|
|
[SerializeField]
|
|
Text m_textModelAuthor = default;
|
|
[SerializeField]
|
|
Text m_textModelContact = default;
|
|
[SerializeField]
|
|
Text m_textModelReference = default;
|
|
[SerializeField]
|
|
RawImage m_thumbnail = default;
|
|
|
|
[SerializeField, Header("CharacterPermission")]
|
|
Text m_textPermissionAllowed = default;
|
|
[SerializeField]
|
|
Text m_textPermissionViolent = default;
|
|
[SerializeField]
|
|
Text m_textPermissionSexual = default;
|
|
[SerializeField]
|
|
Text m_textPermissionCommercial = default;
|
|
[SerializeField]
|
|
Text m_textPermissionOther = default;
|
|
|
|
[SerializeField, Header("DistributionLicense")]
|
|
Text m_textDistributionLicense = default;
|
|
[SerializeField]
|
|
Text m_textDistributionOther = default;
|
|
|
|
public void Start()
|
|
{
|
|
m_textModelTitle.text = "";
|
|
m_textModelVersion.text = "";
|
|
m_textModelAuthor.text = "";
|
|
m_textModelContact.text = "";
|
|
m_textModelReference.text = "";
|
|
|
|
m_textPermissionAllowed.text = "";
|
|
m_textPermissionViolent.text = "";
|
|
m_textPermissionSexual.text = "";
|
|
m_textPermissionCommercial.text = "";
|
|
m_textPermissionOther.text = "";
|
|
|
|
m_textDistributionLicense.text = "";
|
|
m_textDistributionOther.text = "";
|
|
}
|
|
|
|
public void UpdateMeta(Texture2D thumbnail, UniGLTF.Extensions.VRMC_vrm.Meta meta, Migration.Vrm0Meta meta0)
|
|
{
|
|
m_thumbnail.texture = thumbnail;
|
|
|
|
if (meta != null)
|
|
{
|
|
m_textModelTitle.text = meta.Name;
|
|
m_textModelVersion.text = meta.Version;
|
|
m_textModelAuthor.text = meta.Authors[0];
|
|
m_textModelContact.text = meta.ContactInformation;
|
|
if (meta.References != null && meta.References.Count > 0)
|
|
{
|
|
m_textModelReference.text = meta.References[0];
|
|
}
|
|
// 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_textDistributionLicense.text = meta.ModificationLicense.ToString();
|
|
m_textDistributionOther.text = meta.OtherLicenseUrl;
|
|
}
|
|
|
|
if (meta0 != null)
|
|
{
|
|
m_textModelTitle.text = meta0.title;
|
|
m_textModelVersion.text = meta0.version;
|
|
m_textModelAuthor.text = meta0.author;
|
|
m_textModelContact.text = meta0.contactInformation;
|
|
m_textModelReference.text = meta0.reference;
|
|
m_textPermissionAllowed.text = meta0.allowedUser.ToString();
|
|
m_textPermissionViolent.text = meta0.violentUsage.ToString();
|
|
m_textPermissionSexual.text = meta0.sexualUsage.ToString();
|
|
m_textPermissionCommercial.text = meta0.commercialUsage.ToString();
|
|
m_textPermissionOther.text = meta0.otherPermissionUrl;
|
|
// m_textDistributionLicense.text = meta0.ModificationLicense.ToString();
|
|
m_textDistributionOther.text = meta0.otherLicenseUrl;
|
|
}
|
|
}
|
|
}
|
|
[SerializeField]
|
|
TextFields m_texts = default;
|
|
|
|
[Serializable]
|
|
class UIFields
|
|
{
|
|
[SerializeField]
|
|
Toggle ToggleMotionTPose = default;
|
|
|
|
[SerializeField]
|
|
Toggle ToggleMotionBVH = default;
|
|
|
|
[SerializeField]
|
|
ToggleGroup ToggleMotion = default;
|
|
|
|
public bool IsBvhEnabled
|
|
{
|
|
get => ToggleMotion.ActiveToggles().FirstOrDefault() == ToggleMotionBVH;
|
|
set
|
|
{
|
|
ToggleMotionTPose.isOn = !value;
|
|
ToggleMotionBVH.isOn = value;
|
|
}
|
|
}
|
|
}
|
|
[SerializeField]
|
|
UIFields m_ui = default;
|
|
|
|
[SerializeField]
|
|
HumanPoseClip m_pose = default;
|
|
|
|
private void Reset()
|
|
{
|
|
var buttons = GameObject.FindObjectsOfType<Button>();
|
|
m_open = buttons.First(x => x.name == "Open");
|
|
|
|
var toggles = GameObject.FindObjectsOfType<Toggle>();
|
|
m_enableLipSync = toggles.First(x => x.name == "EnableLipSync");
|
|
m_enableAutoBlink = toggles.First(x => x.name == "EnableAutoBlink");
|
|
m_enableAutoExpression = toggles.First(x => x.name == "EnableAutoExpression");
|
|
|
|
var texts = GameObject.FindObjectsOfType<Text>();
|
|
m_version = texts.First(x => x.name == "Version");
|
|
|
|
m_src = GameObject.FindObjectOfType<Animator>();
|
|
|
|
m_target = GameObject.FindObjectOfType<VRM10TargetMover>().gameObject;
|
|
}
|
|
|
|
Loaded m_loaded;
|
|
|
|
private void Start()
|
|
{
|
|
m_version.text = string.Format("VRMViewer {0}.{1}",
|
|
VRMVersion.MAJOR, VRMVersion.MINOR);
|
|
m_open.onClick.AddListener(OnOpenClicked);
|
|
|
|
// load initial bvh
|
|
if (m_motion != null)
|
|
{
|
|
LoadMotion(m_motion.text);
|
|
}
|
|
|
|
string[] cmds = System.Environment.GetCommandLineArgs();
|
|
if (cmds.Length > 1)
|
|
{
|
|
LoadModel(cmds[1]);
|
|
}
|
|
|
|
m_texts.Start();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
_cancellationTokenSource?.Dispose();
|
|
}
|
|
|
|
private void LoadMotion(string source)
|
|
{
|
|
var context = new UniHumanoid.BvhImporterContext();
|
|
context.Parse("tmp.bvh", source);
|
|
context.Load();
|
|
m_src = context.Root.GetComponent<Animator>();
|
|
m_ui.IsBvhEnabled = true;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
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);
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.Escape))
|
|
{
|
|
if (_cancellationTokenSource != null)
|
|
{
|
|
_cancellationTokenSource.Cancel();
|
|
}
|
|
}
|
|
|
|
if (m_loaded != null)
|
|
{
|
|
if (m_ui.IsBvhEnabled && m_src != null)
|
|
{
|
|
m_loaded.UpdateControlRigImplicit(m_src);
|
|
}
|
|
else
|
|
{
|
|
m_loaded.TPoseControlRig();
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnOpenClicked()
|
|
{
|
|
#if UNITY_STANDALONE_WIN
|
|
var path = VRM10FileDialogForWindows.FileDialog("open VRM", "vrm", "bvh");
|
|
#elif UNITY_EDITOR
|
|
var path = UnityEditor.EditorUtility.OpenFilePanel("Open VRM", "", "vrm");
|
|
#else
|
|
var path = Application.dataPath + "/default.vrm";
|
|
#endif
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var ext = Path.GetExtension(path).ToLower();
|
|
if (ext == ".bvh")
|
|
{
|
|
LoadMotion(path);
|
|
return;
|
|
}
|
|
|
|
if (ext != ".vrm")
|
|
{
|
|
Debug.LogWarning($"{path} is not vrm");
|
|
return;
|
|
}
|
|
|
|
LoadModel(path);
|
|
}
|
|
|
|
static IMaterialDescriptorGenerator GetVrmMaterialDescriptorGenerator(bool useUrp)
|
|
{
|
|
if (useUrp)
|
|
{
|
|
return new Vrm10UrpMaterialDescriptorGenerator();
|
|
}
|
|
else
|
|
{
|
|
return new Vrm10MaterialDescriptorGenerator();
|
|
}
|
|
}
|
|
|
|
static IMaterialDescriptorGenerator GetMaterialDescriptorGenerator(bool useUrp)
|
|
{
|
|
if (useUrp)
|
|
{
|
|
return new GltfUrpMaterialDescriptorGenerator();
|
|
}
|
|
else
|
|
{
|
|
return new GltfMaterialDescriptorGenerator();
|
|
}
|
|
}
|
|
|
|
async void LoadModel(string path)
|
|
{
|
|
// cleanup
|
|
m_loaded?.Dispose();
|
|
m_loaded = null;
|
|
_cancellationTokenSource?.Dispose();
|
|
_cancellationTokenSource = new CancellationTokenSource();
|
|
var cancellationToken = _cancellationTokenSource.Token;
|
|
|
|
try
|
|
{
|
|
Debug.LogFormat("{0}", path);
|
|
var vrm10Instance = await Vrm10.LoadPathAsync(path,
|
|
canLoadVrm0X: true,
|
|
showMeshes: false,
|
|
awaitCaller: m_useAsync.enabled ? (IAwaitCaller)new RuntimeOnlyAwaitCaller() : (IAwaitCaller)new ImmediateCaller(),
|
|
materialGenerator: GetVrmMaterialDescriptorGenerator(m_useUrpMaterial.isOn),
|
|
vrmMetaInformationCallback: m_texts.UpdateMeta,
|
|
ct: cancellationToken);
|
|
if (cancellationToken.IsCancellationRequested)
|
|
{
|
|
UnityObjectDestroyer.DestroyRuntimeOrEditor(vrm10Instance.gameObject);
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
}
|
|
|
|
if (vrm10Instance == null)
|
|
{
|
|
Debug.LogWarning("LoadPathAsync is null");
|
|
return;
|
|
}
|
|
|
|
var instance = vrm10Instance.GetComponent<RuntimeGltfInstance>();
|
|
instance.ShowMeshes();
|
|
instance.EnableUpdateWhenOffscreen();
|
|
m_loaded = new Loaded(instance, m_target.transform);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (ex is OperationCanceledException)
|
|
{
|
|
Debug.LogWarning($"Canceled to Load: {path}");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"Failed to Load: {path}");
|
|
Debug.LogException(ex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|