314 lines
11 KiB
C#
314 lines
11 KiB
C#
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<bool> m_meshFolds = new List<bool>();
|
|
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<BlendShapeBinding>();
|
|
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;
|
|
}
|
|
}
|
|
}
|