mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-08-27 13:24:42 -05:00
Merge pull request #1877 from ousttrue/feature10/vrm1_editor_setup
[1.0] AddComponent による新規セットアップ関連
This commit is contained in:
@@ -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("\\", "/");
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -8,7 +8,11 @@ namespace UniHumanoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Bone割り当てを保持する。
|
||||
/// ヒエラルキーのルート(おそらくHipsの親)にアタッチする
|
||||
/// ヒエラルキーのルートにアタッチする。
|
||||
/// root は以下の条件を満たすべし。
|
||||
/// * root は 原点に配置、回転なし、スケールなし。
|
||||
/// * root は Hips の祖先
|
||||
/// * root の親は null
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class Humanoid : MonoBehaviour
|
||||
|
||||
@@ -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<ExpressionPreset, VRM10Expression> expressions)
|
||||
static VRM10Object CreateAsset(string path, Dictionary<ExpressionPreset, VRM10Expression> expressions, Vrm10Instance instance)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
@@ -27,6 +27,8 @@ namespace UniVRM10
|
||||
}
|
||||
|
||||
var asset = ScriptableObject.CreateInstance<VRM10Object>();
|
||||
|
||||
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<VRM10Expression>();
|
||||
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<ExpressionPreset, VRM10Expression>();
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user