diff --git a/Scripts/BlendShape/BlendShapeClip.cs b/Scripts/BlendShape/BlendShapeClip.cs index ca1cc7cbf..45a4120bb 100644 --- a/Scripts/BlendShape/BlendShapeClip.cs +++ b/Scripts/BlendShape/BlendShapeClip.cs @@ -17,9 +17,10 @@ namespace VRM { public String RelativePath; public int Index; - public string ValueName; // _Color - public float MinValue; - public float MaxValue; + public string ValueName; + public Vector4 TargetValue; + + public Vector4 BaseValue; // <- Get From RelativePath/Materials[Index][ValueName] } [CreateAssetMenu(menuName = "VRM/BlendShapeClip")] @@ -66,8 +67,7 @@ namespace VRM if (sr != null) { var m = sr.sharedMaterials[x.Index]; - var color = m.GetColor(x.ValueName); - color.a = x.MinValue + (x.MaxValue - x.MinValue) * value; + var color = x.BaseValue + (x.TargetValue - x.BaseValue) * value; m.SetColor(x.ValueName, color); } } diff --git a/Scripts/BlendShape/Editor/BlendShapeClipEditor.cs b/Scripts/BlendShape/Editor/BlendShapeClipEditor.cs index 8c816b6db..9303865b2 100644 --- a/Scripts/BlendShape/Editor/BlendShapeClipEditor.cs +++ b/Scripts/BlendShape/Editor/BlendShapeClipEditor.cs @@ -31,7 +31,7 @@ namespace VRM if (m_prefab != null) { - m_scene = PreviewSceneManager.GetOrCreate(m_prefab, m_target.Values); + m_scene = PreviewSceneManager.GetOrCreate(m_prefab, m_target.Values, m_target.MaterialValues); if (m_scene != null) { m_scene.gameObject.SetActive(false); @@ -110,6 +110,7 @@ namespace VRM if (m_scene != null) { //Debug.LogFormat("OnDestroy"); + m_scene.Clean(); GameObject.DestroyImmediate(m_scene.gameObject); m_scene = null; } @@ -138,7 +139,7 @@ namespace VRM if (m_changed && m_scene!=null) { - m_scene.Bake(m_target.Values); + m_scene.Bake(m_target.Values, m_target.MaterialValues); } } @@ -214,7 +215,7 @@ namespace VRM return changed; } - public static int MaterialValueBindingHeight = 60; + public static int MaterialValueBindingHeight = 90; public static bool DrawMaterialValueBinding(Rect position, SerializedProperty property, PreviewSceneManager scene) { @@ -233,20 +234,44 @@ namespace VRM y += height; rect = new Rect(position.x, y, position.width, height); - int blendShapeIndex; - if (IntPopup(rect, property.FindPropertyRelative("Index"), scene.GetMaterialNames(pathIndex), out blendShapeIndex)) + int materialIndex; + if (IntPopup(rect, property.FindPropertyRelative("Index"), scene.GetMaterialNames(pathIndex), out materialIndex)) { changed = true; } - /* y += height; rect = new Rect(position.x, y, position.width, height); - if (FloatSlider(rect, property.FindPropertyRelative("Weight"), 100)) + int propIndex; + if (StringPopup(rect, property.FindPropertyRelative("ValueName"), scene.GetMaterialPropNames(pathIndex, materialIndex), out propIndex)) { changed = true; } - */ + + if (propIndex >= 0) + { + var propTypes = scene.GetMaterialPropTypes(pathIndex, materialIndex); + if (propTypes != null && propIndex < propTypes.Length) + { + switch (propTypes[propIndex]) + { + case ShaderUtil.ShaderPropertyType.Color: + { + var baseValues= scene.GetMaterialPropBaseValues(pathIndex, materialIndex); + property.FindPropertyRelative("BaseValue").vector4Value = baseValues[propIndex]; + + // max + y += height; + rect = new Rect(position.x, y, position.width, height); + if (ColorProp(rect, property.FindPropertyRelative("TargetValue"))) + { + changed = true; + } + } + break; + } + } + } } return changed; } @@ -308,5 +333,19 @@ namespace VRM } } + static bool ColorProp(Rect rect, SerializedProperty prop) + { + var oldValue = (Color)prop.vector4Value; + var newValue = EditorGUI.ColorField(rect, prop.displayName, oldValue); + if (newValue != oldValue) + { + prop.vector4Value = newValue; + return true; + } + else + { + return false; + } + } } } diff --git a/Scripts/BlendShape/MeshPreviewItem.cs b/Scripts/BlendShape/MeshPreviewItem.cs index 50840a224..7f31450fb 100644 --- a/Scripts/BlendShape/MeshPreviewItem.cs +++ b/Scripts/BlendShape/MeshPreviewItem.cs @@ -1,10 +1,15 @@ using System.Linq; using UnityEngine; using UniGLTF; - +using System; +using System.Collections.Generic; +#if UNITY_EDITOR +using UnityEditor; +#endif namespace VRM { + [Serializable] public class MeshPreviewItem { public string Path @@ -25,11 +30,15 @@ namespace VRM private set; } - public int BlendShapeCount + public string[] BlendShapeNames { get; private set; } + public int BlendShapeCount + { + get { return BlendShapeNames.Length; } + } Material[] m_materials; public Material[] Materials @@ -38,14 +47,65 @@ namespace VRM private set { m_materials = value; - m_MaterialNames = value.Select(x => x.name).ToArray(); + m_materialNames = value.Select(x => x.name).ToArray(); + +#if UNITY_EDITOR + int i = 0; + foreach (var x in value) + { + var propNames = Enumerable.Range(0, ShaderUtil.GetPropertyCount(x.shader)) + .Where(y => ShaderUtil.GetPropertyType(x.shader, y) == ShaderUtil.ShaderPropertyType.Color) + .Select(y => ShaderUtil.GetPropertyName(x.shader, y)) + .ToArray(); + m_materialPropNamesList.Add(propNames); + + var propTypes = Enumerable.Range(0, ShaderUtil.GetPropertyCount(x.shader)) + .Select(y => ShaderUtil.GetPropertyType(x.shader, y)) + .Where(y => y == ShaderUtil.ShaderPropertyType.Color) + .ToArray(); + m_materialPropTypesList.Add(propTypes); + + var propValues = propNames.Select(y => (Vector4)x.GetColor(y)).ToArray(); + m_materialPropDefaultValueList.Add(propValues); + } +#endif } } - string[] m_MaterialNames; + string[] m_materialNames; public string[] MaterialsNames { - get { return m_MaterialNames; } + get { return m_materialNames; } + } + + List m_materialPropNamesList = new List(); + public string[] GetMaterialPropNames(int materialIndex) + { + if(m_materialPropNamesList==null || materialIndex<0 || materialIndex >= m_materialPropNamesList.Count) + { + return null; + } + return m_materialPropNamesList[materialIndex]; + } + + List m_materialPropTypesList = new List(); + public ShaderUtil.ShaderPropertyType[] GetMaterialPropTypes(int materialIndex) + { + if (m_materialPropTypesList == null || materialIndex < 0 || materialIndex >= m_materialPropTypesList.Count) + { + return null; + } + return m_materialPropTypesList[materialIndex]; + } + + List m_materialPropDefaultValueList = new List(); + public Vector4[] GetMaterialPropBaseValues(int materialIndex) + { + if (m_materialPropDefaultValueList == null || materialIndex < 0 || materialIndex >= m_materialPropDefaultValueList.Count) + { + return null; + } + return m_materialPropDefaultValueList[materialIndex]; } Transform m_transform; @@ -64,7 +124,7 @@ namespace VRM m_transform = transform; } - public void Bake(BlendShapeBinding[] values) + public void Bake(BlendShapeBinding[] values, MaterialValueBinding[] materialValues) { if (SkinnedMeshRenderer == null) return; @@ -84,16 +144,50 @@ namespace VRM } } } + + if (materialValues != null) + { + // clear + for (int i = 0; i < Materials.Length; ++i) + { + var names = m_materialPropNamesList[i]; + var defaultValues = m_materialPropDefaultValueList[i]; + for (int j = 0; j < names.Length; ++j) + { + Materials[i].SetColor(names[j], defaultValues[j]); + } + } + + foreach (var x in materialValues) + { + if(x.RelativePath == Path) + { + var material = Materials[x.Index]; + material.SetColor(x.ValueName, x.TargetValue); + } + } + } + SkinnedMeshRenderer.BakeMesh(Mesh); } - public static MeshPreviewItem Create(Transform t, Transform root) + public void Clean() + { + foreach (var x in Materials) + { + UnityEngine.Object.DestroyImmediate(x); + } + } + + public static MeshPreviewItem Create(Transform t, Transform root, Func getOrCreateMaterial) { var meshFilter = t.GetComponent(); var meshRenderer = t.GetComponent(); var skinnedMeshRenderer = t.GetComponent(); if (meshFilter != null && meshRenderer != null) { + // copy + meshRenderer.sharedMaterials = meshRenderer.sharedMaterials.Select(x => getOrCreateMaterial(x)).ToArray(); return new MeshPreviewItem(t.RelativePathFrom(root), t) { Mesh = meshFilter.sharedMesh, @@ -102,14 +196,17 @@ namespace VRM } else if (skinnedMeshRenderer != null) { + // copy + skinnedMeshRenderer.sharedMaterials = skinnedMeshRenderer.sharedMaterials.Select(x => getOrCreateMaterial(x)).ToArray(); if (skinnedMeshRenderer.sharedMesh.blendShapeCount > 0) { // bake required + var sharedMesh = skinnedMeshRenderer.sharedMesh; return new MeshPreviewItem(t.RelativePathFrom(root), t) { SkinnedMeshRenderer = skinnedMeshRenderer, Mesh = new Mesh(), // for bake - BlendShapeCount = skinnedMeshRenderer.sharedMesh.blendShapeCount, + BlendShapeNames = Enumerable.Range(0, sharedMesh.blendShapeCount).Select(x => sharedMesh.GetBlendShapeName(x)).ToArray(), Materials = skinnedMeshRenderer.sharedMaterials }; } diff --git a/Scripts/BlendShape/PreviewSceneManager.cs b/Scripts/BlendShape/PreviewSceneManager.cs index e85211a91..1901814d2 100644 --- a/Scripts/BlendShape/PreviewSceneManager.cs +++ b/Scripts/BlendShape/PreviewSceneManager.cs @@ -20,7 +20,7 @@ namespace VRM public GameObject Prefab; #if UNITY_EDITOR - public static PreviewSceneManager GetOrCreate(GameObject prefab, BlendShapeBinding[] values) + public static PreviewSceneManager GetOrCreate(GameObject prefab, BlendShapeBinding[] values, MaterialValueBinding[] materialValues) { if (prefab == null) { @@ -50,7 +50,7 @@ namespace VRM ); go.name = "__PREVIEW_SCENE_MANGER__"; manager = go.AddComponent(); - manager.Initialize(prefab, values); + manager.Initialize(prefab, values, materialValues); // HideFlags are special editor-only settings that let you have *secret* GameObjects in a scene, or to tell Unity not to save that temporary GameObject as part of the scene go.hideFlags |= HideFlags.DontSaveInEditor; @@ -66,7 +66,15 @@ namespace VRM } #endif - private void Initialize(GameObject prefab, BlendShapeBinding[] values) + public void Clean() + { + foreach(var x in m_meshes) + { + x.Clean(); + } + } + + private void Initialize(GameObject prefab, BlendShapeBinding[] values, MaterialValueBinding[] materialValues) { Prefab = prefab; @@ -81,23 +89,29 @@ namespace VRM } */ + var map = new Dictionary(); + Func getOrCreateMaterial = src => + { + Material dst; + if(!map.TryGetValue(src, out dst)) + { + dst = new Material(src); + map.Add(src, dst); + } + return dst; + }; + m_meshes = transform.Traverse() - .Select(x => MeshPreviewItem.Create(x, transform)) + .Select(x => MeshPreviewItem.Create(x, transform, getOrCreateMaterial)) .Where(x => x != null) .ToArray() ; + m_blendShapeMeshes = m_meshes + .Where(x => x.SkinnedMeshRenderer != null + && x.SkinnedMeshRenderer.sharedMesh.blendShapeCount>0) + .ToArray(); - foreach(var x in m_meshes) - { - if (x.SkinnedMeshRenderer != null) - { - var mesh = x.SkinnedMeshRenderer.sharedMesh; - m_blendShapeNameMap.Add(m_blendShapeNameMap.Count, - Enumerable.Range(0, mesh.blendShapeCount).Select(y => mesh.GetBlendShapeName(y)).ToArray()); - } - } - - Bake(values); + Bake(values, materialValues); m_rendererPathList = m_meshes.Select(x => x.Path).ToArray(); m_skinnedMeshRendererPathList = m_meshes @@ -107,6 +121,7 @@ namespace VRM } MeshPreviewItem[] m_meshes; + MeshPreviewItem[] m_blendShapeMeshes; public IEnumerable EnumRenderItems { get @@ -133,14 +148,14 @@ namespace VRM get { return m_skinnedMeshRendererPathList; } } - Dictionary m_blendShapeNameMap = new Dictionary(); - public string[] GetBlendShapeNames(int skinnedMeshIndex) + public string[] GetBlendShapeNames(int blendShapeMeshIndex) { - string[] names = null; - if(m_blendShapeNameMap.TryGetValue(skinnedMeshIndex, out names)) + if (blendShapeMeshIndex >= 0 && blendShapeMeshIndex < m_blendShapeMeshes.Length) { - return names; + var item = m_blendShapeMeshes[blendShapeMeshIndex]; + return item.BlendShapeNames; } + return null; } @@ -153,8 +168,53 @@ namespace VRM return null; } + public string[] GetMaterialPropNames(int rendererIndex, int materialIndex) + { + if(rendererIndex<0 || rendererIndex >= m_meshes.Length) + { + return null; + } + + if(materialIndex<0 || materialIndex >= m_meshes[rendererIndex].MaterialsNames.Length) + { + return null; + } + + return m_meshes[rendererIndex].GetMaterialPropNames(materialIndex); + } + + public ShaderUtil.ShaderPropertyType[] GetMaterialPropTypes(int rendererIndex, int materialIndex) + { + if (rendererIndex < 0 || rendererIndex >= m_meshes.Length) + { + return null; + } + + if (materialIndex < 0 || materialIndex >= m_meshes[rendererIndex].MaterialsNames.Length) + { + return null; + } + + return m_meshes[rendererIndex].GetMaterialPropTypes(materialIndex); + } + + public Vector4[] GetMaterialPropBaseValues(int rendererIndex, int materialIndex) + { + if (rendererIndex < 0 || rendererIndex >= m_meshes.Length) + { + return null; + } + + if (materialIndex < 0 || materialIndex >= m_meshes[rendererIndex].MaterialsNames.Length) + { + return null; + } + + return m_meshes[rendererIndex].GetMaterialPropBaseValues(materialIndex); + } + Bounds m_bounds; - public void Bake(BlendShapeBinding[] values) + public void Bake(BlendShapeBinding[] values, MaterialValueBinding[] materialValues) { //Debug.LogFormat("Bake"); m_bounds = default(Bounds); @@ -162,7 +222,7 @@ namespace VRM { foreach (var x in m_meshes) { - x.Bake(values); + x.Bake(values, materialValues); m_bounds.Expand(x.Mesh.bounds.size); } }