Skip to content

Commit cb2d5da

Browse files
Andrew Kimfacebook-github-bot
authored andcommitted
feat(Editor): Update helper menus to add retargeting processors on the one-click setup
Summary: Update HelperMenus to add the required retargeting processors to a character. Update RetargetingProcessorScriptableObjectDrawer so that if the scriptable object is an instance in the scene, it can be saved on disk to a file. Reviewed By: sohailshafiiWk Differential Revision: D50812698 fbshipit-source-id: bc2b8359c71cf777f94ab7a03951af58b90e92ea
1 parent 29e529b commit cb2d5da

File tree

7 files changed

+221
-32
lines changed

7 files changed

+221
-32
lines changed

Editor/Meta.Movement.Editor.asmdef

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"rootNamespace": "",
44
"references": [
55
"GUID:502e28d01b3ebaf4689df96d8655467c",
6+
"GUID:2a230cb87a1d3ba4a98bdc0ddae76e6c",
67
"GUID:f64c9ebcd7899c3448a08dc9f9ddbe30",
78
"GUID:d2761a0af0f567748a72629d4bb18a26",
89
"GUID:7305c54a43f3814439df347c7519653e",

Editor/ThirdParty/RetargetingProcessorScriptableObjectDrawer.cs

Lines changed: 102 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ namespace Oculus.Movement.AnimationRigging
2121
[CustomPropertyDrawer(typeof(RetargetingProcessor), true)]
2222
public class RetargetingProcessorScriptableObjectDrawer : PropertyDrawer
2323
{
24+
private const int buttonWidth = 66;
25+
private static readonly List<string> ignoreClassFullNames = new List<string> { "TMPro.TMP_FontAsset" };
26+
27+
/// <inheritdoc />
2428
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
2529
{
2630
float totalHeight = EditorGUIUtility.singleLineHeight;
@@ -39,7 +43,10 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent
3943
{
4044
do
4145
{
42-
if (prop.name == "m_Script") continue;
46+
if (prop.name == "m_Script")
47+
{
48+
continue;
49+
}
4350
var subProp = serializedObject.FindProperty(prop.name);
4451
float height = EditorGUI.GetPropertyHeight(subProp, null, true) +
4552
EditorGUIUtility.standardVerticalSpacing;
@@ -55,10 +62,7 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent
5562
return totalHeight;
5663
}
5764

58-
const int buttonWidth = 66;
59-
60-
static readonly List<string> ignoreClassFullNames = new List<string> { "TMPro.TMP_FontAsset" };
61-
65+
/// <inheritdoc />
6266
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
6367
{
6468
EditorGUI.BeginProperty(position, label, property);
@@ -98,24 +102,63 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
98102

99103
var indentedPosition = EditorGUI.IndentedRect(position);
100104
var indentOffset = indentedPosition.x - position.x;
105+
var saveToAssets = false;
106+
var propertyWidth = buttonWidth;
101107
propertyRect = new Rect(position.x + (EditorGUIUtility.labelWidth - indentOffset), position.y,
102108
position.width - (EditorGUIUtility.labelWidth - indentOffset), EditorGUIUtility.singleLineHeight);
103109

104-
if (propertySO != null || property.objectReferenceValue == null)
110+
if (property.propertyType == SerializedPropertyType.ObjectReference &&
111+
property.objectReferenceValue != null)
112+
{
113+
var data = (ScriptableObject)property.objectReferenceValue;
114+
if (!AssetDatabase.Contains(data))
115+
{
116+
saveToAssets = true;
117+
propertyWidth = Mathf.RoundToInt(buttonWidth * 1.5f);
118+
}
119+
}
120+
121+
if (propertySO != null || property.objectReferenceValue == null || saveToAssets)
105122
{
106-
propertyRect.width -= buttonWidth;
123+
propertyRect.width -= propertyWidth;
107124
}
108125

109126
EditorGUI.ObjectField(propertyRect, property, type, GUIContent.none);
110-
if (GUI.changed) property.serializedObject.ApplyModifiedProperties();
127+
if (GUI.changed)
128+
{
129+
property.serializedObject.ApplyModifiedProperties();
130+
}
111131

112-
var buttonRect = new Rect(position.x + position.width - buttonWidth, position.y, buttonWidth,
132+
var buttonRect = new Rect(position.x + position.width - propertyWidth, position.y, propertyWidth,
113133
EditorGUIUtility.singleLineHeight);
114134

115135
if (property.propertyType == SerializedPropertyType.ObjectReference && property.objectReferenceValue != null)
116136
{
117137
var data = (ScriptableObject)property.objectReferenceValue;
118138

139+
if (saveToAssets)
140+
{
141+
if (GUI.Button(buttonRect, "Save to Assets"))
142+
{
143+
string selectedAssetPath = "Assets";
144+
if (property.serializedObject.targetObject is MonoBehaviour)
145+
{
146+
MonoScript ms = MonoScript.FromMonoBehaviour((MonoBehaviour)property.serializedObject.targetObject);
147+
selectedAssetPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(ms));
148+
}
149+
150+
var replacementObject = ReplaceAssetWithSavePrompt(data.GetType(), selectedAssetPath, data as RetargetingProcessor);
151+
if (replacementObject != null)
152+
{
153+
Undo.DestroyObjectImmediate(data);
154+
property.objectReferenceValue = replacementObject;
155+
}
156+
property.serializedObject.ApplyModifiedProperties();
157+
GUIUtility.ExitGUI();
158+
return;
159+
}
160+
}
161+
119162
if (property.isExpanded)
120163
{
121164
// Draw a background that shows us clearly which fields are part of the ScriptableObject
@@ -137,7 +180,10 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
137180
do
138181
{
139182
// Don't bother drawing the class file
140-
if (prop.name == "m_Script") continue;
183+
if (prop.name == "m_Script")
184+
{
185+
continue;
186+
}
141187
float height = EditorGUI.GetPropertyHeight(prop, new GUIContent(prop.displayName), true);
142188
EditorGUI.PropertyField(new Rect(position.x, y, position.width - buttonWidth, height), prop,
143189
true);
@@ -146,7 +192,9 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
146192
}
147193

148194
if (GUI.changed)
195+
{
149196
serializedObject.ApplyModifiedProperties();
197+
}
150198
serializedObject.Dispose();
151199
EditorGUI.indentLevel--;
152200
}
@@ -163,19 +211,22 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
163211
}
164212

165213
property.objectReferenceValue = CreateAssetWithSavePrompt(type, selectedAssetPath);
214+
property.serializedObject.ApplyModifiedProperties();
215+
GUIUtility.ExitGUI();
216+
return;
166217
}
167218
}
168219

169220
property.serializedObject.ApplyModifiedProperties();
170221
EditorGUI.EndProperty();
171222
}
172223

173-
public static T _GUILayout<T>(string label, T objectReferenceValue, ref bool isExpanded) where T : ScriptableObject
224+
private static T _GUILayout<T>(string label, T objectReferenceValue, ref bool isExpanded) where T : ScriptableObject
174225
{
175226
return _GUILayout<T>(new GUIContent(label), objectReferenceValue, ref isExpanded);
176227
}
177228

178-
public static T _GUILayout<T>(GUIContent label, T objectReferenceValue, ref bool isExpanded)
229+
private static T _GUILayout<T>(GUIContent label, T objectReferenceValue, ref bool isExpanded)
179230
where T : ScriptableObject
180231
{
181232
Rect position = EditorGUILayout.BeginVertical();
@@ -239,7 +290,7 @@ public static T _GUILayout<T>(GUIContent label, T objectReferenceValue, ref bool
239290
return objectReferenceValue;
240291
}
241292

242-
static void DrawScriptableObjectChildFields<T>(T objectReferenceValue) where T : ScriptableObject
293+
private static void DrawScriptableObjectChildFields<T>(T objectReferenceValue) where T : ScriptableObject
243294
{
244295
// Draw a background that shows us clearly which fields are part of the ScriptableObject
245296
EditorGUI.indentLevel++;
@@ -259,13 +310,15 @@ static void DrawScriptableObjectChildFields<T>(T objectReferenceValue) where T :
259310
}
260311

261312
if (GUI.changed)
313+
{
262314
serializedObject.ApplyModifiedProperties();
315+
}
263316
serializedObject.Dispose();
264317
EditorGUILayout.EndVertical();
265318
EditorGUI.indentLevel--;
266319
}
267320

268-
public static T DrawScriptableObjectField<T>(GUIContent label, T objectReferenceValue, ref bool isExpanded)
321+
private static T DrawScriptableObjectField<T>(GUIContent label, T objectReferenceValue, ref bool isExpanded)
269322
where T : ScriptableObject
270323
{
271324
Rect position = EditorGUILayout.BeginVertical();
@@ -329,11 +382,14 @@ public static T DrawScriptableObjectField<T>(GUIContent label, T objectReference
329382
}
330383

331384
// Creates a new ScriptableObject via the default Save File panel
332-
static ScriptableObject CreateAssetWithSavePrompt(Type type, string path)
385+
private static ScriptableObject CreateAssetWithSavePrompt(Type type, string path)
333386
{
334387
path = EditorUtility.SaveFilePanelInProject("Save ScriptableObject", type.Name + ".asset", "asset",
335388
"Enter a file name for the ScriptableObject.", path);
336-
if (path == "") return null;
389+
if (path == "")
390+
{
391+
return null;
392+
}
337393
ScriptableObject asset = ScriptableObject.CreateInstance(type);
338394
AssetDatabase.CreateAsset(asset, path);
339395
AssetDatabase.SaveAssets();
@@ -343,16 +399,43 @@ static ScriptableObject CreateAssetWithSavePrompt(Type type, string path)
343399
return asset;
344400
}
345401

346-
Type GetFieldType()
402+
private static ScriptableObject ReplaceAssetWithSavePrompt(Type type, string path, RetargetingProcessor processor)
403+
{
404+
path = EditorUtility.SaveFilePanelInProject("Save ScriptableObject", processor.name + ".asset", "asset",
405+
"Enter a file name for the ScriptableObject.", path);
406+
if (path == "")
407+
{
408+
return null;
409+
}
410+
ScriptableObject asset = ScriptableObject.CreateInstance(type);
411+
var newProcessor = asset as RetargetingProcessor;
412+
if (newProcessor != null)
413+
{
414+
newProcessor.CopyData(processor);
415+
}
416+
AssetDatabase.CreateAsset(asset, path);
417+
AssetDatabase.SaveAssets();
418+
AssetDatabase.Refresh();
419+
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
420+
EditorGUIUtility.PingObject(asset);
421+
return asset;
422+
}
423+
424+
private Type GetFieldType()
347425
{
348426
Type type = fieldInfo.FieldType;
349-
if (type.IsArray) type = type.GetElementType();
427+
if (type.IsArray)
428+
{
429+
type = type.GetElementType();
430+
}
350431
else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
432+
{
351433
type = type.GetGenericArguments()[0];
434+
}
352435
return type;
353436
}
354437

355-
static bool AreAnySubPropertiesVisible(SerializedProperty property)
438+
private static bool AreAnySubPropertiesVisible(SerializedProperty property)
356439
{
357440
var data = (ScriptableObject)property.objectReferenceValue;
358441
SerializedObject serializedObject = new SerializedObject(data);

Editor/Utils/HelperMenus.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) Meta Platforms, Inc. and affiliates.
22

3+
using Oculus.Interaction.Input;
34
using Oculus.Movement.AnimationRigging;
45
using Oculus.Movement.Tracking;
56
using System;
@@ -98,6 +99,11 @@ private static void SetupCharacterForAnimationRiggingRetargetingConstraints()
9899
AddAnimationRiggingLayer(activeGameObject, retargetingLayer, rigBuilder,
99100
constraintMonos.ToArray(), retargetingLayer);
100101

102+
// Add retargeting processors to the retargeting layer.
103+
AddCorrectBonesRetargetingProcessor(retargetingLayer);
104+
AddCorrectHandRetargetingProcessor(retargetingLayer, Handedness.Left);
105+
AddCorrectHandRetargetingProcessor(retargetingLayer, Handedness.Right);
106+
101107
Undo.SetCurrentGroupName("Setup Animation Rigging Retargeting");
102108
}
103109

@@ -300,6 +306,55 @@ private static void AddAnimationRiggingLayer(GameObject mainParent,
300306
Undo.RegisterCreatedObjectUndo(rigSetup, "Create Anim Rig Setup");
301307
}
302308

309+
private static void AddCorrectBonesRetargetingProcessor(RetargetingLayer retargetingLayer)
310+
{
311+
bool needCorrectBones = true;
312+
foreach (var processor in retargetingLayer.RetargetingProcessors)
313+
{
314+
if (processor as RetargetingProcessorCorrectBones != null)
315+
{
316+
needCorrectBones = false;
317+
}
318+
}
319+
320+
if (needCorrectBones)
321+
{
322+
var retargetingProcessorCorrectBones = ScriptableObject.CreateInstance<RetargetingProcessorCorrectBones>();
323+
Undo.RegisterCreatedObjectUndo(retargetingProcessorCorrectBones, "Create correct bones retargeting processor.");
324+
Undo.RecordObject (retargetingLayer, "Add retargeting processor to retargeting layer.");
325+
retargetingProcessorCorrectBones.name = "CorrectBones";
326+
retargetingLayer.AddRetargetingProcessor(retargetingProcessorCorrectBones);
327+
}
328+
}
329+
330+
private static void AddCorrectHandRetargetingProcessor(RetargetingLayer retargetingLayer, Handedness handedness)
331+
{
332+
bool needCorrectHand = true;
333+
foreach (var processor in retargetingLayer.RetargetingProcessors)
334+
{
335+
var correctHand = processor as RetargetingProcessorCorrectHand;
336+
if (correctHand != null)
337+
{
338+
if (correctHand.Handedness == handedness)
339+
{
340+
needCorrectHand = false;
341+
}
342+
}
343+
}
344+
345+
if (needCorrectHand)
346+
{
347+
var retargetingProcessorCorrectHand = ScriptableObject.CreateInstance<RetargetingProcessorCorrectHand>();
348+
var handednessString = handedness == Handedness.Left ? "Left" : "Right";
349+
Undo.RegisterCreatedObjectUndo(retargetingProcessorCorrectHand, $"Create correct hand ({handednessString}) retargeting processor.");
350+
Undo.RecordObject (retargetingLayer, "Add retargeting processor to retargeting layer.");
351+
retargetingProcessorCorrectHand.Handedness = handedness;
352+
retargetingProcessorCorrectHand.HandIKType = RetargetingProcessorCorrectHand.IKType.CCDIK;
353+
retargetingProcessorCorrectHand.name = $"Correct{handednessString}Hand";
354+
retargetingLayer.AddRetargetingProcessor(retargetingProcessorCorrectHand);
355+
}
356+
}
357+
303358
private static RetargetedBoneTarget[] AddSpineBoneTargets(GameObject rigObject,
304359
Animator animator)
305360
{

Runtime/Scripts/AnimationRigging/RetargetingLayer.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public RetargetingAnimationConstraint RetargetingConstraint
167167
/// </summary>
168168
[SerializeField]
169169
[Tooltip(RetargetingLayerTooltips.RetargetingProcessors)]
170-
protected List<RetargetingProcessor> _retargetingProcessors;
170+
protected List<RetargetingProcessor> _retargetingProcessors = new();
171171
/// <inheritdoc cref="_retargetingProcessors"/>
172172
public List<RetargetingProcessor> RetargetingProcessors
173173
{
@@ -661,6 +661,15 @@ public void RemoveProcessor(IOVRSkeletonProcessor processor)
661661
_skeletonPostProcessing -= processor.ProcessSkeleton;
662662
}
663663

664+
/// <summary>
665+
/// Add the specified retargeting processor.
666+
/// </summary>
667+
/// <param name="processor">The processor to be added.</param>
668+
public void AddRetargetingProcessor(RetargetingProcessor processor)
669+
{
670+
_retargetingProcessors.Add(processor);
671+
}
672+
664673
/// <summary>
665674
/// Gets number of transforms being retargeted currently. This can change during
666675
/// initialization.

Runtime/Scripts/RetargetingProcessing/RetargetingProcessor.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ public class RetargetingProcessor : ScriptableObject, IRetargetingProcessor
1515
[Range(0.0f, 1.0f)]
1616
public float Weight = 1.0f;
1717

18+
/// <summary>
19+
/// Deep copy data from another processor to this processor.
20+
/// </summary>
21+
/// <param name="source">The source processor to copy from.</param>
22+
public virtual void CopyData(RetargetingProcessor source)
23+
{
24+
Weight = source.Weight;
25+
}
26+
1827
/// <inheritdoc />
1928
public virtual void SetupRetargetingProcessor(RetargetingLayer retargetingLayer)
2029
{

Runtime/Scripts/RetargetingProcessing/RetargetingProcessorCorrectBones.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ public float RightHandCorrectionWeightLateUpdate
6767
set => _rightHandCorrectionWeightLateUpdate = value;
6868
}
6969

70+
/// <inheritdoc />
71+
public override void CopyData(RetargetingProcessor source)
72+
{
73+
base.CopyData(source);
74+
var sourceCorrectBones = source as RetargetingProcessorCorrectBones;
75+
if (sourceCorrectBones == null)
76+
{
77+
Debug.LogError($"Failed to copy properties from {source.name} processor to {name} processor");
78+
return;
79+
}
80+
_correctPositionsLateUpdate = sourceCorrectBones.CorrectPositionsLateUpdate;
81+
_shoulderCorrectionWeightLateUpdate = sourceCorrectBones.ShoulderCorrectionWeightLateUpdate;
82+
_leftHandCorrectionWeightLateUpdate = sourceCorrectBones.LeftHandCorrectionWeightLateUpdate;
83+
_rightHandCorrectionWeightLateUpdate = sourceCorrectBones.RightHandCorrectionWeightLateUpdate;
84+
}
85+
7086
/// <inheritdoc />
7187
public override void ProcessRetargetingLayer(RetargetingLayer retargetingLayer, IList<OVRBone> ovrBones)
7288
{
@@ -76,8 +92,8 @@ public override void ProcessRetargetingLayer(RetargetingLayer retargetingLayer,
7692
}
7793

7894
bool handCorrectionTurnedOn =
79-
_leftHandCorrectionWeightLateUpdate > Mathf.Epsilon ||
80-
_rightHandCorrectionWeightLateUpdate > Mathf.Epsilon;
95+
LeftHandCorrectionWeightLateUpdate > Mathf.Epsilon ||
96+
RightHandCorrectionWeightLateUpdate > Mathf.Epsilon;
8197

8298
if (ovrBones == null)
8399
{

0 commit comments

Comments
 (0)