Skip to content

Commit 7a6a9c2

Browse files
authored
Merge pull request #10007 from Zee2/scalehandle-fix
Fixing BC Scale Handles
2 parents 452e47b + 201df8b commit 7a6a9c2

File tree

3 files changed

+109
-79
lines changed

3 files changed

+109
-79
lines changed

Assets/MRTK/SDK/Features/UX/Scripts/BoundsControl/BoundsControl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,7 @@ private void UpdateVisuals()
15411541
links.UpdateLinkScales(currentBoundsExtents);
15421542

15431543
translationHandles.CalculateHandlePositions(ref boundsCorners);
1544-
scaleHandles.UpdateHandles(ref boundsCorners);
1544+
scaleHandles.CalculateHandlePositions(ref boundsCorners);
15451545

15461546
boxDisplay.UpdateDisplay(currentBoundsExtents, flattenAxis);
15471547

Assets/MRTK/SDK/Features/UX/Scripts/BoundsControl/Visuals/ScaleHandles.cs

Lines changed: 93 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,17 @@ public class ScaleHandles : HandlesBase
1616
protected override HandlesBaseConfiguration BaseConfig => config;
1717
private ScaleHandlesConfiguration config;
1818
private bool areHandlesFlattened = false;
19+
20+
/// <summary>
21+
/// Cached handle positions - we keep track of handle positions in this array
22+
/// in case we have to reload the handles due to configuration changes.
23+
/// </summary>
24+
protected Vector3[] HandlePositions { get; private set; }
25+
private const int NumHandles = 8;
1926
internal ScaleHandles(ScaleHandlesConfiguration configuration)
2027
{
28+
HandlePositions = new Vector3[NumHandles];
29+
2130
Debug.Assert(configuration != null, "Can't create BoundsControlScaleHandles without valid configuration");
2231
config = configuration;
2332
config.handlesChanged.AddListener(HandlesChanged);
@@ -39,16 +48,30 @@ internal void UpdateVisibilityInInspector(HideFlags flags)
3948
}
4049
}
4150

42-
internal void UpdateHandles(ref Vector3[] boundsCorners)
51+
internal void UpdateHandles()
4352
{
4453
for (int i = 0; i < handles.Count; ++i)
4554
{
46-
handles[i].position = boundsCorners[i];
55+
handles[i].position = HandlePositions[i];
56+
}
57+
}
58+
59+
internal void CalculateHandlePositions(ref Vector3[] boundsCorners)
60+
{
61+
if (boundsCorners != null && HandlePositions != null)
62+
{
63+
for (int i = 0; i < HandlePositions.Length; ++i)
64+
{
65+
HandlePositions[i] = boundsCorners[i];
66+
}
67+
UpdateHandles();
4768
}
4869
}
4970

5071
internal void Create(ref Vector3[] boundsCorners, Transform parent, bool isFlattened)
5172
{
73+
CalculateHandlePositions(ref boundsCorners);
74+
5275
// create corners
5376
for (int i = 0; i < boundsCorners.Length; ++i)
5477
{
@@ -57,20 +80,9 @@ internal void Create(ref Vector3[] boundsCorners, Transform parent, bool isFlatt
5780
name = "corner_" + i.ToString()
5881
};
5982
corner.transform.parent = parent;
60-
corner.transform.localPosition = boundsCorners[i];
83+
corner.transform.localPosition = HandlePositions[i];
6184

62-
GameObject visualsScale = new GameObject();
63-
visualsScale.name = "visualsScale";
64-
visualsScale.transform.parent = corner.transform;
65-
visualsScale.transform.localPosition = Vector3.zero;
66-
67-
// Compute mirroring scale
68-
{
69-
Vector3 p = boundsCorners[i];
70-
visualsScale.transform.localScale = new Vector3(Mathf.Sign(p[0]), Mathf.Sign(p[1]), Mathf.Sign(p[2]));
71-
}
72-
73-
Bounds visualBounds = CreateVisual(visualsScale, isFlattened);
85+
Bounds visualBounds = CreateVisual(i, corner, isFlattened);
7486
var invScale = visualBounds.size.x == 0.0f ? 0.0f : config.HandleSize / visualBounds.size.x;
7587
VisualUtils.AddComponentsToAffordance(corner, new Bounds(visualBounds.center * invScale, visualBounds.size * invScale),
7688
HandlePrefabCollider.Box, CursorContextInfo.CursorAction.Scale, config.ColliderPadding, parent, config.DrawTetherWhenManipulating);
@@ -85,93 +97,117 @@ protected override void RecreateVisuals()
8597
{
8698
for (int i = 0; i < handles.Count; ++i)
8799
{
88-
// get parent of visual
89-
Transform visualsScaleParent = handles[i].Find("visualsScale");
90-
if (visualsScaleParent)
100+
101+
Transform obsoleteChild = handles[i].Find(visualsName);
102+
if (obsoleteChild)
103+
{
104+
obsoleteChild.parent = null;
105+
Object.Destroy(obsoleteChild.gameObject);
106+
}
107+
else
91108
{
92-
// get old child and remove it
93-
Transform obsoleteChild = visualsScaleParent.Find(visualsName);
94-
if (obsoleteChild)
95-
{
96-
obsoleteChild.parent = null;
97-
Object.Destroy(obsoleteChild.gameObject);
98-
}
99-
else
100-
{
101-
Debug.LogError("couldn't find corner visual on recreating visuals");
102-
}
103-
104-
// create new visual
105-
Bounds cornerBounds = CreateVisual(visualsScaleParent.gameObject, areHandlesFlattened);
106-
107-
// update handle collider bounds
108-
UpdateColliderBounds(handles[i], cornerBounds.size);
109+
Debug.LogError("Couldn't find corner visual on recreating visuals");
109110
}
111+
112+
// create new visual
113+
Bounds visualBounds = CreateVisual(i, handles[i].gameObject, areHandlesFlattened);
114+
115+
// update handle collider bounds
116+
UpdateColliderBounds(handles[i], visualBounds.size);
110117
}
111118

112119
objectsChangedEvent.Invoke(this);
113120
}
114121

115122
protected override void UpdateColliderBounds(Transform handle, Vector3 visualSize)
116123
{
117-
var invScale = visualSize.x == 0.0f ? 0.0f : config.HandleSize / visualSize.x;
124+
float maxDim = VisualUtils.GetMaxComponent(visualSize);
125+
float invScale = maxDim == 0.0f ? 0.0f : config.HandleSize / maxDim;
118126
GetVisual(handle).transform.localScale = new Vector3(invScale, invScale, invScale);
119127
BoxCollider collider = handle.gameObject.GetComponent<BoxCollider>();
120128
Vector3 colliderSize = visualSize * invScale;
121129
collider.size = colliderSize;
122130
collider.size += BaseConfig.ColliderPadding;
123-
collider.center = new Vector3(collider.size.x, collider.size.y, collider.size.z) * 0.5f;
131+
collider.center = Vector3.zero;
124132
}
125133

126134
/// <summary>
127135
/// Creates the corner visual and returns the bounds of the created visual
128136
/// </summary>
137+
/// <param name="handleIndex">cornerIndex</param>
129138
/// <param name="parent">parent of visual</param>
130139
/// <param name="isFlattened">instantiate in flattened mode - slate</param>
131140
/// <returns>bounds of the created visual</returns>
132-
private Bounds CreateVisual(GameObject parent, bool isFlattened)
141+
private Bounds CreateVisual(int handleIndex, GameObject parent, bool isFlattened)
133142
{
134143
// figure out which prefab to instantiate
135-
GameObject cornerVisual = null;
144+
GameObject handleVisual = null;
136145
areHandlesFlattened = isFlattened;
137146
GameObject prefabType = isFlattened ? config.HandleSlatePrefab : config.HandlePrefab;
138147
if (prefabType == null)
139148
{
140149
// instantiate default prefab, a cube with box collider
141-
cornerVisual = GameObject.CreatePrimitive(PrimitiveType.Cube);
142-
cornerVisual.transform.parent = parent.transform;
143-
cornerVisual.transform.localPosition = Vector3.zero;
150+
handleVisual = GameObject.CreatePrimitive(PrimitiveType.Cube);
151+
144152
// deactivate collider on visuals and register for deletion - actual collider
145153
// of handle is attached to the handle gameobject, not the visual
146-
var collider = cornerVisual.GetComponent<BoxCollider>();
154+
var collider = handleVisual.GetComponent<BoxCollider>();
147155
collider.enabled = false;
148-
Object.Destroy(collider);
156+
UnityEngine.Object.Destroy(collider);
149157
}
150158
else
151159
{
152-
cornerVisual = GameObject.Instantiate(prefabType, parent.transform);
160+
handleVisual = GameObject.Instantiate(prefabType, parent.transform);
153161
}
162+
163+
// this is the size of the corner visuals
164+
var handleVisualBounds = VisualUtils.GetMaxBounds(handleVisual);
165+
float maxDim = VisualUtils.GetMaxComponent(handleVisualBounds.size);
166+
float invScale = maxDim == 0.0f ? 0.0f : config.HandleSize / maxDim;
167+
168+
handleVisual.name = visualsName;
169+
handleVisual.transform.parent = parent.transform;
170+
handleVisual.transform.localScale = new Vector3(invScale, invScale, invScale);
171+
handleVisual.transform.localPosition = Vector3.zero;
172+
handleVisual.transform.localRotation = Quaternion.identity;
154173

155174
if (isFlattened)
156175
{
157176
// Rotate 2D slate handle asset for proper orientation
158-
cornerVisual.transform.Rotate(0, 0, -90);
177+
parent.transform.Rotate(0, 0, -90);
178+
}
179+
else
180+
{
181+
Quaternion realignment = GetRotationRealignment(handleIndex);
182+
parent.transform.localRotation = realignment;
159183
}
160184

161-
cornerVisual.name = visualsName;
185+
if(config.HandleMaterial != null)
186+
{
187+
VisualUtils.ApplyMaterialToAllRenderers(handleVisual, config.HandleMaterial);
188+
}
162189

163-
// this is the size of the corner visuals
164-
var cornerbounds = VisualUtils.GetMaxBounds(cornerVisual);
165-
float maxDim = Mathf.Max(Mathf.Max(cornerbounds.size.x, cornerbounds.size.y), cornerbounds.size.z);
166-
cornerbounds.size = maxDim * Vector3.one;
190+
return handleVisualBounds;
191+
}
192+
193+
protected Quaternion GetRotationRealignment(int handleIndex)
194+
{
195+
// Helper lambda to sign a vector.
196+
Vector3 signVector(Vector3 i) => new Vector3(Mathf.Sign(i.x), Mathf.Sign(i.y), Mathf.Sign(i.z));
167197

168-
// we need to multiply by this amount to get to desired scale handle size
169-
var invScale = cornerbounds.size.x == 0.0f ? 0.0f : config.HandleSize / cornerbounds.size.x;
170-
cornerVisual.transform.localScale = new Vector3(invScale, invScale, invScale);
198+
// The neutral handle is the handle position at which
199+
// the corner handle model is appropriately aligned, sans any rotation
200+
Vector3 neutralHandle = signVector(HandlePositions[6]);
201+
Vector3 handlePos = signVector(HandlePositions[handleIndex]);
171202

172-
VisualUtils.ApplyMaterialToAllRenderers(cornerVisual, config.HandleMaterial);
203+
// Flip the handle if it's on the underside of the bounds.
204+
Quaternion flip = Quaternion.Euler(0, 0, handlePos.y > 0 ? 0 : 90);
173205

174-
return cornerbounds;
206+
float angleAroundVertical = Vector3.SignedAngle(new Vector3(neutralHandle.x, 0, neutralHandle.z),
207+
new Vector3(handlePos.x, 0, handlePos.z),
208+
Vector3.up);
209+
210+
return Quaternion.Euler(0, angleAroundVertical, 0) * flip;
175211
}
176212

177213
internal void Reset(bool areHandlesActive, FlattenModeType flattenAxis)
@@ -191,13 +227,7 @@ internal void Reset(bool areHandlesActive, FlattenModeType flattenAxis)
191227

192228
protected override Transform GetVisual(Transform handle)
193229
{
194-
Transform firstChild = handle.GetChild(0);
195-
if (firstChild == null)
196-
{
197-
return null;
198-
}
199-
200-
Transform visual = firstChild.GetChild(0);
230+
Transform visual = handle.GetChild(0);
201231
if (visual != null && visual.name == visualsName)
202232
{
203233
return visual;

0 commit comments

Comments
 (0)