mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-08-20 09:34:42 -05:00
VrmAnimation の Expression 実装を、透明な transform の x 座標を転用するのをやめて、
AnimationClip に普通の float curve として格納する様に変更した。 VrmAnimationInstance の public field と連動する。
This commit is contained in:
@@ -9,14 +9,14 @@ namespace UniGLTF
|
||||
{
|
||||
public static class AnimationImporterUtil
|
||||
{
|
||||
private enum TangentMode
|
||||
public enum TangentMode
|
||||
{
|
||||
Linear,
|
||||
Constant,
|
||||
Cubicspline
|
||||
}
|
||||
|
||||
private static TangentMode GetTangentMode(string interpolation)
|
||||
public static TangentMode GetTangentMode(string interpolation)
|
||||
{
|
||||
if (string.IsNullOrEmpty(interpolation) || interpolation == glTFAnimationTarget.Interpolations.LINEAR.ToString())
|
||||
{
|
||||
@@ -36,7 +36,7 @@ namespace UniGLTF
|
||||
}
|
||||
}
|
||||
|
||||
private static void CalculateTanget(List<Keyframe> keyframes, int current)
|
||||
public static void CalculateTangent(List<Keyframe> keyframes, int current)
|
||||
{
|
||||
int back = current - 1;
|
||||
if (back < 0)
|
||||
@@ -140,7 +140,7 @@ namespace UniGLTF
|
||||
keyframes[i].Add(new Keyframe(time, reversed[i], 0, 0));
|
||||
if (keyframes[i].Count > 0)
|
||||
{
|
||||
CalculateTanget(keyframes[i], keyframes[i].Count - 1);
|
||||
CalculateTangent(keyframes[i], keyframes[i].Count - 1);
|
||||
}
|
||||
}
|
||||
else if (tangentMode == TangentMode.Constant)
|
||||
@@ -235,7 +235,7 @@ namespace UniGLTF
|
||||
var input = data.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = data.FlatternFloatArrayFromAccessor(sampler.output);
|
||||
|
||||
AnimationImporterUtil.SetAnimationCurve(
|
||||
SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localPosition.x", "localPosition.y", "localPosition.z" },
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniHumanoid;
|
||||
using UnityEngine;
|
||||
@@ -8,9 +9,28 @@ namespace UniVRM10
|
||||
{
|
||||
public SkinnedMeshRenderer BoxMan;
|
||||
public (INormalizedPoseProvider, ITPoseProvider) ControlRig;
|
||||
public Dictionary<ExpressionKey, Transform> ExpressionMap = new Dictionary<ExpressionKey, Transform>();
|
||||
public Dictionary<ExpressionKey, Func<float>> ExpressionMap = new();
|
||||
|
||||
public void Initialize(IReadOnlyList<(ExpressionKey, Transform)> expressions)
|
||||
public float preset_happy;
|
||||
public float preset_angry;
|
||||
public float preset_sad;
|
||||
public float preset_relaxed;
|
||||
public float preset_surprised;
|
||||
public float preset_aa;
|
||||
public float preset_ih;
|
||||
public float preset_ou;
|
||||
public float preset_ee;
|
||||
public float preset_oh;
|
||||
public float preset_blink;
|
||||
public float preset_blinkleft;
|
||||
public float preset_blinkright;
|
||||
// public float preset_lookup;
|
||||
// public float preset_lookdown;
|
||||
// public float preset_lookleft;
|
||||
// public float preset_lookright;
|
||||
public float preset_neutral;
|
||||
|
||||
public void Initialize(IEnumerable<ExpressionKey> keys)
|
||||
{
|
||||
var humanoid = gameObject.AddComponent<Humanoid>();
|
||||
if (humanoid.AssignBonesFromAnimator())
|
||||
@@ -28,9 +48,29 @@ namespace UniVRM10
|
||||
mesh.name = "box-man";
|
||||
}
|
||||
|
||||
foreach (var (preset, transform) in expressions)
|
||||
foreach (var key in keys)
|
||||
{
|
||||
ExpressionMap.Add(preset, transform);
|
||||
switch (key.Preset)
|
||||
{
|
||||
case ExpressionPreset.happy: ExpressionMap.Add(key, () => preset_happy); break;
|
||||
case ExpressionPreset.angry: ExpressionMap.Add(key, () => preset_angry); break;
|
||||
case ExpressionPreset.sad: ExpressionMap.Add(key, () => preset_sad); break;
|
||||
case ExpressionPreset.relaxed: ExpressionMap.Add(key, () => preset_relaxed); break;
|
||||
case ExpressionPreset.surprised: ExpressionMap.Add(key, () => preset_surprised); break;
|
||||
case ExpressionPreset.aa: ExpressionMap.Add(key, () => preset_aa); break;
|
||||
case ExpressionPreset.ih: ExpressionMap.Add(key, () => preset_ih); break;
|
||||
case ExpressionPreset.ou: ExpressionMap.Add(key, () => preset_ou); break;
|
||||
case ExpressionPreset.ee: ExpressionMap.Add(key, () => preset_ee); break;
|
||||
case ExpressionPreset.oh: ExpressionMap.Add(key, () => preset_oh); break;
|
||||
case ExpressionPreset.blink: ExpressionMap.Add(key, () => preset_blink); break;
|
||||
case ExpressionPreset.blinkLeft: ExpressionMap.Add(key, () => preset_blinkleft); break;
|
||||
case ExpressionPreset.blinkRight: ExpressionMap.Add(key, () => preset_blinkright); break;
|
||||
// case ExpressionPreset.lookUp: ExpressionMap.Add(key, () => preset_lookUp); break;
|
||||
// case ExpressionPreset.lookDown: ExpressionMap.Add(key, () => preset_lookDown); break;
|
||||
// case ExpressionPreset.lookLeft: ExpressionMap.Add(key, () => preset_lookLeft); break;
|
||||
// case ExpressionPreset.lookRight: ExpressionMap.Add(key, () => preset_lookRight); break;
|
||||
case ExpressionPreset.neutral: ExpressionMap.Add(key, () => preset_neutral); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UniGLTF;
|
||||
using UniGLTF.Extensions.VRMC_vrm_animation;
|
||||
using UniHumanoid;
|
||||
using UniJSON;
|
||||
using UnityEngine;
|
||||
@@ -10,9 +12,10 @@ using VRMShaders;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
|
||||
public class VrmAnimationImporter : UniGLTF.ImporterContext
|
||||
{
|
||||
VRMC_vrm_animation m_vrma;
|
||||
|
||||
public VrmAnimationImporter(GltfData data,
|
||||
IReadOnlyDictionary<SubAssetKey, UnityEngine.Object> externalObjectMap = null,
|
||||
ITextureDeserializer textureDeserializer = null,
|
||||
@@ -20,6 +23,23 @@ namespace UniVRM10
|
||||
: base(data, externalObjectMap, textureDeserializer, materialGenerator)
|
||||
{
|
||||
InvertAxis = Axes.X;
|
||||
|
||||
m_vrma = GetExtension(Data);
|
||||
}
|
||||
|
||||
private static VRMC_vrm_animation GetExtension(GltfData data)
|
||||
{
|
||||
if (data.GLTF.extensions is UniGLTF.glTFExtensionImport extensions)
|
||||
{
|
||||
foreach (var kv in extensions.ObjectItems())
|
||||
{
|
||||
if (kv.Key.GetString() == "VRMC_vrm_animation")
|
||||
{
|
||||
return UniGLTF.Extensions.VRMC_vrm_animation.GltfDeserializer.Deserialize(kv.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int? GetNodeIndex(UniGLTF.Extensions.VRMC_vrm_animation.Humanoid humanoid, HumanBodyBones bone)
|
||||
@@ -88,86 +108,132 @@ namespace UniVRM10
|
||||
public Dictionary<HumanBodyBones, Transform> GetHumanMap()
|
||||
{
|
||||
var humanMap = new Dictionary<HumanBodyBones, Transform>();
|
||||
if (Data.GLTF.extensions is UniGLTF.glTFExtensionImport extensions)
|
||||
if (m_vrma is UniGLTF.Extensions.VRMC_vrm_animation.VRMC_vrm_animation animation && animation.Humanoid != null)
|
||||
{
|
||||
foreach (var kv in extensions.ObjectItems())
|
||||
foreach (HumanBodyBones bone in UniGLTF.Utils.CachedEnum.GetValues<HumanBodyBones>())
|
||||
{
|
||||
if (kv.Key.GetString() == "VRMC_vrm_animation")
|
||||
// Debug.Log($"{bone} => {index}");
|
||||
var node = GetNodeIndex(animation.Humanoid, bone);
|
||||
if (node.HasValue)
|
||||
{
|
||||
var animation = UniGLTF.Extensions.VRMC_vrm_animation.GltfDeserializer.Deserialize(kv.Value);
|
||||
if (animation.Humanoid != null)
|
||||
{
|
||||
foreach (HumanBodyBones bone in UniGLTF.Utils.CachedEnum.GetValues<HumanBodyBones>())
|
||||
{
|
||||
// Debug.Log($"{bone} => {index}");
|
||||
var node = GetNodeIndex(animation.Humanoid, bone);
|
||||
if (node.HasValue)
|
||||
{
|
||||
humanMap.Add(bone, Nodes[node.Value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
humanMap.Add(bone, Nodes[node.Value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return humanMap;
|
||||
}
|
||||
|
||||
public List<(ExpressionKey, Transform)> GetExpressions()
|
||||
class ExpressionInfo
|
||||
{
|
||||
var expressions = new List<(ExpressionKey, Transform)>();
|
||||
if (Data.GLTF.extensions is UniGLTF.glTFExtensionImport extensions)
|
||||
public ExpressionKey Key;
|
||||
public int ChannelIndex;
|
||||
public string PropertyName;
|
||||
public glTFAnimationChannel Channel;
|
||||
}
|
||||
|
||||
ExpressionInfo GetExpression(glTFAnimation animation, ExpressionKey key, string propertyName, Expression expression)
|
||||
{
|
||||
if (expression.Node.HasValue)
|
||||
{
|
||||
foreach (var kv in extensions.ObjectItems())
|
||||
for (int i = 0; i < animation.channels.Count; ++i)
|
||||
{
|
||||
if (kv.Key.GetString() == "VRMC_vrm_animation")
|
||||
var channel = animation.channels[i];
|
||||
if (channel.target.node == expression.Node.Value)
|
||||
{
|
||||
var animation = UniGLTF.Extensions.VRMC_vrm_animation.GltfDeserializer.Deserialize(kv.Value);
|
||||
if (animation.Expressions != null)
|
||||
return new ExpressionInfo
|
||||
{
|
||||
foreach (ExpressionPreset preset in UniGLTF.Utils.CachedEnum.GetValues<ExpressionPreset>())
|
||||
{
|
||||
var node = GetNodeIndex(animation.Expressions, preset);
|
||||
if (node.HasValue)
|
||||
{
|
||||
expressions.Add((ExpressionKey.CreateFromPreset(preset), Nodes[node.Value]));
|
||||
}
|
||||
}
|
||||
}
|
||||
Key = key,
|
||||
ChannelIndex = i,
|
||||
PropertyName = propertyName,
|
||||
Channel = channel,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return expressions;
|
||||
return null;
|
||||
}
|
||||
|
||||
static int? GetNodeIndex(UniGLTF.Extensions.VRMC_vrm_animation.Expressions expressions, ExpressionPreset preset)
|
||||
IEnumerable<ExpressionInfo> IterateExpressions()
|
||||
{
|
||||
switch (preset)
|
||||
if (m_vrma is UniGLTF.Extensions.VRMC_vrm_animation.VRMC_vrm_animation animation && animation.Expressions != null)
|
||||
{
|
||||
case ExpressionPreset.happy: return expressions.Preset?.Happy?.Node;
|
||||
case ExpressionPreset.angry: return expressions.Preset?.Angry?.Node;
|
||||
case ExpressionPreset.sad: return expressions.Preset?.Sad?.Node;
|
||||
case ExpressionPreset.relaxed: return expressions.Preset?.Relaxed?.Node;
|
||||
case ExpressionPreset.surprised: return expressions.Preset?.Surprised?.Node;
|
||||
case ExpressionPreset.aa: return expressions.Preset?.Aa?.Node;
|
||||
case ExpressionPreset.ih: return expressions.Preset?.Ih?.Node;
|
||||
case ExpressionPreset.ou: return expressions.Preset?.Ou?.Node;
|
||||
case ExpressionPreset.ee: return expressions.Preset?.Ee?.Node;
|
||||
case ExpressionPreset.oh: return expressions.Preset?.Oh?.Node;
|
||||
case ExpressionPreset.blink: return expressions.Preset?.Blink?.Node;
|
||||
case ExpressionPreset.blinkLeft: return expressions.Preset?.BlinkLeft?.Node;
|
||||
case ExpressionPreset.blinkRight: return expressions.Preset?.BlinkRight?.Node;
|
||||
case ExpressionPreset.lookUp: return expressions.Preset?.LookUp?.Node;
|
||||
case ExpressionPreset.lookDown: return expressions.Preset?.LookDown?.Node;
|
||||
case ExpressionPreset.lookLeft: return expressions.Preset?.LookLeft?.Node;
|
||||
case ExpressionPreset.lookRight: return expressions.Preset?.LookRight?.Node;
|
||||
case ExpressionPreset.neutral: return expressions.Preset?.Neutral?.Node;
|
||||
var gltfAnimation = Data.GLTF.animations[0];
|
||||
if (animation.Expressions.Preset is Preset preset)
|
||||
{
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.happy), "preset_happy", preset.Happy) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.angry), "preset_angry", preset.Angry) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.sad), "preset_sad", preset.Sad) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.relaxed), "preset_relaxed", preset.Relaxed) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.surprised), "preset_surprised", preset.Surprised) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.aa), "preset_aa", preset.Aa) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.ih), "preset_ih", preset.Ih) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.ou), "preset_ou", preset.Ou) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.ee), "preset_ee", preset.Ee) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.oh), "preset_oh", preset.Oh) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.blink), "preset_blink", preset.Blink) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.blinkLeft), "preset_blinkLeft", preset.BlinkLeft) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.blinkRight), "preset_blinkRight", preset.BlinkRight) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.lookUp), "preset_lookUp", preset.LookUp) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.lookDown), "preset_lookDown", preset.LookDown) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.lookLeft), "preset_lookLeft", preset.LookLeft) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.lookRight), "preset_lookRight", preset.LookRight) is ExpressionInfo info) yield return info; }
|
||||
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.neutral), "preset_neutral", preset.Neutral) is ExpressionInfo info) yield return info; }
|
||||
}
|
||||
if (animation.Expressions.Custom != null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// xyz translation カーブから x だけの カーブを生成する
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="output"></param>
|
||||
/// <returns></returns>
|
||||
static AnimationCurve CreateCurve(
|
||||
float[] input,
|
||||
float[] output)
|
||||
{
|
||||
var keyframes = new List<Keyframe>();
|
||||
for (var inputIndex = 0; inputIndex < input.Length; ++inputIndex)
|
||||
{
|
||||
var time = input[inputIndex];
|
||||
// translation
|
||||
var value = output[inputIndex * 3];
|
||||
keyframes.Add(new Keyframe(time, value, 0, 0));
|
||||
if (keyframes.Count > 0)
|
||||
{
|
||||
AnimationImporterUtil.CalculateTangent(keyframes, keyframes.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
var curve = new AnimationCurve();
|
||||
foreach (var keyFrame in keyframes)
|
||||
{
|
||||
curve.AddKey(keyFrame);
|
||||
}
|
||||
return curve;
|
||||
}
|
||||
|
||||
public override async Task<RuntimeGltfInstance> LoadAsync(IAwaitCaller awaitCaller, Func<string, IDisposable> measureTime = null)
|
||||
{
|
||||
// Expression は AnimationClip を分ける。
|
||||
// glTFData から関連 Animation を取り除いて、取っておく。
|
||||
var expressions = IterateExpressions().ToArray();
|
||||
foreach (var channelIndex in expressions.Select(x => x.ChannelIndex).OrderByDescending(x => x))
|
||||
{
|
||||
var nodeIndex = Data.GLTF.animations[0].channels[channelIndex].target.node;
|
||||
Data.GLTF.nodes.RemoveAt(nodeIndex);
|
||||
// 後ろから順に channel を除去
|
||||
Data.GLTF.animations[0].channels.RemoveAt(channelIndex);
|
||||
|
||||
Debug.Log($"remove: {channelIndex}");
|
||||
}
|
||||
Data.GLTF.scenes[0].nodes = Data.GLTF.scenes[0].nodes.Take(1).ToArray();
|
||||
|
||||
// Humanoid Animation が Gltf アニメーションとしてロードされる
|
||||
var instance = await base.LoadAsync(awaitCaller, measureTime);
|
||||
|
||||
// setup humanoid
|
||||
@@ -185,9 +251,30 @@ namespace UniVRM10
|
||||
animator.avatar = avatar;
|
||||
}
|
||||
|
||||
if (expressions.Length > 0)
|
||||
{
|
||||
var animation = instance.GetComponent<Animation>();
|
||||
var clip = animation.clip;
|
||||
|
||||
// Expression の float カーブを追加する
|
||||
// VrmAnimationInstance の "preset_xx" field に連動する
|
||||
var gltfAnimation = Data.GLTF.animations[0];
|
||||
foreach (var expression in expressions)
|
||||
{
|
||||
var channel = expression.Channel;
|
||||
var sampler = gltfAnimation.samplers[channel.sampler];
|
||||
var input = Data.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = Data.FlatternFloatArrayFromAccessor(sampler.output);
|
||||
var curve = CreateCurve(
|
||||
input.ToArray(),
|
||||
output.ToArray());
|
||||
clip.SetCurve("", typeof(VrmAnimationInstance), expression.PropertyName, curve);
|
||||
}
|
||||
}
|
||||
|
||||
// VRMA-animation solver
|
||||
var animationInstance = instance.gameObject.AddComponent<VrmAnimationInstance>();
|
||||
animationInstance.Initialize(GetExpressions());
|
||||
animationInstance.Initialize(expressions.Select(x => x.Key));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UniHumanoid;
|
||||
@@ -13,7 +14,7 @@ namespace UniVRM10.VRM10Viewer
|
||||
public SkinnedMeshRenderer BoxMan => m_boxMan;
|
||||
(INormalizedPoseProvider, ITPoseProvider) m_controlRig;
|
||||
(INormalizedPoseProvider, ITPoseProvider) IMotion.ControlRig => m_controlRig;
|
||||
public IDictionary<ExpressionKey, Transform> ExpressionMap { get; } = new Dictionary<ExpressionKey, Transform>();
|
||||
public IDictionary<ExpressionKey, Func<float>> ExpressionMap { get; } = new Dictionary<ExpressionKey, Func<float>>();
|
||||
public BvhMotion(UniHumanoid.BvhImporterContext context)
|
||||
{
|
||||
m_context = context;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace UniVRM10.VRM10Viewer
|
||||
public interface IMotion : IDisposable
|
||||
{
|
||||
(INormalizedPoseProvider, ITPoseProvider) ControlRig { get; }
|
||||
IDictionary<ExpressionKey, Transform> ExpressionMap { get; }
|
||||
IDictionary<ExpressionKey, Func<float>> ExpressionMap { get; }
|
||||
|
||||
void ShowBoxMan(bool enable);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UniGLTF;
|
||||
@@ -12,7 +13,7 @@ namespace UniVRM10.VRM10Viewer
|
||||
private readonly VrmAnimationInstance m_instance;
|
||||
|
||||
(INormalizedPoseProvider, ITPoseProvider) IMotion.ControlRig => m_instance.ControlRig;
|
||||
IDictionary<ExpressionKey, Transform> IMotion.ExpressionMap => m_instance.ExpressionMap;
|
||||
IDictionary<ExpressionKey, Func<float>> IMotion.ExpressionMap => m_instance.ExpressionMap;
|
||||
|
||||
public VrmAnimation(VrmAnimationInstance instance,
|
||||
Vector3 hips = default, Dictionary<HumanBodyBones, Quaternion> map = null)
|
||||
|
||||
@@ -370,7 +370,7 @@ namespace UniVRM10.VRM10Viewer
|
||||
foreach (var (k, v) in Motion.ExpressionMap)
|
||||
{
|
||||
// VRMA-expression use localPosition.x as expression weight
|
||||
m_loaded.Runtime.Expression.SetWeight(k, -v.transform.localPosition.x);
|
||||
m_loaded.Runtime.Expression.SetWeight(k, v());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user