Initial Commit

This commit is contained in:
2025-07-04 20:33:06 +03:00
commit 8f09347ae0
9219 changed files with 2447903 additions and 0 deletions
@@ -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();
}
}
}
@@ -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: