mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-13 05:54:59 -05:00
Merge branch 'master' into fix/springboneruntime_default
This commit is contained in:
commit
030052a4a4
|
|
@ -5,7 +5,7 @@ namespace UniGLTF
|
|||
{
|
||||
public const int MAJOR = 0;
|
||||
public const int MINOR = 127;
|
||||
public const int PATCH = 1;
|
||||
public const string VERSION = "0.127.1";
|
||||
public const int PATCH = 2;
|
||||
public const string VERSION = "0.127.2";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ namespace UniGLTF
|
|||
{
|
||||
public const int MAJOR = 2;
|
||||
public const int MINOR = 63;
|
||||
public const int PATCH = 1;
|
||||
public const string VERSION = "2.63.1";
|
||||
public const int PATCH = 2;
|
||||
public const string VERSION = "2.63.2";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "com.vrmc.gltf",
|
||||
"version": "0.127.1",
|
||||
"version": "0.127.2",
|
||||
"displayName": "UniGLTF",
|
||||
"description": "GLTF importer and exporter",
|
||||
"unity": "2021.3",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ namespace VRM.RuntimeExporterSample
|
|||
[SerializeField]
|
||||
public bool UseNormalize = true;
|
||||
|
||||
[SerializeField]
|
||||
public bool BakeBlendShapes = false;
|
||||
|
||||
GameObject m_model;
|
||||
|
||||
void OnGUI()
|
||||
|
|
@ -29,7 +32,7 @@ namespace VRM.RuntimeExporterSample
|
|||
|
||||
if (GUILayout.Button("Export"))
|
||||
{
|
||||
Export(m_model, UseNormalize);
|
||||
Export(m_model, UseNormalize, BakeBlendShapes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +115,7 @@ namespace VRM.RuntimeExporterSample
|
|||
}
|
||||
|
||||
|
||||
static void Export(GameObject model, bool useNormalize)
|
||||
static void Export(GameObject model, bool useNormalize, bool bakeBlendShape)
|
||||
{
|
||||
//#if UNITY_STANDALONE_WIN
|
||||
#if false
|
||||
|
|
@ -125,7 +128,7 @@ namespace VRM.RuntimeExporterSample
|
|||
return;
|
||||
}
|
||||
|
||||
var bytes = useNormalize ? ExportCustom(model) : ExportSimple(model);
|
||||
var bytes = useNormalize ? ExportCustom(model, false, bakeBlendShape) : ExportSimple(model);
|
||||
|
||||
File.WriteAllBytes(path, bytes);
|
||||
Debug.LogFormat("export to {0}", path);
|
||||
|
|
@ -138,10 +141,10 @@ namespace VRM.RuntimeExporterSample
|
|||
return bytes;
|
||||
}
|
||||
|
||||
static byte[] ExportCustom(GameObject exportRoot, bool forceTPose = false)
|
||||
static byte[] ExportCustom(GameObject exportRoot, bool forceTPose, bool bakeBlendShape)
|
||||
{
|
||||
// normalize
|
||||
VRMBoneNormalizer.Execute(exportRoot, forceTPose);
|
||||
VRMBoneNormalizer.Execute(exportRoot, forceTPose, bakeBlendShape);
|
||||
|
||||
return ExportSimple(exportRoot);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ namespace VRM.SimpleViewer
|
|||
/// </summary>
|
||||
public class ViewerUI : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
TextFields m_texts = default;
|
||||
|
||||
#region UI
|
||||
[SerializeField]
|
||||
Text m_version = default;
|
||||
|
|
@ -146,9 +149,6 @@ namespace VRM.SimpleViewer
|
|||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
TextFields m_texts = default;
|
||||
|
||||
[Serializable]
|
||||
class UIFields
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "com.vrmc.univrm",
|
||||
"version": "0.127.1",
|
||||
"version": "0.127.2",
|
||||
"displayName": "VRM",
|
||||
"description": "VRM importer",
|
||||
"unity": "2021.3",
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
"name": "VRM Consortium"
|
||||
},
|
||||
"dependencies": {
|
||||
"com.vrmc.gltf": "0.127.1",
|
||||
"com.vrmc.gltf": "0.127.2",
|
||||
"com.unity.ugui": "1.0.0"
|
||||
},
|
||||
"samples": [
|
||||
|
|
|
|||
54
Assets/VRM10/Samples~/VRM10Viewer/FaceCameraTarget.asset
Normal file
54
Assets/VRM10/Samples~/VRM10Viewer/FaceCameraTarget.asset
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!86 &8600000
|
||||
CustomRenderTexture:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: FaceCameraTarget
|
||||
m_ImageContentsHash:
|
||||
serializedVersion: 2
|
||||
Hash: 00000000000000000000000000000000
|
||||
m_ForcedFallbackFormat: 4
|
||||
m_DownscaleFallback: 0
|
||||
m_IsAlphaChannelOptional: 0
|
||||
serializedVersion: 5
|
||||
m_Width: 300
|
||||
m_Height: 300
|
||||
m_AntiAliasing: 1
|
||||
m_MipCount: -1
|
||||
m_DepthStencilFormat: 94
|
||||
m_ColorFormat: 8
|
||||
m_MipMap: 0
|
||||
m_GenerateMips: 1
|
||||
m_SRGB: 0
|
||||
m_UseDynamicScale: 0
|
||||
m_BindMS: 0
|
||||
m_EnableCompatibleFormat: 1
|
||||
m_TextureSettings:
|
||||
serializedVersion: 2
|
||||
m_FilterMode: 1
|
||||
m_Aniso: 0
|
||||
m_MipBias: 0
|
||||
m_WrapU: 1
|
||||
m_WrapV: 1
|
||||
m_WrapW: 1
|
||||
m_Dimension: 2
|
||||
m_VolumeDepth: 1
|
||||
m_ShadowSamplingMode: 2
|
||||
m_Material: {fileID: 0}
|
||||
m_InitSource: 0
|
||||
m_InitMaterial: {fileID: 0}
|
||||
m_InitColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_InitTexture: {fileID: 0}
|
||||
m_UpdateMode: 0
|
||||
m_InitializationMode: 2
|
||||
m_UpdateZoneSpace: 0
|
||||
m_CurrentUpdateZoneSpace: 0
|
||||
m_UpdateZones: []
|
||||
m_UpdatePeriod: 0
|
||||
m_ShaderPass: 0
|
||||
m_CubemapFaceMask: 4294967295
|
||||
m_DoubleBuffered: 0
|
||||
m_WrapUpdateZones: 0
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 30519662c19bd4842a70b1f1ea87a8be
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 8600000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
|
|
@ -6,23 +7,27 @@ namespace UniVRM10.VRM10Viewer
|
|||
{
|
||||
public class VRM10AIUEO : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
public Vrm10Instance Controller;
|
||||
private void Reset()
|
||||
{
|
||||
Controller = GetComponent<Vrm10Instance>();
|
||||
}
|
||||
|
||||
Coroutine m_coroutine;
|
||||
|
||||
[SerializeField]
|
||||
float m_wait = 0.5f;
|
||||
|
||||
private void Awake()
|
||||
public float Aa = 0.0f;
|
||||
public float Ih = 0.0f;
|
||||
public float Ou = 0.0f;
|
||||
public float Ee = 0.0f;
|
||||
public float Oh = 0.0f;
|
||||
|
||||
void SetWeight(ExpressionPreset preset, float value)
|
||||
{
|
||||
if (Controller == null)
|
||||
switch (preset)
|
||||
{
|
||||
Controller = GetComponent<Vrm10Instance>();
|
||||
case ExpressionPreset.aa: Aa = value; break;
|
||||
case ExpressionPreset.ih: Ih = value; break;
|
||||
case ExpressionPreset.ou: Ou = value; break;
|
||||
case ExpressionPreset.ee: Ee = value; break;
|
||||
case ExpressionPreset.oh: Oh = value; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -30,17 +35,17 @@ namespace UniVRM10.VRM10Viewer
|
|||
{
|
||||
for (var value = 0.0f; value <= 1.0f; value += velocity)
|
||||
{
|
||||
Controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(preset), value);
|
||||
SetWeight(preset, value);
|
||||
yield return null;
|
||||
}
|
||||
Controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(preset), 1.0f);
|
||||
SetWeight(preset, 1.0f);
|
||||
yield return new WaitForSeconds(wait);
|
||||
for (var value = 1.0f; value >= 0; value -= velocity)
|
||||
{
|
||||
Controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(preset), value);
|
||||
SetWeight(preset, value);
|
||||
yield return null;
|
||||
}
|
||||
Controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(preset), 0);
|
||||
SetWeight(preset, 0);
|
||||
yield return new WaitForSeconds(wait * 2);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,23 +9,27 @@ namespace UniVRM10.VRM10Viewer
|
|||
/// </summary>
|
||||
public class VRM10AutoExpression : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
public Vrm10Instance Controller;
|
||||
private void Reset()
|
||||
{
|
||||
Controller = GetComponent<Vrm10Instance>();
|
||||
}
|
||||
|
||||
Coroutine m_coroutine;
|
||||
|
||||
[SerializeField]
|
||||
float m_wait = 0.5f;
|
||||
|
||||
private void Awake()
|
||||
public float Happy = 0.0f;
|
||||
public float Angry = 0.0f;
|
||||
public float Sad = 0.0f;
|
||||
public float Relaxed = 0.0f;
|
||||
public float Surprised = 0.0f;
|
||||
|
||||
void SetWeight(ExpressionPreset preset, float value)
|
||||
{
|
||||
if (Controller == null)
|
||||
switch (preset)
|
||||
{
|
||||
Controller = GetComponent<Vrm10Instance>();
|
||||
case ExpressionPreset.happy: Happy = value; break;
|
||||
case ExpressionPreset.angry: Angry = value; break;
|
||||
case ExpressionPreset.sad: Sad = value; break;
|
||||
case ExpressionPreset.relaxed: Relaxed = value; break;
|
||||
case ExpressionPreset.surprised: Surprised = value; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -33,17 +37,17 @@ namespace UniVRM10.VRM10Viewer
|
|||
{
|
||||
for (var value = 0.0f; value <= 1.0f; value += velocity)
|
||||
{
|
||||
Controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(preset), value);
|
||||
SetWeight(preset, value);
|
||||
yield return null;
|
||||
}
|
||||
Controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(preset), 1.0f);
|
||||
SetWeight(preset, 1.0f);
|
||||
yield return new WaitForSeconds(wait);
|
||||
for (var value = 1.0f; value >= 0; value -= velocity)
|
||||
{
|
||||
Controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(preset), value);
|
||||
SetWeight(preset, value);
|
||||
yield return null;
|
||||
}
|
||||
Controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(preset), 0);
|
||||
SetWeight(preset, 0);
|
||||
yield return new WaitForSeconds(wait * 2);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ namespace UniVRM10.VRM10Viewer
|
|||
/// </summary>
|
||||
public class VRM10Blinker : MonoBehaviour
|
||||
{
|
||||
Vrm10Instance m_controller;
|
||||
|
||||
[FormerlySerializedAs("m_interVal")]
|
||||
[SerializeField]
|
||||
public float Interval = 5.0f;
|
||||
|
|
@ -46,6 +44,8 @@ namespace UniVRM10.VRM10Viewer
|
|||
}
|
||||
}
|
||||
|
||||
public float BlinkValue = 0;
|
||||
|
||||
IEnumerator BlinkRoutine()
|
||||
{
|
||||
while (true)
|
||||
|
|
@ -72,10 +72,10 @@ namespace UniVRM10.VRM10Viewer
|
|||
break;
|
||||
}
|
||||
|
||||
m_controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(ExpressionPreset.blink), value);
|
||||
BlinkValue = value;
|
||||
yield return null;
|
||||
}
|
||||
m_controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(ExpressionPreset.blink), 1.0f);
|
||||
BlinkValue = 1.0f;
|
||||
|
||||
// wait...
|
||||
yield return new WaitForSeconds(ClosingTime);
|
||||
|
|
@ -91,16 +91,15 @@ namespace UniVRM10.VRM10Viewer
|
|||
break;
|
||||
}
|
||||
|
||||
m_controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(ExpressionPreset.blink), value);
|
||||
BlinkValue = value;
|
||||
yield return null;
|
||||
}
|
||||
m_controller.Runtime.Expression.SetWeight(ExpressionKey.CreateFromPreset(ExpressionPreset.blink), 0);
|
||||
BlinkValue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_controller = GetComponent<Vrm10Instance>();
|
||||
m_coroutine = StartCoroutine(BlinkRoutine());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,73 +7,20 @@ namespace UniVRM10.VRM10Viewer
|
|||
class Loaded : IDisposable
|
||||
{
|
||||
RuntimeGltfInstance m_instance;
|
||||
Vrm10Instance m_controller;
|
||||
public Vrm10Instance Instance => m_controller;
|
||||
public Vrm10RuntimeControlRig ControlRig => m_controller.Runtime.ControlRig;
|
||||
public Vrm10Runtime Runtime => m_controller.Runtime;
|
||||
Vrm10Instance m_vrm;
|
||||
public Vrm10Instance Instance => m_vrm;
|
||||
public Vrm10RuntimeControlRig ControlRig => m_vrm.Runtime.ControlRig;
|
||||
public Vrm10Runtime Runtime => m_vrm.Runtime;
|
||||
|
||||
VRM10AIUEO 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, Transform lookAtTarget)
|
||||
public Loaded(RuntimeGltfInstance instance)
|
||||
{
|
||||
m_instance = instance;
|
||||
|
||||
m_controller = instance.GetComponent<Vrm10Instance>();
|
||||
if (m_controller != null)
|
||||
m_vrm = instance.GetComponent<Vrm10Instance>();
|
||||
if (m_vrm != null)
|
||||
{
|
||||
// VRM
|
||||
m_controller.UpdateType = Vrm10Instance.UpdateTypes.LateUpdate; // after HumanPoseTransfer's setPose
|
||||
{
|
||||
m_lipSync = instance.gameObject.AddComponent<VRM10AIUEO>();
|
||||
m_blink = instance.gameObject.AddComponent<VRM10Blinker>();
|
||||
m_autoExpression = instance.gameObject.AddComponent<VRM10AutoExpression>();
|
||||
|
||||
m_controller.LookAtTargetType = VRM10ObjectLookAt.LookAtTargetTypes.SpecifiedTransform;
|
||||
m_controller.LookAtTarget = lookAtTarget;
|
||||
}
|
||||
m_vrm.UpdateType = Vrm10Instance.UpdateTypes.LateUpdate; // after HumanPoseTransfer's setPose
|
||||
m_vrm.LookAtTargetType = VRM10ObjectLookAt.LookAtTargetTypes.YawPitchValue;
|
||||
}
|
||||
|
||||
var animation = instance.GetComponent<Animation>();
|
||||
|
|
@ -90,4 +37,4 @@ namespace UniVRM10.VRM10Viewer
|
|||
GameObject.Destroy(m_instance.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ namespace VRM.VRM10RokuroCamera
|
|||
|
||||
class _Rokuro
|
||||
{
|
||||
public float Yaw;
|
||||
public float Yaw = 180.0f;
|
||||
public float Pitch;
|
||||
public float ShiftX;
|
||||
public float ShiftY;
|
||||
|
|
@ -71,11 +71,6 @@ namespace VRM.VRM10RokuroCamera
|
|||
private List<Coroutine> _activeCoroutines = new List<Coroutine>();
|
||||
private void OnEnable()
|
||||
{
|
||||
// left mouse drag
|
||||
_activeCoroutines.Add(StartCoroutine(MouseDragOperationCoroutine(0, diff =>
|
||||
{
|
||||
_currentCamera.Rotate(diff.x * RotateSpeed, diff.y * RotateSpeed);
|
||||
})));
|
||||
// right mouse drag
|
||||
_activeCoroutines.Add(StartCoroutine(MouseDragOperationCoroutine(1, diff =>
|
||||
{
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
|
@ -11,8 +12,12 @@ namespace UniVRM10.VRM10Viewer
|
|||
{
|
||||
public class VRM10ViewerUI : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
GameObject Root = default;
|
||||
[SerializeField]
|
||||
Text m_version = default;
|
||||
[SerializeField]
|
||||
Transform m_faceCamera = default;
|
||||
|
||||
[Header("UI")]
|
||||
[SerializeField]
|
||||
|
|
@ -27,34 +32,18 @@ namespace UniVRM10.VRM10Viewer
|
|||
[SerializeField]
|
||||
Toggle m_showBoxMan = default;
|
||||
|
||||
[SerializeField]
|
||||
Toggle m_enableLipSync = default;
|
||||
|
||||
[SerializeField]
|
||||
Toggle m_enableAutoBlink = default;
|
||||
|
||||
[SerializeField]
|
||||
Toggle m_enableAutoExpression = default;
|
||||
|
||||
[SerializeField]
|
||||
Toggle m_useAsync = default;
|
||||
|
||||
[SerializeField]
|
||||
GameObject m_target = default;
|
||||
|
||||
[SerializeField]
|
||||
TextAsset m_motion;
|
||||
|
||||
// springbone
|
||||
[SerializeField]
|
||||
[SerializeField, Header("springbone")]
|
||||
Toggle m_useSpringboneSingelton = default;
|
||||
|
||||
[SerializeField]
|
||||
Toggle m_springbonePause = default;
|
||||
|
||||
[SerializeField]
|
||||
Toggle m_springboneScaling = default;
|
||||
|
||||
[SerializeField]
|
||||
Slider m_springboneExternalX = default;
|
||||
[SerializeField]
|
||||
|
|
@ -67,7 +56,112 @@ namespace UniVRM10.VRM10Viewer
|
|||
[SerializeField]
|
||||
Button m_reconstructSpringBone = default;
|
||||
|
||||
GameObject Root = default;
|
||||
[SerializeField, Header("expression")]
|
||||
Toggle m_enableAutoExpression = default;
|
||||
[Serializable]
|
||||
class EmotionFields
|
||||
{
|
||||
public Slider m_expression;
|
||||
public Toggle m_binary;
|
||||
public bool m_useOverride;
|
||||
public Dropdown m_overrideMouth;
|
||||
public Dropdown m_overrideBlink;
|
||||
public Dropdown m_overrideLookAt;
|
||||
|
||||
public void Reset(ObjectMap map, string name, bool useOveride)
|
||||
{
|
||||
m_expression = map.Get<Slider>($"Slider{name}");
|
||||
m_binary = map.Get<Toggle>($"Binary{name}");
|
||||
m_useOverride = useOveride;
|
||||
if (useOveride)
|
||||
{
|
||||
m_overrideMouth = map.Get<Dropdown>($"Override{name}Mouth");
|
||||
m_overrideBlink = map.Get<Dropdown>($"Override{name}Blink");
|
||||
m_overrideLookAt = map.Get<Dropdown>($"Override{name}LookAt");
|
||||
}
|
||||
}
|
||||
|
||||
static int GetOverrideIndex(UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType.none: return 0;
|
||||
case UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType.block: return 1;
|
||||
case UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType.blend: return 2;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType ToOverrideType(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType.none;
|
||||
case 1: return UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType.block;
|
||||
case 2: return UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType.blend;
|
||||
default: throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLoad(VRM10Expression expression)
|
||||
{
|
||||
m_binary.isOn = expression.IsBinary;
|
||||
if (m_useOverride)
|
||||
{
|
||||
m_overrideMouth.SetValueWithoutNotify(GetOverrideIndex(expression.OverrideMouth));
|
||||
m_overrideBlink.SetValueWithoutNotify(GetOverrideIndex(expression.OverrideBlink));
|
||||
m_overrideLookAt.SetValueWithoutNotify(GetOverrideIndex(expression.OverrideLookAt));
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyRuntime(VRM10Expression expression)
|
||||
{
|
||||
expression.IsBinary = m_binary.isOn;
|
||||
if (m_useOverride)
|
||||
{
|
||||
expression.OverrideMouth = ToOverrideType(m_overrideMouth.value);
|
||||
expression.OverrideBlink = ToOverrideType(m_overrideBlink.value);
|
||||
expression.OverrideLookAt = ToOverrideType(m_overrideLookAt.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
EmotionFields m_happy;
|
||||
[SerializeField]
|
||||
EmotionFields m_angry;
|
||||
[SerializeField]
|
||||
EmotionFields m_sad;
|
||||
[SerializeField]
|
||||
EmotionFields m_relaxed;
|
||||
[SerializeField]
|
||||
EmotionFields m_surprised;
|
||||
|
||||
[SerializeField]
|
||||
Toggle m_enableLipSync = default;
|
||||
[SerializeField]
|
||||
EmotionFields m_lipAa = default;
|
||||
[SerializeField]
|
||||
EmotionFields m_lipIh = default;
|
||||
[SerializeField]
|
||||
EmotionFields m_lipOu = default;
|
||||
[SerializeField]
|
||||
EmotionFields m_lipEe = default;
|
||||
[SerializeField]
|
||||
EmotionFields m_lipOh = default;
|
||||
|
||||
[SerializeField]
|
||||
Toggle m_enableAutoBlink = default;
|
||||
[SerializeField]
|
||||
EmotionFields m_blink = default;
|
||||
|
||||
[SerializeField]
|
||||
GameObject m_lookAtTarget = default;
|
||||
[SerializeField]
|
||||
Toggle m_useLookAtTarget = default;
|
||||
[SerializeField]
|
||||
Slider m_yaw = default;
|
||||
[SerializeField]
|
||||
Slider m_pitch = default;
|
||||
|
||||
IVrm10Animation m_src = default;
|
||||
public IVrm10Animation Motion
|
||||
|
|
@ -123,28 +217,23 @@ namespace UniVRM10.VRM10Viewer
|
|||
[SerializeField]
|
||||
Text m_textDistributionOther = default;
|
||||
|
||||
public void Reset()
|
||||
public void Reset(ObjectMap map)
|
||||
{
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
var texts = GameObject.FindObjectsByType<Text>(FindObjectsSortMode.InstanceID);
|
||||
#else
|
||||
var texts = GameObject.FindObjectsOfType<Text>();
|
||||
#endif
|
||||
m_textModelTitle = texts.First(x => x.name == "Title (1)");
|
||||
m_textModelVersion = texts.First(x => x.name == "Version (1)");
|
||||
m_textModelAuthor = texts.First(x => x.name == "Author (1)");
|
||||
m_textModelCopyright = texts.First(x => x.name == "Copyright (1)");
|
||||
m_textModelContact = texts.First(x => x.name == "Contact (1)");
|
||||
m_textModelReference = texts.First(x => x.name == "Reference (1)");
|
||||
m_textModelTitle = map.Get<Text>("Title (1)");
|
||||
m_textModelVersion = map.Get<Text>("Version (1)");
|
||||
m_textModelAuthor = map.Get<Text>("Author (1)");
|
||||
m_textModelCopyright = map.Get<Text>("Copyright (1)");
|
||||
m_textModelContact = map.Get<Text>("Contact (1)");
|
||||
m_textModelReference = map.Get<Text>("Reference (1)");
|
||||
|
||||
m_textPermissionAllowed = texts.First(x => x.name == "AllowedUser (1)");
|
||||
m_textPermissionViolent = texts.First(x => x.name == "Violent (1)");
|
||||
m_textPermissionSexual = texts.First(x => x.name == "Sexual (1)");
|
||||
m_textPermissionCommercial = texts.First(x => x.name == "Commercial (1)");
|
||||
m_textPermissionOther = texts.First(x => x.name == "Other (1)");
|
||||
m_textPermissionAllowed = map.Get<Text>("AllowedUser (1)");
|
||||
m_textPermissionViolent = map.Get<Text>("Violent (1)");
|
||||
m_textPermissionSexual = map.Get<Text>("Sexual (1)");
|
||||
m_textPermissionCommercial = map.Get<Text>("Commercial (1)");
|
||||
m_textPermissionOther = map.Get<Text>("Other (1)");
|
||||
|
||||
m_textDistributionLicense = texts.First(x => x.name == "LicenseType (1)");
|
||||
m_textDistributionOther = texts.First(x => x.name == "OtherLicense (1)");
|
||||
m_textDistributionLicense = map.Get<Text>("LicenseType (1)");
|
||||
m_textDistributionOther = map.Get<Text>("OtherLicense (1)");
|
||||
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
var images = GameObject.FindObjectsByType<RawImage>(FindObjectsSortMode.InstanceID);
|
||||
|
|
@ -230,22 +319,11 @@ namespace UniVRM10.VRM10Viewer
|
|||
[SerializeField]
|
||||
ToggleGroup ToggleMotion = default;
|
||||
|
||||
public void Reset()
|
||||
public void Reset(ObjectMap map)
|
||||
{
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
var toggles = GameObject.FindObjectsByType<Toggle>(FindObjectsSortMode.InstanceID);
|
||||
#else
|
||||
var toggles = GameObject.FindObjectsOfType<Toggle>();
|
||||
#endif
|
||||
ToggleMotionTPose = toggles.First(x => x.name == "TPose");
|
||||
ToggleMotionBVH = toggles.First(x => x.name == "BVH");
|
||||
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
var groups = GameObject.FindObjectsByType<ToggleGroup>(FindObjectsSortMode.InstanceID);
|
||||
#else
|
||||
var groups = GameObject.FindObjectsOfType<ToggleGroup>();
|
||||
#endif
|
||||
ToggleMotion = groups.First(x => x.name == "_Motion_");
|
||||
ToggleMotionTPose = map.Get<Toggle>("TPose");
|
||||
ToggleMotionBVH = map.Get<Toggle>("BVH");
|
||||
ToggleMotion = map.Get<ToggleGroup>("_Motion_");
|
||||
}
|
||||
|
||||
public bool IsTPose
|
||||
|
|
@ -261,47 +339,71 @@ namespace UniVRM10.VRM10Viewer
|
|||
[SerializeField]
|
||||
UIFields m_ui = default;
|
||||
|
||||
class ObjectMap
|
||||
{
|
||||
Dictionary<string, GameObject> _map = new();
|
||||
public IReadOnlyDictionary<string, GameObject> Objects => _map;
|
||||
|
||||
public ObjectMap(GameObject root)
|
||||
{
|
||||
foreach (var x in root.GetComponentsInChildren<Transform>())
|
||||
{
|
||||
_map[x.name] = x.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
public T Get<T>(string name) where T : Component
|
||||
{
|
||||
return _map[name].GetComponent<T>();
|
||||
}
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
var buttons = GameObject.FindObjectsByType<Button>(FindObjectsSortMode.InstanceID);
|
||||
#else
|
||||
var buttons = GameObject.FindObjectsOfType<Button>();
|
||||
#endif
|
||||
m_openModel = buttons.First(x => x.name == "OpenModel");
|
||||
m_openMotion = buttons.First(x => x.name == "OpenMotion");
|
||||
m_pastePose = buttons.First(x => x.name == "PastePose");
|
||||
var map = new ObjectMap(gameObject);
|
||||
Root = map.Objects["Root"];
|
||||
m_openModel = map.Get<Button>("OpenModel");
|
||||
m_openMotion = map.Get<Button>("OpenMotion");
|
||||
m_pastePose = map.Get<Button>("PastePose");
|
||||
m_showBoxMan = map.Get<Toggle>("ShowBoxMan");
|
||||
m_useAsync = map.Get<Toggle>("UseAsync");
|
||||
m_useSpringboneSingelton = map.Get<Toggle>("UseSingleton");
|
||||
m_springbonePause = map.Get<Toggle>("PauseSpringBone");
|
||||
m_resetSpringBone = map.Get<Button>("ResetSpringBone");
|
||||
m_reconstructSpringBone = map.Get<Button>("ReconstructSpringBone");
|
||||
m_version = map.Get<Text>("VrmVersion");
|
||||
|
||||
m_texts.Reset(map);
|
||||
m_ui.Reset(map);
|
||||
m_springboneScaling = map.Get<Toggle>("ScalingSpringBone");
|
||||
m_springboneExternalX = map.Get<Slider>("SliderExternalX");
|
||||
m_springboneExternalY = map.Get<Slider>("SliderExternalY");
|
||||
m_springboneExternalZ = map.Get<Slider>("SliderExternalZ");
|
||||
m_enableAutoExpression = map.Get<Toggle>("EnableAutoExpression");
|
||||
m_happy.Reset(map, "Happy", true);
|
||||
m_angry.Reset(map, "Angry", true);
|
||||
m_sad.Reset(map, "Sad", true);
|
||||
m_relaxed.Reset(map, "Relaxed", true);
|
||||
m_surprised.Reset(map, "Surprised", true);
|
||||
|
||||
m_enableLipSync = map.Get<Toggle>("EnableLipSync");
|
||||
m_lipAa.Reset(map, "Aa", false);
|
||||
m_lipIh.Reset(map, "Ih", false);
|
||||
m_lipOu.Reset(map, "Ou", false);
|
||||
m_lipEe.Reset(map, "Ee", false);
|
||||
m_lipOh.Reset(map, "Oh", false);
|
||||
|
||||
m_enableAutoBlink = map.Get<Toggle>("EnableAutoBlink");
|
||||
m_blink.Reset(map, "Blink", false);
|
||||
|
||||
m_useLookAtTarget = map.Get<Toggle>("UseLookAtTarget");
|
||||
m_yaw = map.Get<Slider>("SliderYaw");
|
||||
m_pitch = map.Get<Slider>("SliderPitch");
|
||||
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
var toggles = GameObject.FindObjectsByType<Toggle>(FindObjectsSortMode.InstanceID);
|
||||
m_lookAtTarget = GameObject.FindFirstObjectByType<VRM10TargetMover>().gameObject;
|
||||
#else
|
||||
var toggles = GameObject.FindObjectsOfType<Toggle>();
|
||||
#endif
|
||||
m_showBoxMan = toggles.First(x => x.name == "ShowBoxMan");
|
||||
m_enableLipSync = toggles.First(x => x.name == "EnableLipSync");
|
||||
m_enableAutoBlink = toggles.First(x => x.name == "EnableAutoBlink");
|
||||
m_enableAutoExpression = toggles.First(x => x.name == "EnableAutoExpression");
|
||||
m_useAsync = toggles.First(x => x.name == "UseAsync");
|
||||
|
||||
m_useSpringboneSingelton = toggles.First(x => x.name == "UseSingleton");
|
||||
m_springbonePause = toggles.First(x => x.name == "PauseSpringBone");
|
||||
m_resetSpringBone = buttons.First(x => x.name == "ResetSpringBone");
|
||||
|
||||
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
var texts = GameObject.FindObjectsByType<Text>(FindObjectsSortMode.InstanceID);
|
||||
#else
|
||||
var texts = GameObject.FindObjectsOfType<Text>();
|
||||
#endif
|
||||
m_version = texts.First(x => x.name == "VrmVersion");
|
||||
|
||||
m_texts.Reset();
|
||||
m_ui.Reset();
|
||||
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
m_target = GameObject.FindFirstObjectByType<VRM10TargetMover>().gameObject;
|
||||
#else
|
||||
m_target = GameObject.FindObjectOfType<VRM10TargetMover>().gameObject;
|
||||
m_lookAtTarget = GameObject.FindObjectOfType<VRM10TargetMover>().gameObject;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -362,8 +464,16 @@ namespace UniVRM10.VRM10Viewer
|
|||
}
|
||||
}
|
||||
|
||||
VRM10AutoExpression m_autoEmotion;
|
||||
VRM10Blinker m_autoBlink;
|
||||
VRM10AIUEO m_autoLipsync;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
m_autoEmotion = gameObject.AddComponent<VRM10AutoExpression>();
|
||||
m_autoBlink = gameObject.AddComponent<VRM10Blinker>();
|
||||
m_autoLipsync = gameObject.AddComponent<VRM10AIUEO>();
|
||||
|
||||
m_version.text = string.Format("VRMViewer {0}.{1}",
|
||||
VRM10SpecVersion.MAJOR, VRM10SpecVersion.MINOR);
|
||||
|
||||
|
|
@ -415,9 +525,7 @@ namespace UniVRM10.VRM10Viewer
|
|||
|
||||
if (m_loaded != null)
|
||||
{
|
||||
m_loaded.EnableLipSyncValue = m_enableLipSync.isOn;
|
||||
m_loaded.EnableBlinkValue = m_enableAutoBlink.isOn;
|
||||
m_loaded.EnableAutoExpressionValue = m_enableAutoExpression.isOn;
|
||||
var vrm = m_loaded.Instance;
|
||||
|
||||
if (m_loaded.Runtime != null)
|
||||
{
|
||||
|
|
@ -431,13 +539,103 @@ namespace UniVRM10.VRM10Viewer
|
|||
m_loaded.Runtime.VrmAnimation = Motion;
|
||||
}
|
||||
|
||||
m_loaded.Runtime.SpringBone.SetModelLevel(m_loaded.Instance.transform, new BlittableModelLevel
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -596,8 +794,20 @@ namespace UniVRM10.VRM10Viewer
|
|||
var instance = vrm10Instance.GetComponent<RuntimeGltfInstance>();
|
||||
instance.ShowMeshes();
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
m_loaded = new Loaded(instance, m_target.transform);
|
||||
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 ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "com.vrmc.vrm",
|
||||
"version": "0.127.1",
|
||||
"version": "0.127.2",
|
||||
"displayName": "VRM-1.0",
|
||||
"description": "VRM-1.0 importer",
|
||||
"unity": "2021.3",
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
"name": "VRM Consortium"
|
||||
},
|
||||
"dependencies": {
|
||||
"com.vrmc.gltf": "0.127.1"
|
||||
"com.vrmc.gltf": "0.127.2"
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user