From f33b8dbb46430d5a4238454694549e7412ca9f6d Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 21 Oct 2022 15:01:04 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E3=83=92=E3=83=A5=E3=83=BC=E3=83=9E?= =?UTF-8?q?=E3=83=8E=E3=82=A4=E3=83=89=E3=83=9C=E3=83=BC=E3=83=B3=E5=89=B2?= =?UTF-8?q?=E3=82=8A=E5=BD=93=E3=81=A6=E8=A3=9C=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1番目の子孫GameObjectがヒューマノイドの子孫であると見做して自動代入(指とか、足など枝分かれしないところの簡易な補助) --- .../Editor/UniHumanoid/HumanoidEditor.cs | 144 +++++++++++++++--- .../UniGLTF/Runtime/UniHumanoid/Humanoid.cs | 6 +- 2 files changed, 127 insertions(+), 23 deletions(-) diff --git a/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs b/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs index 886ce1257..7a0a01b4e 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 @@ -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(); 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 From b18e5cd63bd38929258d685269ddc563e1e02228 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 21 Oct 2022 15:20:57 +0900 Subject: [PATCH 2/4] =?UTF-8?q?VRM10Object=20=E3=82=92=E4=BD=9C=E6=88=90?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=B9=E3=83=86=E3=83=83=E3=83=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * VRM10Object が無いときにエラー表示 * VRM10Object が無ければ VRM10Instance を停止させる --- .../UniGLTF/ExportDialog/SaveFileDialog.cs | 6 ++--- Assets/VRM10/Editor/Vrm10InstanceEditor.cs | 27 ++++++++++++++++--- .../Components/Vrm10Instance/Vrm10Instance.cs | 7 +++++ .../VRMShaders/GLTF/IO/Runtime/PathObject.cs | 18 +++++++++---- 4 files changed, 47 insertions(+), 11 deletions(-) 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/VRM10/Editor/Vrm10InstanceEditor.cs b/Assets/VRM10/Editor/Vrm10InstanceEditor.cs index 376d37145..3beb96798 100644 --- a/Assets/VRM10/Editor/Vrm10InstanceEditor.cs +++ b/Assets/VRM10/Editor/Vrm10InstanceEditor.cs @@ -115,6 +115,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 +139,17 @@ namespace UniVRM10 } EditorGUILayout.HelpBox("Humanoid OK.", MessageType.Info); + + // VRM10Object + var prop = serializedObject.FindProperty(nameof(Vrm10Instance.Vrm)); + if (prop.objectReferenceValue == null) + { + EditorGUILayout.HelpBox("No VRM10Object.", MessageType.Error); + } if (GUILayout.Button("Create new VRM10Object")) { - var saveName = (instance.name ?? "vrm-1.0"); - var dir = SaveFileDialog.GetDir(SaveTitle, saveName); + var saveName = GetSaveName(instance); + var dir = SaveFileDialog.GetDir(SaveTitle, System.IO.Path.GetDirectoryName(saveName)); if (!string.IsNullOrEmpty(dir)) { var expressions = new Dictionary(); @@ -146,7 +168,6 @@ namespace UniVRM10 { // 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() From 43f78eac121aec4d0c0ffd0d16db4ed7dae18ca1 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 21 Oct 2022 15:37:17 +0900 Subject: [PATCH 3/4] add prefab assignment when asset created --- Assets/VRM10/Editor/Vrm10InstanceEditor.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Assets/VRM10/Editor/Vrm10InstanceEditor.cs b/Assets/VRM10/Editor/Vrm10InstanceEditor.cs index 3beb96798..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); @@ -146,7 +150,7 @@ namespace UniVRM10 { EditorGUILayout.HelpBox("No VRM10Object.", MessageType.Error); } - if (GUILayout.Button("Create new VRM10Object")) + 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)); @@ -159,11 +163,11 @@ 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 From 917f4e953e9f6e12f324e3cfc6018b82c7012c11 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 21 Oct 2022 15:58:31 +0900 Subject: [PATCH 4/4] =?UTF-8?q?prefab=20=E3=81=AF=20GameObject=20=E3=81=8B?= =?UTF-8?q?=E3=82=89=E5=BE=97=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs b/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs index 7a0a01b4e..e1191d6d4 100644 --- a/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs +++ b/Assets/UniGLTF/Editor/UniHumanoid/HumanoidEditor.cs @@ -231,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) @@ -385,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))