KINDNICK_URP/Assets/External/VRM/Runtime/BlendShape/MaterialValueBindingMerger.cs
2025-04-25 21:14:54 +09:00

289 lines
11 KiB
C#

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<MaterialValueBinding>
{
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();
/// <summary>
/// 名前とmaterialのマッピング
/// </summary>
Dictionary<string, Material> m_materialMap = new Dictionary<string, Material>();
delegate void Setter(float value, bool firstValue);
/// <summary>
/// MaterialValueの適用値を蓄積する
/// </summary>
/// <typeparam name="MaterialValueBinding"></typeparam>
/// <typeparam name="float"></typeparam>
/// <returns></returns>
Dictionary<MaterialValueBinding, float> m_materialValueMap = new Dictionary<MaterialValueBinding, float>(comparer);
Dictionary<MaterialValueBinding, Setter> m_materialSetterMap = new Dictionary<MaterialValueBinding, Setter>(comparer);
//BlendShapeClip[] m_clips;
public MaterialValueBindingMerger(Dictionary<BlendShapeKey, BlendShapeClip> clipMap, Transform root)
{
//m_clips = clipMap.Values.ToArray();
foreach (var x in root.Traverse())
{
var renderer = x.GetComponent<Renderer>();
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<BlendShapeClip> 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<MaterialTarget>
{
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<MaterialTarget> m_used = new HashSet<MaterialTarget>();
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();
}
}
}