diff --git a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/SaveFileDialog.cs b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/SaveFileDialog.cs
index b820a39f2..9ac7cea24 100644
--- a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/SaveFileDialog.cs
+++ b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/SaveFileDialog.cs
@@ -35,15 +35,15 @@ namespace UniGLTF
return path;
}
- public static string GetDir(string title, string name)
+ public static string GetDir(string title, string dir = null)
{
- string directory = m_lastExportDir;
+ string directory = string.IsNullOrEmpty(dir) ? m_lastExportDir : dir;
if (string.IsNullOrEmpty(directory))
{
directory = Directory.GetParent(Application.dataPath).ToString();
}
- var path = EditorUtility.SaveFolderPanel(title, directory, name);
+ var path = EditorUtility.SaveFolderPanel(title, directory, null);
if (!string.IsNullOrEmpty(path))
{
m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
diff --git a/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs b/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs
index 886ce1257..e1191d6d4 100644
--- a/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs
+++ b/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs
@@ -173,7 +173,35 @@ namespace UniHumanoid
}
}
- static void HorizontalFields(string label, params SerializedProperty[] props)
+ static int? HorizontalFields(string label, params SerializedProperty[] props)
+ {
+ int? changed = default;
+ try
+ {
+ EditorGUILayout.BeginHorizontal();
+
+ GUILayout.Label(label, GUILayout.Width(LABEL_WIDTH));
+ GUILayout.FlexibleSpace();
+
+ for (int i = 0; i < props.Length; ++i)
+ {
+ var prop = props[i];
+ var prev = prop.objectReferenceValue;
+ EditorGUILayout.PropertyField(prop, GUIContent.none, true, GUILayout.MinWidth(100));
+ if (prev != prop.objectReferenceValue)
+ {
+ changed = i;
+ }
+ }
+ }
+ finally
+ {
+ EditorGUILayout.EndHorizontal();
+ }
+ return changed;
+ }
+
+ static void FingerFields(string label, params SerializedProperty[] props)
{
try
{
@@ -182,9 +210,15 @@ namespace UniHumanoid
GUILayout.Label(label, GUILayout.Width(LABEL_WIDTH));
GUILayout.FlexibleSpace();
- foreach (var prop in props)
+ for (int i = 0; i < props.Length; ++i)
{
+ var prop = props[i];
+ var prev = prop.objectReferenceValue;
EditorGUILayout.PropertyField(prop, GUIContent.none, true, GUILayout.MinWidth(100));
+ if (prev != prop.objectReferenceValue)
+ {
+ SetFirstChildrenIfNull(prop, props.Skip(1).ToArray());
+ }
}
}
finally
@@ -197,7 +231,7 @@ namespace UniHumanoid
static bool s_legFold;
static bool s_armFold;
static bool s_fingerFold;
- static string GetDialogDir(UnityEngine.Object obj)
+ static string GetDialogDir(GameObject obj)
{
var prefab = PrefabUtility.GetCorrespondingObjectFromSource(obj);
if (prefab == null)
@@ -221,6 +255,62 @@ namespace UniHumanoid
return true;
}
+ static void SetFirstChildrenIfNull(SerializedProperty start, params SerializedProperty[] children)
+ {
+ var parent = start.objectReferenceValue as Transform;
+ if (parent == null)
+ {
+ return;
+ }
+ var current = parent;
+ foreach (var prop in children)
+ {
+ if (prop.objectReferenceValue != null)
+ {
+ // already assigned. exit
+ break;
+ }
+
+ if (current.childCount == 0)
+ {
+ // no child. exit
+ break;
+ }
+ current = current.GetChild(0);
+ prop.objectReferenceValue = current;
+ }
+ }
+
+ static bool PropFieldIsUpdated(SerializedProperty prop)
+ {
+ var prev = prop.objectReferenceValue;
+ EditorGUILayout.PropertyField(prop);
+ return prop.objectReferenceValue != prev;
+ }
+
+ static void LRProps(params (string Name, SerializedProperty L, SerializedProperty R)[] fields)
+ {
+
+ for (int i = 0; i < fields.Length; ++i)
+ {
+ var field = fields[i];
+ var changed = HorizontalFields(field.Name, field.L, field.R);
+ if (i == 0)
+ {
+ if (changed == 0)
+ {
+ // left
+ SetFirstChildrenIfNull(field.L, fields.Skip(1).Select(x => x.L).ToArray());
+ }
+ else if (changed == 1)
+ {
+ // right
+ SetFirstChildrenIfNull(field.R, fields.Skip(1).Select(x => x.R).ToArray());
+ }
+ }
+ }
+ }
+
public override void OnInspectorGUI()
{
foreach (var validation in m_target.Validate())
@@ -236,10 +326,16 @@ namespace UniHumanoid
s_spineFold = EditorGUILayout.Foldout(s_spineFold, "Body");
if (s_spineFold)
{
- EditorGUILayout.PropertyField(m_Spine);
+ if (PropFieldIsUpdated(m_Spine))
+ {
+ SetFirstChildrenIfNull(m_Spine, m_Chest, m_UpperChest);
+ }
EditorGUILayout.PropertyField(m_Chest);
EditorGUILayout.PropertyField(m_UpperChest);
- EditorGUILayout.PropertyField(m_Neck);
+ if (PropFieldIsUpdated(m_Neck))
+ {
+ SetFirstChildrenIfNull(m_Neck, m_Head);
+ }
EditorGUILayout.PropertyField(m_Head);
EditorGUILayout.PropertyField(m_Jaw);
HorizontalFields("Eye", m_LeftEye, m_RightEye);
@@ -248,34 +344,38 @@ namespace UniHumanoid
s_legFold = EditorGUILayout.Foldout(s_legFold, "Leg");
if (s_legFold)
{
- HorizontalFields("UpperLeg", m_LeftUpperLeg, m_RightUpperLeg);
- HorizontalFields("LowerLeg", m_LeftLowerLeg, m_RightLowerLeg);
- HorizontalFields("Foot", m_LeftFoot, m_RightFoot);
- HorizontalFields("Toes", m_LeftToes, m_RightToes);
+ LRProps(
+ ("UpperLeg", m_LeftUpperLeg, m_RightUpperLeg),
+ ("LowerLeg", m_LeftLowerLeg, m_RightLowerLeg),
+ ("Foot", m_LeftFoot, m_RightFoot),
+ ("Toes", m_LeftToes, m_RightToes)
+ );
}
s_armFold = EditorGUILayout.Foldout(s_armFold, "Arm");
if (s_armFold)
{
- HorizontalFields("Shoulder", m_LeftShoulder, m_RightShoulder);
- HorizontalFields("UpperArm", m_LeftUpperArm, m_RightUpperArm);
- HorizontalFields("LowerArm", m_LeftLowerArm, m_RightLowerArm);
- HorizontalFields("Hand", m_LeftHand, m_RightHand);
+ LRProps(
+ ("Shoulder", m_LeftShoulder, m_RightShoulder),
+ ("UpperArm", m_LeftUpperArm, m_RightUpperArm),
+ ("LowerArm", m_LeftLowerArm, m_RightLowerArm),
+ ("Hand", m_LeftHand, m_RightHand)
+ );
}
s_fingerFold = EditorGUILayout.Foldout(s_fingerFold, "Finger");
if (s_fingerFold)
{
- HorizontalFields("LeftThumb", m_LeftThumbProximal, m_LeftThumbIntermediate, m_LeftThumbDistal);
- HorizontalFields("LeftIndex", m_LeftIndexProximal, m_LeftIndexIntermediate, m_LeftIndexDistal);
- HorizontalFields("LeftMiddle", m_LeftMiddleProximal, m_LeftMiddleIntermediate, m_LeftMiddleDistal);
- HorizontalFields("LeftRing", m_LeftRingProximal, m_LeftRingIntermediate, m_LeftRingDistal);
- HorizontalFields("LeftLittle", m_LeftLittleProximal, m_LeftLittleIntermediate, m_LeftLittleDistal);
- HorizontalFields("RightThumb", m_RightThumbProximal, m_RightThumbIntermediate, m_RightThumbDistal);
- HorizontalFields("RightIndex", m_RightIndexProximal, m_RightIndexIntermediate, m_RightIndexDistal);
- HorizontalFields("RightMiddle", m_RightMiddleProximal, m_RightMiddleIntermediate, m_RightMiddleDistal);
- HorizontalFields("RightRing", m_RightRingProximal, m_RightRingIntermediate, m_RightRingDistal);
- HorizontalFields("RightLittle", m_RightLittleProximal, m_RightLittleIntermediate, m_RightLittleDistal);
+ FingerFields("LeftThumb", m_LeftThumbProximal, m_LeftThumbIntermediate, m_LeftThumbDistal);
+ FingerFields("LeftIndex", m_LeftIndexProximal, m_LeftIndexIntermediate, m_LeftIndexDistal);
+ FingerFields("LeftMiddle", m_LeftMiddleProximal, m_LeftMiddleIntermediate, m_LeftMiddleDistal);
+ FingerFields("LeftRing", m_LeftRingProximal, m_LeftRingIntermediate, m_LeftRingDistal);
+ FingerFields("LeftLittle", m_LeftLittleProximal, m_LeftLittleIntermediate, m_LeftLittleDistal);
+ FingerFields("RightThumb", m_RightThumbProximal, m_RightThumbIntermediate, m_RightThumbDistal);
+ FingerFields("RightIndex", m_RightIndexProximal, m_RightIndexIntermediate, m_RightIndexDistal);
+ FingerFields("RightMiddle", m_RightMiddleProximal, m_RightMiddleIntermediate, m_RightMiddleDistal);
+ FingerFields("RightRing", m_RightRingProximal, m_RightRingIntermediate, m_RightRingDistal);
+ FingerFields("RightLittle", m_RightLittleProximal, m_RightLittleIntermediate, m_RightLittleDistal);
}
serializedObject.ApplyModifiedProperties();
@@ -285,7 +385,7 @@ namespace UniHumanoid
{
var path = EditorUtility.SaveFilePanel(
"Save avatar",
- GetDialogDir(m_target),
+ GetDialogDir(m_target.gameObject),
string.Format("{0}.avatar.asset", serializedObject.targetObject.name),
"asset");
if (TryGetAssetPath(path, out string unityPath))
diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Humanoid.cs b/Assets/UniGLTF/Runtime/UniHumanoid/Humanoid.cs
index 247e020b7..b896a6017 100644
--- a/Assets/UniGLTF/Runtime/UniHumanoid/Humanoid.cs
+++ b/Assets/UniGLTF/Runtime/UniHumanoid/Humanoid.cs
@@ -8,7 +8,11 @@ namespace UniHumanoid
{
///
/// Bone割り当てを保持する。
- /// ヒエラルキーのルート(おそらくHipsの親)にアタッチする
+ /// ヒエラルキーのルートにアタッチする。
+ /// root は以下の条件を満たすべし。
+ /// * root は 原点に配置、回転なし、スケールなし。
+ /// * root は Hips の祖先
+ /// * root の親は null
///
[DisallowMultipleComponent]
public class Humanoid : MonoBehaviour
diff --git a/Assets/VRM10/Editor/Vrm10InstanceEditor.cs b/Assets/VRM10/Editor/Vrm10InstanceEditor.cs
index 376d37145..6ecb86115 100644
--- a/Assets/VRM10/Editor/Vrm10InstanceEditor.cs
+++ b/Assets/VRM10/Editor/Vrm10InstanceEditor.cs
@@ -13,7 +13,7 @@ namespace UniVRM10
const string SaveTitle = "New folder for vrm-1.0 assets...";
static string[] SaveExtensions = new string[] { "asset" };
- static VRM10Object CreateAsset(string path, Dictionary expressions)
+ static VRM10Object CreateAsset(string path, Dictionary expressions, Vrm10Instance instance)
{
if (string.IsNullOrEmpty(path))
{
@@ -27,6 +27,8 @@ namespace UniVRM10
}
var asset = ScriptableObject.CreateInstance();
+
+ asset.Prefab = PrefabUtility.GetCorrespondingObjectFromOriginalSource(instance?.gameObject);
foreach (var kv in expressions)
{
switch (kv.Key)
@@ -104,10 +106,12 @@ namespace UniVRM10
return true;
}
- static VRM10Expression CreateAndSaveExpression(ExpressionPreset preset, string dir)
+ static VRM10Expression CreateAndSaveExpression(ExpressionPreset preset, string dir, Vrm10Instance instance)
{
+ var prefab = PrefabUtility.GetCorrespondingObjectFromOriginalSource(instance.gameObject);
var clip = ScriptableObject.CreateInstance();
clip.name = preset.ToString();
+ clip.Prefab = prefab;
var path = System.IO.Path.Combine(dir, $"{preset}.asset");
var unityPath = UnityPath.FromFullpath(path);
unityPath.CreateAsset(clip);
@@ -115,6 +119,21 @@ namespace UniVRM10
return loaded;
}
+ static string GetSaveName(Vrm10Instance instance)
+ {
+ if (instance == null)
+ {
+ return "Assets/vrm-1.0.assets";
+ }
+
+ if (VRMShaders.PathObject.TryGetFromAsset(instance, out var asset))
+ {
+ return (asset.Parent.Child(instance.name + ".asset")).UnityAssetPath;
+ }
+
+ return $"Assets/{instance.name}.assets";
+ }
+
void SetupVRM10Object(Vrm10Instance instance)
{
if (!CheckHumanoid(instance.gameObject))
@@ -124,10 +143,17 @@ namespace UniVRM10
}
EditorGUILayout.HelpBox("Humanoid OK.", MessageType.Info);
- if (GUILayout.Button("Create new VRM10Object"))
+
+ // VRM10Object
+ var prop = serializedObject.FindProperty(nameof(Vrm10Instance.Vrm));
+ if (prop.objectReferenceValue == null)
{
- var saveName = (instance.name ?? "vrm-1.0");
- var dir = SaveFileDialog.GetDir(SaveTitle, saveName);
+ EditorGUILayout.HelpBox("No VRM10Object.", MessageType.Error);
+ }
+ if (GUILayout.Button("Create new VRM10Object and default Expressions. select target folder"))
+ {
+ var saveName = GetSaveName(instance);
+ var dir = SaveFileDialog.GetDir(SaveTitle, System.IO.Path.GetDirectoryName(saveName));
if (!string.IsNullOrEmpty(dir))
{
var expressions = new Dictionary();
@@ -137,16 +163,15 @@ namespace UniVRM10
{
continue;
}
- expressions[expression] = CreateAndSaveExpression(expression, dir);
+ expressions[expression] = CreateAndSaveExpression(expression, dir, instance);
}
var path = System.IO.Path.Combine(dir, (instance.name ?? "VRMObject") + ".asset");
- var asset = CreateAsset(path, expressions);
+ var asset = CreateAsset(path, expressions, instance);
if (asset != null)
{
// update editor
serializedObject.Update();
- var prop = serializedObject.FindProperty(nameof(Vrm10Instance.Vrm));
prop.objectReferenceValue = asset;
serializedObject.ApplyModifiedProperties();
}
diff --git a/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs b/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs
index d1397552e..6c8afcc26 100644
--- a/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs
+++ b/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs
@@ -92,6 +92,13 @@ namespace UniVRM10
void Start()
{
+ if (Vrm == null)
+ {
+ Debug.LogError("no VRM10Object");
+ enabled = false;
+ return;
+ }
+
// cause new Vrm10Runtime.
// init LookAt init rotation.
var runtime = Runtime;
diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/PathObject.cs b/Assets/VRMShaders/GLTF/IO/Runtime/PathObject.cs
index 55cddca2e..4ba004bfa 100644
--- a/Assets/VRMShaders/GLTF/IO/Runtime/PathObject.cs
+++ b/Assets/VRMShaders/GLTF/IO/Runtime/PathObject.cs
@@ -161,14 +161,22 @@ namespace VRMShaders
}
var assetPath = AssetDatabase.GetAssetPath(src);
- if (string.IsNullOrEmpty(assetPath))
+ if (!string.IsNullOrEmpty(assetPath))
{
- dst = default;
- return false;
+ dst = FromUnityAssetPath(assetPath);
+ return true;
}
- dst = FromUnityAssetPath(assetPath);
- return true;
+ var prefab = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(src);
+ if (!string.IsNullOrEmpty(prefab))
+ {
+ dst = FromUnityAssetPath(prefab);
+ return true;
+ }
+
+ dst = default;
+ return false;
+
}
public void ImportAsset()