Skip to content

Commit c94580f

Browse files
sohailshafiiWkfacebook-github-bot
authored andcommitted
feat(Runtime): Add tracking bone transforms by proxy
Summary: Adds tracking transforms by proxy. Any job that tracks the source bone transforms of the retargeting layer runs the risk of having to be recreated when the bones are re-initialized. The proxy transforms never get re-created unless bone ID that the proxy is associated changes. Reviewed By: andkim-meta Differential Revision: D46125799 fbshipit-source-id: d25588694fb9505c571ebf0c766ae353922e2e04
1 parent e949046 commit c94580f

File tree

5 files changed

+287
-7
lines changed

5 files changed

+287
-7
lines changed

Runtime/Scripts/AnimationRigging/AnimationRigSetup.cs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,28 @@ public RetargetingLayer RetargetingLayerComp
119119
set { _retargetingLayer = value; }
120120
}
121121

122+
/// <summary>
123+
/// Use proxy transforms to check skeletal changes.
124+
/// Proxy transforms can be used in case the original
125+
/// skeleton updates too much.
126+
/// </summary>
127+
[SerializeField]
128+
[Tooltip(AnimationRigSetupTooltips.CheckSkeletalUpdatesByProxy)]
129+
protected bool _checkSkeletalUpdatesByProxy = false;
130+
131+
/// <inheritdoc cref="_checkSkeletalUpdatesByProxy"/>
132+
public bool CheckSkeletalUpdatesByProxy
133+
{
134+
get { return _checkSkeletalUpdatesByProxy; }
135+
set { _checkSkeletalUpdatesByProxy = value; }
136+
}
137+
122138
private bool _ranSetup;
123139
private int _lastSkeletonChangeCount = -1;
124140
private bool _pendingSkeletalUpdateFromLastFrame = false;
125141
private IOVRSkeletonConstraint[] _iovrSkeletonConstraints;
126142

143+
private int _proxyChangeCount = 0;
127144
private int _lastRetargetedTransformCount = -1;
128145

129146
/// <summary>
@@ -202,7 +219,7 @@ protected virtual void OnApplicationFocus(bool hasFocus)
202219
{
203220
if (!hasFocus)
204221
{
205-
DisableRig();
222+
DisableRigAndUpdateState();
206223
_rigBuilder.Evaluate(Time.deltaTime);
207224
}
208225
else
@@ -239,7 +256,7 @@ private void RunInitialSetup()
239256
}
240257
}
241258

242-
private void DisableRig()
259+
private void DisableRigAndUpdateState()
243260
{
244261
if (_rigBuilder)
245262
{
@@ -258,6 +275,11 @@ private void DisableRig()
258275
{
259276
_lastRetargetedTransformCount = _retargetingLayer.GetNumberOfTransformsRetargeted();
260277
}
278+
279+
if (_checkSkeletalUpdatesByProxy && _retargetingLayer != null)
280+
{
281+
_proxyChangeCount = _retargetingLayer.ProxyChangeCount;
282+
}
261283
}
262284

263285
private void EnableRig()
@@ -293,19 +315,38 @@ private void CheckForSkeletalChanges()
293315
{
294316
return;
295317
}
296-
if (_lastSkeletonChangeCount != _skeleton.SkeletonChangedCount ||
297-
HasRetargeterBeenUpdated())
318+
if (_lastSkeletonChangeCount != _skeleton.SkeletonChangedCount)
298319
{
320+
bool skeletalOrRetargeterChangeDetected =
321+
_checkSkeletalUpdatesByProxy && HasSkeletonProxiesBeenRecreated() ||
322+
HasRetargeterBeenUpdated();
323+
324+
// ONLY regenerate rig if change has been detected
325+
if (!skeletalOrRetargeterChangeDetected)
326+
{
327+
_lastSkeletonChangeCount = _skeleton.SkeletonChangedCount;
328+
return;
329+
}
330+
299331
// Allow rig to initialize next frame so that constraints can
300332
// catch up to skeletal changes in this frame.
301333
_pendingSkeletalUpdateFromLastFrame = true;
302334
_lastSkeletonChangeCount = _skeleton.SkeletonChangedCount;
303335

304-
DisableRig();
336+
DisableRigAndUpdateState();
305337
Debug.LogWarning("Detected skeletal change. Disabling the rig.");
306338
}
307339
}
308340

341+
private bool HasSkeletonProxiesBeenRecreated()
342+
{
343+
if (_retargetingLayer == null)
344+
{
345+
return false;
346+
}
347+
return _proxyChangeCount != _retargetingLayer.ProxyChangeCount;
348+
}
349+
309350
private bool HasRetargeterBeenUpdated()
310351
{
311352
if (_retargetingLayer == null)
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
using System.Collections.Generic;
4+
using UnityEngine;
5+
6+
namespace Oculus.Movement.AnimationRigging.Utils
7+
{
8+
/// <summary>
9+
/// Keeps track of bone transforms, which might get re-allocated, by
10+
/// using proxies. Since proxies don't typically re-allocate, any jobs that
11+
/// depend on them do not require re-allocation. Bone ID changes require
12+
/// reallocation, however.
13+
/// </summary>
14+
public class ProxyTransformLogic
15+
{
16+
/// <summary>
17+
/// Class that tracks proxy and source transforms.
18+
/// </summary>
19+
public class ProxyTransform
20+
{
21+
/// <summary>
22+
/// Proxy transform constructor.
23+
/// </summary>
24+
/// <param name="drivenTransform">Transform to be driven by source;
25+
/// the actual proxy itself.</param>
26+
/// <param name="sourceTransform">Source transform.</param>
27+
/// <param name="boneId">The bone ID of the source.</param>
28+
public ProxyTransform(Transform drivenTransform,
29+
Transform sourceTransform,
30+
OVRSkeleton.BoneId boneId)
31+
{
32+
DrivenTransform = drivenTransform;
33+
SourceTransform = sourceTransform;
34+
BoneId = boneId;
35+
}
36+
37+
/// <summary>
38+
/// Update proxy position to track source.
39+
/// </summary>
40+
public void Update()
41+
{
42+
DrivenTransform.SetPositionAndRotation(
43+
SourceTransform.position,
44+
SourceTransform.rotation);
45+
}
46+
47+
/// <summary>
48+
/// Validate source transform to make sure it's
49+
/// always correct w.r.t. the bone transform.
50+
/// </summary>
51+
/// <param name="bone">Bone to track.</param>
52+
public void ValidateSource(OVRBone bone)
53+
{
54+
SourceTransform = bone.Transform;
55+
}
56+
57+
/// <summary>
58+
/// The driven, proxy transform.
59+
/// </summary>
60+
public Transform DrivenTransform { get; private set; }
61+
/// <summary>
62+
/// The source transform to drive the proxy.
63+
/// </summary>
64+
public Transform SourceTransform { get; private set; }
65+
/// <summary>
66+
/// The bone ID of the source.
67+
/// </summary>
68+
public OVRSkeleton.BoneId BoneId { get; private set; }
69+
}
70+
71+
private ProxyTransform[] _proxyTransforms = null;
72+
/// <summary>
73+
/// Accessor for array of proxy transforms that track skeletal bones.
74+
/// </summary>
75+
public ProxyTransform[] ProxyTransforms => _proxyTransforms;
76+
77+
/// <summary>
78+
/// Triggered if proxy transforms were recreated.
79+
/// </summary>
80+
public int ProxyChangeCount { get; private set; } = 0;
81+
82+
/// <summary>
83+
/// Updates the state of the proxies using the skeletal
84+
/// bones provided.
85+
/// </summary>
86+
/// <param name="bones">Bones of the skeleton tracked by
87+
/// proxy transforms.</param>
88+
public void UpdateState(IList<OVRBone> bones)
89+
{
90+
ReallocateProxiesIfBoneIdsHaveChanged(bones);
91+
92+
ValidateProxySourceTransforms(bones);
93+
94+
UpdateProxyTransforms(bones);
95+
}
96+
97+
private void ReallocateProxiesIfBoneIdsHaveChanged(IList<OVRBone> bones)
98+
{
99+
int numBones = bones.Count;
100+
if (!BoneIdsChanged(bones))
101+
{
102+
return;
103+
}
104+
105+
Debug.Log($"Creating a new set of {numBones} bone proxies.");
106+
CleanUpOldProxyTransforms();
107+
_proxyTransforms = new ProxyTransform[numBones];
108+
for (int boneIndex = 0; boneIndex < numBones; boneIndex++)
109+
{
110+
var originalBone = bones[boneIndex];
111+
var drivenTransform = new GameObject($"Proxy {boneIndex}").transform;
112+
_proxyTransforms[boneIndex] =
113+
new ProxyTransform(drivenTransform,
114+
originalBone.Transform, originalBone.Id);
115+
}
116+
ProxyChangeCount++;
117+
}
118+
119+
private bool BoneIdsChanged(IList<OVRBone> bones)
120+
{
121+
int numBones = bones.Count;
122+
if (_proxyTransforms == null ||
123+
_proxyTransforms.Length != numBones)
124+
{
125+
return true;
126+
}
127+
128+
for (int boneIndex = 0; boneIndex < numBones; boneIndex++)
129+
{
130+
var proxyBone = _proxyTransforms[boneIndex];
131+
var originalBone = bones[boneIndex];
132+
if (proxyBone.BoneId != originalBone.Id)
133+
{
134+
return true;
135+
}
136+
}
137+
138+
return false;
139+
}
140+
141+
private void CleanUpOldProxyTransforms()
142+
{
143+
if (_proxyTransforms == null)
144+
{
145+
return;
146+
}
147+
foreach (var proxyTransform in _proxyTransforms)
148+
{
149+
GameObject.Destroy(proxyTransform.DrivenTransform.gameObject);
150+
}
151+
_proxyTransforms = null;
152+
}
153+
154+
// <summary>
155+
/// Sometimes source transforms change even though the IDs are the same,
156+
/// and proxies are only recreated if the bone IDs of the sources change.
157+
/// Make sure they are set properly.
158+
/// </summary>
159+
/// <param name="bones">The list of bones to track.</param>
160+
private void ValidateProxySourceTransforms(IList<OVRBone> bones)
161+
{
162+
if (_proxyTransforms == null)
163+
{
164+
return;
165+
}
166+
int numBones = bones.Count;
167+
for (int boneIndex = 0; boneIndex < numBones; boneIndex++)
168+
{
169+
_proxyTransforms[boneIndex].ValidateSource(bones[boneIndex]);
170+
}
171+
}
172+
173+
private void UpdateProxyTransforms(IList<OVRBone> bones)
174+
{
175+
var boneCount = bones.Count;
176+
for (int boneIndex = 0; boneIndex < boneCount; boneIndex++)
177+
{
178+
_proxyTransforms[boneIndex].Update();
179+
}
180+
}
181+
}
182+
}

Runtime/Scripts/AnimationRigging/ProxyTransformLogic.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Scripts/AnimationRigging/RetargetingLayer.cs

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

33
using Oculus.Interaction;
4+
using Oculus.Movement.AnimationRigging.Utils;
45
using System.Collections.Generic;
56
using UnityEngine;
67
using UnityEngine.Animations;
@@ -138,11 +139,37 @@ public AvatarMask MaskToSetToTPoseComp
138139
set { _maskToSetToTPose = value; }
139140
}
140141

142+
/// <summary>
143+
/// Create proxy transforms that track the skeletal bones. If the
144+
/// skeletal bone transforms change, that won't necessitate creating new
145+
/// proxy transforms in most cases. This means any Animation jobs
146+
/// that track the skeletal bone transform can use proxies
147+
/// instead, which get re-allocated less often. Re-allocation would mean
148+
/// having to create new animation jobs.
149+
/// </summary>
150+
[SerializeField]
151+
[Tooltip(RetargetingLayerTooltips.EnableTrackingByProxy)]
152+
private bool _enableTrackingByProxy = false;
153+
154+
/// <inheritdoc cref="_enableTrackingByProxy"/>
155+
public bool EnableTrackingByProxy
156+
{
157+
get { return _enableTrackingByProxy; }
158+
set { _enableTrackingByProxy = value; }
159+
}
160+
141161
private Pose[] _defaultPoses;
142162
private IJointConstraint[] _jointConstraints;
163+
private ProxyTransformLogic _proxyTransformLogic = new ProxyTransformLogic();
143164

144165
/// <summary>
145-
/// Gets number of transforms being retargeted currently.
166+
/// Triggered if proxy transforms were recreated.
167+
/// </summary>
168+
public int ProxyChangeCount => _proxyTransformLogic.ProxyChangeCount;
169+
170+
/// <summary>
171+
/// Gets number of transforms being retargeted currently. This can change during
172+
/// initialization.
146173
/// </summary>
147174
/// <returns>Number of transforms with a valid correction quaternion.</returns>
148175
public int GetNumberOfTransformsRetargeted()
@@ -209,6 +236,11 @@ protected override void Update()
209236
UpdateSkeleton();
210237

211238
RecomputeSkeletalOffsetsIfNecessary();
239+
240+
if (_enableTrackingByProxy)
241+
{
242+
_proxyTransformLogic.UpdateState(Bones);
243+
}
212244
}
213245

214246
/// <summary>
@@ -376,7 +408,9 @@ public void FillTransformArrays(List<Transform> sourceTransforms,
376408
}
377409
}
378410

379-
sourceTransforms.Add(currentBone.Transform);
411+
sourceTransforms.Add(_enableTrackingByProxy ?
412+
_proxyTransformLogic.ProxyTransforms[i].DrivenTransform :
413+
currentBone.Transform);
380414
targetTransforms.Add(targetBoneData.OriginalJoint);
381415
shouldUpdatePositions.Add(false);
382416
rotationOffsets.Add(targetBoneData.CorrectionQuaternion.Value);

Runtime/Scripts/Tooltips.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,11 @@ public static class AnimationRigSetupTooltips
983983

984984
public const string RetargetingLayer =
985985
"Retargeting layer component to get data from.";
986+
987+
public const string CheckSkeletalUpdatesByProxy =
988+
"Use proxy transforms to check skeletal changes. " +
989+
"Proxy transforms can be used in case the original " +
990+
"skeleton updates too much.";
986991
}
987992

988993
public static class HipPinningConstraintCalibrationTooltips
@@ -1138,12 +1143,19 @@ public static class RetargetingLayerTooltips
11381143
{
11391144
public const string DisableAvatar =
11401145
"Disable avatar for accurate positions, especially fingers.";
1146+
11411147
public const string PositionsToCorrectLateUpdate =
11421148
"Positions to correct after the fact. Avatar " +
11431149
"masks prevent setting positions of the hands precisely.";
1150+
11441151
public const string MaskToSetToTPose =
11451152
"Since some bones are not affected by retargeting, " +
11461153
"some joints should be reset to t-pose.";
1154+
1155+
public const string EnableTrackingByProxy =
1156+
"Create proxy transforms that track the skeletal bones. If the " +
1157+
"skeletal bone transforms change, that won't necessitate creating new " +
1158+
"proxy transforms in most cases.";
11471159
}
11481160

11491161
public static class LateMirroredObjectTooltips

0 commit comments

Comments
 (0)