Initial Commit
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Editor;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Oculus.Interaction.HandGrab.Editor
|
||||
{
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(DistanceHandGrabInteractable))]
|
||||
public partial class DistanceHandGrabInteractableEditor : SimplifiedEditor
|
||||
{
|
||||
private DistanceHandGrabInteractable _target;
|
||||
private HandGrabScaleKeysEditor<DistanceHandGrabInteractable> _listDrawer;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_target = target as DistanceHandGrabInteractable;
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_listDrawer = new HandGrabScaleKeysEditor<DistanceHandGrabInteractable>(serializedObject,
|
||||
_target.HandGrabPoses, "_handGrabPoses", true);
|
||||
_editorDrawer.Draw("_handGrabPoses", (modeProp) =>
|
||||
{
|
||||
_listDrawer.DrawInspector();
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
_listDrawer.TearDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fbb2e01d7c7a6904eaa71da7a854bfad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Oculus.Interaction.HandGrab.Visuals;
|
||||
|
||||
namespace Oculus.Interaction.HandGrab.Editor
|
||||
{
|
||||
public class HandGhostProviderUtils
|
||||
{
|
||||
public static bool TryGetDefaultProvider(out HandGhostProvider provider)
|
||||
{
|
||||
provider = null;
|
||||
HandGhostProvider[] providers = Resources.FindObjectsOfTypeAll<HandGhostProvider>();
|
||||
if (providers != null && providers.Length > 0)
|
||||
{
|
||||
provider = providers[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
string[] assets = AssetDatabase.FindAssets($"t:{nameof(HandGhostProvider)}");
|
||||
if (assets != null && assets.Length > 0)
|
||||
{
|
||||
string pathPath = AssetDatabase.GUIDToAssetPath(assets[0]);
|
||||
provider = AssetDatabase.LoadAssetAtPath<HandGhostProvider>(pathPath);
|
||||
}
|
||||
|
||||
|
||||
return provider != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62c8971cfb0b66040a9845cfc622ed6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.HandGrab.Editor
|
||||
{
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(HandGrabInteractable))]
|
||||
public partial class HandGrabInteractableEditor : SimplifiedEditor
|
||||
{
|
||||
private HandGrabInteractable _target;
|
||||
private HandGrabScaleKeysEditor<HandGrabInteractable> _listDrawer;
|
||||
private void Awake()
|
||||
{
|
||||
_target = target as HandGrabInteractable;
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_listDrawer = new HandGrabScaleKeysEditor<HandGrabInteractable>(serializedObject,
|
||||
_target.HandGrabPoses, "_handGrabPoses", true);
|
||||
_editorDrawer.Draw("_handGrabPoses", (modeProp) =>
|
||||
{
|
||||
_listDrawer.DrawInspector();
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
_listDrawer.TearDown();
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
|
||||
if (GUILayout.Button("Create Mirrored HandGrabInteractable"))
|
||||
{
|
||||
Mirror();
|
||||
}
|
||||
}
|
||||
|
||||
private void Mirror()
|
||||
{
|
||||
HandGrabInteractable mirrorInteractable =
|
||||
HandGrabUtils.CreateHandGrabInteractable(_target.RelativeTo,
|
||||
$"{_target.gameObject.name}_mirror");
|
||||
|
||||
var data = HandGrabUtils.SaveData(_target);
|
||||
data.poses = null;
|
||||
HandGrabUtils.LoadData(mirrorInteractable, data);
|
||||
foreach (HandGrabPose point in _target.HandGrabPoses)
|
||||
{
|
||||
HandGrabPose mirrorPose = HandGrabUtils.CreateHandGrabPose(mirrorInteractable.transform,
|
||||
mirrorInteractable.RelativeTo);
|
||||
HandGrabUtils.MirrorHandGrabPose(point, mirrorPose, _target.RelativeTo);
|
||||
mirrorPose.transform.SetParent(mirrorInteractable.transform);
|
||||
mirrorInteractable.HandGrabPoses.Add(mirrorPose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5ce7770848930447885c9abba8bb99a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Editor;
|
||||
using Oculus.Interaction.HandGrab.Visuals;
|
||||
using Oculus.Interaction.Input;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.HandGrab.Editor
|
||||
{
|
||||
[CustomEditor(typeof(HandGrabPose))]
|
||||
public class HandGrabPoseEditor : UnityEditor.Editor
|
||||
{
|
||||
private HandGrabPose _handGrabPose;
|
||||
private HandGhostProvider _ghostVisualsProvider;
|
||||
private HandGhost _handGhost;
|
||||
private Handedness _lastHandedness;
|
||||
private Transform _relativeTo;
|
||||
|
||||
private int _editMode = 0;
|
||||
private SerializedProperty _handPoseProperty;
|
||||
private SerializedProperty _relativeToProperty;
|
||||
|
||||
private const float GIZMO_SCALE = 0.005f;
|
||||
private static readonly string[] EDIT_MODES = new string[] { "Edit fingers", "Follow Surface" };
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_handGrabPose = target as HandGrabPose;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_handPoseProperty = serializedObject.FindProperty("_handPose");
|
||||
_relativeToProperty = serializedObject.FindProperty("_relativeTo");
|
||||
AssignMissingGhostProvider();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
DestroyGhost();
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
|
||||
_relativeTo = _relativeToProperty.objectReferenceValue as Transform;
|
||||
|
||||
if (_handGrabPose.UsesHandPose())
|
||||
{
|
||||
EditorGUILayout.PropertyField(_handPoseProperty);
|
||||
}
|
||||
|
||||
GUIStyle boldStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold };
|
||||
EditorGUILayout.LabelField("Interactive Edition (Editor only)", boldStyle);
|
||||
if (_handGrabPose.UsesHandPose())
|
||||
{
|
||||
DrawGhostMenu(_handGrabPose.HandPose);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyGhost();
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void DrawGhostMenu(HandPose handPose)
|
||||
{
|
||||
HandGhostProvider provider = EditorGUILayout.ObjectField("Ghost Provider", _ghostVisualsProvider, typeof(HandGhostProvider), false) as HandGhostProvider;
|
||||
if (_handGhost == null
|
||||
|| _ghostVisualsProvider != provider
|
||||
|| _lastHandedness != handPose.Handedness)
|
||||
{
|
||||
RegenerateGhost(provider);
|
||||
}
|
||||
_ghostVisualsProvider = provider;
|
||||
_lastHandedness = handPose.Handedness;
|
||||
|
||||
if (_handGrabPose.SnapSurface == null)
|
||||
{
|
||||
_editMode = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_editMode = GUILayout.Toolbar(_editMode, EDIT_MODES);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSceneGUI()
|
||||
{
|
||||
if (SceneView.currentDrawingSceneView == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_handGhost == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_editMode == 0)
|
||||
{
|
||||
GhostEditFingers();
|
||||
}
|
||||
else if (_editMode == 1)
|
||||
{
|
||||
GhostFollowSurface();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region ghost
|
||||
|
||||
private void AssignMissingGhostProvider()
|
||||
{
|
||||
if (_ghostVisualsProvider != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HandGhostProviderUtils.TryGetDefaultProvider(out _ghostVisualsProvider);
|
||||
}
|
||||
|
||||
private void RegenerateGhost(HandGhostProvider provider)
|
||||
{
|
||||
DestroyGhost();
|
||||
CreateGhost();
|
||||
}
|
||||
|
||||
private void CreateGhost()
|
||||
{
|
||||
if (_ghostVisualsProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Transform relativeTo = _handGrabPose.RelativeTo;
|
||||
HandGhost ghostPrototype = _ghostVisualsProvider.GetHand(_handGrabPose.HandPose.Handedness);
|
||||
_handGhost = GameObject.Instantiate(ghostPrototype, _handGrabPose.transform);
|
||||
_handGhost.gameObject.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
Pose relativePose = _handGrabPose.RelativePose;
|
||||
Pose pose = PoseUtils.GlobalPoseScaled(relativeTo, relativePose);
|
||||
_handGhost.SetPose(_handGrabPose.HandPose, pose);
|
||||
}
|
||||
|
||||
private void DestroyGhost()
|
||||
{
|
||||
if (_handGhost == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject.DestroyImmediate(_handGhost.gameObject);
|
||||
}
|
||||
|
||||
private void GhostFollowSurface()
|
||||
{
|
||||
if (_handGhost == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pose ghostTargetPose = _handGrabPose.RelativePose;
|
||||
|
||||
if (_handGrabPose.SnapSurface != null)
|
||||
{
|
||||
Vector3 mousePosition = Event.current.mousePosition;
|
||||
Ray ray = HandleUtility.GUIPointToWorldRay(mousePosition);
|
||||
if (_handGrabPose.SnapSurface.CalculateBestPoseAtSurface(ray, out Pose poseInSurface, _relativeTo))
|
||||
{
|
||||
ghostTargetPose = PoseUtils.DeltaScaled(_relativeTo, poseInSurface);
|
||||
}
|
||||
}
|
||||
|
||||
_handGhost.SetRootPose(ghostTargetPose, _relativeTo);
|
||||
Handles.color = EditorConstants.PRIMARY_COLOR_DISABLED;
|
||||
Handles.DrawSolidDisc(_handGhost.transform.position, _handGhost.transform.right, 0.01f);
|
||||
}
|
||||
|
||||
private void GhostEditFingers()
|
||||
{
|
||||
HandPuppet puppet = _handGhost.GetComponent<HandPuppet>();
|
||||
if (puppet != null && puppet.JointMaps != null)
|
||||
{
|
||||
DrawBonesRotator(puppet.JointMaps);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBonesRotator(List<HandJointMap> bones)
|
||||
{
|
||||
bool anyChanged = false;
|
||||
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; i++)
|
||||
{
|
||||
bool changed = false;
|
||||
HandJointId joint = FingersMetadata.HAND_JOINT_IDS[i];
|
||||
HandFinger finger = FingersMetadata.JOINT_TO_FINGER[(int)joint];
|
||||
|
||||
if (_handGrabPose.HandPose.FingersFreedom[(int)finger] == JointFreedom.Free)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
HandJointMap jointMap = bones.Find(b => b.id == joint);
|
||||
if (jointMap == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Transform transform = jointMap.transform;
|
||||
transform.localRotation = jointMap.RotationOffset * _handGrabPose.HandPose.JointRotations[i];
|
||||
|
||||
float scale = GIZMO_SCALE * _handGrabPose.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 (FingersMetadata.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(_handGrabPose, "Bone Rotation");
|
||||
_handGrabPose.HandPose.JointRotations[i] = jointMap.TrackedRotation;
|
||||
anyChanged = true;
|
||||
}
|
||||
|
||||
if (anyChanged)
|
||||
{
|
||||
EditorUtility.SetDirty(_handGrabPose);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92b8a050249b4ea47b9da8fe38ed12a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.HandGrab.Visuals;
|
||||
using Oculus.Interaction.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.HandGrab.Editor
|
||||
{
|
||||
public class HandGrabScaleKeysEditor<TInteractable>
|
||||
where TInteractable : MonoBehaviour, IRelativeToRef
|
||||
{
|
||||
private MonoBehaviour _target;
|
||||
private SerializedObject _serializedObject;
|
||||
private List<HandGrabPose> _handGrabPoses;
|
||||
private bool _markAsOptional;
|
||||
private SerializedProperty _posesProperty;
|
||||
private ReorderableList _list;
|
||||
private HashSet<float> _scalesSet = new HashSet<float>();
|
||||
private IRelativeToRef _relativeToRef;
|
||||
|
||||
private HandGhostProvider _ghostVisualsProvider;
|
||||
private HandGhost _handGhost;
|
||||
private HandPose _ghostHandPose = new HandPose();
|
||||
private Handedness _lastHandedness;
|
||||
|
||||
private float _handScale = 1f;
|
||||
|
||||
private const float POSE_RECT_WIDTH = 40f;
|
||||
private const float LEFT_MARGIN = 15;
|
||||
private const float RIGHT_MARGIN = 65f;
|
||||
private const float MIN_SCALE = 0.5f;
|
||||
private const float MAX_SCALE = 2f;
|
||||
|
||||
public HandGrabScaleKeysEditor(SerializedObject serializedObject,
|
||||
List<HandGrabPose> handGrabPoses, string collectionName, bool markAsOptional)
|
||||
{
|
||||
_target = serializedObject.targetObject as MonoBehaviour;
|
||||
_relativeToRef = serializedObject.targetObject as IRelativeToRef;
|
||||
_posesProperty = serializedObject.FindProperty(collectionName);
|
||||
_handGrabPoses = handGrabPoses;
|
||||
_markAsOptional = markAsOptional;
|
||||
HandGhostProviderUtils.TryGetDefaultProvider(out _ghostVisualsProvider);
|
||||
_list = new ReorderableList(serializedObject, _posesProperty,
|
||||
draggable: true, displayHeader: false,
|
||||
displayAddButton: true, displayRemoveButton: true);
|
||||
|
||||
InitializeListDrawers(_list);
|
||||
}
|
||||
|
||||
public void TearDown()
|
||||
{
|
||||
DestroyGhost();
|
||||
}
|
||||
|
||||
public void DrawInspector()
|
||||
{
|
||||
EditorGUILayout.LabelField($"{(_markAsOptional ? "[Optional]" : "")} Scaled Hand Grab Poses");
|
||||
|
||||
Rect startRect = GUILayoutUtility.GetLastRect();
|
||||
_list.DoLayoutList();
|
||||
ScaledHandPoseSlider();
|
||||
CheckUniqueScales();
|
||||
CheckUniqueHandedness();
|
||||
DrawGenerationMenu();
|
||||
Rect endRect = GUILayoutUtility.GetLastRect();
|
||||
|
||||
DrawBox(
|
||||
new Vector2(startRect.position.x, startRect.position.y + startRect.size.y),
|
||||
endRect.position + endRect.size);
|
||||
|
||||
UpdateGhost();
|
||||
}
|
||||
|
||||
private void DrawBox(Vector2 start, Vector2 end)
|
||||
{
|
||||
float margin = 5f;
|
||||
float height = end.y - start.y;
|
||||
Rect topBar = new Rect(start.x - margin, start.y,
|
||||
margin, 1f);
|
||||
Rect bottomBar = new Rect(start.x- margin, end.y,
|
||||
margin, 1f);
|
||||
Rect leftBar = new Rect(start.x - margin, start.y,
|
||||
1f, height);
|
||||
|
||||
EditorGUI.DrawRect(topBar, Color.gray);
|
||||
EditorGUI.DrawRect(bottomBar, Color.gray);
|
||||
EditorGUI.DrawRect(leftBar, Color.gray);
|
||||
}
|
||||
|
||||
private void InitializeListDrawers(ReorderableList list)
|
||||
{
|
||||
list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
rect.height = EditorGUIUtility.singleLineHeight;
|
||||
SerializedProperty itemProperty = list.serializedProperty.GetArrayElementAtIndex(index);
|
||||
HandGrabPose grabPose = itemProperty.objectReferenceValue as HandGrabPose;
|
||||
float scale = float.NaN;
|
||||
if (grabPose != null)
|
||||
{
|
||||
scale = grabPose.transform.lossyScale.x /
|
||||
_relativeToRef.RelativeTo.lossyScale.x;
|
||||
}
|
||||
|
||||
GUIContent objectLabel = new GUIContent($"Scale x{scale.ToString("F2")}");
|
||||
EditorGUI.PropertyField(rect, itemProperty, objectLabel);
|
||||
};
|
||||
}
|
||||
|
||||
private void ScaledHandPoseSlider()
|
||||
{
|
||||
float minScale = MIN_SCALE;
|
||||
float maxScale = MAX_SCALE;
|
||||
for (int i = 0; i < _handGrabPoses.Count; i++)
|
||||
{
|
||||
HandGrabPose grabPose = _handGrabPoses[i];
|
||||
float scale = grabPose.transform.lossyScale.x;
|
||||
if (scale < minScale)
|
||||
{
|
||||
minScale = scale;
|
||||
}
|
||||
if (scale > maxScale)
|
||||
{
|
||||
maxScale = scale;
|
||||
}
|
||||
}
|
||||
_handScale = EditorGUILayout.Slider(_handScale, minScale, maxScale);
|
||||
Rect backRect = GUILayoutUtility.GetLastRect();
|
||||
backRect.x += LEFT_MARGIN;
|
||||
backRect.width -= RIGHT_MARGIN;
|
||||
|
||||
for (int i = 0; i < _handGrabPoses.Count; i++)
|
||||
{
|
||||
HandGrabPose grabPose = _handGrabPoses[i];
|
||||
if (grabPose == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
float x = backRect.x + Mathf.InverseLerp(minScale, maxScale, grabPose.transform.lossyScale.x) * backRect.width;
|
||||
Rect poseRect = new Rect(x - POSE_RECT_WIDTH * 0.5f, backRect.y, POSE_RECT_WIDTH, backRect.height);
|
||||
EditorGUI.LabelField(poseRect, EditorGUIUtility.IconContent("curvekeyframeselected"));
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckUniqueScales()
|
||||
{
|
||||
_scalesSet.Clear();
|
||||
for (int i = 0; i < _handGrabPoses.Count; i++)
|
||||
{
|
||||
HandGrabPose grabPose = _handGrabPoses[i];
|
||||
if (grabPose == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float scale = grabPose.transform.lossyScale.x /
|
||||
_relativeToRef.RelativeTo.lossyScale.x;
|
||||
if (_scalesSet.Contains(scale))
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
$"Duplicated {nameof(HandGrabPose)} of scale {scale} at index {i}.",
|
||||
MessageType.Warning);
|
||||
}
|
||||
_scalesSet.Add(scale);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckUniqueHandedness()
|
||||
{
|
||||
bool handednessSet = false;
|
||||
Handedness validHandedness = Handedness.Left;
|
||||
for (int i = 0; i < _handGrabPoses.Count; i++)
|
||||
{
|
||||
HandGrabPose grabPose = _handGrabPoses[i];
|
||||
if (grabPose == null || grabPose.HandPose == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Handedness grabPoseHandedness = grabPose.HandPose.Handedness;
|
||||
if (!handednessSet)
|
||||
{
|
||||
handednessSet = true;
|
||||
validHandedness = grabPoseHandedness;
|
||||
}
|
||||
else if (grabPoseHandedness != validHandedness)
|
||||
{
|
||||
EditorGUILayout.HelpBox($"Different Handedness at index {i}. " +
|
||||
$"Ensure all HandGrabPoses have the same Handedness", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGenerationMenu()
|
||||
{
|
||||
if (GUILayout.Button($"Add HandGrabPose Key with Scale {_handScale.ToString("F2")}"))
|
||||
{
|
||||
AddHandGrabPose(_handScale);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Refresh HandGrab Poses"))
|
||||
{
|
||||
RefreshHandPoses();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddHandGrabPose(float scale)
|
||||
{
|
||||
HandGrabPose handGrabPose = HandGrabUtils.CreateHandGrabPose(_target.transform,
|
||||
_relativeToRef.RelativeTo);
|
||||
float relativeScale = scale / _relativeToRef.RelativeTo.lossyScale.x;
|
||||
|
||||
bool rangeFound = GrabPoseFinder.FindInterpolationRange(relativeScale, _handGrabPoses,
|
||||
out HandGrabPose from, out HandGrabPose to, out float t);
|
||||
|
||||
handGrabPose.transform.localScale = Vector3.one * relativeScale;
|
||||
_handGrabPoses.Add(handGrabPose);
|
||||
EditorUtility.SetDirty(_target);
|
||||
|
||||
if (!rangeFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pose relativePose = Pose.identity;
|
||||
PoseUtils.Lerp(from.RelativePose, to.RelativePose, t, ref relativePose);
|
||||
Pose rootPose = PoseUtils.GlobalPoseScaled(_relativeToRef.RelativeTo, relativePose);
|
||||
HandPose resultHandPose = new HandPose(from.HandPose);
|
||||
HandPose.Lerp(from.HandPose, to.HandPose, t, ref resultHandPose);
|
||||
Grab.GrabSurfaces.IGrabSurface surface = from.SnapSurface?.CreateDuplicatedSurface(handGrabPose.gameObject);
|
||||
|
||||
handGrabPose.InjectAllHandGrabPose(_relativeToRef.RelativeTo);
|
||||
handGrabPose.InjectOptionalHandPose(resultHandPose);
|
||||
handGrabPose.InjectOptionalSurface(surface);
|
||||
handGrabPose.transform.SetPose(rootPose);
|
||||
}
|
||||
|
||||
private void RefreshHandPoses()
|
||||
{
|
||||
_handGrabPoses.Clear();
|
||||
HandGrabPose[] handGrabPoses = _target.GetComponentsInChildren<HandGrabPose>();
|
||||
_handGrabPoses.AddRange(handGrabPoses);
|
||||
EditorUtility.SetDirty(_target);
|
||||
}
|
||||
|
||||
#region Ghost
|
||||
|
||||
private void UpdateGhost()
|
||||
{
|
||||
if (_handGrabPoses.Count == 0
|
||||
|| _handGrabPoses[0].HandPose == null)
|
||||
{
|
||||
DestroyGhost();
|
||||
return;
|
||||
}
|
||||
|
||||
Transform relativeTo = _relativeToRef.RelativeTo;
|
||||
Pose rootPose = Pose.identity;
|
||||
float relativeScale = _handScale / relativeTo.lossyScale.x;
|
||||
bool rangeFound = GrabPoseFinder.FindInterpolationRange(relativeScale, _handGrabPoses,
|
||||
out HandGrabPose from, out HandGrabPose to, out float t);
|
||||
if (!rangeFound)
|
||||
{
|
||||
DestroyGhost();
|
||||
return;
|
||||
}
|
||||
|
||||
HandPose.Lerp(from.HandPose, to.HandPose, t, ref _ghostHandPose);
|
||||
PoseUtils.Lerp(from.RelativePose, to.RelativePose, t, ref rootPose);
|
||||
rootPose = PoseUtils.GlobalPoseScaled(relativeTo, rootPose);
|
||||
DisplayGhost(_ghostHandPose, rootPose, relativeScale);
|
||||
}
|
||||
|
||||
private void DisplayGhost(HandPose handPose, Pose rootPose, float scale)
|
||||
{
|
||||
if (_handGhost != null
|
||||
&& _lastHandedness != handPose.Handedness)
|
||||
{
|
||||
DestroyGhost();
|
||||
}
|
||||
|
||||
_lastHandedness = handPose.Handedness;
|
||||
if (_handGhost == null)
|
||||
{
|
||||
HandGhost ghostPrototype = _ghostVisualsProvider.GetHand(_lastHandedness);
|
||||
_handGhost = GameObject.Instantiate(ghostPrototype, _target.transform);
|
||||
_handGhost.gameObject.hideFlags = HideFlags.HideAndDontSave;
|
||||
}
|
||||
_handGhost.transform.localScale = Vector3.one * scale;
|
||||
_handGhost.SetPose(handPose, rootPose);
|
||||
}
|
||||
|
||||
private void DestroyGhost()
|
||||
{
|
||||
if (_handGhost == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GameObject.DestroyImmediate(_handGhost.gameObject);
|
||||
_handGhost = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1a53d90b3c752f4f84f32e5a94ea8bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Editor;
|
||||
using Oculus.Interaction.Input;
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.HandGrab.Editor
|
||||
{
|
||||
[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 += FingersMetadata.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 < FingersMetadata.HAND_JOINT_IDS.Length; i++)
|
||||
{
|
||||
SerializedProperty finger = jointRotations.GetArrayElementAtIndex(i);
|
||||
HandJointId jointID = FingersMetadata.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25b4f8dbe894a92489fe06cc956a42e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.HandGrab.Editor
|
||||
{
|
||||
public class HandWristOffsetUndoWizard : ScriptableWizard
|
||||
{
|
||||
[SerializeField]
|
||||
private HandWristOffset _wristOffset;
|
||||
|
||||
[SerializeField]
|
||||
private HandGrabPose _grabPose;
|
||||
|
||||
[MenuItem("Oculus/Interaction/HandWristOffset Undo Wizard")]
|
||||
private static void CreateWizard()
|
||||
{
|
||||
ScriptableWizard.DisplayWizard<HandWristOffsetUndoWizard>("HandWristOffset Undo Wizard", "Close", "Undo Offset");
|
||||
}
|
||||
|
||||
private void OnWizardCreate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void OnWizardOtherButton()
|
||||
{
|
||||
List<HandGrabPose> children = new List<HandGrabPose>(_grabPose.GetComponentsInChildren<HandGrabPose>());
|
||||
children.Remove(_grabPose);
|
||||
foreach (HandGrabPose childPoint in children)
|
||||
{
|
||||
if (childPoint == _grabPose)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
childPoint.transform.SetParent(_grabPose.transform.parent, true);
|
||||
UndoOffset(childPoint);
|
||||
}
|
||||
|
||||
UndoOffset(_grabPose);
|
||||
|
||||
foreach (HandGrabPose childPoint in children)
|
||||
{
|
||||
childPoint.transform.SetParent(_grabPose.transform, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void UndoOffset(HandGrabPose grabPose)
|
||||
{
|
||||
Pose offset = Pose.identity;
|
||||
_wristOffset.GetOffset(ref offset, grabPose.HandPose.Handedness, grabPose.transform.localScale.x);
|
||||
offset.Invert();
|
||||
|
||||
Undo.RecordObject(grabPose.transform, "Transform Changed");
|
||||
Pose pose = grabPose.transform.GetPose(Space.Self);
|
||||
pose.Premultiply(offset);
|
||||
grabPose.transform.SetPose(pose, Space.Self);
|
||||
EditorUtility.SetDirty(grabPose.transform);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 070da65b3b3353149bcd22ecad3b01ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user