using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UniGLTF; namespace VRM { /// /// Base + (A.Target - Base) * A.Weight + (B.Target - Base) * B.Weight ... /// class MaterialValueBindingMerger { struct DictionaryKeyMaterialValueBindingComparer : IEqualityComparer { public bool Equals(MaterialValueBinding x, MaterialValueBinding y) { return x.TargetValue == y.TargetValue && x.BaseValue == y.BaseValue && x.MaterialName == y.MaterialName && x.ValueName == y.ValueName; } public int GetHashCode(MaterialValueBinding obj) { return obj.GetHashCode(); } } static DictionaryKeyMaterialValueBindingComparer comparer = new DictionaryKeyMaterialValueBindingComparer(); /// /// 名前とmaterialのマッピング /// Dictionary m_materialMap = new Dictionary(); delegate void Setter(float value, bool firstValue); /// /// MaterialValueの適用値を蓄積する /// /// /// /// Dictionary m_materialValueMap = new Dictionary(comparer); Dictionary m_materialSetterMap = new Dictionary(comparer); //BlendShapeClip[] m_clips; public MaterialValueBindingMerger(Dictionary clipMap, Transform root) { //m_clips = clipMap.Values.ToArray(); foreach (var x in root.Traverse()) { var renderer = x.GetComponent(); if (renderer != null) { foreach (var y in renderer.sharedMaterials.Where(y => y != null)) { if (!string.IsNullOrEmpty(y.name)) { if (!m_materialMap.ContainsKey(y.name)) { m_materialMap.Add(y.name, y); } } } } } foreach (var kv in clipMap) { foreach (var binding in kv.Value.MaterialValues) { if (!m_materialSetterMap.ContainsKey(binding)) { Material target; if (m_materialMap.TryGetValue(binding.MaterialName, out target)) { if (binding.ValueName.EndsWith("_ST_S")) { var valueName = binding.ValueName.Substring(0, binding.ValueName.Length - 2); Setter setter = (value, firstValue) => { var propValue = firstValue ? (binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value) : (target.GetVector(valueName) + (binding.TargetValue - binding.BaseValue) * value) ; var src = target.GetVector(valueName); src.x = propValue.x; // horizontal only src.z = propValue.z; // horizontal only target.SetVector(valueName, src); }; m_materialSetterMap.Add(binding, setter); } else if (binding.ValueName.EndsWith("_ST_T")) { var valueName = binding.ValueName.Substring(0, binding.ValueName.Length - 2); Setter setter = (value, firstValue) => { var propValue = firstValue ? (binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value) : (target.GetVector(valueName) + (binding.TargetValue - binding.BaseValue) * value) ; var src = target.GetVector(valueName); src.y = propValue.y; // vertical only src.w = propValue.w; // vertical only target.SetVector(valueName, src); }; m_materialSetterMap.Add(binding, setter); } else { Setter vec4Setter = (value, firstValue) => { var propValue = firstValue ? (binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value) : (target.GetVector(binding.ValueName) + (binding.TargetValue - binding.BaseValue) * value) ; target.SetColor(binding.ValueName, propValue); }; m_materialSetterMap.Add(binding, vec4Setter); } } else { Debug.LogWarningFormat("material: {0} not found", binding.MaterialName); } } } } } public void RestoreMaterialInitialValues(IEnumerable clips) { if (m_materialMap != null) { foreach (var x in clips) { foreach (var y in x.MaterialValues) { // restore values Material material; if (m_materialMap.TryGetValue(y.MaterialName, out material)) { var valueName = y.ValueName; if (valueName.EndsWith("_ST_S") || valueName.EndsWith("_ST_T")) { valueName = valueName.Substring(0, valueName.Length - 2); } #if UNITY_EDITOR // restore only material with asset if (!string.IsNullOrEmpty(UnityEditor.AssetDatabase.GetAssetPath(material))) { material.SetColor(valueName, y.BaseValue); } #endif } else { Debug.LogWarningFormat("{0} not found", y.MaterialName); } } } } } public void ImmediatelySetValue(BlendShapeClip clip, float value) { foreach (var binding in clip.MaterialValues) { Setter setter; if (m_materialSetterMap.TryGetValue(binding, out setter)) { setter(value, true); } } } public void AccumulateValue(BlendShapeClip clip, float value) { foreach (var binding in clip.MaterialValues) { // 積算 float acc; if (m_materialValueMap.TryGetValue(binding, out acc)) { m_materialValueMap[binding] = acc + value; } else { m_materialValueMap[binding] = value; } } } struct MaterialTarget : IEquatable { public string MaterialName; public string ValueName; public bool Equals(MaterialTarget other) { return MaterialName == other.MaterialName && ValueName == other.ValueName; } public override bool Equals(object obj) { if (obj is MaterialTarget) { return Equals((MaterialTarget)obj); } else { return false; } } public override int GetHashCode() { if (MaterialName == null || ValueName == null) { return 0; } return MaterialName.GetHashCode() + ValueName.GetHashCode(); } public static MaterialTarget Create(MaterialValueBinding binding) { return new MaterialTarget { MaterialName=binding.MaterialName, ValueName=binding.ValueName }; } } HashSet m_used = new HashSet(); public void Apply() { // clear //RestoreMaterialInitialValues(m_clips); m_used.Clear(); // (binding.Value-Base) * weight を足す foreach (var kv in m_materialValueMap) { var key = MaterialTarget.Create(kv.Key); if (!m_used.Contains(key)) { // restore value Material material; if (m_materialMap.TryGetValue(key.MaterialName, out material)) { var value = kv.Key.BaseValue; var valueName = key.ValueName; if (valueName.EndsWith("_ST_S")) { valueName = valueName.Substring(0, valueName.Length - 2); var v=material.GetVector(valueName); value.y = v.y; value.w = v.w; } else if (valueName.EndsWith("_ST_T")) { valueName = valueName.Substring(0, valueName.Length - 2); var v = material.GetVector(valueName); value.x = v.x; value.z = v.z; } material.SetColor(valueName, value); } m_used.Add(key); } Setter setter; if (m_materialSetterMap.TryGetValue(kv.Key, out setter)) { setter(kv.Value, false); } } m_materialValueMap.Clear(); } } }