From 7308551d0930fd360a5e733f53d1f1df903c695c Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Tue, 15 Aug 2023 17:59:31 +0900 Subject: [PATCH 1/6] Pass IEqualityComparer --- .../Expression/DefaultExpressionValidator.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs index b56e3d318..b671a1608 100644 --- a/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs +++ b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs @@ -14,8 +14,14 @@ namespace UniVRM10 private DefaultExpressionValidator(VRM10ObjectExpression expressionAvatar) { - _keys = expressionAvatar.Clips.Select(x => expressionAvatar.CreateKey(x.Clip)).ToArray(); - _expressions = expressionAvatar.Clips.ToDictionary(x => expressionAvatar.CreateKey(x.Clip), x => x.Clip); + _keys = expressionAvatar.Clips + .Select(x => expressionAvatar.CreateKey(x.Clip)) + .ToArray(); + _expressions = expressionAvatar.Clips.ToDictionary( + x => expressionAvatar.CreateKey(x.Clip), + x => x.Clip, + ExpressionKey.Comparer + ); } public void Validate(IReadOnlyDictionary inputWeights, IDictionary actualWeights, From 95f405dae9a4db79404c0ad786920cbaa8356230 Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Tue, 15 Aug 2023 21:20:57 +0900 Subject: [PATCH 2/6] Add Alternative Implementation --- .../Expression/MorphTargetBindingMerger.meta | 3 + .../MorphTargetBindingMerger2.cs | 69 +++++++++++++++++++ .../MorphTargetBindingMerger2.cs.meta | 3 + .../RuntimeMorphTargetBinding.cs | 67 ++++++++++++++++++ .../RuntimeMorphTargetBinding.cs.meta | 3 + 5 files changed, 145 insertions(+) create mode 100644 Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.meta create mode 100644 Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs create mode 100644 Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs.meta create mode 100644 Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs create mode 100644 Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs.meta diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.meta b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.meta new file mode 100644 index 000000000..e8493187f --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6ced9964a99042e383f60c2f065fa807 +timeCreated: 1692091240 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs new file mode 100644 index 000000000..9c840b211 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace UniVRM10 +{ + internal sealed class MorphTargetBindingMerger2 + { + private readonly Dictionary> _weights = new(ExpressionKey.Comparer); + private readonly Dictionary _accumulatedWeights = new(RuntimeMorphTargetBinding.Comparer); + private readonly RuntimeMorphTargetBinding[] _targetIds; + + public MorphTargetBindingMerger2(Dictionary expressions, Transform modelRoot) + { + foreach (var (expressionKey, expression) in expressions) + { + _weights.Add(expressionKey, new Dictionary(RuntimeMorphTargetBinding.Comparer)); + foreach (var binding in expression.MorphTargetBindings) + { + var id = RuntimeMorphTargetBinding.Create(binding, modelRoot); + if (id == null) + { + Debug.LogWarning($"Invalid MorphTargetBinding found: {binding}"); + continue; + } + if (_weights[expressionKey].ContainsKey(id.Value)) + { + Debug.LogWarning($"Duplicate MorphTargetBinding found: {binding}"); + continue; + } + _weights[expressionKey].Add(id.Value, binding.Weight * MorphTargetBinding.VRM_TO_UNITY); + _accumulatedWeights.TryAdd(id.Value, (false, 0f)); + } + } + _targetIds = _accumulatedWeights.Keys.ToArray(); + } + + public void AccumulateValue(ExpressionKey key, float value) + { + if (_weights.TryGetValue(key, out var weightsPerBinding)) + { + foreach (var (id, weight) in weightsPerBinding) + { + var (_, currentWeight) = _accumulatedWeights[id]; + _accumulatedWeights[id] = (true, currentWeight + weight * value); + } + } + } + + public void Apply() + { + foreach (var (id, (isAccumulated, totalWeight)) in _accumulatedWeights) + { + if (isAccumulated) + { + if (id.TargetRenderer == null) continue; + + id.TargetRenderer.SetBlendShapeWeight(id.TargetBlendShapeIndex, totalWeight); + } + } + + // NOTE: Reset + foreach (var id in _targetIds) + { + _accumulatedWeights[id] = (false, 0f); + } + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs.meta b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs.meta new file mode 100644 index 000000000..881314e68 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 166681d1a9384d8ba5ed2ee1f8c127a5 +timeCreated: 1692089928 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs new file mode 100644 index 000000000..42103e4ed --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UniVRM10 +{ + internal readonly struct RuntimeMorphTargetBinding : IEquatable + { + public static RuntimeMorphTargetBinding? Create(MorphTargetBinding binding, Transform modelRoot) + { + if (modelRoot == null) return null; + + var targetGameObject = modelRoot.Find(binding.RelativePath); + if (targetGameObject == null) return null; + + var targetRenderer = targetGameObject.GetComponent(); + if (targetRenderer == null) return null; + if (targetRenderer.sharedMesh == null) return null; + if (targetRenderer.sharedMesh.blendShapeCount <= binding.Index) return null; + + return new RuntimeMorphTargetBinding(targetRenderer, binding.Index, binding.Weight * MorphTargetBinding.VRM_TO_UNITY); + } + + public SkinnedMeshRenderer TargetRenderer { get; } + public int TargetRendererInstanceId { get; } // NOTE: Compare key + public int TargetBlendShapeIndex { get; } // NOTE: Compare key + public float Weight { get; } + + private RuntimeMorphTargetBinding(SkinnedMeshRenderer targetRenderer, int targetBlendShapeIndex, float weight) + { + TargetRenderer = targetRenderer; + TargetRendererInstanceId = TargetRenderer.GetInstanceID(); + TargetBlendShapeIndex = targetBlendShapeIndex; + Weight = weight; + } + + public bool Equals(RuntimeMorphTargetBinding other) + { + return TargetRendererInstanceId == other.TargetRendererInstanceId && TargetBlendShapeIndex == other.TargetBlendShapeIndex; + } + + public override bool Equals(object obj) + { + return obj is RuntimeMorphTargetBinding other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(TargetRendererInstanceId, TargetBlendShapeIndex); + } + + public static IEqualityComparer Comparer { get; } = new EqualityComparer(); + + private sealed class EqualityComparer : IEqualityComparer + { + public bool Equals(RuntimeMorphTargetBinding x, RuntimeMorphTargetBinding y) + { + return x.TargetRendererInstanceId == y.TargetRendererInstanceId && x.TargetBlendShapeIndex == y.TargetBlendShapeIndex; + } + + public int GetHashCode(RuntimeMorphTargetBinding obj) + { + return HashCode.Combine(obj.TargetRendererInstanceId, obj.TargetBlendShapeIndex); + } + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs.meta b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs.meta new file mode 100644 index 000000000..c98efb4ac --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 80f2db6641bf44f192a67c98006db559 +timeCreated: 1692091250 \ No newline at end of file From 9574c1596667d9df96e764f5d4bed2613fef3bee Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Wed, 16 Aug 2023 23:01:12 +0900 Subject: [PATCH 3/6] implement MorphTargetBindingMerger2 with minimum Dictionary access. --- .../MorphTargetBindingMerger2.cs | 105 +++++++++++------- .../MorphTargetIdentifier.cs | 67 +++++++++++ .../MorphTargetIdentifier.cs.meta | 3 + .../RuntimeMorphTargetBinding.cs | 63 ++--------- 4 files changed, 142 insertions(+), 96 deletions(-) create mode 100644 Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetIdentifier.cs create mode 100644 Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetIdentifier.cs.meta diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs index 9c840b211..95e305f3a 100644 --- a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -6,63 +7,87 @@ namespace UniVRM10 { internal sealed class MorphTargetBindingMerger2 { - private readonly Dictionary> _weights = new(ExpressionKey.Comparer); - private readonly Dictionary _accumulatedWeights = new(RuntimeMorphTargetBinding.Comparer); - private readonly RuntimeMorphTargetBinding[] _targetIds; + private readonly ExpressionKey[] _keyOrder; + // NOTE: Dictionary Access is very slow. So we use this only. + private readonly Dictionary _keyIndexReverseMap = new(ExpressionKey.Comparer); + // NOTE: idx dimension0: _keyOrder, idx dimension1: no specific order + private readonly RuntimeMorphTargetBinding[][] _bindings; + + private readonly MorphTargetIdentifier[] _morphTargetOrder; + // NOTE: idx: _morphTargetOrder + private readonly float?[] _accumulatedWeights; public MorphTargetBindingMerger2(Dictionary expressions, Transform modelRoot) { - foreach (var (expressionKey, expression) in expressions) + _keyOrder = expressions.Keys.ToArray(); + for (var keyIdx = 0; keyIdx < _keyOrder.Length; ++keyIdx) { - _weights.Add(expressionKey, new Dictionary(RuntimeMorphTargetBinding.Comparer)); - foreach (var binding in expression.MorphTargetBindings) - { - var id = RuntimeMorphTargetBinding.Create(binding, modelRoot); - if (id == null) - { - Debug.LogWarning($"Invalid MorphTargetBinding found: {binding}"); - continue; - } - if (_weights[expressionKey].ContainsKey(id.Value)) - { - Debug.LogWarning($"Duplicate MorphTargetBinding found: {binding}"); - continue; - } - _weights[expressionKey].Add(id.Value, binding.Weight * MorphTargetBinding.VRM_TO_UNITY); - _accumulatedWeights.TryAdd(id.Value, (false, 0f)); - } + _keyIndexReverseMap.Add(_keyOrder[keyIdx], keyIdx); } - _targetIds = _accumulatedWeights.Keys.ToArray(); + + var morphTargetList = new List(); + _bindings = new RuntimeMorphTargetBinding[_keyOrder.Length][]; + for (var keyIdx = 0; keyIdx < _keyOrder.Length; ++keyIdx) + { + var key = _keyOrder[keyIdx]; + var expression = expressions[key]; + + var bindingsPerKey = new List(); + foreach (var rawBinding in expression.MorphTargetBindings) + { + var morphTarget = MorphTargetIdentifier.Create(rawBinding, modelRoot); + if (!morphTarget.HasValue) + { + Debug.LogWarning($"Invalid {nameof(MorphTargetBinding)} found: {rawBinding}"); + continue; + } + if (bindingsPerKey.FindIndex(x => morphTarget.Value.Equals(x.TargetIdentifier)) >= 0) + { + Debug.LogWarning($"Duplicate MorphTargetBinding found: {rawBinding}"); + continue; + } + + var morphTargetIdx = morphTargetList.FindIndex(x => morphTarget.Value.Equals(x)); + if (morphTargetIdx < 0) + { + morphTargetIdx = morphTargetList.Count; + morphTargetList.Add(morphTarget.Value); + } + + var bindingWeight = rawBinding.Weight * MorphTargetBinding.VRM_TO_UNITY; + bindingsPerKey.Add(new RuntimeMorphTargetBinding(morphTarget.Value, inputWeight => + { + _accumulatedWeights[morphTargetIdx] = (_accumulatedWeights[morphTargetIdx] ?? 0f) + bindingWeight * inputWeight; + })); + } + _bindings[keyIdx] = bindingsPerKey.ToArray(); + } + _morphTargetOrder = morphTargetList.ToArray(); + _accumulatedWeights = new float?[_morphTargetOrder.Length]; } public void AccumulateValue(ExpressionKey key, float value) { - if (_weights.TryGetValue(key, out var weightsPerBinding)) + if (!_keyIndexReverseMap.TryGetValue(key, out var idx)) return; + + foreach (var binding in _bindings[idx]) { - foreach (var (id, weight) in weightsPerBinding) - { - var (_, currentWeight) = _accumulatedWeights[id]; - _accumulatedWeights[id] = (true, currentWeight + weight * value); - } + binding.WeightApplier(value); } } public void Apply() { - foreach (var (id, (isAccumulated, totalWeight)) in _accumulatedWeights) + for (var idx = 0; idx < _morphTargetOrder.Length; ++idx) { - if (isAccumulated) + var morphTarget = _morphTargetOrder[idx]; + var weight = _accumulatedWeights[idx]; + if (!weight.HasValue) continue; + if (morphTarget.TargetRenderer) { - if (id.TargetRenderer == null) continue; - - id.TargetRenderer.SetBlendShapeWeight(id.TargetBlendShapeIndex, totalWeight); + morphTarget.TargetRenderer.SetBlendShapeWeight(morphTarget.TargetBlendShapeIndex, weight.Value); } - } - - // NOTE: Reset - foreach (var id in _targetIds) - { - _accumulatedWeights[id] = (false, 0f); + _accumulatedWeights[idx] = null; } } } diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetIdentifier.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetIdentifier.cs new file mode 100644 index 000000000..c230a708b --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetIdentifier.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UniVRM10 +{ + internal readonly struct MorphTargetIdentifier : IEquatable + { + public static MorphTargetIdentifier? Create(MorphTargetBinding binding, Transform modelRoot) + { + if (modelRoot == null) return null; + + var targetGameObject = modelRoot.Find(binding.RelativePath); + if (targetGameObject == null) return null; + + var targetRenderer = targetGameObject.GetComponent(); + if (targetRenderer == null) return null; + if (targetRenderer.sharedMesh == null) return null; + if (targetRenderer.sharedMesh.blendShapeCount <= binding.Index) return null; + + return new MorphTargetIdentifier(targetRenderer, binding.Index); + } + + public SkinnedMeshRenderer TargetRenderer { get; } + public int TargetRendererInstanceId { get; } + public int TargetBlendShapeIndex { get; } + + public MorphTargetIdentifier(SkinnedMeshRenderer targetRenderer, int targetBlendShapeIndex) + { + TargetRenderer = targetRenderer; + TargetRendererInstanceId = targetRenderer.GetInstanceID(); + TargetBlendShapeIndex = targetBlendShapeIndex; + } + + public bool Equals(MorphTargetIdentifier other) + { + return TargetRendererInstanceId == other.TargetRendererInstanceId && TargetBlendShapeIndex == other.TargetBlendShapeIndex; + } + + public override bool Equals(object obj) + { + return obj is MorphTargetIdentifier other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(TargetRendererInstanceId, TargetBlendShapeIndex); + } + + #region IEqualityComparer + public static IEqualityComparer Comparer { get; } = new EqualityComparer(); + + private sealed class EqualityComparer : IEqualityComparer + { + public bool Equals(MorphTargetIdentifier x, MorphTargetIdentifier y) + { + return x.TargetRendererInstanceId == y.TargetRendererInstanceId && x.TargetBlendShapeIndex == y.TargetBlendShapeIndex; + } + + public int GetHashCode(MorphTargetIdentifier obj) + { + return HashCode.Combine(obj.TargetRendererInstanceId, obj.TargetBlendShapeIndex); + } + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetIdentifier.cs.meta b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetIdentifier.cs.meta new file mode 100644 index 000000000..7a6652743 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetIdentifier.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2d943de827f04f72aa4ea46868826987 +timeCreated: 1692191013 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs index 42103e4ed..0657cc055 100644 --- a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/RuntimeMorphTargetBinding.cs @@ -4,64 +4,15 @@ using UnityEngine; namespace UniVRM10 { - internal readonly struct RuntimeMorphTargetBinding : IEquatable + internal readonly struct RuntimeMorphTargetBinding { - public static RuntimeMorphTargetBinding? Create(MorphTargetBinding binding, Transform modelRoot) + public MorphTargetIdentifier TargetIdentifier { get; } + public Action WeightApplier { get; } + + public RuntimeMorphTargetBinding(MorphTargetIdentifier targetIdentifier, Action weightApplier) { - if (modelRoot == null) return null; - - var targetGameObject = modelRoot.Find(binding.RelativePath); - if (targetGameObject == null) return null; - - var targetRenderer = targetGameObject.GetComponent(); - if (targetRenderer == null) return null; - if (targetRenderer.sharedMesh == null) return null; - if (targetRenderer.sharedMesh.blendShapeCount <= binding.Index) return null; - - return new RuntimeMorphTargetBinding(targetRenderer, binding.Index, binding.Weight * MorphTargetBinding.VRM_TO_UNITY); - } - - public SkinnedMeshRenderer TargetRenderer { get; } - public int TargetRendererInstanceId { get; } // NOTE: Compare key - public int TargetBlendShapeIndex { get; } // NOTE: Compare key - public float Weight { get; } - - private RuntimeMorphTargetBinding(SkinnedMeshRenderer targetRenderer, int targetBlendShapeIndex, float weight) - { - TargetRenderer = targetRenderer; - TargetRendererInstanceId = TargetRenderer.GetInstanceID(); - TargetBlendShapeIndex = targetBlendShapeIndex; - Weight = weight; - } - - public bool Equals(RuntimeMorphTargetBinding other) - { - return TargetRendererInstanceId == other.TargetRendererInstanceId && TargetBlendShapeIndex == other.TargetBlendShapeIndex; - } - - public override bool Equals(object obj) - { - return obj is RuntimeMorphTargetBinding other && Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(TargetRendererInstanceId, TargetBlendShapeIndex); - } - - public static IEqualityComparer Comparer { get; } = new EqualityComparer(); - - private sealed class EqualityComparer : IEqualityComparer - { - public bool Equals(RuntimeMorphTargetBinding x, RuntimeMorphTargetBinding y) - { - return x.TargetRendererInstanceId == y.TargetRendererInstanceId && x.TargetBlendShapeIndex == y.TargetBlendShapeIndex; - } - - public int GetHashCode(RuntimeMorphTargetBinding obj) - { - return HashCode.Combine(obj.TargetRendererInstanceId, obj.TargetBlendShapeIndex); - } + TargetIdentifier = targetIdentifier; + WeightApplier = weightApplier; } } } \ No newline at end of file From 96d819f0c60ff80e149277c43302b2efaa5617f2 Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Wed, 16 Aug 2023 23:01:28 +0900 Subject: [PATCH 4/6] ordinal key comparer --- Assets/VRM10/Runtime/Components/Expression/ExpressionKey.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/VRM10/Runtime/Components/Expression/ExpressionKey.cs b/Assets/VRM10/Runtime/Components/Expression/ExpressionKey.cs index f0f36f54d..a31f0e8ff 100644 --- a/Assets/VRM10/Runtime/Components/Expression/ExpressionKey.cs +++ b/Assets/VRM10/Runtime/Components/Expression/ExpressionKey.cs @@ -156,7 +156,7 @@ namespace UniVRM10 if (Preset != other.Preset) return false; if (Preset != ExpressionPreset.custom) return true; - return Name == other.Name; + return Name.Equals(other.Name, StringComparison.Ordinal); } public override bool Equals(object obj) From adc22ed4165ade98e33f254bdd23cff72e97ec71 Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Thu, 17 Aug 2023 16:20:28 +0900 Subject: [PATCH 5/6] comment --- .../MorphTargetBindingMerger2.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs index 95e305f3a..9441ba8b9 100644 --- a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs @@ -5,16 +5,26 @@ using UnityEngine; namespace UniVRM10 { + /// + /// Optimized implementation of MorphTarget binding merger. + /// + /// Dictionary を使用すると、その GetEnumerator()(foreach) や get/set を 100,1000 オーダーで呼び出すことになる。 + /// するとモバイル環境ではかなりの定常負荷になってしまうため、その使用を避けて実装する。 + /// internal sealed class MorphTargetBindingMerger2 { private readonly ExpressionKey[] _keyOrder; - // NOTE: Dictionary Access is very slow. So we use this only. private readonly Dictionary _keyIndexReverseMap = new(ExpressionKey.Comparer); - // NOTE: idx dimension0: _keyOrder, idx dimension1: no specific order - private readonly RuntimeMorphTargetBinding[][] _bindings; + /// + /// index access with [_keyOrder][*] + /// + private readonly RuntimeMorphTargetBinding[][] _bindings; private readonly MorphTargetIdentifier[] _morphTargetOrder; - // NOTE: idx: _morphTargetOrder + + /// + /// index access with [_morphTargetOrder] + /// private readonly float?[] _accumulatedWeights; public MorphTargetBindingMerger2(Dictionary expressions, Transform modelRoot) From 53416516de2f4c78bcefc4196fdd47f8b06e9573 Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Thu, 17 Aug 2023 17:10:38 +0900 Subject: [PATCH 6/6] replace implementation of MorphTargetBindingMerger --- .../Components/Expression/ExpressionMerger.cs | 4 +- .../Expression/MorphTargetBindingMerger.cs | 115 ------------------ .../MorphTargetBindingMerger.cs.meta | 11 -- ...Merger2.cs => MorphTargetBindingMerger.cs} | 4 +- ....meta => MorphTargetBindingMerger.cs.meta} | 0 5 files changed, 3 insertions(+), 131 deletions(-) delete mode 100644 Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs delete mode 100644 Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs.meta rename Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/{MorphTargetBindingMerger2.cs => MorphTargetBindingMerger.cs} (96%) rename Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/{MorphTargetBindingMerger2.cs.meta => MorphTargetBindingMerger.cs.meta} (100%) diff --git a/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs b/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs index 64fb82b0d..c67515913 100644 --- a/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs +++ b/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; -using VrmLib; - namespace UniVRM10 { @@ -63,7 +61,7 @@ namespace UniVRM10 return; } - m_morphTargetBindingMerger.AccumulateValue(clip, value); + m_morphTargetBindingMerger.AccumulateValue(key, value); m_materialValueBindingMerger.AccumulateValue(clip, value); } diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs deleted file mode 100644 index f9758414f..000000000 --- a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace UniVRM10 -{ - /// - /// A.Value * A.Weight + B.Value * B.Weight ... - /// - internal sealed class MorphTargetBindingMerger - { - class DictionaryKeyMorphTargetBindingComparer : IEqualityComparer - { - public bool Equals(MorphTargetBinding x, MorphTargetBinding y) - { - return x.RelativePath == y.RelativePath - && x.Index == y.Index; - } - - public int GetHashCode(MorphTargetBinding obj) - { - return obj.RelativePath.GetHashCode() + obj.Index; - } - } - - private static DictionaryKeyMorphTargetBindingComparer comparer = new DictionaryKeyMorphTargetBindingComparer(); - - /// - /// MorphTargetBinding の適用値を蓄積する - /// - /// - /// - /// - Dictionary m_morphTargetValueMap = new Dictionary(comparer); - - /// - /// - /// - /// - Dictionary> m_morphTargetSetterMap = new Dictionary>(comparer); - - public MorphTargetBindingMerger(Dictionary clipMap, Transform root) - { - foreach (var kv in clipMap) - { - foreach (var binding in kv.Value.MorphTargetBindings) - { - if (!m_morphTargetSetterMap.ContainsKey(binding)) - { - var _target = root.Find(binding.RelativePath); - SkinnedMeshRenderer target = null; - if (_target != null) - { - target = _target.GetComponent(); - } - if (target != null) - { - if (binding.Index >= 0 && binding.Index < target.sharedMesh.blendShapeCount) - { - m_morphTargetSetterMap.Add(binding, x => - { - if (target == null) - { - // recompile in editor ? - return; - } - // VRM-1.0 weight is 0-1 - target.SetBlendShapeWeight(binding.Index, x * MorphTargetBinding.VRM_TO_UNITY); - }); - } - else - { - Debug.LogWarningFormat("Invalid morphTarget binding: {0}: {1}", target.name, binding.Index); - } - - } - else - { - Debug.LogWarningFormat("SkinnedMeshRenderer: {0} not found", binding.RelativePath); - } - } - } - } - } - - public void AccumulateValue(VRM10Expression clip, float value) - { - foreach (var binding in clip.MorphTargetBindings) - { - float acc; - if (m_morphTargetValueMap.TryGetValue(binding, out acc)) - { - m_morphTargetValueMap[binding] = acc + binding.Weight * value; - } - else - { - m_morphTargetValueMap[binding] = binding.Weight * value; - } - } - } - - public void Apply() - { - foreach (var kv in m_morphTargetValueMap) - { - Action setter; - if (m_morphTargetSetterMap.TryGetValue(kv.Key, out setter)) - { - setter(kv.Value); - } - } - m_morphTargetValueMap.Clear(); - } - } -} diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs.meta b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs.meta deleted file mode 100644 index 6c9b5f681..000000000 --- a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b955b3371ff699842a355d0e1348a859 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger.cs similarity index 96% rename from Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs rename to Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger.cs index 9441ba8b9..3c99a1a72 100644 --- a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger.cs @@ -11,7 +11,7 @@ namespace UniVRM10 /// Dictionary を使用すると、その GetEnumerator()(foreach) や get/set を 100,1000 オーダーで呼び出すことになる。 /// するとモバイル環境ではかなりの定常負荷になってしまうため、その使用を避けて実装する。 /// - internal sealed class MorphTargetBindingMerger2 + internal sealed class MorphTargetBindingMerger { private readonly ExpressionKey[] _keyOrder; private readonly Dictionary _keyIndexReverseMap = new(ExpressionKey.Comparer); @@ -27,7 +27,7 @@ namespace UniVRM10 /// private readonly float?[] _accumulatedWeights; - public MorphTargetBindingMerger2(Dictionary expressions, Transform modelRoot) + public MorphTargetBindingMerger(Dictionary expressions, Transform modelRoot) { _keyOrder = expressions.Keys.ToArray(); for (var keyIdx = 0; keyIdx < _keyOrder.Length; ++keyIdx) diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs.meta b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger.cs.meta similarity index 100% rename from Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger2.cs.meta rename to Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger/MorphTargetBindingMerger.cs.meta