Initial Commit
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Oculus.Interaction.Body.PoseDetection;
|
||||
using Oculus.Interaction.Body.Input;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Oculus.Interaction.Body.Editor
|
||||
{
|
||||
using JointComparerConfig = BodyPoseComparerActiveState.JointComparerConfig;
|
||||
|
||||
[CustomPropertyDrawer(typeof(JointComparerConfig))]
|
||||
public class BodyPoseComparerConfigPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
private static GUIContent[] _jointNames;
|
||||
private static BodyJointId[] _joints;
|
||||
|
||||
private int _controlCount = 0;
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
return GetLineHeight() * _controlCount;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (_jointNames == null || _joints == null)
|
||||
{
|
||||
UpdateJointLists();
|
||||
}
|
||||
|
||||
_controlCount = 0;
|
||||
|
||||
Rect pos = new Rect(position.x, position.y, position.width,
|
||||
EditorGUIUtility.singleLineHeight);
|
||||
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
var joint = property.FindPropertyRelative(nameof(JointComparerConfig.Joint));
|
||||
var maxAngle = property.FindPropertyRelative(nameof(JointComparerConfig.MaxDelta));
|
||||
var width = property.FindPropertyRelative(nameof(JointComparerConfig.Width));
|
||||
|
||||
DrawJointPopup(joint, "Body Joint", ref pos);
|
||||
DrawControl(maxAngle, "Max Angle Delta", ref pos);
|
||||
DrawControl(width, "Threshold Width", ref pos);
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
private void DrawJointPopup(SerializedProperty jointProp, string name, ref Rect position)
|
||||
{
|
||||
int currentJoint = GetDisplayIndexFromJoint((BodyJointId)jointProp.intValue);
|
||||
int selectedJoint = EditorGUI.Popup(position,
|
||||
new GUIContent(name, jointProp.tooltip),
|
||||
currentJoint, _jointNames);
|
||||
|
||||
position.y += GetLineHeight();
|
||||
++_controlCount;
|
||||
jointProp.intValue = (int)_joints[selectedJoint];
|
||||
}
|
||||
|
||||
private void DrawControl(SerializedProperty property, string name, ref Rect position)
|
||||
{
|
||||
EditorGUI.PropertyField(position, property, new GUIContent(name, property.tooltip));
|
||||
position.y += GetLineHeight();
|
||||
++_controlCount;
|
||||
}
|
||||
|
||||
private float GetLineHeight()
|
||||
{
|
||||
return EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
|
||||
private static int GetDisplayIndexFromJoint(BodyJointId jointId)
|
||||
{
|
||||
for (int i = 0; i < _joints.Length; ++i)
|
||||
{
|
||||
if (jointId == _joints[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void UpdateJointLists()
|
||||
{
|
||||
List<string> jointNames = new List<string>();
|
||||
List<BodyJointId> joints = new List<BodyJointId>();
|
||||
|
||||
for (int i = (int)BodyJointId.Body_Hips; i < (int)BodyJointId.Body_End; ++i)
|
||||
{
|
||||
BodyJointId jointId = (BodyJointId)i;
|
||||
InspectorNameAttribute inspectorNameAttribute =
|
||||
typeof(BodyJointId)
|
||||
.GetMember(jointId.ToString())
|
||||
.First()
|
||||
.GetCustomAttribute<InspectorNameAttribute>();
|
||||
|
||||
joints.Add(jointId);
|
||||
jointNames.Add(inspectorNameAttribute != null ?
|
||||
inspectorNameAttribute.displayName :
|
||||
jointId.ToString());
|
||||
}
|
||||
|
||||
_joints = joints.ToArray();
|
||||
_jointNames = jointNames
|
||||
.Select(s => new GUIContent(s))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57db1cbbceaaab34dafc9c1f0a32da20
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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 UnityEngine;
|
||||
using UnityEditor;
|
||||
using Oculus.Interaction.Body.PoseDetection;
|
||||
using Oculus.Interaction.Body.Input;
|
||||
using System.IO;
|
||||
|
||||
namespace Oculus.Interaction.Body.Editor
|
||||
{
|
||||
public class BodyPoseRecorder : EditorWindow
|
||||
{
|
||||
private const string POSE_DIRECTORY = "BodyPoses";
|
||||
|
||||
[Tooltip("The " + nameof(IBody) + " that provides joint " +
|
||||
"data for pose creation.")]
|
||||
[SerializeField, Interface(typeof(IBody))]
|
||||
private UnityEngine.Object _body;
|
||||
|
||||
[Tooltip("The captured body pose will be written into this " +
|
||||
nameof(BodyPoseData) + " asset. If this field is left " +
|
||||
"unassigned, each capture will create a new asset in the " +
|
||||
POSE_DIRECTORY + " directory.")]
|
||||
[SerializeField, Optional]
|
||||
private BodyPoseData _targetAsset;
|
||||
|
||||
private SerializedObject _serializedEditor;
|
||||
|
||||
private float _captureDelay = 5f;
|
||||
private float _nextCaptureTime;
|
||||
private bool _willCapture = false;
|
||||
private bool _beepOnCapture = true;
|
||||
private GUIStyle _richTextStyle;
|
||||
|
||||
private IBody Body => _body as IBody;
|
||||
|
||||
private float TimeUntilCapture =>
|
||||
Mathf.Max(_nextCaptureTime - Time.unscaledTime, 0);
|
||||
|
||||
[MenuItem("Oculus/Interaction/Body Pose Recorder")]
|
||||
private static void ShowWindow()
|
||||
{
|
||||
BodyPoseRecorder window = GetWindow<BodyPoseRecorder>();
|
||||
window.titleContent = new GUIContent("Body Pose Recorder");
|
||||
window._serializedEditor = null;
|
||||
window.Show();
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
window.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
[InitializeOnEnterPlayMode]
|
||||
private static void OnPlayModeEnter()
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
if (HasOpenInstances<BodyPoseRecorder>())
|
||||
{
|
||||
BodyPoseRecorder window = GetWindow<BodyPoseRecorder>();
|
||||
window.Initialize();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
_targetAsset = null;
|
||||
_body = FindObjectOfType<Input.Body>();
|
||||
_serializedEditor = new SerializedObject(this);
|
||||
_richTextStyle = EditorGUIUtility.GetBuiltinSkin(
|
||||
EditorGUIUtility.isProSkin ?
|
||||
EditorSkin.Scene :
|
||||
EditorSkin.Inspector).label;
|
||||
_richTextStyle.richText = true;
|
||||
_richTextStyle.wordWrap = true;
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
EditorGUILayout.LabelField("Body Pose Recorder only works in Play mode");
|
||||
return;
|
||||
}
|
||||
if (_serializedEditor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HandleCapture();
|
||||
DrawUI();
|
||||
|
||||
if (_willCapture)
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawUI()
|
||||
{
|
||||
GUI.enabled = !_willCapture;
|
||||
|
||||
SerializedProperty bodyProp =
|
||||
_serializedEditor.FindProperty(nameof(_body));
|
||||
SerializedProperty targetAssetProp =
|
||||
_serializedEditor.FindProperty(nameof(_targetAsset));
|
||||
|
||||
GUILayout.Label("<size=16>Source</size>\n" + bodyProp.tooltip, _richTextStyle);
|
||||
EditorGUILayout.PropertyField(
|
||||
bodyProp, new GUIContent("IBody", bodyProp.tooltip), true);
|
||||
|
||||
GUILayout.Space(20);
|
||||
|
||||
GUILayout.Label("<size=16>Target</size>\n" + targetAssetProp.tooltip, _richTextStyle);
|
||||
EditorGUILayout.PropertyField(
|
||||
targetAssetProp, new GUIContent("Target Asset", targetAssetProp.tooltip), true);
|
||||
|
||||
GUILayout.Space(20);
|
||||
|
||||
GUILayout.Label("<size=16>Capture Settings</size>", _richTextStyle);
|
||||
_captureDelay = EditorGUILayout.FloatField("Capture Delay (Seconds)", _captureDelay);
|
||||
_captureDelay = Mathf.Max(_captureDelay, 0);
|
||||
_beepOnCapture = EditorGUILayout.Toggle("Play Sound on Capture", _beepOnCapture);
|
||||
_serializedEditor.ApplyModifiedProperties();
|
||||
|
||||
GUILayout.Space(20);
|
||||
GUI.enabled = Body != null;
|
||||
|
||||
string buttonLabel = _willCapture ?
|
||||
$"Capturing in {TimeUntilCapture.ToString("#.#")} " +
|
||||
$"seconds\n(Click To Cancel)" : "Capture Body Pose";
|
||||
|
||||
if (GUILayout.Button(buttonLabel, GUILayout.Height(36)))
|
||||
{
|
||||
_willCapture = !_willCapture;
|
||||
_nextCaptureTime = Time.unscaledTime + _captureDelay;
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
}
|
||||
|
||||
private void HandleCapture()
|
||||
{
|
||||
if (!_willCapture)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (TimeUntilCapture <= 0f)
|
||||
{
|
||||
_willCapture = false;
|
||||
if (_beepOnCapture)
|
||||
{
|
||||
EditorApplication.Beep();
|
||||
}
|
||||
|
||||
BodyPoseData assetToWrite = _targetAsset;
|
||||
if (assetToWrite == null)
|
||||
{
|
||||
assetToWrite = GeneratePoseAsset();
|
||||
}
|
||||
|
||||
assetToWrite.SetBodyPose(Body);
|
||||
Debug.Log($"Captured Body Pose into " +
|
||||
$"{AssetDatabase.GetAssetPath(assetToWrite)}");
|
||||
EditorUtility.SetDirty(assetToWrite);
|
||||
AssetDatabase.SaveAssetIfDirty(assetToWrite);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public BodyPoseData GeneratePoseAsset()
|
||||
{
|
||||
var poseDataAsset = ScriptableObject.CreateInstance<BodyPoseData>();
|
||||
string parentDir = Path.Combine("Assets", POSE_DIRECTORY);
|
||||
if (!Directory.Exists(parentDir))
|
||||
{
|
||||
Directory.CreateDirectory(parentDir);
|
||||
}
|
||||
string name = "BodyPose-" + $"{System.DateTime.Now.ToString("yyyyMMdd-HHmmss")}";
|
||||
AssetDatabase.CreateAsset(poseDataAsset, Path.Combine(parentDir, $"{name}.asset"));
|
||||
return poseDataAsset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 580dbef906f46f1449a123971c210b75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user