Merge pull request #58 from dwango/work_blendshape

Work blendshape
This commit is contained in:
ousttrue 2018-11-05 15:56:41 +09:00 committed by GitHub
commit 40e2f3c01b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1701 additions and 794 deletions

2
MToon

@ -1 +1 @@
Subproject commit 15dd8816dc1775871e5ca02802a561e6448e5792
Subproject commit bf08610b38e27ae915b0613ee4dfb574bc8e381b

View File

@ -3,6 +3,10 @@ using System.Linq;
using System;
using System.Collections.Generic;
using UniGLTF;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace VRM
{
@ -12,6 +16,24 @@ namespace VRM
[SerializeField]
public List<BlendShapeClip> Clips = new List<BlendShapeClip>();
/// <summary>
/// NullのClipを削除して詰める
/// </summary>
public void RemoveNullClip()
{
if (Clips == null)
{
return;
}
for (int i = Clips.Count - 1; i >= 0; --i)
{
if (Clips[i] == null)
{
Clips.RemoveAt(i);
}
}
}
#if UNITY_EDITOR
[ContextMenu("Restore")]
void Restore()
@ -37,6 +59,19 @@ namespace VRM
}
Clips = Clips.OrderBy(x => BlendShapeKey.CreateFrom(x)).ToList();
}
static public BlendShapeClip CreateBlendShapeClip(string path)
{
//Debug.LogFormat("{0}", path);
var clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.BlendShapeName = Path.GetFileNameWithoutExtension(path);
AssetDatabase.CreateAsset(clip, path);
AssetDatabase.ImportAsset(path);
return clip;
//Clips.Add(clip);
//EditorUtility.SetDirty(this);
//AssetDatabase.SaveAssets();
}
#endif
/// <summary>

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VRM
{
///
/// A.Value * A.Weight + B.Value * B.Weight ...
///
class BlendShapeBindingMerger
{
/// <summary>
/// BlendShapeの適用値を蓄積する
/// </summary>
/// <typeparam name="BlendShapeBinding"></typeparam>
/// <typeparam name="float"></typeparam>
/// <returns></returns>
Dictionary<BlendShapeBinding, float> m_blendShapeValueMap = new Dictionary<BlendShapeBinding, float>();
/// <summary>
///
/// </summary>
/// <returns></returns>
Dictionary<BlendShapeBinding, Action<float>> m_blendShapeSetterMap = new Dictionary<BlendShapeBinding, Action<float>>();
public BlendShapeBindingMerger(Dictionary<BlendShapeKey, BlendShapeClip> clipMap, Transform root)
{
foreach (var kv in clipMap)
{
foreach (var binding in kv.Value.Values)
{
if (!m_blendShapeSetterMap.ContainsKey(binding))
{
var _target = root.Find(binding.RelativePath);
SkinnedMeshRenderer target = null;
if (_target != null)
{
target = _target.GetComponent<SkinnedMeshRenderer>();
}
if (target != null)
{
if (binding.Index >= 0 && binding.Index < target.sharedMesh.blendShapeCount)
{
m_blendShapeSetterMap.Add(binding, x =>
{
target.SetBlendShapeWeight(binding.Index, x);
});
}
else
{
Debug.LogWarningFormat("Invalid blendshape binding: {0}: {1}", target.name, binding);
}
}
else
{
Debug.LogWarningFormat("SkinnedMeshRenderer: {0} not found", binding.RelativePath);
}
}
}
}
}
public void ImmediatelySetValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.Values)
{
Action<float> setter;
if (m_blendShapeSetterMap.TryGetValue(binding, out setter))
{
setter(binding.Weight * value);
}
}
}
public void AccumulateValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.Values)
{
float acc;
if (m_blendShapeValueMap.TryGetValue(binding, out acc))
{
m_blendShapeValueMap[binding] = acc + binding.Weight * value;
}
else
{
m_blendShapeValueMap[binding] = binding.Weight * value;
}
}
}
public void Apply()
{
foreach (var kv in m_blendShapeValueMap)
{
Action<float> setter;
if (m_blendShapeSetterMap.TryGetValue(kv.Key, out setter))
{
setter(kv.Value);
}
}
m_blendShapeValueMap.Clear();
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ca4c8446451eeed46b1598db9e08bb73
timeCreated: 1541229189
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -73,7 +73,7 @@ namespace VRM
public BlendShapePreset Preset;
/// <summary>
/// BlendShapeに対する参照(indexベース)
/// BlendShapeに対する参照(index ベース)
/// </summary>
/// <value></value>
[SerializeField]
@ -91,5 +91,8 @@ namespace VRM
/// </summary>
[SerializeField]
public bool IsBinary;
// [SerializeField]
// public Texture2D Thumbnail;
}
}

View File

@ -7,133 +7,38 @@ using UnityEngine;
namespace VRM
{
/// <summary>
/// ブレンドシェイプを蓄えてまとめて適用するクラス
/// </summary>
class BlendShapeMerger
{
/// <summary>
/// Key からBlendShapeClipを得る
/// </summary>
Dictionary<BlendShapeKey, BlendShapeClip> m_clipMap;
/// <summary>
/// BlendShape のWeightを記録する
/// </summary>
Dictionary<BlendShapeKey, float> m_valueMap;
Dictionary<string, Material> m_materialMap;
BlendShapeBindingMerger m_blendShapeBindingMerger;
Dictionary<BlendShapeBinding, float> m_blendShapeValueMap = new Dictionary<BlendShapeBinding, float>();
Dictionary<BlendShapeBinding, Action<float>> m_blendShapeSetterMap = new Dictionary<BlendShapeBinding, Action<float>>();
MaterialValueBindingMerger m_materialValueBindingMerger;
Dictionary<MaterialValueBinding, float> m_materialValueMap = new Dictionary<MaterialValueBinding, float>();
Dictionary<MaterialValueBinding, Action<float>> m_materialSetterMap = new Dictionary<MaterialValueBinding, Action<float>>();
public BlendShapeMerger(IEnumerable<BlendShapeClip> clips, Transform root)
{
m_materialMap = new Dictionary<string, Material>();
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);
}
}
}
}
}
m_clipMap = clips.ToDictionary(x => BlendShapeKey.CreateFrom(x), x => x);
m_valueMap = new Dictionary<BlendShapeKey, float>();
foreach (var kv in m_clipMap)
{
foreach (var binding in kv.Value.Values)
{
if (!m_blendShapeSetterMap.ContainsKey(binding))
{
var _target = root.Find(binding.RelativePath);
SkinnedMeshRenderer target = null;
if (_target != null)
{
target = _target.GetComponent<SkinnedMeshRenderer>();
}
if (target != null)
{
if (binding.Index >= 0 && binding.Index < target.sharedMesh.blendShapeCount)
{
m_blendShapeSetterMap.Add(binding, x =>
{
target.SetBlendShapeWeight(binding.Index, x);
});
}
else
{
Debug.LogWarningFormat("Invalid blendshape binding: {0}: {1}", target.name, binding);
}
}
else
{
Debug.LogWarningFormat("SkinnedMeshRenderer: {0} not found", binding.RelativePath);
}
}
}
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);
Action<float> setter = value =>
{
var propValue = binding.BaseValue + (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);
Action<float> setter = value =>
{
var propValue = binding.BaseValue + (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
{
Action<float> vec4Setter = x =>
{
var propValue = binding.BaseValue + (binding.TargetValue - binding.BaseValue) * x;
target.SetColor(binding.ValueName, propValue);
};
m_materialSetterMap.Add(binding, vec4Setter);
}
}
else
{
Debug.LogWarningFormat("material: {0} not found", binding.MaterialName);
}
}
}
}
m_blendShapeBindingMerger = new BlendShapeBindingMerger(m_clipMap, root);
m_materialValueBindingMerger = new MaterialValueBindingMerger(m_clipMap, root);
}
/*
public void Clear()
{
foreach (var kv in m_valueMap.ToArray())
@ -142,40 +47,36 @@ namespace VRM
}
Apply();
}
*/
/// <summary>
/// 蓄積した値を適用する
/// </summary>
public void Apply()
{
foreach (var kv in m_blendShapeValueMap)
{
Action<float> setter;
if (m_blendShapeSetterMap.TryGetValue(kv.Key, out setter))
{
setter(kv.Value);
}
}
m_blendShapeValueMap.Clear();
foreach (var kv in m_materialValueMap)
{
Action<float> setter;
if (m_materialSetterMap.TryGetValue(kv.Key, out setter))
{
setter(kv.Value);
}
}
m_materialValueMap.Clear();
m_blendShapeBindingMerger.Apply();
m_materialValueBindingMerger.Apply();
}
/// <summary>
/// まとめて反映する。1フレームに1回呼び出されることを想定
/// </summary>
/// <param name="values"></param>
public void SetValues(IEnumerable<KeyValuePair<BlendShapeKey, float>> values)
{
foreach (var kv in values)
{
SetValue(kv.Key, kv.Value, false);
AccumulateValue(kv.Key, kv.Value);
}
Apply();
}
public void SetValue(BlendShapeKey key, float value, bool replace)
/// <summary>
/// 即時に反映しない。後にApplyによって反映する
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AccumulateValue(BlendShapeKey key, float value)
{
m_valueMap[key] = value;
@ -185,57 +86,48 @@ namespace VRM
return;
}
foreach (var binding in clip.Values)
if (clip.IsBinary)
{
if (replace)
{
// 値置き換え
Action<float> setter;
if (m_blendShapeSetterMap.TryGetValue(binding, out setter))
{
setter(binding.Weight * value);
}
}
else
{
// 積算
float acc;
if (m_blendShapeValueMap.TryGetValue(binding, out acc))
{
m_blendShapeValueMap[binding] = acc + binding.Weight * value;
}
else
{
m_blendShapeValueMap[binding] = binding.Weight * value;
}
}
value = Mathf.Round(value);
}
// materialの更新
foreach (var binding in clip.MaterialValues)
m_blendShapeBindingMerger.AccumulateValue(clip, value);
m_materialValueBindingMerger.AccumulateValue(clip, value);
}
/// <summary>
/// 即時に反映する
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void ImmediatelySetValue(BlendShapeKey key, float value)
{
m_valueMap[key] = value;
BlendShapeClip clip;
if (!m_clipMap.TryGetValue(key, out clip))
{
if (replace)
{
// 値置き換え
Action<float> setter;
if (m_materialSetterMap.TryGetValue(binding, out setter))
{
setter(value);
}
}
else
{
// 積算
float acc;
if (m_materialValueMap.TryGetValue(binding, out acc))
{
m_materialValueMap[binding] = acc + value;
}
else
{
m_materialValueMap[binding] = value;
}
}
return;
}
if (clip.IsBinary)
{
value = Mathf.Round(value);
}
m_blendShapeBindingMerger.ImmediatelySetValue(clip, value);
m_materialValueBindingMerger.ImmediatelySetValue(clip, value);
}
public void SetValue(BlendShapeKey key, float value, bool immediately)
{
if (immediately)
{
ImmediatelySetValue(key, value);
}
else
{
AccumulateValue(key, value);
}
}
@ -251,25 +143,7 @@ namespace VRM
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))
{
material.SetColor(y.ValueName, y.BaseValue);
}
else
{
Debug.LogWarningFormat("{0} not found", y.MaterialName);
}
}
}
}
m_materialValueBindingMerger.RestoreMaterialInitialValues(clips);
}
}
}

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using UniGLTF;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
@ -12,260 +13,147 @@ namespace VRM
[CustomEditor(typeof(BlendShapeAvatar))]
public class BlendShapeAvatarEditor : PreviewEditor
{
static String[] Presets = ((BlendShapePreset[])Enum.GetValues(typeof(BlendShapePreset)))
.Select(x => x.ToString()).ToArray();
ReorderableList m_clipList;
BlendShapeAvatar m_target;
void AddBlendShapeClip()
BlendShapeClipSelector m_selector;
SerializedBlendShapeEditor m_clipEditor;
protected override PreviewSceneManager.BakeValue GetBakeValue()
{
var dir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(m_target));
var path = EditorUtility.SaveFilePanel(
"Create BlendShapeClip",
dir,
string.Format("BlendShapeClip#{0}.asset", m_target.Clips.Count),
"asset");
if (string.IsNullOrEmpty(path))
var clip = m_selector.Selected;
var value = new PreviewSceneManager.BakeValue();
if (clip != null)
{
return;
value.BlendShapeBindings = clip.Values;
value.MaterialValueBindings = clip.MaterialValues;
value.Weight = 1.0f;
}
path = path.ToUnityRelativePath();
//Debug.LogFormat("{0}", path);
var clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.BlendShapeName = Path.GetFileNameWithoutExtension(path);
clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(m_target));
AssetDatabase.CreateAsset(clip, path);
AssetDatabase.ImportAsset(path);
m_target.Clips.Add(clip);
EditorUtility.SetDirty(m_target);
AssetDatabase.SaveAssets();
return value;
}
BlendShapeClip m_currentClip;
BlendShapeClip CurrentClip
void OnSelected(BlendShapeClip clip)
{
get { return m_currentClip; }
set
if (PreviewSceneManager == null)
{
if (m_currentClip == value) return;
m_currentClip = value;
//ClearBlendShape();
if (m_currentClip != null)
m_clipEditor = null;
}
else if (clip != null)
{
m_clipEditor = new SerializedBlendShapeEditor(clip, PreviewSceneManager);
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
{
Bake(m_currentClip.Values, m_currentClip.MaterialValues, 1.0f);
}
BlendShapeBindings = clip.Values,
MaterialValueBindings = clip.MaterialValues,
Weight = 1.0f
});
}
}
void OnPrefabChanged()
{
if (m_currentClip != null)
else
{
Bake(m_currentClip.Values, m_currentClip.MaterialValues, 1.0f);
m_clipEditor = null;
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue());
}
}
protected override void OnEnable()
{
PrefabChanged += OnPrefabChanged;
m_selector = new BlendShapeClipSelector((BlendShapeAvatar)target, OnSelected);
var prop = serializedObject.FindProperty("Clips");
m_clipList = new ReorderableList(serializedObject, prop);
m_clipList.drawHeaderCallback = (rect) =>
EditorGUI.LabelField(rect, "BlendShapeClips");
m_clipList.elementHeight = BlendShapeClipDrawer.Height;
m_clipList.drawElementCallback = (rect, index, isActive, isFocused) =>
{
var element = prop.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
EditorGUI.PropertyField(rect, element);
};
m_clipList.onAddCallback += (list) =>
{
// Add slot
prop.arraySize++;
// select last item
list.index = prop.arraySize - 1;
// get last item
var element = prop.GetArrayElementAtIndex(list.index);
element.objectReferenceValue = null;
var dir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(target));
var path = EditorUtility.SaveFilePanel(
"Create BlendShapeClip",
dir,
string.Format("BlendShapeClip#{0}.asset", list.count),
"asset");
if (!string.IsNullOrEmpty(path))
{
var clip = BlendShapeAvatar.CreateBlendShapeClip(path.ToUnityRelativePath());
//clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(target));
element.objectReferenceValue = clip;
}
};
m_clipList.onSelectCallback += (list) =>
{
var a = list.serializedProperty;
var selected = a.GetArrayElementAtIndex(list.index);
OnSelected((BlendShapeClip)selected.objectReferenceValue);
};
//m_clipList.onCanRemoveCallback += list => true;
base.OnEnable();
m_target = (BlendShapeAvatar)target;
// remove missing values
foreach(var x in m_target.Clips.Select((x, i) => new { i, x }).Where(x => x.x == null).Reverse())
{
m_target.Clips.RemoveAt(x.i);
}
if(m_target.Clips.Count > 0)
{
CurrentClip = m_target.Clips[0];
}
OnSelected(m_selector.Selected);
}
protected override void OnDisable()
{
base.OnDisable();
PrefabChanged -= OnPrefabChanged;
}
int m_mode;
static readonly string[] MODES = new string[]{
"Editor",
"List"
};
List<bool> m_meshFolds = new List<bool>();
int m_preset;
public override void OnInspectorGUI()
{
serializedObject.Update();
base.OnInspectorGUI();
// buttons
if (m_target.Clips != null)
m_mode = GUILayout.Toolbar(m_mode, MODES);
switch (m_mode)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Select BlendShapeClip", EditorStyles.boldLabel);
var array = m_target.Clips
.Select(x => x != null
? BlendShapeKey.CreateFrom(x).ToString()
: "null"
).ToArray();
var preset = GUILayout.SelectionGrid(m_preset, array, 4);
if (preset != m_preset)
{
CurrentClip = m_target.Clips[preset];
m_preset = preset;
}
}
// Add
if (GUILayout.Button("Add BlendShapeClip"))
{
AddBlendShapeClip();
}
if (CurrentClip != null)
{
// clip
EditorGUILayout.Space();
EditorGUILayout.LabelField("CurrentClip", EditorStyles.boldLabel);
/*var loadClip = (BlendShapeClip)*/
GUI.enabled = false;
EditorGUILayout.ObjectField("Current clip",
CurrentClip, typeof(BlendShapeClip), false);
GUI.enabled = true;
CurrentClip.Preset = (BlendShapePreset)EditorGUILayout.Popup("Preset", (int)CurrentClip.Preset, Presets);
GUI.enabled = false;
CurrentClip.BlendShapeName = EditorGUILayout.TextField("BlendShapeName", CurrentClip.BlendShapeName);
GUI.enabled = true;
var key = BlendShapeKey.CreateFrom(CurrentClip);
if (m_target.Clips.Where(x => key.Match(x)).Count() > 1)
{
EditorGUILayout.HelpBox("duplicate clip", MessageType.Error);
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("BlendShapeValues", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Clear"))
{
ClearBlendShape();
}
if (CurrentClip != null && GUILayout.Button("Apply"))
{
string maxWeightString;
CurrentClip.Values = GetBindings(out maxWeightString);
EditorUtility.SetDirty(CurrentClip);
}
EditorGUILayout.EndHorizontal();
// sliders
bool changed = false;
int foldIndex = 0;
if (PreviewSceneManager != null)
{
foreach (var item in PreviewSceneManager.EnumRenderItems.Where(x => x.SkinnedMeshRenderer != null))
case 0:
m_selector.SelectGUI();
if (m_clipEditor != null)
{
var mesh = item.SkinnedMeshRenderer.sharedMesh;
if (mesh != null && mesh.blendShapeCount > 0)
Separator();
var result = m_clipEditor.Draw();
if (result.Changed)
{
//var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, m_target.transform);
//EditorGUILayout.LabelField(m_target.name + "/" + item.Path);
if (foldIndex >= m_meshFolds.Count)
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
{
m_meshFolds.Add(false);
}
m_meshFolds[foldIndex] = EditorGUILayout.Foldout(m_meshFolds[foldIndex], item.SkinnedMeshRenderer.name);
if (m_meshFolds[foldIndex])
{
//EditorGUI.indentLevel += 1;
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var src = item.SkinnedMeshRenderer.GetBlendShapeWeight(i);
var dst = EditorGUILayout.Slider(mesh.GetBlendShapeName(i), src, 0, 100.0f);
if (dst != src)
{
item.SkinnedMeshRenderer.SetBlendShapeWeight(i, dst);
changed = true;
}
}
//EditorGUI.indentLevel -= 1;
}
++foldIndex;
BlendShapeBindings = result.BlendShapeBindings,
MaterialValueBindings = result.MaterialValueBindings,
Weight = 1.0f,
});
}
}
break;
if (changed)
{
PreviewSceneManager.Bake();
}
}
case 1:
m_clipList.DoLayoutList();
break;
default:
throw new NotImplementedException();
}
}
BlendShapeBinding[] GetBindings(out string _maxWeightName)
{
var maxWeight = 0.0f;
var maxWeightName = "";
// weightのついたblendShapeを集める
var values = PreviewSceneManager.EnumRenderItems
.Where(x => x.SkinnedMeshRenderer!=null)
.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;
}
private void ClearBlendShape()
{
foreach (var item in PreviewSceneManager.EnumRenderItems.Where(x => x.SkinnedMeshRenderer!=null))
{
var renderer = item.SkinnedMeshRenderer;
var mesh = renderer.sharedMesh;
if (mesh != null)
{
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
renderer.SetBlendShapeWeight(i, 0);
}
}
}
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@ -0,0 +1,63 @@
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace VRM
{
[CustomPropertyDrawer(typeof(BlendShapeClip))]
public class BlendShapeClipDrawer : PropertyDrawer
{
//public const int Height = 132;
//public const int ThumbnailSize = 128;
public const int Height = 80;
public const int ThumbnailSize = 0;
public override void OnGUI(Rect position,
SerializedProperty property, GUIContent label)
{
using (new EditorGUI.PropertyScope(position, label, property))
{
//EditorGUIUtility.labelWidth = 80;
position.height = EditorGUIUtility.singleLineHeight;
var halfWidth = position.width * 0.5f;
var rect = new Rect(position.x + ThumbnailSize, position.y, position.width - ThumbnailSize, position.height);
EditorGUI.PropertyField(rect, property);
var clip = property.objectReferenceValue as BlendShapeClip;
if (clip != null)
{
var clipObj = new SerializedObject(clip);
//var thumbnail = clipObj.FindProperty("Thumbnail");
var blendShapeName = clipObj.FindProperty("BlendShapeName");
var preset = clipObj.FindProperty("Preset");
var isBinary = clipObj.FindProperty("IsBinary");
/*
EditorGUI.ObjectField(new Rect(position)
{
width = ThumbnailSize,
height = ThumbnailSize
}, thumbnail.objectReferenceValue, typeof(Texture), false);
*/
rect.y += (EditorGUIUtility.singleLineHeight + 2);
EditorGUI.PropertyField(rect, blendShapeName);
rect.y += (EditorGUIUtility.singleLineHeight + 2);
EditorGUI.PropertyField(rect, preset);
rect.y += (EditorGUIUtility.singleLineHeight + 2);
EditorGUI.PropertyField(rect, isBinary);
clipObj.ApplyModifiedProperties();
}
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6a31667cfcf461c47b3b384211e2d9ff
timeCreated: 1541179390
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,4 +1,6 @@
using System;
using System.IO;
using UniGLTF;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
@ -9,113 +11,154 @@ namespace VRM
[CustomEditor(typeof(BlendShapeClip))]
public class BlendShapeClipEditor : PreviewEditor
{
float m_previewSlider;
#region for Editor
SerializedProperty m_BlendShapeNameProp;
SerializedProperty m_PresetProp;
SerializedProperty m_ValuesProp;
ReorderableList m_ValuesList;
SerializedProperty m_MaterialValuesProp;
ReorderableList m_MaterialValuesList;
#endregion
SerializedBlendShapeEditor m_serializedEditor;
BlendShapeClip m_target;
bool m_changed;
protected override PreviewSceneManager.BakeValue GetBakeValue()
{
return new PreviewSceneManager.BakeValue
{
BlendShapeBindings = m_target.Values,
MaterialValueBindings = m_target.MaterialValues,
Weight = 1.0f,
};
}
//SerializedProperty m_thumbnailProp;
SerializedProperty m_isBinaryProp;
protected override GameObject GetPrefab()
{
return m_target.Prefab;
}
void OnPrefabChanged()
{
m_target.Prefab = Prefab;
Bake(m_target.Values, m_target.MaterialValues, 1.0f);
}
protected override void OnEnable()
{
m_target = (BlendShapeClip)target;
PrefabChanged += OnPrefabChanged;
base.OnEnable();
m_previewSlider = 1.0f;
Bake(m_target.Values, m_target.MaterialValues, m_previewSlider);
m_BlendShapeNameProp = serializedObject.FindProperty("BlendShapeName");
m_PresetProp = serializedObject.FindProperty("Preset");
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 (DrawBlendShapeBinding(rect, element, PreviewSceneManager))
{
m_changed = true;
}
};
m_MaterialValuesProp = serializedObject.FindProperty("MaterialValues");
m_MaterialValuesList = new ReorderableList(serializedObject, m_MaterialValuesProp);
m_MaterialValuesList.elementHeight = MaterialValueBindingHeight;
m_MaterialValuesList.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = m_MaterialValuesProp.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if (DrawMaterialValueBinding(rect, element, PreviewSceneManager))
{
m_changed = true;
}
};
}
protected override void OnDisable()
float m_previewSlider = 1.0f;
static Texture2D SaveResizedImage(RenderTexture rt, UnityPath path, int size)
{
base.OnDisable();
PrefabChanged -= OnPrefabChanged;
var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
RenderTexture.active = rt;
tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
tex.Apply();
//TextureScale.Scale(tex, size, size);
tex = TextureScale.GetResized(tex, size, size);
byte[] bytes;
switch (path.Extension.ToLower())
{
case ".png":
bytes = tex.EncodeToPNG();
break;
case ".jpg":
bytes = tex.EncodeToJPG();
break;
default:
throw new Exception();
}
if (Application.isPlaying)
{
UnityEngine.Object.Destroy(tex);
}
else
{
UnityEngine.Object.DestroyImmediate(tex);
}
File.WriteAllBytes(path.FullPath, bytes);
path.ImportAsset();
return path.LoadAsset<Texture2D>();
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
m_changed = false;
if (PreviewSceneManager == null)
{
return;
}
serializedObject.Update();
EditorGUILayout.Space();
var previewSlider = EditorGUILayout.Slider("Preview Weight", m_previewSlider, 0, 1.0f);
if (m_serializedEditor == null)
{
m_serializedEditor = new SerializedBlendShapeEditor(serializedObject, PreviewSceneManager);
//m_thumbnailProp = serializedObject.FindProperty("Thumbnail");
m_isBinaryProp = serializedObject.FindProperty("IsBinary");
}
int thumbnailSize = 96;
EditorGUILayout.BeginHorizontal();
/*
var objectReferenceValue = EditorGUILayout.ObjectField(m_thumbnailProp.objectReferenceValue, typeof(Texture), false,
GUILayout.Width(thumbnailSize), GUILayout.Height(thumbnailSize));
if (m_thumbnailProp.objectReferenceValue != objectReferenceValue)
{
m_thumbnailProp.objectReferenceValue = objectReferenceValue;
serializedObject.ApplyModifiedProperties();
}
*/
var changed = false;
EditorGUILayout.BeginVertical();
base.OnInspectorGUI();
EditorGUILayout.LabelField("Preview Weight");
var previewSlider = EditorGUILayout.Slider(m_previewSlider, 0, 1.0f);
GUI.enabled = PreviewTexture != null;
/*
if (GUILayout.Button("save thumbnail"))
{
//var ext = "jpg";
var ext = "png";
var asset = UnityPath.FromAsset(target);
var path = EditorUtility.SaveFilePanel(
"save thumbnail",
asset.Parent.FullPath,
string.Format("{0}.{1}", asset.FileNameWithoutExtension, ext),
ext);
if (!string.IsNullOrEmpty(path))
{
var thumbnail = SaveResizedImage(PreviewTexture, UnityPath.FromFullpath(path),
BlendShapeClipDrawer.ThumbnailSize);
m_thumbnailProp.objectReferenceValue = thumbnail;
serializedObject.ApplyModifiedProperties();
}
}
*/
GUI.enabled = true;
EditorGUILayout.EndVertical();
if (m_isBinaryProp.boolValue)
{
previewSlider = Mathf.Round(previewSlider);
}
if (previewSlider != m_previewSlider)
{
m_previewSlider = previewSlider;
m_changed = true;
changed = true;
}
EditorGUILayout.EndHorizontal();
Separator();
EditorGUILayout.Space();
serializedObject.Update();
EditorGUILayout.PropertyField(m_BlendShapeNameProp, true);
EditorGUILayout.PropertyField(m_PresetProp, true);
EditorGUILayout.LabelField("BlendShapeBindings", EditorStyles.boldLabel);
m_ValuesList.DoLayoutList();
EditorGUILayout.LabelField("MaterialValueBindings", EditorStyles.boldLabel);
m_MaterialValuesList.DoLayoutList();
serializedObject.ApplyModifiedProperties();
if (m_changed && PreviewSceneManager != null)
var result = m_serializedEditor.Draw();
if ((changed || result.Changed) && PreviewSceneManager != null)
{
PreviewSceneManager.Bake(m_target.Values, m_target.MaterialValues, m_previewSlider);
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
{
BlendShapeBindings = result.BlendShapeBindings,
MaterialValueBindings = result.MaterialValueBindings,
Weight = m_previewSlider
});
}
}
@ -123,202 +166,5 @@ namespace VRM
{
return BlendShapeKey.CreateFrom((BlendShapeClip)target).ToString();
}
public static int BlendShapeBindingHeight = 60;
public static bool DrawBlendShapeBinding(Rect position, SerializedProperty property,
PreviewSceneManager scene)
{
bool changed = false;
if (scene != null)
{
var height = 16;
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int pathIndex;
if (StringPopup(rect, property.FindPropertyRelative("RelativePath"), scene.SkinnedMeshRendererPathList, out pathIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
int blendShapeIndex;
if (IntPopup(rect, property.FindPropertyRelative("Index"), scene.GetBlendShapeNames(pathIndex), out blendShapeIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
if (FloatSlider(rect, property.FindPropertyRelative("Weight"), 100))
{
changed = true;
}
}
return changed;
}
public static int MaterialValueBindingHeight = 90;
public static bool DrawMaterialValueBinding(Rect position, SerializedProperty property,
PreviewSceneManager scene)
{
bool changed = false;
if (scene != null)
{
var height = 16;
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int materialIndex;
if (StringPopup(rect, property.FindPropertyRelative("MaterialName"), scene.MaterialNames, out materialIndex))
{
changed = true;
}
if (materialIndex >= 0)
{
var materialItem = scene.GetMaterialItem(scene.MaterialNames[materialIndex]);
if (materialItem != null)
{
y += height;
rect = new Rect(position.x, y, position.width, height);
// プロパティ名のポップアップ
int propIndex;
if (StringPopup(rect, property.FindPropertyRelative("ValueName"), materialItem.PropNames, out propIndex))
{
changed = true;
}
if (propIndex >= 0)
{
// 有効なプロパティ名が選択された
var propItem = materialItem.PropMap[materialItem.PropNames[propIndex]];
{
switch (propItem.PropertyType)
{
case ShaderUtil.ShaderPropertyType.Color:
{
property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
// max
y += height;
rect = new Rect(position.x, y, position.width, height);
if (ColorProp(rect, property.FindPropertyRelative("TargetValue")))
{
changed = true;
}
}
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
{
property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
// max
y += height;
rect = new Rect(position.x, y, position.width, height);
if (OffsetProp(rect, property.FindPropertyRelative("TargetValue")))
{
changed = true;
}
}
break;
}
}
}
}
}
}
return changed;
}
static bool StringPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = Array.IndexOf(options, prop.stringValue);
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.stringValue = options[newIndex];
return true;
}
else
{
return false;
}
}
static bool IntPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = prop.intValue;
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.intValue = newIndex;
return true;
}
else
{
return false;
}
}
static bool FloatSlider(Rect rect, SerializedProperty prop, float maxValue)
{
var oldValue = prop.floatValue;
var newValue = EditorGUI.Slider(rect, prop.floatValue, 0, 100f);
if (newValue != oldValue)
{
prop.floatValue = newValue;
return true;
}
else
{
return false;
}
}
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;
}
}
static bool OffsetProp(Rect rect, SerializedProperty prop)
{
var oldValue = prop.vector4Value;
var newValue = EditorGUI.Vector4Field(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector4Value = newValue;
return true;
}
else
{
return false;
}
}
}
}

View File

@ -0,0 +1,341 @@
using System;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public static class BlendShapeClipEditorHelper
{
///
/// BlendShape List のElement描画
///
public static bool DrawBlendShapeBinding(Rect position, SerializedProperty property,
PreviewSceneManager scene)
{
bool changed = false;
if (scene != null)
{
var height = 16;
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int pathIndex;
if (StringPopup(rect, property.FindPropertyRelative("RelativePath"), scene.SkinnedMeshRendererPathList, out pathIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
int blendShapeIndex;
if (IntPopup(rect, property.FindPropertyRelative("Index"), scene.GetBlendShapeNames(pathIndex), out blendShapeIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
if (FloatSlider(rect, property.FindPropertyRelative("Weight"), 100))
{
changed = true;
}
}
return changed;
}
///
/// Material List のElement描画
///
public static bool DrawMaterialValueBinding(Rect position, SerializedProperty property,
PreviewSceneManager scene)
{
bool changed = false;
if (scene != null)
{
var height = 16;
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int materialIndex;
if (StringPopup(rect, property.FindPropertyRelative("MaterialName"), scene.MaterialNames, out materialIndex))
{
changed = true;
}
if (materialIndex >= 0)
{
var materialItem = scene.GetMaterialItem(scene.MaterialNames[materialIndex]);
if (materialItem != null)
{
y += height;
rect = new Rect(position.x, y, position.width, height);
// プロパティ名のポップアップ
int propIndex;
if (StringPopup(rect, property.FindPropertyRelative("ValueName"), materialItem.PropNames, out propIndex))
{
changed = true;
}
if (propIndex >= 0)
{
// 有効なプロパティ名が選択された
var propItem = materialItem.PropMap[materialItem.PropNames[propIndex]];
{
switch (propItem.PropertyType)
{
case ShaderUtil.ShaderPropertyType.Color:
{
property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
// max
y += height;
rect = new Rect(position.x, y, position.width, height);
if (ColorProp(rect, property.FindPropertyRelative("TargetValue")))
{
changed = true;
}
}
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
{
property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
// max
y += height;
rect = new Rect(position.x, y, position.width, height);
if (OffsetProp(rect, property.FindPropertyRelative("TargetValue")))
{
changed = true;
}
}
break;
}
}
}
}
}
}
return changed;
}
#region Private
static bool StringPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = Array.IndexOf(options, prop.stringValue);
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.stringValue = options[newIndex];
return true;
}
else
{
return false;
}
}
static bool IntPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = prop.intValue;
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.intValue = newIndex;
return true;
}
else
{
return false;
}
}
static bool FloatSlider(Rect rect, SerializedProperty prop, float maxValue)
{
var oldValue = prop.floatValue;
var newValue = EditorGUI.Slider(rect, prop.floatValue, 0, 100f);
if (newValue != oldValue)
{
prop.floatValue = newValue;
return true;
}
else
{
return false;
}
}
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;
}
}
static Rect AdvanceRect(ref float x, float y, float w, float h)
{
var rect = new Rect(x, y, w, h);
x += w;
return rect;
}
static float[] v2 = new float[2];
static GUIContent[] l2 = new GUIContent[]{
new GUIContent("x"),
new GUIContent("y")
};
static Vector4 TilingOffset(Rect rect, string label, Vector4 src)
{
/*
var style = new GUIStyle()
{
alignment = TextAnchor.MiddleRight,
};
*/
var quad = (rect.width - 56);
var x = rect.x;
//EditorGUIUtility.labelWidth = 18;
EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Tiling");
v2[0] = src.x;
v2[1] = src.y;
EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
src.x = v2[0];
src.y = v2[1];
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
//src.y = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.y);
rect.y += EditorGUIUtility.singleLineHeight;
x = rect.x;
EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Offset");
v2[0] = src.z;
v2[1] = src.w;
EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
src.z = v2[0];
src.w = v2[1];
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad * 2, rect.height), "Offset X", style);
//src.z = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "X", src.z);
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
//src.w = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.w);
return src;
}
static bool OffsetProp(Rect rect, SerializedProperty prop)
{
var oldValue = prop.vector4Value;
//var newValue = EditorGUI.Vector4Field(rect, prop.displayName, oldValue);
var newValue = TilingOffset(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector4Value = newValue;
return true;
}
else
{
return false;
}
}
#endregion
}
/// https://gist.github.com/gszauer/7799899
public class TextureScale
{
private static Color[] texColors;
private static Color[] newColors;
private static int w;
private static float ratioX;
private static float ratioY;
private static int w2;
public static void Scale(Texture2D tex, int newWidth, int newHeight)
{
texColors = tex.GetPixels();
newColors = new Color[newWidth * newHeight];
ratioX = 1.0f / ((float)newWidth / (tex.width - 1));
ratioY = 1.0f / ((float)newHeight / (tex.height - 1));
w = tex.width;
w2 = newWidth;
BilinearScale(0, newHeight);
tex.Resize(newWidth, newHeight);
tex.SetPixels(newColors);
tex.Apply();
}
private static void BilinearScale(int start, int end)
{
for (var y = start; y < end; y++)
{
int yFloor = (int)Mathf.Floor(y * ratioY);
var y1 = yFloor * w;
var y2 = (yFloor + 1) * w;
var yw = y * w2;
for (var x = 0; x < w2; x++)
{
int xFloor = (int)Mathf.Floor(x * ratioX);
var xLerp = x * ratioX - xFloor;
newColors[yw + x] = ColorLerpUnclamped(ColorLerpUnclamped(texColors[y1 + xFloor], texColors[y1 + xFloor + 1], xLerp),
ColorLerpUnclamped(texColors[y2 + xFloor], texColors[y2 + xFloor + 1], xLerp),
y * ratioY - yFloor);
}
}
}
private static Color ColorLerpUnclamped(Color c1, Color c2, float value)
{
return new Color(c1.r + (c2.r - c1.r) * value,
c1.g + (c2.g - c1.g) * value,
c1.b + (c2.b - c1.b) * value,
c1.a + (c2.a - c1.a) * value);
}
/// http://light11.hatenadiary.com/entry/2018/04/19/194015
public static Texture2D GetResized(Texture2D texture, int width, int height)
{
// リサイズ後のサイズを持つRenderTextureを作成して書き込む
var rt = RenderTexture.GetTemporary(width, height);
Graphics.Blit(texture, rt);
// リサイズ後のサイズを持つTexture2Dを作成してRenderTextureから書き込む
var preRT = RenderTexture.active;
RenderTexture.active = rt;
var ret = new Texture2D(width, height);
ret.ReadPixels(new Rect(0, 0, width, height), 0, 0);
ret.Apply();
RenderTexture.active = preRT;
RenderTexture.ReleaseTemporary(rt);
return ret;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fcb56a7eb1db73c4cb9ea1689635b246
timeCreated: 1541144400
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,99 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEditor;
using System.IO;
using UniGLTF;
namespace VRM
{
class BlendShapeClipSelector
{
BlendShapeAvatar m_avatar;
public BlendShapeClip Selected
{
get
{
if (m_avatar == null || m_avatar.Clips == null)
{
return null;
}
if (m_selectedIndex < 0 || m_selectedIndex >= m_avatar.Clips.Count)
{
return null;
}
return m_avatar.Clips[m_selectedIndex];
}
}
int m_selectedIndex;
int SelectedIndex
{
get { return m_selectedIndex; }
set
{
if (m_selectedIndex == value) return;
m_selectedIndex = value;
if (m_onSelected != null)
{
m_onSelected(Selected);
}
}
}
Action<BlendShapeClip> m_onSelected;
public BlendShapeClipSelector(BlendShapeAvatar avatar, Action<BlendShapeClip> onSelected)
{
avatar.RemoveNullClip();
m_avatar = avatar;
m_onSelected = onSelected;
onSelected(Selected);
}
public void SelectGUI()
{
if (m_avatar != null && m_avatar.Clips != null)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Select BlendShapeClip", EditorStyles.boldLabel);
var array = m_avatar.Clips
.Select(x => x != null
? BlendShapeKey.CreateFrom(x).ToString()
: "null"
).ToArray();
SelectedIndex = GUILayout.SelectionGrid(SelectedIndex, array, 4);
}
if (GUILayout.Button("Add BlendShapeClip"))
{
var dir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(m_avatar));
var path = EditorUtility.SaveFilePanel(
"Create BlendShapeClip",
dir,
string.Format("BlendShapeClip#{0}.asset", m_avatar.Clips.Count),
"asset");
if (!string.IsNullOrEmpty(path))
{
var clip = BlendShapeAvatar.CreateBlendShapeClip(path.ToUnityRelativePath());
//clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(target));
m_avatar.Clips.Add(clip);
}
}
}
public void DuplicateWarn()
{
var key = BlendShapeKey.CreateFrom(Selected);
if (m_avatar.Clips.Where(x => key.Match(x)).Count() > 1)
{
EditorGUILayout.HelpBox("duplicate clip: " + key, MessageType.Error);
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7842ed7a65c676740aa02678316dd625
timeCreated: 1541138009
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -2,6 +2,8 @@
using UnityEngine;
using UnityEditorInternal;
using System;
using System.Linq;
using System.Collections.Generic;
namespace VRM
{
@ -11,7 +13,7 @@ namespace VRM
/// * https://github.com/Unity-Technologies/UnityCsReference/blob/11bcfd801fccd2a52b09bb6fd636c1ddcc9f1705/Editor/Mono/Inspector/ModelInspector.cs
///
/// </summary>
public class PreviewEditor : Editor
public abstract class PreviewEditor : Editor
{
/// <summary>
/// PreviewRenderUtilityを管理する。
@ -46,6 +48,8 @@ namespace VRM
private set
{
if (m_prefab == value) return;
//Debug.LogFormat("Prefab = {0}", value);
m_prefab = value;
if (m_scene != null)
@ -62,30 +66,23 @@ namespace VRM
{
m_scene.gameObject.SetActive(false);
}
RaisePrefabChanged();
Bake();
}
}
}
protected event Action PrefabChanged;
void RaisePrefabChanged()
{
var handler = PrefabChanged;
if (handler == null) return;
handler();
}
protected abstract PreviewSceneManager.BakeValue GetBakeValue();
/// <summary>
/// シーンにBlendShapeとMaterialMorphを適用する
/// Preview シーンに BlendShape と MaterialValue を適用する
/// </summary>
/// <param name="values"></param>
/// <param name="materialValues"></param>
/// <param name="weight"></param>
protected void Bake(BlendShapeBinding[] values, MaterialValueBinding[] materialValues, float weight)
protected void Bake()
{
if (m_scene != null)
{
//Debug.Log("Bake");
m_scene.Bake(values, materialValues, weight);
m_scene.Bake(GetBakeValue());
}
}
@ -110,6 +107,7 @@ namespace VRM
protected virtual void OnEnable()
{
m_renderer = new PreviewFaceRenderer();
Prefab = GetPrefab();
}
@ -133,20 +131,35 @@ namespace VRM
}
}
protected static void Separator()
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
//GUILayout.Space();
GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
Prefab = (GameObject)EditorGUILayout.ObjectField("prefab", Prefab, typeof(GameObject), false);
Prefab = (GameObject)EditorGUILayout.ObjectField("Preview Prefab", Prefab, typeof(GameObject), false);
//Separator();
}
private static int sliderHash = "Slider".GetHashCode();
Vector2 m_previewDir;
float m_distance = 1.0f;
float m_yaw = 180.0f;
float m_pitch;
Vector3 m_position = new Vector3(0, 0, -0.8f);
// very important to override this, it tells Unity to render an ObjectPreview at the bottom of the inspector
public override bool HasPreviewGUI() { return true; }
public RenderTexture PreviewTexture;
// the main ObjectPreview function... it's called constantly, like other IMGUI On*GUI() functions
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
@ -155,23 +168,31 @@ namespace VRM
{
if (Event.current.type == EventType.Repaint)
{
EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40f),
EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40f),
"Mesh preview requires\nrender texture support");
}
return;
}
var src = r;
var min = Mathf.Min(r.width, r.height);
r.width = min;
r.height = min;
r.x = src.x + (src.width - min) / 2;
r.y = src.y + (src.height - min) / 2;
//previewDir = Drag2D(previewDir, r);
{
int controlId = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
Event current = Event.current;
switch (current.GetTypeForControl(controlId))
Event e = Event.current;
switch (e.GetTypeForControl(controlId))
{
case EventType.MouseDown:
if (r.Contains(current.mousePosition) && (double)r.width > 50.0)
if (r.Contains(e.mousePosition) && (double)r.width > 50.0)
{
GUIUtility.hotControl = controlId;
current.Use();
e.Use();
EditorGUIUtility.SetWantsMouseJumping(1);
break;
}
@ -186,25 +207,41 @@ namespace VRM
case EventType.MouseDrag:
if (GUIUtility.hotControl == controlId)
{
m_previewDir -= current.delta * (!current.shift ? 1f : 3f) / Mathf.Min(r.width, r.height) * 140f;
m_previewDir.y = Mathf.Clamp(m_previewDir.y, -90f, 90f);
current.Use();
GUI.changed = true;
if (e.button == 2)
{
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height);
m_position.x -= shift.x;
m_position.y += shift.y;
e.Use();
GUI.changed = true;
}
else if (
e.button == 0 ||
e.button == 1)
{
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height) * 140f;
m_yaw += shift.x;
m_pitch += shift.y;
m_pitch = Mathf.Clamp(m_pitch, -90f, 90f);
e.Use();
GUI.changed = true;
}
break;
}
break;
case EventType.ScrollWheel:
//Debug.LogFormat("wheel: {0}", current.delta);
if (r.Contains(current.mousePosition)){
if (current.delta.y > 0)
if (r.Contains(e.mousePosition))
{
if (e.delta.y > 0)
{
m_distance *= 1.1f;
m_position.z *= 1.1f;
Repaint();
}
else if (current.delta.y < 0)
else if (e.delta.y < 0)
{
m_distance *= 0.9f;
m_position.z *= 0.9f;
Repaint();
}
}
@ -222,11 +259,11 @@ namespace VRM
if (m_renderer != null && m_scene != null)
{
var texture = m_renderer.Render(r, background, m_scene, m_previewDir, m_distance);
if (texture != null)
PreviewTexture = m_renderer.Render(r, background, m_scene, m_yaw, m_pitch, m_position) as RenderTexture;
if (PreviewTexture != null)
{
// draw the RenderTexture in the ObjectPreview pane
GUI.DrawTexture(r, texture, ScaleMode.StretchToFill, false);
GUI.DrawTexture(r, PreviewTexture, ScaleMode.StretchToFill, false);
}
}
}

View File

@ -81,7 +81,8 @@ namespace VRM
//const float FACTOR = 0.1f;
public Texture Render(Rect r, GUIStyle background, PreviewSceneManager scene, Vector2 drag, float distance)
public Texture Render(Rect r, GUIStyle background, PreviewSceneManager scene,
float yaw, float pitch, Vector3 position)
{
if (scene == null) return null;
@ -90,7 +91,7 @@ namespace VRM
m_previewUtility.BeginPreview(r, background); // set up the PreviewRenderUtility's mini internal scene
// setup the ObjectPreview's camera
scene.SetupCamera(PreviewCamera, scene.TargetPosition, -drag.x, drag.y, distance);
scene.SetupCamera(PreviewCamera, scene.TargetPosition, yaw, pitch, position);
foreach (var item in scene.EnumRenderItems)
{
@ -115,7 +116,7 @@ namespace VRM
}
}
#region IDisposable Support
#region IDisposable Support
private bool disposedValue = false; // 重複する呼び出しを検出するには
protected virtual void Dispose(bool disposing)
@ -153,6 +154,6 @@ namespace VRM
// TODO: 上のファイナライザーがオーバーライドされる場合は、次の行のコメントを解除してください。
// GC.SuppressFinalize(this);
}
#endregion
#endregion
}
}

View File

@ -0,0 +1,309 @@
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))
{
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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: eaafa3ff7bf991642b922e6af7ecbbc0
timeCreated: 1541081003
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -23,22 +23,14 @@ namespace VRM
m_key = key;
}
public void Slider()
public KeyValuePair<BlendShapeKey, float> Slider()
{
if (m_target.BlendShapeAvatar == null)
{
return;
}
var oldValue = m_target.GetValue(m_key);
var enable = GUI.enabled;
GUI.enabled = Application.isPlaying;
var newValue = EditorGUILayout.Slider(m_key.ToString(), oldValue, 0, 1.0f);
GUI.enabled = enable;
if (Application.isPlaying && oldValue != newValue)
{
m_target.SetValue(m_key, newValue);
}
return new KeyValuePair<BlendShapeKey, float>(m_key, newValue);
}
}
List<BlendShapeSlider> m_sliders;
@ -65,12 +57,14 @@ namespace VRM
EditorGUILayout.HelpBox("Enable when playing", MessageType.Info);
}
if (m_target.BlendShapeAvatar == null)
{
return;
}
if (m_sliders != null)
{
foreach (var slider in m_sliders)
{
slider.Slider();
}
m_target.SetValues(m_sliders.Select(x => x.Slider()));
}
}
}

View File

@ -0,0 +1,194 @@
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
{
/// <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>();
Dictionary<MaterialValueBinding, Setter> m_materialSetterMap = new Dictionary<MaterialValueBinding, Setter>();
public MaterialValueBindingMerger(Dictionary<BlendShapeKey, BlendShapeClip> clipMap, Transform root)
{
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(binding.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(binding.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))
{
material.SetColor(y.ValueName, y.BaseValue);
}
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;
}
}
}
Dictionary<string, int> m_used = new Dictionary<string, int>();
public void Apply()
{
m_used.Clear();
// (binding.Value-Base) * weight を足す
foreach (var kv in m_materialValueMap)
{
Setter setter;
if (m_materialSetterMap.TryGetValue(kv.Key, out setter))
{
int count;
if (m_used.TryGetValue(kv.Key.MaterialName, out count))
{
m_used[kv.Key.MaterialName] += 1;
}
else
{
m_used.Add(kv.Key.MaterialName, 1);
}
setter(kv.Value, count == 0);
}
}
m_materialValueMap.Clear();
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4ccc88d77d5d1e74499d053083ade08d
timeCreated: 1541229189
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -50,6 +50,7 @@ namespace VRM
switch (propType)
{
case ShaderUtil.ShaderPropertyType.Color:
// 色
item.PropMap.Add(name, new PropItem
{
PropertyType = propType,
@ -59,13 +60,35 @@ namespace VRM
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
name += "_ST";
item.PropMap.Add(name, new PropItem
// テクスチャ
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(name);
name += "_ST";
item.PropMap.Add(name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(name);
}
// 縦横分離用
{
var st_name = name + "_S";
item.PropMap.Add(st_name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(st_name);
}
{
var st_name = name + "_T";
item.PropMap.Add(st_name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(st_name);
}
break;
}
}
@ -130,7 +153,7 @@ namespace VRM
Materials = materials;
}
public void Bake(BlendShapeBinding[] values, float weight)
public void Bake(IEnumerable<BlendShapeBinding> values, float weight)
{
if (SkinnedMeshRenderer == null) return;
@ -147,15 +170,15 @@ namespace VRM
{
if (x.RelativePath == Path)
{
if(x.Index>=0 && x.Index < SkinnedMeshRenderer.sharedMesh.blendShapeCount)
if (x.Index >= 0 && x.Index < SkinnedMeshRenderer.sharedMesh.blendShapeCount)
{
SkinnedMeshRenderer.SetBlendShapeWeight(x.Index, x.Weight * weight);
}
else
{
Debug.LogWarningFormat("Out of range {0}: 0 <= {1} < {2}",
SkinnedMeshRenderer.name,
x.Index,
Debug.LogWarningFormat("Out of range {0}: 0 <= {1} < {2}",
SkinnedMeshRenderer.name,
x.Index,
SkinnedMeshRenderer.sharedMesh.blendShapeCount);
}
}

View File

@ -80,6 +80,7 @@ namespace VRM
private void Initialize(GameObject prefab)
{
//Debug.LogFormat("[PreviewSceneManager.Initialize] {0}", prefab);
Prefab = prefab;
var materialNames = new List<string>();
@ -90,7 +91,7 @@ namespace VRM
if (string.IsNullOrEmpty(src.name)) return null; // !
Material dst;
if(!map.TryGetValue(src, out dst))
if (!map.TryGetValue(src, out dst))
{
dst = new Material(src);
map.Add(src, dst);
@ -111,7 +112,7 @@ namespace VRM
m_blendShapeMeshes = m_meshes
.Where(x => x.SkinnedMeshRenderer != null
&& x.SkinnedMeshRenderer.sharedMesh.blendShapeCount>0)
&& x.SkinnedMeshRenderer.sharedMesh.blendShapeCount > 0)
.ToArray();
//Bake(values, materialValues);
@ -183,7 +184,8 @@ namespace VRM
public MaterialItem GetMaterialItem(string materialName)
{
MaterialItem item;
if(!m_materialMap.TryGetValue(materialName, out item)){
if (!m_materialMap.TryGetValue(materialName, out item))
{
return null;
}
@ -204,22 +206,34 @@ namespace VRM
}
#if UNITY_EDITOR
Bounds m_bounds;
public void Bake(BlendShapeBinding[] values=null, MaterialValueBinding[] materialValues=null, float weight=1.0f)
public struct BakeValue
{
//Debug.LogFormat("Bake");
public IEnumerable<BlendShapeBinding> BlendShapeBindings;
public IEnumerable<MaterialValueBinding> MaterialValueBindings;
public float Weight;
}
Bounds m_bounds;
public void Bake(BakeValue bake)
{
//
// Bake BlendShape
//
m_bounds = default(Bounds);
if (m_meshes != null)
{
foreach (var x in m_meshes)
{
x.Bake(values, weight);
x.Bake(bake.BlendShapeBindings, bake.Weight);
m_bounds.Expand(x.Mesh.bounds.size);
}
}
// Udpate Material
if (materialValues != null && m_materialMap != null)
//
// Update Material
//
if (bake.MaterialValueBindings != null && m_materialMap != null)
{
// clear
//Debug.LogFormat("clear material");
@ -231,7 +245,7 @@ namespace VRM
}
}
foreach (var x in materialValues)
foreach (var x in bake.MaterialValueBindings)
{
MaterialItem item;
if (m_materialMap.TryGetValue(x.MaterialName, out item))
@ -240,28 +254,29 @@ namespace VRM
PropItem prop;
if (item.PropMap.TryGetValue(x.ValueName, out prop))
{
var value = x.BaseValue + (x.TargetValue - x.BaseValue) * weight;
item.Material.SetColor(x.ValueName, value);
var valueName = x.ValueName;
if (valueName.EndsWith("_ST_S")
|| valueName.EndsWith("_ST_T"))
{
valueName = valueName.Substring(0, valueName.Length - 2);
}
var value = item.Material.GetVector(valueName);
Debug.LogFormat("{0} => {1}", valueName, x.TargetValue);
value += ((x.TargetValue - x.BaseValue) * bake.Weight);
item.Material.SetColor(valueName, value);
}
}
}
}
}
#endif
/*
int PreviewLayer
{
get;
set;
}
*/
/// <summary>
/// カメラパラメーターを決める
/// </summary>
/// <param name="camera"></param>
public void SetupCamera(Camera camera, Vector3 target, float yaw, float pitch, float distance)
public void SetupCamera(Camera camera, Vector3 target, float yaw, float pitch, Vector3 position)
{
camera.backgroundColor = Color.gray;
camera.clearFlags = CameraClearFlags.Color;
@ -273,16 +288,16 @@ namespace VRM
camera.fieldOfView = 27f;
camera.nearClipPlane = 0.3f;
camera.farClipPlane = distance /*+ magnitude*/ * 2.1f;
camera.farClipPlane = -position.z /*+ magnitude*/ * 2.1f;
#if false
// this used to be "-Vector3.forward * num" but I hardcoded my camera position instead
camera.transform.position = new Vector3(0f, 1.4f, distance);
camera.transform.rotation = Quaternion.Euler(0, 180f, 0);
#else
camera.transform.position = target + Quaternion.Euler(pitch, yaw, 0) * Vector3.forward * distance;
camera.transform.LookAt(target);
#endif
var t = Matrix4x4.Translate(position);
var r = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(pitch, yaw, 0), Vector3.one);
// 回転してから移動
var m = r * t;
camera.transform.position = target + m.ExtractPosition();
camera.transform.rotation = m.ExtractRotation();
//camera.transform.LookAt(target);
//previewLayer のみ表示する
//camera.cullingMask = 1 << PreviewLayer;

View File

@ -159,6 +159,7 @@ namespace VRM
}
}
/*
/// <summary>
/// Clear all blendShape values
/// </summary>
@ -169,6 +170,7 @@ namespace VRM
m_merger.Clear();
}
}
*/
/// <summary>
/// Apply blendShape values that use SetValue apply=false

View File

@ -152,6 +152,7 @@ namespace VRM
{
asset.BlendShapeName = groupName;
asset.Preset = EnumUtil.TryParseOrDefault<BlendShapePreset>(group.presetName);
asset.IsBinary = group.isBinary;
if (asset.Preset == BlendShapePreset.Unknown)
{
// fallback
@ -250,7 +251,7 @@ namespace VRM
AvatarDescription = GLTF.extensions.VRM.humanoid.ToDescription(Nodes);
AvatarDescription.name = "AvatarDescription";
HumanoidAvatar = AvatarDescription.CreateAvatar(Root.transform);
if(!HumanoidAvatar.isValid || !HumanoidAvatar.isHuman)
if (!HumanoidAvatar.isValid || !HumanoidAvatar.isHuman)
{
throw new Exception("fail to create avatar");
}
@ -289,7 +290,7 @@ namespace VRM
meta.Title = gltfMeta.title;
var thumbnail = GetTexture(gltfMeta.texture);
if (thumbnail!=null)
if (thumbnail != null)
{
// ロード済み
meta.Thumbnail = thumbnail.Texture;

View File

@ -91,7 +91,7 @@ namespace VRM
[JsonSchema(Description = "Expression name")]
public string name;
[JsonSchema(Description = "Predefined Expression name", EnumValues =new object[] {
[JsonSchema(Description = "Predefined Expression name", EnumValues = new object[] {
"unknown",
"neutral",
"a",
@ -119,10 +119,14 @@ namespace VRM
[JsonSchema(Description = "Material animation references.")]
public List<glTF_VRM_MaterialValueBind> materialValues = new List<glTF_VRM_MaterialValueBind>();
[JsonSchema(Description = "0 or 1. Do not allow an intermediate value. Value should rounded")]
public bool isBinary;
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => name);
f.KeyValue(() => presetName);
f.KeyValue(() => isBinary);
f.KeyValue(() => binds);
f.KeyValue(() => materialValues);
}
@ -157,6 +161,7 @@ namespace VRM
{
name = clip.BlendShapeName,
presetName = clip.Preset.ToString().ToLower(),
isBinary = clip.IsBinary,
binds = list,
materialValues = materialList,
};

View File

@ -70,12 +70,12 @@ namespace VRM
}
bool m_foldoutInfo = true;
bool m_foldoutPersmission=true;
bool m_foldoutDistribution=true;
bool m_foldoutPersmission = true;
bool m_foldoutDistribution = true;
void VRMMetaObjectGUI(SerializedObject so)
{
InitMap(so);
if (m_propMap == null || m_propMap.Count==0) return;
if (m_propMap == null || m_propMap.Count == 0) return;
so.Update();
@ -95,8 +95,8 @@ namespace VRM
EditorGUILayout.PropertyField(m_propMap["Author"]);
EditorGUILayout.PropertyField(m_propMap["ContactInformation"]);
EditorGUILayout.PropertyField(m_propMap["Reference"]);
var thumbnail = m_propMap["Thumbnail"];
thumbnail.objectReferenceValue = TextureField("", (Texture2D)thumbnail.objectReferenceValue, 100);
//var thumbnail = m_propMap["Thumbnail"];
//thumbnail.objectReferenceValue = TextureField("", (Texture2D)thumbnail.objectReferenceValue, 100);
}
EditorGUILayout.LabelField("License ", EditorStyles.boldLabel);
@ -121,7 +121,7 @@ namespace VRM
EditorGUILayout.PropertyField(m_propMap["OtherLicenseUrl"]);
}
}
so.ApplyModifiedProperties();
}