289 lines
11 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|