You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
333 lines
12 KiB
333 lines
12 KiB
using System; |
|
using System.Collections.Generic; |
|
using UnityEditor; |
|
using UnityEngine; |
|
|
|
namespace UDE_HAND_INTERACTION |
|
{ |
|
[CustomEditor(typeof(HandInteraction))] |
|
public class HandInteractionEditor : Editor |
|
{ |
|
private HandInteraction _handInteraction; |
|
|
|
private VirtualHand virtualHand; |
|
|
|
public HandPerfabResource _HPResource; |
|
private Handedness _lastHandedness; |
|
|
|
private const float GIZMO_SCALE = 0.007f; |
|
|
|
private void Awake() |
|
{ |
|
_handInteraction = target as HandInteraction; |
|
} |
|
|
|
private void OnEnable() |
|
{ |
|
AssignMissingGhostProvider(); |
|
} |
|
|
|
private void OnDisable() |
|
{ |
|
DestroyGhost(); |
|
} |
|
|
|
public override void OnInspectorGUI() |
|
{ |
|
base.OnInspectorGUI(); |
|
|
|
_handInteraction._relativeTo = _handInteraction.transform.parent; |
|
|
|
DrawGhostMenu(_handInteraction.HandPose); |
|
serializedObject.ApplyModifiedProperties(); |
|
} |
|
|
|
public void OnSceneGUI() |
|
{ |
|
if (SceneView.currentDrawingSceneView == null) |
|
{ |
|
return; |
|
} |
|
|
|
if (virtualHand == null) |
|
{ |
|
return; |
|
} |
|
|
|
EditFingers(); |
|
} |
|
|
|
private void DrawGhostMenu(HandPose handPose) |
|
{ |
|
HandPerfabResource resource = _handInteraction._HPResource; //EditorGUILayout.ObjectField("Ghost Provider", _HPResource, typeof(HandPerfabResource), false) as HandPerfabResource; |
|
if (virtualHand == null || _HPResource != resource || _lastHandedness != handPose.Handedness) |
|
{ |
|
RegenerateGhost(); |
|
} |
|
_HPResource = resource; |
|
_lastHandedness = handPose.Handedness; |
|
} |
|
|
|
private void EditFingers() |
|
{ |
|
DollHand puppet = virtualHand.GetComponent<DollHand>(); |
|
if (puppet != null && puppet.JointSets != null) |
|
{ |
|
DrawBonesRotator(puppet.JointSets); |
|
} |
|
} |
|
|
|
private void DrawBonesRotator(List<HandJointsSet> bones) |
|
{ |
|
bool anyChanged = false; |
|
for (int i = 0; i < FingersData.HAND_JOINT_IDS.Length; i++) |
|
{ |
|
bool changed = false; |
|
HandJointId joint = FingersData.HAND_JOINT_IDS[i]; |
|
HandFinger finger = FingersData.JOINT_TO_FINGER[(int)joint]; |
|
|
|
if (_handInteraction.HandPose.FingersFreedom[(int)finger] == JointFreedom.Free) |
|
{ |
|
continue; |
|
} |
|
|
|
HandJointsSet jointSet = bones.Find(b => b.id == joint); |
|
if (jointSet == null) |
|
{ |
|
continue; |
|
} |
|
|
|
Transform transform = jointSet.transform; |
|
transform.localRotation = jointSet.RotationOffset * _handInteraction.HandPose.JointRotations[i]; |
|
|
|
float scale = GIZMO_SCALE * _handInteraction.transform.lossyScale.x; |
|
Handles.color = EditorConstants.PRIMARY_COLOR; |
|
Quaternion entryRotation = transform.rotation; |
|
Quaternion rotation = Handles.Disc(entryRotation, transform.position, |
|
transform.forward, scale, false, 0); |
|
if (rotation != entryRotation) |
|
{ |
|
changed = true; |
|
} |
|
|
|
if (FingersData.HAND_JOINT_CAN_SPREAD[i]) |
|
{ |
|
Handles.color = EditorConstants.SECONDARY_COLOR; |
|
Quaternion curlRotation = rotation; |
|
rotation = Handles.Disc(curlRotation, transform.position, |
|
transform.up, scale, false, 0); |
|
if (rotation != curlRotation) |
|
{ |
|
changed = true; |
|
} |
|
} |
|
|
|
if (!changed) |
|
{ |
|
continue; |
|
} |
|
|
|
transform.rotation = rotation; |
|
Undo.RecordObject(_handInteraction, "Bone Rotation"); |
|
_handInteraction.HandPose.JointRotations[i] = jointSet.TrackedRotation; |
|
anyChanged = true; |
|
} |
|
|
|
if (anyChanged) |
|
{ |
|
EditorUtility.SetDirty(_handInteraction); |
|
} |
|
} |
|
|
|
private void RegenerateGhost() |
|
{ |
|
DestroyGhost(); |
|
CreateGhost(); |
|
} |
|
|
|
private void CreateGhost() |
|
{ |
|
if (_HPResource == null) |
|
{ |
|
return; |
|
} |
|
Transform relativeTo = _handInteraction.RelativeTo; |
|
VirtualHand ghostPrototype = _HPResource.GetHand(_handInteraction.HandPose.Handedness); |
|
if(relativeTo != null) |
|
{ |
|
virtualHand = Instantiate(ghostPrototype, _handInteraction.transform); |
|
} |
|
else |
|
{ |
|
virtualHand = Instantiate(ghostPrototype); |
|
} |
|
virtualHand.gameObject.hideFlags = HideFlags.HideAndDontSave; |
|
Pose relativePose = _handInteraction.RelativePose; |
|
Pose pose = PoseManager.GlobalPoseScaled(relativeTo, relativePose); |
|
virtualHand.SetPose(_handInteraction.HandPose, pose); |
|
} |
|
|
|
private void DestroyGhost() |
|
{ |
|
if (virtualHand == null) |
|
{ |
|
return; |
|
} |
|
|
|
DestroyImmediate(virtualHand.gameObject); |
|
} |
|
|
|
void AssignMissingGhostProvider() |
|
{ |
|
if (_HPResource != null) |
|
{ |
|
return; |
|
} |
|
|
|
TryGetDefaultResProvider(out _HPResource); |
|
} |
|
|
|
bool TryGetDefaultResProvider(out HandPerfabResource provider) |
|
{ |
|
provider = null; |
|
HandPerfabResource[] providers = Resources.FindObjectsOfTypeAll<HandPerfabResource>(); |
|
if (providers != null && providers.Length > 0) |
|
{ |
|
provider = providers[0]; |
|
return true; |
|
} |
|
|
|
string[] assets = AssetDatabase.FindAssets($"t:{nameof(HandPerfabResource)}"); |
|
if (assets != null && assets.Length > 0) |
|
{ |
|
string pathPath = AssetDatabase.GUIDToAssetPath(assets[0]); |
|
provider = AssetDatabase.LoadAssetAtPath<HandPerfabResource>(pathPath); |
|
} |
|
|
|
|
|
return provider != null; |
|
} |
|
|
|
[MenuItem("UDE Interaction/Clear All Redundant Virtual Hand")] |
|
static void Clear() |
|
{ |
|
var VirtualHands = FindObjectsOfType<SkinnedMeshRenderer>(); |
|
foreach (var VirtualHand in VirtualHands) |
|
{ |
|
if (VirtualHand.name.Split("_")[0] == "vr" && VirtualHand.transform.parent.name.Contains("(Clone)")) |
|
{ |
|
DestroyImmediate(VirtualHand); |
|
} |
|
} |
|
} |
|
} |
|
|
|
public class EditorConstants |
|
{ |
|
public static readonly Color PRIMARY_COLOR = new Color(0f, 1f, 1f, 0.5f); |
|
public static readonly Color PRIMARY_COLOR_DISABLED = new Color(0f, 1f, 1f, 0.1f); |
|
public static readonly Color RED_COLOR = new Color(1f, 0f, 0f, 1f); |
|
public static readonly Color YELLOW_COLOR = new Color(1f, 1f, 0f, 1f); |
|
|
|
public static readonly Color SECONDARY_COLOR = new Color(0.5f, 0.3f, 1f, 0.5f); |
|
public static readonly Color SECONDARY_COLOR_DISABLED = new Color(0.5f, 0.3f, 1f, 0.1f); |
|
|
|
public static readonly float LINE_THICKNESS = 2f; |
|
|
|
public static readonly float ROW_HEIGHT = 20f; |
|
} |
|
|
|
[CustomPropertyDrawer(typeof(HandPose))] |
|
public class HandPoseEditor : PropertyDrawer |
|
{ |
|
private bool _foldedFreedom = true; |
|
private bool _foldedRotations = false; |
|
|
|
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) |
|
{ |
|
float multiplier = 4; |
|
|
|
if (_foldedFreedom) |
|
{ |
|
multiplier += Constants.NUM_FINGERS; |
|
} |
|
if (_foldedRotations) |
|
{ |
|
multiplier += FingersData.HAND_JOINT_IDS.Length; |
|
} |
|
|
|
return EditorConstants.ROW_HEIGHT * multiplier; |
|
} |
|
|
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) |
|
{ |
|
EditorGUI.BeginProperty(position, label, property); |
|
Rect labelPos = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); |
|
EditorGUI.indentLevel++; |
|
|
|
Rect rowRect = new Rect(position.x, labelPos.y + EditorConstants.ROW_HEIGHT, position.width, EditorConstants.ROW_HEIGHT); |
|
DrawFlagProperty<Handedness>(property, rowRect, "Handedness:", "_handedness", false); |
|
rowRect.y += EditorConstants.ROW_HEIGHT; |
|
rowRect = DrawFingersFreedomMenu(property, rowRect); |
|
rowRect = DrawJointAngles(property, rowRect); |
|
EditorGUI.indentLevel--; |
|
EditorGUI.EndProperty(); |
|
|
|
} |
|
|
|
private Rect DrawFingersFreedomMenu(SerializedProperty property, Rect position) |
|
{ |
|
_foldedFreedom = EditorGUI.Foldout(position, _foldedFreedom, "Fingers Freedom", true); |
|
position.y += EditorConstants.ROW_HEIGHT; |
|
if (_foldedFreedom) |
|
{ |
|
SerializedProperty fingersFreedom = property.FindPropertyRelative("_fingersFreedom"); |
|
EditorGUI.indentLevel++; |
|
for (int i = 0; i < Constants.NUM_FINGERS; i++) |
|
{ |
|
SerializedProperty finger = fingersFreedom.GetArrayElementAtIndex(i); |
|
HandFinger fingerID = (HandFinger)i; |
|
JointFreedom current = (JointFreedom)finger.intValue; |
|
JointFreedom selected = (JointFreedom)EditorGUI.EnumPopup(position, $"{fingerID}", current); |
|
finger.intValue = (int)selected; |
|
position.y += EditorConstants.ROW_HEIGHT; |
|
} |
|
EditorGUI.indentLevel--; |
|
} |
|
return position; |
|
} |
|
|
|
private Rect DrawJointAngles(SerializedProperty property, Rect position) |
|
{ |
|
_foldedRotations = EditorGUI.Foldout(position, _foldedRotations, "Joint Angles", true); |
|
position.y += EditorConstants.ROW_HEIGHT; |
|
if (_foldedRotations) |
|
{ |
|
SerializedProperty jointRotations = property.FindPropertyRelative("_jointRotations"); |
|
EditorGUI.indentLevel++; |
|
for (int i = 0; i < FingersData.HAND_JOINT_IDS.Length; i++) |
|
{ |
|
SerializedProperty finger = jointRotations.GetArrayElementAtIndex(i); |
|
HandJointId jointID = FingersData.HAND_JOINT_IDS[i]; |
|
Vector3 current = finger.quaternionValue.eulerAngles; |
|
Vector3 rotation = EditorGUI.Vector3Field(position, $"{jointID}", current); |
|
finger.quaternionValue = Quaternion.Euler(rotation); |
|
position.y += EditorConstants.ROW_HEIGHT; |
|
} |
|
EditorGUI.indentLevel--; |
|
} |
|
|
|
return position; |
|
} |
|
|
|
private void DrawFlagProperty<TEnum>(SerializedProperty parentProperty, Rect position, string title, string fieldName, bool isFlags) where TEnum : Enum |
|
{ |
|
SerializedProperty fieldProperty = parentProperty.FindPropertyRelative(fieldName); |
|
TEnum value = (TEnum)Enum.ToObject(typeof(TEnum), fieldProperty.intValue); |
|
Enum selectedValue = isFlags ? |
|
EditorGUI.EnumFlagsField(position, title, value) |
|
: EditorGUI.EnumPopup(position, title, value); |
|
fieldProperty.intValue = (int)Enum.ToObject(typeof(TEnum), selectedValue); |
|
} |
|
} |
|
}
|
|
|