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,103 @@
/*
* 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 System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Oculus.Interaction.GrabAPI
{
[CustomPropertyDrawer(typeof(GrabbingRule))]
public class GrabbingRuleEditor : PropertyDrawer
{
private static Dictionary<string, bool> _unfolds = new Dictionary<string, bool>();
private static readonly string[] FINGER_PROPERTY_NAMES = new string[]
{
"_thumbRequirement",
"_indexRequirement",
"_middleRequirement",
"_ringRequirement",
"_pinkyRequirement",
};
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
InitializeUnfold(property);
if (_unfolds[property.propertyPath])
{
return EditorConstants.ROW_HEIGHT * (Constants.NUM_FINGERS + 2);
}
else
{
return EditorConstants.ROW_HEIGHT * 1;
}
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
InitializeUnfold(property);
Rect rowRect = new Rect(position.x, position.y, position.width, EditorConstants.ROW_HEIGHT);
_unfolds[property.propertyPath] = EditorGUI.Foldout(rowRect, _unfolds[property.propertyPath], label, true);
if (_unfolds[property.propertyPath])
{
EditorGUI.indentLevel++;
for (int i = 0; i < Constants.NUM_FINGERS; i++)
{
rowRect.y += EditorConstants.ROW_HEIGHT;
SerializedProperty finger = property.FindPropertyRelative(FINGER_PROPERTY_NAMES[i]);
HandFinger fingerID = (HandFinger)i;
FingerRequirement current = (FingerRequirement)finger.intValue;
FingerRequirement selected = (FingerRequirement)EditorGUI.EnumPopup(rowRect, $"{fingerID}: ", current);
finger.intValue = (int)selected;
}
rowRect.y += EditorConstants.ROW_HEIGHT;
DrawFlagProperty<FingerUnselectMode>(property, rowRect, "Unselect Mode", "_unselectMode", false);
EditorGUI.indentLevel--;
}
EditorGUI.EndProperty();
}
private void InitializeUnfold(SerializedProperty property)
{
if (!_unfolds.ContainsKey(property.propertyPath))
{
_unfolds.Add(property.propertyPath, false);
}
}
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: 17f32e95fcaa23e45a5ac1297f201be2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f0d69d4b88de08343adc54816c3220a1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -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:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eef729dcc033d3e419718da8e409c0f3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,321 @@
/*
* 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 System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Oculus.Interaction.Grab.GrabSurfaces.Editor
{
[CustomEditor(typeof(BezierGrabSurface))]
[CanEditMultipleObjects]
public class BezierGrabSurfaceEditor : UnityEditor.Editor
{
private BezierGrabSurface _surface;
private SerializedProperty _relativeToProperty;
private Transform _relativeTo;
private bool IsSelectedIndexValid => _selectedIndex >= 0
&& _selectedIndex < _surface.ControlPoints.Count;
private int _selectedIndex = -1;
private const float PICK_SIZE = 0.1f;
private const float AXIS_SIZE = 0.5f;
private const int CURVE_STEPS = 50;
private const float SEPARATION = 0.1f;
private void OnEnable()
{
_surface = (target as BezierGrabSurface);
_relativeToProperty = serializedObject.FindProperty("_relativeTo");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
_relativeTo = _relativeToProperty.objectReferenceValue as Transform;
if (_relativeTo == null)
{
return;
}
if (GUILayout.Button("Add ControlPoint At Start"))
{
AddControlPoint(true, _relativeTo);
}
if (GUILayout.Button("Add ControlPoint At End"))
{
AddControlPoint(false, _relativeTo);
}
if (!IsSelectedIndexValid)
{
_selectedIndex = -1;
GUILayout.Label($"No Selected Point");
}
else
{
GUILayout.Label($"Selected Point: {_selectedIndex}");
if (GUILayout.Button("Align Selected Tangent"))
{
AlignTangent(_selectedIndex, _relativeTo);
}
if (GUILayout.Button("Smooth Selected Tangent"))
{
SmoothTangent(_selectedIndex, _relativeTo);
}
}
serializedObject.ApplyModifiedProperties();
}
public void OnSceneGUI()
{
if (_relativeTo == null)
{
return;
}
Handles.color = EditorConstants.PRIMARY_COLOR;
DrawEndsCaps(_surface.ControlPoints, _relativeTo);
if (Event.current.type == EventType.Repaint)
{
DrawCurve(_surface.ControlPoints, _relativeTo);
}
}
private void AddControlPoint(bool addFirst, Transform relativeTo)
{
BezierControlPoint controlPoint = new BezierControlPoint();
if (_surface.ControlPoints.Count == 0)
{
Pose pose = _surface.transform.GetPose();
controlPoint.SetPose(pose, relativeTo);
_surface.ControlPoints.Add(controlPoint);
_selectedIndex = 0;
return;
}
else if (_surface.ControlPoints.Count == 1)
{
controlPoint = _surface.ControlPoints[0];
Pose pose = controlPoint.GetPose(relativeTo);
pose.position += relativeTo.forward * SEPARATION;
controlPoint.SetPose(pose, relativeTo);
}
else if (_surface.ControlPoints.Count > 1)
{
BezierControlPoint firstControlPoint;
BezierControlPoint secondControlPoint;
if (addFirst)
{
firstControlPoint = _surface.ControlPoints[1];
secondControlPoint = _surface.ControlPoints[0];
}
else
{
firstControlPoint = _surface.ControlPoints[_surface.ControlPoints.Count - 2];
secondControlPoint = _surface.ControlPoints[_surface.ControlPoints.Count - 1];
}
Pose firstPose = firstControlPoint.GetPose(relativeTo);
Pose secondPose = secondControlPoint.GetPose(relativeTo);
Pose controlPointPose;
controlPointPose.position = 2 * secondPose.position - firstPose.position;
controlPointPose.rotation = secondPose.rotation;
controlPoint.SetPose(controlPointPose, relativeTo);
}
if (addFirst)
{
_surface.ControlPoints.Insert(0, controlPoint);
_selectedIndex = 0;
AlignTangent(0, relativeTo);
}
else
{
_surface.ControlPoints.Add(controlPoint);
_selectedIndex = _surface.ControlPoints.Count - 1;
AlignTangent(_selectedIndex - 1, relativeTo);
}
}
private void AlignTangent(int index, Transform relativeTo)
{
BezierControlPoint controlPoint = _surface.ControlPoints[index];
BezierControlPoint nextControlPoint = _surface.ControlPoints[(index + 1) % _surface.ControlPoints.Count];
Vector3 tangent = (nextControlPoint.GetPose(relativeTo).position + controlPoint.GetPose(relativeTo).position) * 0.5f;
controlPoint.SetTangent(tangent, relativeTo);
_surface.ControlPoints[index] = controlPoint;
}
private void SmoothTangent(int index, Transform relativeTo)
{
BezierControlPoint controlPoint = _surface.ControlPoints[index];
BezierControlPoint prevControlPoint = _surface.ControlPoints[(index + _surface.ControlPoints.Count - 1) % _surface.ControlPoints.Count];
Vector3 tangent = prevControlPoint.GetTangent(relativeTo);
tangent = (controlPoint.GetPose(relativeTo).position - tangent) * 0.5f;
controlPoint.SetTangent(tangent, relativeTo);
_surface.ControlPoints[index] = controlPoint;
}
private void DrawEndsCaps(List<BezierControlPoint> controlPoints, Transform relativeTo)
{
Handles.color = EditorConstants.PRIMARY_COLOR;
for (int i = 0; i < controlPoints.Count; i++)
{
DrawControlPoint(i, relativeTo);
}
Handles.color = EditorConstants.PRIMARY_COLOR_DISABLED;
if (IsSelectedIndexValid)
{
DrawControlPointHandles(_selectedIndex, relativeTo);
DrawTangentLine(_selectedIndex, relativeTo);
}
}
private void DrawCurve(List<BezierControlPoint> controlPoints, Transform relativeTo)
{
Handles.color = EditorConstants.PRIMARY_COLOR;
for (int i = 0; i < controlPoints.Count && controlPoints.Count > 1; i++)
{
BezierControlPoint fromControlPoint = _surface.ControlPoints[i];
Pose from = fromControlPoint.GetPose(relativeTo);
BezierControlPoint toControlPoint = _surface.ControlPoints[(i + 1) % controlPoints.Count];
if (toControlPoint.Disconnected)
{
continue;
}
Pose to = toControlPoint.GetPose(relativeTo);
Vector3 tangent = fromControlPoint.GetTangent(relativeTo);
DrawBezier(from.position, tangent, to.position, CURVE_STEPS);
}
}
private void DrawBezier(Vector3 start, Vector3 middle, Vector3 end, int steps)
{
Vector3 from = start;
Vector3 to;
float t;
for (int i = 1; i < steps; i++)
{
t = i / (steps - 1f);
to = BezierGrabSurface.EvaluateBezier(start, middle, end, t);
#if UNITY_2020_2_OR_NEWER
Handles.DrawLine(from, to, EditorConstants.LINE_THICKNESS);
#else
Handles.DrawLine(from, to);
#endif
from = to;
}
}
private void DrawTangentLine(int index, Transform relativeTo)
{
BezierControlPoint controlPoint = _surface.ControlPoints[index];
Pose pose = controlPoint.GetPose(relativeTo);
Vector3 center = pose.position;
Vector3 tangent = controlPoint.GetTangent(relativeTo);
#if UNITY_2020_2_OR_NEWER
Handles.DrawLine(center, tangent, EditorConstants.LINE_THICKNESS);
#else
Handles.DrawLine(center, tangent);
#endif
}
private void DrawControlPoint(int index, Transform relativeTo)
{
BezierControlPoint controlPoint = _surface.ControlPoints[index];
Pose pose = controlPoint.GetPose(relativeTo);
float handleSize = HandleUtility.GetHandleSize(pose.position);
Handles.color = EditorConstants.PRIMARY_COLOR;
if (Handles.Button(pose.position, pose.rotation, handleSize * PICK_SIZE, handleSize * PICK_SIZE, Handles.DotHandleCap))
{
_selectedIndex = index;
}
Handles.color = Color.red;
Handles.DrawLine(pose.position, pose.position + pose.right * handleSize * AXIS_SIZE);
Handles.color = Color.green;
Handles.DrawLine(pose.position, pose.position + pose.up * handleSize * AXIS_SIZE);
Handles.color = Color.blue;
Handles.DrawLine(pose.position, pose.position + pose.forward * handleSize * AXIS_SIZE);
}
private void DrawControlPointHandles(int index, Transform relativeTo)
{
BezierControlPoint controlPoint = _surface.ControlPoints[index];
Pose pose = controlPoint.GetPose(relativeTo);
if (Tools.current == Tool.Move)
{
EditorGUI.BeginChangeCheck();
Quaternion pointRotation = Tools.pivotRotation == PivotRotation.Global ? Quaternion.identity : pose.rotation;
pose.position = Handles.PositionHandle(pose.position, pointRotation);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(_surface, "Change ControlPoint Position");
controlPoint.SetPose(pose, relativeTo);
_surface.ControlPoints[index] = controlPoint;
}
}
else if (Tools.current == Tool.Rotate)
{
Quaternion originalRotation = pose.rotation;
if (Tools.pivotRotation == PivotRotation.Global)
{
Quaternion offset = Handles.RotationHandle(Quaternion.identity, pose.position);
pose.rotation = offset * pose.rotation;
}
else
{
pose.rotation = Handles.RotationHandle(pose.rotation, pose.position);
}
pose.rotation.Normalize();
if (originalRotation != pose.rotation)
{
Undo.RecordObject(_surface, "Change ControlPoint Rotation");
controlPoint.SetPose(pose, relativeTo);
_surface.ControlPoints[index] = controlPoint;
}
}
Vector3 tangent = controlPoint.GetTangent(relativeTo);
Quaternion tangentRotation = Tools.pivotRotation == PivotRotation.Global ? Quaternion.identity : pose.rotation;
EditorGUI.BeginChangeCheck();
tangent = Handles.PositionHandle(tangent, tangentRotation);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(_surface, "Change ControlPoint Tangent");
controlPoint.SetTangent(tangent, relativeTo);
_surface.ControlPoints[index] = controlPoint;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 718688abdc60caa4984fe98623ff42dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,193 @@
/*
* 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 UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace Oculus.Interaction.Grab.GrabSurfaces.Editor
{
[CustomEditor(typeof(BoxGrabSurface))]
[CanEditMultipleObjects]
public class BoxGrabSurfaceEditor : UnityEditor.Editor
{
private BoxBoundsHandle _boxHandle = new BoxBoundsHandle();
private BoxGrabSurface _surface;
private Transform _relativeTo;
private SerializedProperty _relativeToProperty;
private void OnEnable()
{
_boxHandle.handleColor = EditorConstants.PRIMARY_COLOR;
_boxHandle.wireframeColor = EditorConstants.PRIMARY_COLOR_DISABLED;
_boxHandle.axes = PrimitiveBoundsHandle.Axes.X | PrimitiveBoundsHandle.Axes.Z;
_surface = (target as BoxGrabSurface);
_relativeToProperty = serializedObject.FindProperty("_relativeTo");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
_relativeTo = _relativeToProperty.objectReferenceValue as Transform;
}
public void OnSceneGUI()
{
if (_relativeTo == null)
{
return;
}
DrawRotator(_surface, _relativeTo);
DrawBoxEditor(_surface, _relativeTo);
DrawSlider(_surface, _relativeTo);
if (Event.current.type == EventType.Repaint)
{
DrawSnapLines(_surface, _relativeTo);
}
}
private void DrawSnapLines(BoxGrabSurface surface, Transform relativeTo)
{
Handles.color = EditorConstants.PRIMARY_COLOR;
Vector3 size = surface.GetSize(relativeTo);
Quaternion rotation = surface.GetRotation(relativeTo);
Vector3 surfacePosition = surface.GetReferencePose(relativeTo).position;
float widthOffset = surface.GetWidthOffset(relativeTo);
Vector4 snapOffset = surface.GetSnapOffset(relativeTo);
Vector3 rightAxis = rotation * Vector3.right;
Vector3 forwardAxis = rotation * Vector3.forward;
Vector3 forwardOffset = forwardAxis * size.z;
Vector3 bottomLeft = surfacePosition - rightAxis * size.x * (1f - widthOffset);
Vector3 bottomRight = surfacePosition + rightAxis * size.x * (widthOffset);
Vector3 topLeft = bottomLeft + forwardOffset;
Vector3 topRight = bottomRight + forwardOffset;
Handles.DrawLine(bottomLeft + rightAxis * snapOffset.y, bottomRight + rightAxis * snapOffset.x);
Handles.DrawLine(topLeft - rightAxis * snapOffset.x, topRight - rightAxis * snapOffset.y);
Handles.DrawLine(bottomLeft - forwardAxis * snapOffset.z, topLeft - forwardAxis * snapOffset.w);
Handles.DrawLine(bottomRight + forwardAxis * snapOffset.w, topRight + forwardAxis * snapOffset.z);
}
private void DrawSlider(BoxGrabSurface surface, Transform relativeTo)
{
Handles.color = EditorConstants.PRIMARY_COLOR;
Vector3 size = surface.GetSize(relativeTo);
Quaternion rotation = surface.GetRotation(relativeTo);
Vector3 surfacePosition = surface.GetReferencePose(relativeTo).position;
float widthOffset = surface.GetWidthOffset(relativeTo);
Vector4 snapOffset = surface.GetSnapOffset(relativeTo);
EditorGUI.BeginChangeCheck();
Vector3 rightDir = rotation * Vector3.right;
Vector3 forwardDir = rotation * Vector3.forward;
Vector3 bottomRight = surfacePosition
+ rightDir * size.x * (widthOffset);
Vector3 bottomLeft = surfacePosition
- rightDir * size.x * (1f - widthOffset);
Vector3 topRight = bottomRight + forwardDir * size.z;
Vector3 rightHandle = DrawOffsetHandle(bottomRight + rightDir * snapOffset.x, rightDir);
Vector3 leftHandle = DrawOffsetHandle(bottomLeft + rightDir * snapOffset.y, -rightDir);
Vector3 topHandle = DrawOffsetHandle(topRight + forwardDir * snapOffset.z, forwardDir);
Vector3 bottomHandle = DrawOffsetHandle(bottomRight + forwardDir * snapOffset.w, -forwardDir);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(surface, "Change Offset Box");
Vector4 offset = snapOffset;
offset.x = DistanceToHandle(bottomRight, rightHandle, rightDir);
offset.y = DistanceToHandle(bottomLeft, leftHandle, rightDir);
offset.z = DistanceToHandle(topRight, topHandle, forwardDir);
offset.w = DistanceToHandle(bottomRight, bottomHandle, forwardDir);
surface.SetSnapOffset(offset, relativeTo);
}
}
private Vector3 DrawOffsetHandle(Vector3 point, Vector3 dir)
{
float size = HandleUtility.GetHandleSize(point) * 0.2f;
return Handles.Slider(point, dir, size, Handles.ConeHandleCap, 0f);
}
private float DistanceToHandle(Vector3 origin, Vector3 handlePoint, Vector3 dir)
{
float distance = Vector3.Distance(origin, handlePoint);
if (Vector3.Dot(handlePoint - origin, dir) < 0f)
{
distance = -distance;
}
return distance;
}
private void DrawRotator(BoxGrabSurface surface, Transform relativeTo)
{
EditorGUI.BeginChangeCheck();
Quaternion rotation = Handles.RotationHandle(
surface.GetRotation(relativeTo),
surface.GetReferencePose(relativeTo).position);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(surface, "Change Rotation Box");
surface.SetRotation(rotation, relativeTo);
}
}
private void DrawBoxEditor(BoxGrabSurface surface, Transform relativeTo)
{
Quaternion rot = surface.GetRotation(relativeTo);
Vector3 size = surface.GetSize(relativeTo);
float widthOffset = surface.GetWidthOffset(relativeTo);
Vector3 snapP = surface.GetReferencePose(relativeTo).position;
_boxHandle.size = size;
float widthPos = Mathf.Lerp(-size.x * 0.5f, size.x * 0.5f, widthOffset);
_boxHandle.center = new Vector3(widthPos, 0f, size.z * 0.5f);
Matrix4x4 handleMatrix = Matrix4x4.TRS(
snapP,
rot,
Vector3.one
);
using (new Handles.DrawingScope(handleMatrix))
{
EditorGUI.BeginChangeCheck();
_boxHandle.DrawHandle();
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(surface, "Change Box Properties");
surface.SetSize(_boxHandle.size, relativeTo);
float width = _boxHandle.size.x;
if (width != 0f)
{
width = (_boxHandle.center.x + width * 0.5f) / width;
}
surface.SetWidthOffset(width, relativeTo);
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: baf3d860debef0947b62cdebdd94cb74
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,172 @@
/*
* 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 UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace Oculus.Interaction.Grab.GrabSurfaces.Editor
{
[CustomEditor(typeof(CylinderGrabSurface))]
[CanEditMultipleObjects]
public class CylinderGrabSurfaceEditor : UnityEditor.Editor
{
private const float DRAW_SURFACE_ANGULAR_RESOLUTION = 5f;
private ArcHandle _arcEndHandle = new ArcHandle();
private ArcHandle _arcStartHandle = new ArcHandle();
private Vector3[] _surfaceEdges;
private CylinderGrabSurface _surface;
private SerializedProperty _relativeToProperty;
private Transform _relativeTo;
private void OnEnable()
{
_arcStartHandle.SetColorWithRadiusHandle(EditorConstants.PRIMARY_COLOR_DISABLED, 0f);
_arcEndHandle.SetColorWithRadiusHandle(EditorConstants.PRIMARY_COLOR, 0f);
_surface = target as CylinderGrabSurface;
_relativeToProperty = serializedObject.FindProperty("_relativeTo");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
_relativeTo = _relativeToProperty.objectReferenceValue as Transform;
}
public void OnSceneGUI()
{
if (_relativeTo == null)
{
return;
}
DrawEndsCaps(_surface, _relativeTo);
float oldArcStart = _surface.ArcOffset;
Quaternion look = Quaternion.LookRotation(_surface.GetPerpendicularDir(_relativeTo), _surface.GetDirection(_relativeTo));
float newArcStart = DrawArcEditor(_surface, _arcStartHandle, _relativeTo,
oldArcStart,_surface.GetStartPoint(_relativeTo),
look);
_surface.ArcOffset = newArcStart;
_surface.ArcLength -= newArcStart - oldArcStart;
_surface.ArcLength = DrawArcEditor(_surface, _arcEndHandle, _relativeTo,
_surface.ArcLength,
_surface.GetStartPoint(_relativeTo),
Quaternion.LookRotation(_surface.GetStartArcDir(_relativeTo), _surface.GetDirection(_relativeTo)));
if (Event.current.type == EventType.Repaint)
{
DrawSurfaceVolume(_surface, _relativeTo);
}
}
private void DrawEndsCaps(CylinderGrabSurface surface, Transform relativeTo)
{
EditorGUI.BeginChangeCheck();
Quaternion handleRotation = relativeTo.rotation;
Vector3 startPosition = Handles.PositionHandle(surface.GetStartPoint(relativeTo), handleRotation);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(surface, "Change Start Cylinder Position");
surface.SetStartPoint(startPosition, relativeTo);
}
EditorGUI.BeginChangeCheck();
Vector3 endPosition = Handles.PositionHandle(surface.GetEndPoint(relativeTo), handleRotation);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(surface, "Change Start Cylinder Position");
surface.SetEndPoint(endPosition, relativeTo);
}
}
private void DrawSurfaceVolume(CylinderGrabSurface surface, Transform relativeTo)
{
Vector3 start = surface.GetStartPoint(relativeTo);
Vector3 end = surface.GetEndPoint(relativeTo);
Vector3 startArc = surface.GetStartArcDir(relativeTo);
Vector3 endArc = surface.GetEndArcDir(relativeTo);
Vector3 direction = surface.GetDirection(relativeTo);
float radius = surface.GetRadius(relativeTo);
Handles.color = EditorConstants.PRIMARY_COLOR;
Handles.DrawWireArc(end,
direction,
startArc,
surface.ArcLength,
radius);
Handles.DrawLine(start, end);
Handles.DrawLine(start, start + startArc * radius);
Handles.DrawLine(start, start + endArc * radius);
Handles.DrawLine(end, end + startArc * radius);
Handles.DrawLine(end, end + endArc * radius);
int edgePoints = Mathf.CeilToInt((2 * surface.ArcLength) / DRAW_SURFACE_ANGULAR_RESOLUTION) + 3;
if (_surfaceEdges == null
|| _surfaceEdges.Length != edgePoints)
{
_surfaceEdges = new Vector3[edgePoints];
}
Handles.color = EditorConstants.PRIMARY_COLOR_DISABLED;
int i = 0;
for (float angle = 0f; angle < surface.ArcLength; angle += DRAW_SURFACE_ANGULAR_RESOLUTION)
{
Vector3 dir = Quaternion.AngleAxis(angle, direction) * startArc;
_surfaceEdges[i++] = start + dir * radius;
_surfaceEdges[i++] = end + dir * radius;
}
_surfaceEdges[i++] = start + endArc * radius;
_surfaceEdges[i++] = end + endArc * radius;
Handles.DrawPolyLine(_surfaceEdges);
}
private float DrawArcEditor(CylinderGrabSurface surface, ArcHandle handle, Transform relativeTo,
float inputAngle, Vector3 position, Quaternion rotation)
{
handle.radius = surface.GetRadius(relativeTo);
handle.angle = inputAngle;
Matrix4x4 handleMatrix = Matrix4x4.TRS(
position,
rotation,
Vector3.one
);
using (new Handles.DrawingScope(handleMatrix))
{
EditorGUI.BeginChangeCheck();
handle.DrawHandle();
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(surface, "Change Cylinder Properties");
return handle.angle;
}
}
return inputAngle;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 69099ed7427360a4ab3734573c188647
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,97 @@
/*
* 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 UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace Oculus.Interaction.Grab.GrabSurfaces.Editor
{
[CustomEditor(typeof(SphereGrabSurface))]
[CanEditMultipleObjects]
public class SphereGrabSurfaceEditor : UnityEditor.Editor
{
private SphereBoundsHandle _sphereHandle = new SphereBoundsHandle();
private SphereGrabSurface _surface;
private SerializedProperty _relativeToProperty;
private Transform _relativeTo;
private void OnEnable()
{
_sphereHandle.SetColor(EditorConstants.PRIMARY_COLOR);
_sphereHandle.midpointHandleDrawFunction = null;
_surface = target as SphereGrabSurface;
_relativeToProperty = serializedObject.FindProperty("_relativeTo");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
_relativeTo = _relativeToProperty.objectReferenceValue as Transform;
}
public void OnSceneGUI()
{
if (_relativeTo == null)
{
return;
}
DrawCentre(_surface, _relativeTo);
Handles.color = Color.white;
DrawSphereEditor(_surface, _relativeTo);
if (Event.current.type == EventType.Repaint)
{
DrawSurfaceVolume(_surface, _relativeTo);
}
}
private void DrawCentre(SphereGrabSurface surface, Transform relativeTo)
{
EditorGUI.BeginChangeCheck();
Quaternion handleRotation = relativeTo.rotation;
Vector3 centrePosition = Handles.PositionHandle(surface.GetCentre(relativeTo), handleRotation);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(surface, "Change Centre Sphere Position");
surface.SetCentre(centrePosition, relativeTo);
}
}
private void DrawSurfaceVolume(SphereGrabSurface surface, Transform relativeTo)
{
Handles.color = EditorConstants.PRIMARY_COLOR;
Vector3 startLine = surface.GetCentre(relativeTo);
Vector3 endLine = startLine + surface.GetDirection(relativeTo) * surface.GetRadius(relativeTo);
Handles.DrawDottedLine(startLine, endLine, 5);
}
private void DrawSphereEditor(SphereGrabSurface surface, Transform relativeTo)
{
_sphereHandle.radius = surface.GetRadius(relativeTo);
_sphereHandle.center = surface.GetCentre(relativeTo);
EditorGUI.BeginChangeCheck();
_sphereHandle.DrawHandle();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ee7657a153e652d448fa1b7775ca7f8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5760a4e866e11cb46a5adff4e32acc44
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,122 @@
/*
* 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.Input;
using UnityEditor;
using UnityEngine;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System;
namespace Oculus.Interaction.HandGrab.Visuals.Editor
{
[CustomEditor(typeof(HandPuppet))]
public class HandPuppetEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
HandPuppet puppet = target as HandPuppet;
if (GUILayout.Button("Auto-Assign Bones"))
{
SkinnedMeshRenderer skinnedHand = puppet.GetComponentInChildren<SkinnedMeshRenderer>();
if (skinnedHand != null)
{
SetPrivateValue(puppet, "_jointMaps", AutoAsignBones(skinnedHand));
}
}
}
private List<HandJointMap> AutoAsignBones(SkinnedMeshRenderer skinnedHand)
{
List<HandJointMap> maps = new List<HandJointMap>();
Transform root = skinnedHand.rootBone;
Regex regEx = new Regex(@"Hand(\w*)(\d)");
foreach (var bone in FingersMetadata.HAND_JOINT_IDS)
{
Match match = regEx.Match(bone.ToString());
if (match != Match.Empty)
{
string boneName = match.Groups[1].Value.ToLower();
string boneNumber = match.Groups[2].Value;
Transform skinnedBone = RecursiveSearchForChildrenContainingPattern(root, "col", boneName, boneNumber);
if (skinnedBone != null)
{
maps.Add(new HandJointMap()
{
id = bone,
transform = skinnedBone,
rotationOffset = Vector3.zero
});
}
}
}
return maps;
}
private Transform RecursiveSearchForChildrenContainingPattern(Transform root, string ignorePattern, params string[] args)
{
if (root == null)
{
return null;
}
for (int i = 0; i < root.childCount; i++)
{
Transform child = root.GetChild(i);
string childName = child.name.ToLower();
bool shouldCheck = string.IsNullOrEmpty(ignorePattern)|| !childName.Contains(ignorePattern);
if (shouldCheck)
{
bool containsAllArgs = args.All(a => childName.Contains(a));
Transform result = containsAllArgs ? child
: RecursiveSearchForChildrenContainingPattern(child, ignorePattern, args);
if (result != null)
{
return result;
}
}
}
return null;
}
private static void SetPrivateValue(object instance, string fieldName, object value)
{
FieldInfo fieldData = GetPrivateField(instance, fieldName);
fieldData.SetValue(instance, value);
}
private static FieldInfo GetPrivateField(object instance, string fieldName)
{
Type type = instance.GetType();
FieldInfo fieldData = null;
while (type != null && fieldData == null)
{
fieldData = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
type = type.BaseType;
}
return fieldData;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fb714303644f5c343bd2c80559c02856
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: