mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-14 06:19:47 -05:00
104 lines
4.2 KiB
C#
104 lines
4.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
namespace UniVRM10
|
|
{
|
|
/// <summary>
|
|
/// Optimized implementation of MorphTarget binding merger.
|
|
///
|
|
/// Dictionary を使用すると、その GetEnumerator()(foreach) や get/set を 100,1000 オーダーで呼び出すことになる。
|
|
/// するとモバイル環境ではかなりの定常負荷になってしまうため、その使用を避けて実装する。
|
|
/// </summary>
|
|
internal sealed class MorphTargetBindingMerger
|
|
{
|
|
private readonly ExpressionKey[] _keyOrder;
|
|
private readonly Dictionary<ExpressionKey, int> _keyIndexReverseMap = new(ExpressionKey.Comparer);
|
|
|
|
/// <summary>
|
|
/// index access with [_keyOrder][*]
|
|
/// </summary>
|
|
private readonly RuntimeMorphTargetBinding[][] _bindings;
|
|
private readonly MorphTargetIdentifier[] _morphTargetOrder;
|
|
|
|
/// <summary>
|
|
/// index access with [_morphTargetOrder]
|
|
/// </summary>
|
|
private readonly float?[] _accumulatedWeights;
|
|
|
|
public MorphTargetBindingMerger(Dictionary<ExpressionKey, VRM10Expression> expressions, Transform modelRoot)
|
|
{
|
|
_keyOrder = expressions.Keys.ToArray();
|
|
for (var keyIdx = 0; keyIdx < _keyOrder.Length; ++keyIdx)
|
|
{
|
|
_keyIndexReverseMap.Add(_keyOrder[keyIdx], keyIdx);
|
|
}
|
|
|
|
var morphTargetList = new List<MorphTargetIdentifier>();
|
|
_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<RuntimeMorphTargetBinding>();
|
|
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 (!_keyIndexReverseMap.TryGetValue(key, out var idx)) return;
|
|
|
|
foreach (var binding in _bindings[idx])
|
|
{
|
|
binding.WeightApplier(value);
|
|
}
|
|
}
|
|
|
|
public void Apply()
|
|
{
|
|
for (var idx = 0; idx < _morphTargetOrder.Length; ++idx)
|
|
{
|
|
var morphTarget = _morphTargetOrder[idx];
|
|
var weight = _accumulatedWeights[idx];
|
|
if (!weight.HasValue) continue;
|
|
if (morphTarget.TargetRenderer)
|
|
{
|
|
morphTarget.TargetRenderer.SetBlendShapeWeight(morphTarget.TargetBlendShapeIndex, weight.Value);
|
|
}
|
|
_accumulatedWeights[idx] = null;
|
|
}
|
|
}
|
|
}
|
|
} |