using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditorInternal; using UnityEngine; namespace VRM { public class SerializedBlendShapeEditor { BlendShapeClip m_targetObject; SerializedObject m_serializedObject; #region Properties SerializedProperty m_thumbnail; SerializedProperty m_blendShapeNameProp; SerializedProperty m_presetProp; SerializedProperty m_isBinaryProp; #endregion #region BlendShapeBind public static int BlendShapeBindingHeight = 60; ReorderableList m_ValuesList; SerializedProperty m_valuesProp; #endregion #region MaterialValueBind const int MaterialValueBindingHeight = 90; ReorderableList m_MaterialValuesList; SerializedProperty m_materialsProp; #endregion #region Editor values //float m_previewSlider = 1.0f; bool m_changed; int m_mode; static string[] MODES = new[]{ "BlendShape", "BlendShape List", "Material List" }; MeshPreviewItem[] m_items; #endregion public SerializedBlendShapeEditor(SerializedObject serializedObject, PreviewSceneManager previewSceneManager) : this( serializedObject, (BlendShapeClip)serializedObject.targetObject, previewSceneManager) { } public SerializedBlendShapeEditor(BlendShapeClip blendShapeClip, PreviewSceneManager previewSceneManager) : this( new SerializedObject(blendShapeClip), blendShapeClip, previewSceneManager) { } public SerializedBlendShapeEditor(SerializedObject serializedObject, BlendShapeClip targetObject, PreviewSceneManager previewSceneManager) { this.m_serializedObject = serializedObject; this.m_targetObject = targetObject; //m_thumbnail = serializedObject.FindProperty("Thumbnail"); m_blendShapeNameProp = serializedObject.FindProperty("BlendShapeName"); m_presetProp = serializedObject.FindProperty("Preset"); m_isBinaryProp = serializedObject.FindProperty("IsBinary"); m_valuesProp = serializedObject.FindProperty("Values"); m_ValuesList = new ReorderableList(serializedObject, m_valuesProp); m_ValuesList.elementHeight = BlendShapeBindingHeight; m_ValuesList.drawElementCallback = (rect, index, isActive, isFocused) => { var element = m_valuesProp.GetArrayElementAtIndex(index); rect.height -= 4; rect.y += 2; if (BlendShapeClipEditorHelper.DrawBlendShapeBinding(rect, element, previewSceneManager)) { m_changed = true; } }; m_materialsProp = serializedObject.FindProperty("MaterialValues"); m_MaterialValuesList = new ReorderableList(serializedObject, m_materialsProp); m_MaterialValuesList.elementHeight = MaterialValueBindingHeight; m_MaterialValuesList.drawElementCallback = (rect, index, isActive, isFocused) => { var element = m_materialsProp.GetArrayElementAtIndex(index); rect.height -= 4; rect.y += 2; if (BlendShapeClipEditorHelper.DrawMaterialValueBinding(rect, element, previewSceneManager)) { m_changed = true; } }; m_items = previewSceneManager.EnumRenderItems .Where(x => x.SkinnedMeshRenderer != null) .ToArray(); } public struct DrawResult { public bool Changed; public BlendShapeBinding[] BlendShapeBindings; public MaterialValueBinding[] MaterialValueBindings; } public DrawResult Draw() { m_changed = false; m_serializedObject.Update(); // Readonly のBlendShapeClip参照 GUI.enabled = false; EditorGUILayout.ObjectField("Current clip", m_targetObject, typeof(BlendShapeClip), false); GUI.enabled = true; EditorGUILayout.PropertyField(m_blendShapeNameProp, true); EditorGUILayout.PropertyField(m_presetProp, true); // v0.45 Added. Binary flag EditorGUILayout.PropertyField(m_isBinaryProp, true); EditorGUILayout.Space(); //m_mode = EditorGUILayout.Popup("SourceType", m_mode, MODES); m_mode = GUILayout.Toolbar(m_mode, MODES); switch (m_mode) { case 0: { ClipGUI(); } break; case 1: { if (GUILayout.Button("Clear")) { m_changed = true; m_valuesProp.arraySize = 0; } m_ValuesList.DoLayoutList(); } break; case 2: { if (GUILayout.Button("Clear")) { m_changed = true; m_materialsProp.arraySize = 0; } m_MaterialValuesList.DoLayoutList(); } break; } m_serializedObject.ApplyModifiedProperties(); return new DrawResult { Changed = m_changed, BlendShapeBindings = m_targetObject.Values, MaterialValueBindings = m_targetObject.MaterialValues }; } void ClipGUI() { var changed = BlendShapeBindsGUI(); if (changed) { string maxWeightName; var bindings = GetBindings(out maxWeightName); m_valuesProp.ClearArray(); m_valuesProp.arraySize = bindings.Length; for (int i = 0; i < bindings.Length; ++i) { var item = m_valuesProp.GetArrayElementAtIndex(i); var endProperty = item.GetEndProperty(); while (item.NextVisible(true)) { if (SerializedProperty.EqualContents(item, endProperty)) { break; } switch (item.name) { case "RelativePath": item.stringValue = bindings[i].RelativePath; break; case "Index": item.intValue = bindings[i].Index; break; case "Weight": item.floatValue = bindings[i].Weight; break; default: throw new Exception(); } } } m_changed = true; } } List m_meshFolds = new List(); bool BlendShapeBindsGUI() { bool changed = false; int foldIndex = 0; // すべてのSkinnedMeshRendererを列挙する foreach (var renderer in m_items.Select(x => x.SkinnedMeshRenderer)) { if (renderer == null) { continue; } var mesh = renderer.sharedMesh; if (mesh != null && mesh.blendShapeCount > 0) { //var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, m_target.transform); //EditorGUILayout.LabelField(m_target.name + "/" + item.Path); if (foldIndex >= m_meshFolds.Count) { m_meshFolds.Add(false); } m_meshFolds[foldIndex] = EditorGUILayout.Foldout(m_meshFolds[foldIndex], renderer.name); if (m_meshFolds[foldIndex]) { //EditorGUI.indentLevel += 1; for (int i = 0; i < mesh.blendShapeCount; ++i) { var src = renderer.GetBlendShapeWeight(i); var dst = EditorGUILayout.Slider(mesh.GetBlendShapeName(i), src, 0, 100.0f); if (dst != src) { renderer.SetBlendShapeWeight(i, dst); changed = true; } } //EditorGUI.indentLevel -= 1; } ++foldIndex; } } return changed; } BlendShapeBinding[] GetBindings(out string _maxWeightName) { var maxWeight = 0.0f; var maxWeightName = ""; // weightのついたblendShapeを集める var values = m_items .SelectMany(x => { var mesh = x.SkinnedMeshRenderer.sharedMesh; var relativePath = x.Path; var list = new List(); if (mesh != null) { for (int i = 0; i < mesh.blendShapeCount; ++i) { var weight = x.SkinnedMeshRenderer.GetBlendShapeWeight(i); if (weight == 0) { continue; } var name = mesh.GetBlendShapeName(i); if (weight > maxWeight) { maxWeightName = name; maxWeight = weight; } list.Add(new BlendShapeBinding { Index = i, RelativePath = relativePath, Weight = weight }); } } return list; }).ToArray() ; _maxWeightName = maxWeightName; return values; } } }