clean project
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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,425 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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
|
||||
|
||||
SerializedProperty _rootProperty;
|
||||
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 _visBorderColor = new Color32(0,0,0,255);
|
||||
private const float _visHeight = 20.0f;
|
||||
private const float _visMargin = 10.0f;
|
||||
|
||||
private IReadOnlyDictionary<TFeature, FeatureDescription> _featureDescriptions;
|
||||
|
||||
protected abstract IReadOnlyDictionary<TFeature, FeatureDescription> CreateFeatureDescriptions();
|
||||
|
||||
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("Midpoint", thresholdMidpoint);
|
||||
float newWidth = EditorGUILayout.Slider("Width", thresholdWidth, 0.0f,
|
||||
thresholdMaxWidth);
|
||||
|
||||
if (Math.Abs(newMidpoint - thresholdMidpoint) > float.Epsilon ||
|
||||
Math.Abs(newWidth - thresholdWidth) > float.Epsilon)
|
||||
{
|
||||
// save new values.
|
||||
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,
|
||||
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, float minVal,
|
||||
float maxVal)
|
||||
{
|
||||
var 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;
|
||||
for (var firstStateIdx = 0;
|
||||
firstStateIdx < prop.ThresholdsProp.arraySize;
|
||||
firstStateIdx++)
|
||||
{
|
||||
var model = new FeatureStateThresholdModel(
|
||||
prop.ThresholdsProp.GetArrayElementAtIndex(firstStateIdx));
|
||||
|
||||
float firstPc = ((model.ToFirstWhenBelow - minVal)) / range;
|
||||
EditorGUI.DrawRect(
|
||||
new Rect(Mathf.Floor(xOffset), lastRect.yMax + _visMargin,
|
||||
Mathf.Ceil(widgetWidth * firstPc), _visHeight), stateColor);
|
||||
xOffset += widgetWidth * firstPc;
|
||||
minVal = model.ToFirstWhenBelow;
|
||||
|
||||
float secondPc = ((model.ToSecondWhenAbove - minVal)) / range;
|
||||
EditorGUI.DrawRect(
|
||||
new Rect(Mathf.Floor(xOffset), lastRect.yMax + _visMargin,
|
||||
Mathf.Ceil(widgetWidth * secondPc), _visHeight), transitionColor);
|
||||
xOffset += widgetWidth * secondPc;
|
||||
minVal = model.ToSecondWhenAbove;
|
||||
}
|
||||
|
||||
float lastPc = ((maxVal - minVal)) / range;
|
||||
EditorGUI.DrawRect(
|
||||
new Rect(Mathf.Floor(xOffset), lastRect.yMax + _visMargin,
|
||||
Mathf.Ceil(widgetWidth * lastPc), _visHeight), stateColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 536a9cf65c7544ab9e4051eac951baa7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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,27 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a4a7445af8149e0887018ba0cba6abb
|
||||
timeCreated: 1627935350
|
||||
@@ -0,0 +1,66 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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,27 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 195af56ffbd76364e83ff93de249a25a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user