using System; using System.Collections.Generic; using UniGLTF.Extensions.VRMC_vrm; using UnityEngine; using VRMShaders.VRM10.MToon10.Runtime; namespace UniVRM10 { /// /// Base + (A.Target - Base) * A.Weight + (B.Target - Base) * B.Weight ... /// internal sealed class MaterialValueBindingMerger { private static readonly string COLOR_PROPERTY = MToon10Prop.BaseColorFactor.ToUnityShaderLabName(); private static readonly string EMISSION_COLOR_PROPERTY = MToon10Prop.EmissiveFactor.ToUnityShaderLabName(); private static readonly string RIM_COLOR_PROPERTY = MToon10Prop.ParametricRimColorFactor.ToUnityShaderLabName(); private static readonly string OUTLINE_COLOR_PROPERTY = MToon10Prop.OutlineColorFactor.ToUnityShaderLabName(); private static readonly string SHADE_COLOR_PROPERTY = MToon10Prop.ShadeColorFactor.ToUnityShaderLabName(); private static readonly string MATCAP_COLOR_PROPERTY = MToon10Prop.MatcapColorFactor.ToUnityShaderLabName(); public static string GetProperty(MaterialColorType bindType) { switch (bindType) { // case MaterialBindType.UvOffset: // case MaterialBindType.UvScale: // return UV_PROPERTY; case MaterialColorType.color: return COLOR_PROPERTY; case MaterialColorType.emissionColor: return EMISSION_COLOR_PROPERTY; case MaterialColorType.shadeColor: return SHADE_COLOR_PROPERTY; case MaterialColorType.rimColor: return RIM_COLOR_PROPERTY; case MaterialColorType.outlineColor: return OUTLINE_COLOR_PROPERTY; case MaterialColorType.matcapColor: return MATCAP_COLOR_PROPERTY; } throw new NotImplementedException(); } #region MaterialMap /// /// MaterialValueBinding の対象になるマテリアルの情報を記録する /// Dictionary m_materialMap = new Dictionary(); void InitializeMaterialMap(Dictionary clipMap, Transform root) { Dictionary materialNameMap = new Dictionary(); foreach (var renderer in root.GetComponentsInChildren()) { foreach (var material in renderer.sharedMaterials) { if (material != null && !materialNameMap.ContainsKey(material.name)) { materialNameMap.Add(material.name, material); } } } foreach (var kv in clipMap) { foreach (var binding in kv.Value.MaterialColorBindings) { PreviewMaterialItem item; if (!m_materialMap.TryGetValue(binding.MaterialName, out item)) { if (!materialNameMap.TryGetValue(binding.MaterialName, out Material material)) { // not found skip continue; } item = new PreviewMaterialItem(material); m_materialMap.Add(binding.MaterialName, item); } // color default value var propName = GetProperty(binding.BindType); if (!item.PropMap.ContainsKey(binding.BindType)) { item.PropMap.Add(binding.BindType, new PropItem { Name = propName, DefaultValues = item.Material.GetVector(propName), }); } } foreach (var binding in kv.Value.MaterialUVBindings) { PreviewMaterialItem item; if (!m_materialMap.TryGetValue(binding.MaterialName, out item)) { if (!materialNameMap.TryGetValue(binding.MaterialName, out Material material)) { // not found skip continue; } item = new PreviewMaterialItem(material); m_materialMap.Add(binding.MaterialName, item); } } } } /// /// m_materialMap に記録した値に Material を復旧する /// public void RestoreMaterialInitialValues() { foreach (var kv in m_materialMap) { kv.Value.RestoreInitialValues(); } } #endregion #region Accumulate struct DictionaryKeyMaterialValueBindingComparer : IEqualityComparer { public bool Equals(MaterialColorBinding x, MaterialColorBinding y) { return x.TargetValue == y.TargetValue && x.MaterialName == y.MaterialName && x.BindType == y.BindType; } public int GetHashCode(MaterialColorBinding obj) { return obj.GetHashCode(); } } static DictionaryKeyMaterialValueBindingComparer comparer = new DictionaryKeyMaterialValueBindingComparer(); /// /// MaterialValueの適用値を蓄積する /// /// /// /// Dictionary m_materialColorMap = new Dictionary(comparer); /// /// UV Scale/Offset /// Dictionary m_materialUVMap = new Dictionary(); public void AccumulateValue(VRM10Expression clip, float value) { // material color foreach (var binding in clip.MaterialColorBindings) { float acc; if (m_materialColorMap.TryGetValue(binding, out acc)) { m_materialColorMap[binding] = acc + value; } else { m_materialColorMap[binding] = value; } } // maetrial uv foreach (var binding in clip.MaterialUVBindings) { if (m_materialMap.TryGetValue(binding.MaterialName, out PreviewMaterialItem item)) { var delta = binding.ScalingOffset - item.DefaultUVScaleOffset; Vector4 acc; if (m_materialUVMap.TryGetValue(binding.MaterialName, out acc)) { m_materialUVMap[binding.MaterialName] = acc + delta * value; } else { m_materialUVMap[binding.MaterialName] = item.DefaultUVScaleOffset + delta * 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(MaterialColorBinding binding) { return new MaterialTarget { MaterialName = binding.MaterialName, ValueName = GetProperty(binding.BindType), }; } } // UVアクセスするテクスチャーのScaleOffsetプロパティの一覧 static Dictionary UVPropMap = new Dictionary { {"Standard", new string[]{ "_MainTex_ST", }}, {"VRM10/MToon10", new string[]{ "_MainTex_ST", }}, }; static string[] DefaultProps = { "_MainTex_ST" }; public static String[] GetUVProps(string shaderName) { if (UVPropMap.TryGetValue(shaderName, out string[] props)) { return props; } else { return DefaultProps; } } HashSet m_used = new HashSet(); public void Apply() { { m_used.Clear(); foreach (var kv in m_materialColorMap) { var key = MaterialTarget.Create(kv.Key); PreviewMaterialItem item; if (m_materialMap.TryGetValue(key.MaterialName, out item)) { // 初期値(コンストラクタで記録) var initial = item.PropMap[kv.Key.BindType].DefaultValues; if (!m_used.Contains(key)) { // // m_used に入っていない場合は、このフレームで初回の呼び出しになる。 // (Apply はフレームに一回呼ばれる想定) // 初回は、値を初期値に戻す。 // item.Material.SetColor(key.ValueName, initial); m_used.Add(key); } // 現在値 var current = item.Material.GetVector(key.ValueName); // 変化量 var value = (kv.Key.TargetValue - initial) * kv.Value; // 適用 item.Material.SetColor(key.ValueName, current + value); } else { // エラー? } } m_materialColorMap.Clear(); } { foreach (var kv in m_materialUVMap) { PreviewMaterialItem item; if (m_materialMap.TryGetValue(kv.Key, out item)) { // // Standard and MToon use _MainTex_ST as uv0 scale/offset // foreach (var prop in GetUVProps(item.Material.shader.name)) { item.Material.SetVector(prop, kv.Value); } } } m_materialUVMap.Clear(); } } #endregion public MaterialValueBindingMerger(Dictionary clipMap, Transform root) { InitializeMaterialMap(clipMap, root); } } }