Initial Commit
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+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:
|
||||
@@ -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:
|
||||
Reference in New Issue
Block a user