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,330 @@
/*
* 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.PoseDetection.Editor.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection.Editor
{
namespace Model
{
public class FeatureConfigList
{
private readonly SerializedProperty _root;
private uint _flags;
private readonly IReadOnlyDictionary<int, FeatureDescription> _featureDescriptions;
public FeatureConfigList(SerializedProperty root,
IReadOnlyDictionary<int, FeatureDescription> featureDescriptions)
{
Assert.IsNotNull(root);
_root = root;
_flags = GetFlags(_root);
_featureDescriptions = featureDescriptions;
}
private uint GetFlags(SerializedProperty root)
{
if (!root.isArray)
{
return 0U;
}
uint flags = 0U;
for (int i = 0; i < root.arraySize; i++)
{
var elemProp = root.GetArrayElementAtIndex(i);
var featureProp = elemProp.FindPropertyRelative("_feature");
flags |= 1U << featureProp.enumValueIndex;
}
return flags;
}
public uint Flags
{
get => _flags;
set
{
uint flagsToCreate = value;
for (int i = 0; i < _root.arraySize;)
{
var elemProp = _root.GetArrayElementAtIndex(i);
var featureProp = elemProp.FindPropertyRelative("_feature");
uint propFlags = (1U << featureProp.enumValueIndex);
if ((flagsToCreate & propFlags) == 0U)
{
// Feature is in list, but not flags... delete list entry.
_root.DeleteArrayElementAtIndex(i);
}
else
{
// Feature is in list, and in flags; remove from list of things we need to create.
flagsToCreate &= ~propFlags;
i++;
}
}
// Create missing elements.
foreach (var feature in _featureDescriptions.Keys)
{
uint flags = 1U << feature;
if ((flagsToCreate & flags) == 0U)
{
continue;
}
var lastIndex = _root.arraySize;
_root.InsertArrayElementAtIndex(lastIndex);
var model = new FeatureConfig(_root.GetArrayElementAtIndex(lastIndex));
model.Feature = feature;
model.FeatureState = _featureDescriptions[feature].FeatureStates[0].Id;
}
_flags = value;
}
}
public IEnumerable<FeatureConfig> ConfigModels
{
get
{
List<FeatureConfig> models = new List<FeatureConfig>(_root.arraySize);
for (int i = 0; i < _root.arraySize; i++)
{
models.Add(new FeatureConfig(_root.GetArrayElementAtIndex(i)));
}
return models;
}
}
}
public class FeatureConfig
{
SerializedProperty _modeProp;
SerializedProperty _featureProp;
SerializedProperty _stateProp;
public FeatureConfig(SerializedProperty root)
{
_modeProp = root.FindPropertyRelative("_mode");
_featureProp = root.FindPropertyRelative("_feature");
_stateProp = root.FindPropertyRelative("_state");
}
public FeatureStateActiveMode Mode
{
get => (FeatureStateActiveMode)_modeProp.enumValueIndex;
set
{
if (value != (FeatureStateActiveMode)_modeProp.enumValueIndex)
{
_modeProp.enumValueIndex = (int)value;
}
}
}
public int Feature
{
get => _featureProp.enumValueIndex;
set
{
if (value != _featureProp.enumValueIndex)
{
_featureProp.enumValueIndex = value;
}
}
}
public string FeatureState
{
get => _stateProp.stringValue;
set
{
if (value != _stateProp.stringValue)
{
_stateProp.stringValue = value;
}
}
}
}
}
public abstract class FeatureListPropertyDrawer : PropertyDrawer
{
public float ControlLineHeight => EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; // = 18 + 2
public float BottomMargin => EditorGUIUtility.standardVerticalSpacing * 2;
private const float IndentSize = 16;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var height = base.GetPropertyHeight(property, label); // includes height of first line
var model = CreateModel(property);
if (property.isExpanded)
{
var controlCount = model.ConfigModels.Count();
height += controlCount * ControlLineHeight;
height += BottomMargin;
}
return height;
}
public override bool CanCacheInspectorGUI(SerializedProperty property)
{
return true;
}
public override void OnGUI(Rect drawerPos, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(drawerPos, label, property);
var oldIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var labelPos = new Rect(drawerPos)
{
width = EditorGUIUtility.labelWidth - drawerPos.x,
height = EditorGUIUtility.singleLineHeight
};
var model = CreateModel(property);
property.isExpanded = EditorGUI.Foldout(labelPos, property.isExpanded, label, true);
if (property.isExpanded)
{
RenderExpanded(drawerPos, model);
}
else
{
RenderCollapsed(drawerPos, model);
}
EditorGUI.indentLevel = oldIndent;
EditorGUI.EndProperty();
}
private void RenderExpanded(Rect drawerPos, FeatureConfigList model) {
Rect controlPos = new Rect(drawerPos.x, drawerPos.y, drawerPos.width,
EditorGUIUtility.singleLineHeight);
var flagsPos = Indent(controlPos, EditorGUIUtility.labelWidth);
var newFlags = EnumToFlags(EditorGUI.EnumFlagsField(flagsPos, FlagsToEnum(model.Flags)));
if (newFlags != model.Flags)
{
model.Flags = newFlags;
}
controlPos = Indent(controlPos, IndentSize);
foreach (var configModel in model.ConfigModels)
{
controlPos.y += ControlLineHeight;
// Render the label
float indent = 0f;
var labelPos = Indent(controlPos, indent);
labelPos.width = EditorGUIUtility.labelWidth - IndentSize;
var featureName = FeatureToString(configModel.Feature);
featureName = ObjectNames.NicifyVariableName(featureName);
EditorGUI.PrefixLabel(labelPos, new GUIContent(featureName));
// Render the mode dropdown
indent += labelPos.width;
var modePos = Indent(controlPos, indent);
var allowedModes = GetAllowedModes(configModel);
if (allowedModes.Length > 1)
{
modePos.width = 70;
configModel.Mode =
(FeatureStateActiveMode)EditorGUI.EnumPopup(modePos, configModel.Mode);
}
else if (allowedModes.Length == 1)
{
configModel.Mode = allowedModes[0];
modePos.width = 15;
EditorGUI.SelectableLabel(modePos,
ObjectNames.NicifyVariableName(allowedModes[0] + ": "));
}
else
{
modePos.width = -2;
}
// Render the state dropdown
indent += modePos.width + 2;
var statePos = Indent(controlPos, indent);
var featureStates = GetStatesForFeature(configModel.Feature);
string[] options = featureStates.Select(fs => fs.Name).ToArray();
int selectedIndex = Array.FindIndex(featureStates, fs => fs.Id == configModel.FeatureState);
int newSelectedIndex = EditorGUI.Popup(statePos, selectedIndex, options);
if (newSelectedIndex != selectedIndex) {
configModel.FeatureState = featureStates[newSelectedIndex].Id;
}
}
}
private void RenderCollapsed(Rect drawerPos, FeatureConfigList model)
{
Rect controlPos = drawerPos;
controlPos.height = EditorGUIUtility.singleLineHeight;
var valuePos = Indent(controlPos, EditorGUIUtility.labelWidth + 2);
valuePos.width = drawerPos.width - valuePos.x;
var flagsEnum = FlagsToEnum(model.Flags);
var values = Enum.GetValues(flagsEnum.GetType())
.Cast<Enum>()
.Where(e => flagsEnum.HasFlag(e))
.Select(e => e.ToString());
EditorGUI.SelectableLabel(valuePos, String.Join(", ", values));
}
private static Rect Indent(Rect position, float indentWidth)
{
return new Rect(position)
{
x = position.x + indentWidth,
width = position.width - indentWidth
};
}
protected virtual FeatureStateActiveMode[] GetAllowedModes(FeatureConfig model)
{
var statesForFeature = GetStatesForFeature(model.Feature);
if (statesForFeature.Length > 2)
return (FeatureStateActiveMode[])Enum.GetValues(typeof(FeatureStateActiveMode));
else
return new[] { FeatureStateActiveMode.Is };
}
protected abstract Enum FlagsToEnum(uint flags);
protected abstract uint EnumToFlags(Enum flags);
protected abstract string FeatureToString(int featureIdx);
protected abstract FeatureStateDescription[] GetStatesForFeature(int featureIdx);
protected abstract Model.FeatureConfigList CreateModel(SerializedProperty property);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 39acc9895ae6408898722a714edbe5ce
timeCreated: 1631575366
@@ -0,0 +1,498 @@
/*
* 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;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection.Editor
{
public abstract class FeatureStateThresholdsEditor<TFeature> : UnityEditor.Editor
where TFeature : unmanaged, Enum
{
#region static helpers
public static readonly TFeature[] FeatureEnumValues = (TFeature[])Enum.GetValues(typeof(TFeature));
public static TFeature IntToFeature(int value)
{
return FeatureEnumValues[value];
}
public static int FeatureToInt(TFeature feature)
{
for (int i = 0; i < FeatureEnumValues.Length; i++)
{
TFeature enumVal = FeatureEnumValues[i];
if (enumVal.Equals(feature))
{
return i;
}
}
throw new ArgumentOutOfRangeException();
}
#endregion
#region Model Classes
public class FeatureStateThresholdsModel
{
private readonly SerializedProperty _thresholdsProp;
private readonly SerializedProperty _featureProp;
public FeatureStateThresholdsModel(SerializedProperty self)
{
_featureProp = self.FindPropertyRelative("_feature");
_thresholdsProp = self.FindPropertyRelative("_thresholds");
Assert.IsNotNull(_featureProp);
Assert.IsNotNull(_thresholdsProp);
}
public TFeature Feature
{
get => IntToFeature(_featureProp.enumValueIndex);
set => _featureProp.enumValueIndex = FeatureToInt(value);
}
public SerializedProperty ThresholdsProp => _thresholdsProp;
}
public class FeatureStateThresholdModel
{
private readonly SerializedProperty _thresholdMidpointProp;
private readonly SerializedProperty _thresholdWidthProp;
private readonly SerializedProperty _firstStateProp;
private readonly SerializedProperty _secondStateProp;
public FeatureStateThresholdModel(SerializedProperty self)
{
_thresholdMidpointProp = self.FindPropertyRelative("_thresholdMidpoint");
_thresholdWidthProp = self.FindPropertyRelative("_thresholdWidth");
_firstStateProp = self.FindPropertyRelative("_firstState");
_secondStateProp = self.FindPropertyRelative("_secondState");
Assert.IsNotNull(_thresholdMidpointProp);
Assert.IsNotNull(_thresholdWidthProp);
Assert.IsNotNull(_firstStateProp);
Assert.IsNotNull(_secondStateProp);
}
public float ThresholdMidpoint
{
get => _thresholdMidpointProp.floatValue;
set { _thresholdMidpointProp.floatValue = value; }
}
public float ThresholdWidth
{
get => _thresholdWidthProp.floatValue;
set { _thresholdWidthProp.floatValue = value; }
}
public string FirstStateId
{
get => _firstStateProp.stringValue;
set => _firstStateProp.stringValue = value;
}
public string SecondStateId
{
get => _secondStateProp.stringValue;
set => _secondStateProp.stringValue = value;
}
public float ToFirstWhenBelow => ThresholdMidpoint - ThresholdWidth * 0.5f;
public float ToSecondWhenAbove => ThresholdMidpoint + ThresholdWidth * 0.5f;
}
#endregion
private SerializedProperty _rootProperty;
private SerializedProperty _minTimeInStateProp;
private readonly bool[] _featureVisible = new bool[FeatureEnumValues.Length];
private readonly Color _visStateColorPro = new Color32(194, 194, 194, 255);
private readonly Color _visStateColorLight = new Color32(56, 56, 56, 255);
private readonly Color _visTransitionColorPro = new Color32(80, 80, 80, 255);
private readonly Color _visTransitionColorLight = new Color32(160, 160, 160, 255);
private readonly Color _visDragColor = new Color32(0, 0, 128, 255);
private readonly Color _visBorderColor = new Color32(0, 0, 0, 255);
private const float _visHeight = 20.0f;
private const float _visMargin = 10.0f;
private const float _dragMargin = 10f;
private IReadOnlyDictionary<TFeature, FeatureDescription> _featureDescriptions;
protected abstract IReadOnlyDictionary<TFeature, FeatureDescription> CreateFeatureDescriptions();
protected abstract string FeatureMidpointTooltip { get; }
protected abstract string FeatureWidthTooltip { get; }
void OnEnable()
{
if (_featureDescriptions == null)
{
_featureDescriptions = CreateFeatureDescriptions();
}
if (_featureDescriptions.Count != FeatureEnumValues.Length)
{
throw new InvalidOperationException(
"CreateFeatureDescriptions() must return one key for each enum value.");
}
_rootProperty = serializedObject.FindProperty("_featureThresholds");
_minTimeInStateProp = serializedObject.FindProperty("_minTimeInState");
for (var index = 0; index < _featureVisible.Length; index++)
{
_featureVisible[index] = true;
}
}
public override void OnInspectorGUI()
{
if (_rootProperty == null || !_rootProperty.isArray || _minTimeInStateProp == null)
{
return;
}
EditorGUILayout.LabelField("All Features", EditorStyles.whiteLargeLabel);
EditorGUILayout.PropertyField(_minTimeInStateProp);
GUILayout.Space(10);
EditorGUILayout.LabelField("Per Feature", EditorStyles.whiteLargeLabel);
foreach (TFeature feature in FeatureEnumValues)
{
FeatureStateThresholdsModel foundFeatureProp = null;
for (int i = 0; i < _rootProperty.arraySize; ++i)
{
var featureThresholdsProp =
new FeatureStateThresholdsModel(
_rootProperty.GetArrayElementAtIndex(i));
if (featureThresholdsProp.Feature.Equals(feature))
{
foundFeatureProp = featureThresholdsProp;
break;
}
}
ref bool isVisible = ref _featureVisible[FeatureToInt(feature)];
isVisible = EditorGUILayout.BeginFoldoutHeaderGroup(isVisible, $"{feature} Thresholds");
if (!isVisible)
{
EditorGUILayout.EndFoldoutHeaderGroup();
continue;
}
if (!IsFeatureThresholdsValid(foundFeatureProp))
{
if (GUILayout.Button("Create Config"))
{
foundFeatureProp = CreateFeatureStateConfig(feature);
}
else
{
foundFeatureProp = null;
}
}
if (foundFeatureProp != null)
{
RenderFeatureStates(feature, foundFeatureProp);
}
EditorGUILayout.EndFoldoutHeaderGroup();
}
serializedObject.ApplyModifiedProperties();
}
private FeatureStateThresholdsModel CreateFeatureStateConfig(
TFeature feature)
{
// Delete any old invalid configs for this feature.
for (int i = 0; i < _rootProperty.arraySize;)
{
var model =
new FeatureStateThresholdsModel(
_rootProperty.GetArrayElementAtIndex(i));
if (model.Feature.Equals(feature))
{
_rootProperty.DeleteArrayElementAtIndex(i);
}
else
{
i++;
}
}
// Create a new config
int insertIndex = _rootProperty.arraySize;
_rootProperty.InsertArrayElementAtIndex(insertIndex);
var featureStateThresholds = new FeatureStateThresholdsModel(
_rootProperty.GetArrayElementAtIndex(insertIndex))
{
Feature = feature
};
// Set initial state
ResetFeatureStates(featureStateThresholds);
return featureStateThresholds;
}
private void ResetFeatureStates(FeatureStateThresholdsModel foundFeatureProp)
{
var states = _featureDescriptions[foundFeatureProp.Feature].FeatureStates;
var thresholdsArrayProp = foundFeatureProp.ThresholdsProp;
foundFeatureProp.ThresholdsProp.arraySize = states.Length - 1;
var featureDescription = _featureDescriptions[foundFeatureProp.Feature];
float minExpectedValue = featureDescription.MinValueHint;
float maxExpectedValue = featureDescription.MaxValueHint;
float range = maxExpectedValue - minExpectedValue;
float initialWidth = range * 0.075f;
float numStatesMultiplier = range / (states.Length);
for (int stateIdx = 0; stateIdx < states.Length - 1; ++stateIdx)
{
var featureState = states[stateIdx];
FeatureStateThresholdModel model = new FeatureStateThresholdModel(
thresholdsArrayProp.GetArrayElementAtIndex(stateIdx));
model.ThresholdMidpoint = minExpectedValue + (stateIdx + 1) * numStatesMultiplier;
model.ThresholdWidth = initialWidth;
model.FirstStateId = featureState.Id;
model.SecondStateId = states[stateIdx + 1].Id;
}
}
private bool IsFeatureThresholdsValid(FeatureStateThresholdsModel foundFeatureModel)
{
if (foundFeatureModel == null)
{
return false;
}
var states = _featureDescriptions[foundFeatureModel.Feature].FeatureStates;
if (foundFeatureModel.ThresholdsProp.arraySize != states.Length - 1)
{
return false;
}
for (var firstStateIdx = 0; firstStateIdx < states.Length - 1; firstStateIdx++)
{
var model = new FeatureStateThresholdModel(
foundFeatureModel.ThresholdsProp.GetArrayElementAtIndex(firstStateIdx));
if (states[firstStateIdx].Id != model.FirstStateId ||
states[firstStateIdx + 1].Id != model.SecondStateId)
{
return false;
}
}
return true;
}
private void RenderFeatureStates(TFeature feature, FeatureStateThresholdsModel featureStateThresholdsModel)
{
FeatureDescription featureDescription = _featureDescriptions[feature];
// Indent block
using (new EditorGUI.IndentLevelScope())
{
RenderFeatureDescription(featureDescription);
var states = _featureDescriptions[feature].FeatureStates;
float minVal = float.MaxValue;
float maxVal = float.MinValue;
bool overlappingValues = false;
float thresholdMaxWidth =
featureDescription.MaxValueHint - featureDescription.MinValueHint;
for (var firstStateIdx = 0; firstStateIdx < states.Length - 1; firstStateIdx++)
{
var firstState = states[firstStateIdx];
var secondState = states[firstStateIdx + 1];
EditorGUILayout.LabelField($"{firstState.Name} ⟷ {secondState.Name}", EditorStyles.label);
// Indent block
using (new EditorGUI.IndentLevelScope())
{
var model = new FeatureStateThresholdModel(
featureStateThresholdsModel.ThresholdsProp.GetArrayElementAtIndex(firstStateIdx));
if (model.ToFirstWhenBelow <= maxVal || model.ToSecondWhenAbove <= maxVal)
{
overlappingValues = true;
}
float thresholdMidpoint = model.ThresholdMidpoint;
float thresholdWidth = model.ThresholdWidth;
float newMidpoint = EditorGUILayout.FloatField(new GUIContent("Midpoint", FeatureMidpointTooltip), thresholdMidpoint);
float newWidth = EditorGUILayout.Slider(new GUIContent("Width", FeatureWidthTooltip), thresholdWidth, 0.0f,
thresholdMaxWidth);
if (Math.Abs(newMidpoint - thresholdMidpoint) > float.Epsilon ||
Math.Abs(newWidth - thresholdWidth) > float.Epsilon)
{
model.ThresholdMidpoint = newMidpoint;
model.ThresholdWidth = newWidth;
}
minVal = Mathf.Min(minVal, model.ToFirstWhenBelow);
maxVal = Mathf.Max(maxVal, model.ToSecondWhenAbove);
}
}
float range = maxVal - minVal;
if (range <= 0.0f)
{
EditorGUILayout.HelpBox("Invalid threshold values", MessageType.Warning);
}
else
{
if (overlappingValues)
{
EditorGUILayout.HelpBox("Overlapping threshold values",
MessageType.Warning);
}
RenderFeatureStateGraphic(featureStateThresholdsModel,
states,
Mathf.Min(featureDescription.MinValueHint, minVal),
Mathf.Max(featureDescription.MaxValueHint, maxVal));
}
}
}
private void RenderFeatureDescription(FeatureDescription featureDescription)
{
if (!String.IsNullOrWhiteSpace(featureDescription.ShortDescription))
{
EditorGUILayout.HelpBox(featureDescription.ShortDescription, MessageType.Info);
}
EditorGUILayout.LabelField(
new GUIContent("Expected value range", featureDescription.Description),
new GUIContent($"[{featureDescription.MinValueHint}, {featureDescription.MaxValueHint}]"));
}
private void RenderFeatureStateGraphic(FeatureStateThresholdsModel prop,
FeatureStateDescription[] stateDescriptions,
float minVal, float maxVal)
{
Rect lastRect = GUILayoutUtility.GetLastRect();
float xOffset = lastRect.xMin + _visMargin;
float widgetWidth = lastRect.width - _visMargin;
GUILayout.Space(_visHeight + _visMargin * 2);
EditorGUI.DrawRect(
new Rect(xOffset - 1, lastRect.yMax + _visMargin - 1, widgetWidth + 2.0f,
_visHeight + 2.0f), _visBorderColor);
float range = maxVal - minVal;
Color stateColor = EditorGUIUtility.isProSkin
? _visStateColorPro
: _visStateColorLight;
Color transitionColor = EditorGUIUtility.isProSkin
? _visTransitionColorPro
: _visTransitionColorLight;
GUIStyle richTextStyle = new GUIStyle();
richTextStyle.alignment = TextAnchor.MiddleCenter;
richTextStyle.normal.textColor = transitionColor;
for (var firstStateIdx = 0;
firstStateIdx < prop.ThresholdsProp.arraySize;
firstStateIdx++)
{
var model = new FeatureStateThresholdModel(
prop.ThresholdsProp.GetArrayElementAtIndex(firstStateIdx));
float firstPc = ((model.ToFirstWhenBelow - minVal)) / range;
DrawStateRect(firstPc, firstStateIdx);
xOffset += widgetWidth * firstPc;
minVal = model.ToFirstWhenBelow;
float secondPc = ((model.ToSecondWhenAbove - minVal)) / range;
Rect rect = DrawTransitionRect(secondPc);
UpdateDrag(rect, model, range / widgetWidth);
xOffset += widgetWidth * secondPc;
minVal = model.ToSecondWhenAbove;
}
float lastPc = ((maxVal - minVal)) / range;
DrawStateRect(lastPc, prop.ThresholdsProp.arraySize);
Rect DrawStateRect(float pc, int stateIndex)
{
Rect rect = new Rect(xOffset, lastRect.yMax + _visMargin,
widgetWidth * pc, _visHeight);
EditorGUI.DrawRect(rect, stateColor);
EditorGUI.LabelField(rect,
$"{stateDescriptions[stateIndex].Name}", richTextStyle);
return rect;
}
Rect DrawTransitionRect(float pc)
{
Rect rect = new Rect(xOffset, lastRect.yMax + _visMargin,
widgetWidth * pc, _visHeight);
EditorGUI.DrawRect(rect, transitionColor);
Rect leftRect = new Rect(rect);
leftRect.width = _dragMargin;
Rect rightRect = new Rect(rect);
rightRect.width = _dragMargin;
rightRect.x = rect.x + rect.width - _dragMargin;
EditorGUI.DrawRect(leftRect, _visDragColor);
EditorGUI.DrawRect(rightRect, _visDragColor);
return rect;
}
}
private void UpdateDrag(Rect rect, FeatureStateThresholdModel model, float factor)
{
Event currentEvent = Event.current;
Vector2 prevPos = currentEvent.mousePosition - currentEvent.delta;
if (currentEvent.type != EventType.MouseDrag
|| !rect.Contains(prevPos))
{
return;
}
float delta = currentEvent.delta.x * factor;
if (prevPos.x < rect.x + _dragMargin)
{
model.ThresholdMidpoint += delta * 0.5f;
model.ThresholdWidth -= delta;
}
else if (prevPos.x > rect.x + rect.width - _dragMargin)
{
model.ThresholdMidpoint += delta * 0.5f;
model.ThresholdWidth += delta;
}
else
{
model.ThresholdMidpoint += delta;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 536a9cf65c7544ab9e4051eac951baa7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,69 @@
/*
* 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.PoseDetection.Editor.Model;
using System;
using System.Linq;
using UnityEditor;
namespace Oculus.Interaction.PoseDetection.Editor
{
[CustomPropertyDrawer(typeof(ShapeRecognizer.FingerFeatureConfigList))]
public class FingerFeatureListPropertyDrawer : FeatureListPropertyDrawer
{
[Flags]
enum FingerFeatureFlags
{
Curl = 1 << 0,
Flexion = 1 << 1,
Abduction = 1 << 2,
Opposition = 1 << 3
}
protected override Enum FlagsToEnum(uint flags)
{
return (FingerFeatureFlags)flags;
}
protected override uint EnumToFlags(Enum flags)
{
return (uint)(FingerFeatureFlags)flags;
}
protected override string FeatureToString(int featureIdx)
{
return ((FingerFeature)featureIdx).ToString();
}
protected override FeatureStateDescription[] GetStatesForFeature(int featureIdx)
{
return FingerFeatureProperties.FeatureDescriptions[(FingerFeature)featureIdx].FeatureStates;
}
protected override FeatureConfigList CreateModel(SerializedProperty property)
{
var descriptions = FingerFeatureProperties.FeatureDescriptions
.ToDictionary(p => (int)p.Key, p => p.Value);
return new FeatureConfigList(property.FindPropertyRelative("_value"),
descriptions);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dffceaad44e34343a4e55875976ac097
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,39 @@
/*
* 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;
namespace Oculus.Interaction.PoseDetection.Editor
{
[CustomEditor(typeof(FingerFeatureStateThresholds))]
public class FingerFeatureStateThresholdsEditor
: FeatureStateThresholdsEditor<FingerFeature>
{
protected override IReadOnlyDictionary<FingerFeature, FeatureDescription> CreateFeatureDescriptions()
{
return FingerFeatureProperties.FeatureDescriptions;
}
protected override string FeatureMidpointTooltip => FingerFeatureProperties.FeatureStateThresholdMidpointHelpText;
protected override string FeatureWidthTooltip => FingerFeatureProperties.FeatureStateThresholdWidthHelpText;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7a4a7445af8149e0887018ba0cba6abb
timeCreated: 1627935350
@@ -0,0 +1,88 @@
/*
* 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;
namespace Oculus.Interaction.PoseDetection.Editor
{
[CustomPropertyDrawer(typeof(JointRotationActiveState.JointRotationFeatureConfigList))]
public class JointRotationConfigListEditor : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property.FindPropertyRelative("_values"));
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
EditorGUI.PropertyField(position, property.FindPropertyRelative("_values"), new GUIContent("Joints"), true);
EditorGUI.EndProperty();
}
}
[CustomPropertyDrawer(typeof(JointRotationActiveState.JointRotationFeatureConfig))]
public class JointRotationFeatureConfigEditor : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return GetLineHeight() * 3;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Rect pos = new Rect(position.x, position.y, position.width,
EditorGUIUtility.singleLineHeight);
EditorGUI.BeginProperty(position, label, property);
var joint = property.FindPropertyRelative("_feature");
var relativeTo = property.FindPropertyRelative("_relativeTo");
var handAxis = property.FindPropertyRelative("_handAxis");
var worldAxis = property.FindPropertyRelative("_worldAxis");
DrawControl(joint, "Joint", ref pos);
DrawControl(relativeTo, "Relative To", ref pos);
if ((JointRotationActiveState.RelativeTo)relativeTo.enumValueIndex ==
JointRotationActiveState.RelativeTo.Hand)
{
DrawControl(handAxis, "Hand Axis", ref pos);
}
else
{
DrawControl(worldAxis, "World Axis", ref pos);
}
EditorGUI.EndProperty();
}
private void DrawControl(SerializedProperty property, string name, ref Rect position)
{
EditorGUI.PropertyField(position, property, new GUIContent(name));
position.y += GetLineHeight();
}
private float GetLineHeight()
{
return EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 548ff893703b939438b2a96d8c8fcba0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,95 @@
/*
* 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;
namespace Oculus.Interaction.PoseDetection.Editor
{
[CustomPropertyDrawer(typeof(JointVelocityActiveState.JointVelocityFeatureConfigList))]
public class JointVelocityConfigListEditor : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property.FindPropertyRelative("_values"));
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
EditorGUI.PropertyField(position, property.FindPropertyRelative("_values"), new GUIContent("Joints"), true);
EditorGUI.EndProperty();
}
}
[CustomPropertyDrawer(typeof(JointVelocityActiveState.JointVelocityFeatureConfig))]
public class JointVelocityFeatureConfigEditor : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return GetLineHeight() * 3;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Rect pos = new Rect(position.x, position.y, position.width,
EditorGUIUtility.singleLineHeight);
EditorGUI.BeginProperty(position, label, property);
var joint = property.FindPropertyRelative("_feature");
var relativeTo = property.FindPropertyRelative("_relativeTo");
var handAxis = property.FindPropertyRelative("_handAxis");
var worldAxis = property.FindPropertyRelative("_worldAxis");
var headAxis = property.FindPropertyRelative("_headAxis");
DrawControl(joint, "Joint", ref pos);
DrawControl(relativeTo, "Relative To", ref pos);
if ((JointVelocityActiveState.RelativeTo)relativeTo.enumValueIndex ==
JointVelocityActiveState.RelativeTo.Hand)
{
DrawControl(handAxis, "Hand Axis", ref pos);
}
else if ((JointVelocityActiveState.RelativeTo)relativeTo.enumValueIndex ==
JointVelocityActiveState.RelativeTo.World)
{
DrawControl(worldAxis, "World Axis", ref pos);
}
else if ((JointVelocityActiveState.RelativeTo)relativeTo.enumValueIndex ==
JointVelocityActiveState.RelativeTo.Head)
{
DrawControl(headAxis, "Head Axis", ref pos);
}
EditorGUI.EndProperty();
}
private void DrawControl(SerializedProperty property, string name, ref Rect position)
{
EditorGUI.PropertyField(position, property, new GUIContent(name));
position.y += GetLineHeight();
}
private float GetLineHeight()
{
return EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 51ada7d46c40f684689664f1b670f7a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,74 @@
/*
* 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.PoseDetection.Editor.Model;
using System;
using System.Linq;
using UnityEditor;
namespace Oculus.Interaction.PoseDetection.Editor
{
[Flags]
public enum TransformFeatureFlags
{
WristUp = 1 << 0,
WristDown = 1 << 1,
PalmDown = 1 << 2,
PalmUp = 1 << 3,
PalmTowardsFace = 1 << 4,
PalmAwayFromFace = 1 << 5,
FingersUp = 1 << 6,
FingersDown = 1 << 7,
PinchClear = 1 << 8
}
[CustomPropertyDrawer(typeof(TransformFeatureConfigList))]
public class TransformConfigEditor : FeatureListPropertyDrawer
{
protected override Enum FlagsToEnum(uint flags)
{
return (TransformFeatureFlags)flags;
}
protected override uint EnumToFlags(Enum flags)
{
return (uint)(TransformFeatureFlags)flags;
}
protected override string FeatureToString(int featureIdx)
{
return ((TransformFeature)featureIdx).ToString();
}
protected override FeatureStateDescription[] GetStatesForFeature(int featureIdx)
{
return TransformFeatureProperties.FeatureDescriptions[(TransformFeature)featureIdx].FeatureStates;
}
protected override FeatureConfigList CreateModel(SerializedProperty property)
{
var descriptions = TransformFeatureProperties.FeatureDescriptions
.ToDictionary(p => (int)p.Key, p => p.Value);
return new FeatureConfigList(property.FindPropertyRelative("_values"),
descriptions);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ca473a2dca4a42988bd13c46f6ed077f
timeCreated: 1631569404
@@ -0,0 +1,40 @@
/*
* 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;
namespace Oculus.Interaction.PoseDetection.Editor
{
[CustomEditor(typeof(TransformFeatureStateThresholds))]
class TransformFeatureStateThresholdModel
: FeatureStateThresholdsEditor<TransformFeature>
{
protected override IReadOnlyDictionary<TransformFeature, FeatureDescription> CreateFeatureDescriptions()
{
return TransformFeatureProperties.FeatureDescriptions;
}
protected override string FeatureMidpointTooltip => TransformFeatureProperties.FeatureStateThresholdMidpointHelpText;
protected override string FeatureWidthTooltip => TransformFeatureProperties.FeatureStateThresholdWidthHelpText;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 195af56ffbd76364e83ff93de249a25a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: