@@ -16,8 +16,17 @@ public class ScaleHandles : HandlesBase
16
16
protected override HandlesBaseConfiguration BaseConfig => config ;
17
17
private ScaleHandlesConfiguration config ;
18
18
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 ;
19
26
internal ScaleHandles ( ScaleHandlesConfiguration configuration )
20
27
{
28
+ HandlePositions = new Vector3 [ NumHandles ] ;
29
+
21
30
Debug . Assert ( configuration != null , "Can't create BoundsControlScaleHandles without valid configuration" ) ;
22
31
config = configuration ;
23
32
config . handlesChanged . AddListener ( HandlesChanged ) ;
@@ -39,16 +48,30 @@ internal void UpdateVisibilityInInspector(HideFlags flags)
39
48
}
40
49
}
41
50
42
- internal void UpdateHandles ( ref Vector3 [ ] boundsCorners )
51
+ internal void UpdateHandles ( )
43
52
{
44
53
for ( int i = 0 ; i < handles . Count ; ++ i )
45
54
{
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 ( ) ;
47
68
}
48
69
}
49
70
50
71
internal void Create ( ref Vector3 [ ] boundsCorners , Transform parent , bool isFlattened )
51
72
{
73
+ CalculateHandlePositions ( ref boundsCorners ) ;
74
+
52
75
// create corners
53
76
for ( int i = 0 ; i < boundsCorners . Length ; ++ i )
54
77
{
@@ -57,20 +80,9 @@ internal void Create(ref Vector3[] boundsCorners, Transform parent, bool isFlatt
57
80
name = "corner_" + i . ToString ( )
58
81
} ;
59
82
corner . transform . parent = parent ;
60
- corner . transform . localPosition = boundsCorners [ i ] ;
83
+ corner . transform . localPosition = HandlePositions [ i ] ;
61
84
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 ) ;
74
86
var invScale = visualBounds . size . x == 0.0f ? 0.0f : config . HandleSize / visualBounds . size . x ;
75
87
VisualUtils . AddComponentsToAffordance ( corner , new Bounds ( visualBounds . center * invScale , visualBounds . size * invScale ) ,
76
88
HandlePrefabCollider . Box , CursorContextInfo . CursorAction . Scale , config . ColliderPadding , parent , config . DrawTetherWhenManipulating ) ;
@@ -85,93 +97,117 @@ protected override void RecreateVisuals()
85
97
{
86
98
for ( int i = 0 ; i < handles . Count ; ++ i )
87
99
{
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
91
108
{
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" ) ;
109
110
}
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 ) ;
110
117
}
111
118
112
119
objectsChangedEvent . Invoke ( this ) ;
113
120
}
114
121
115
122
protected override void UpdateColliderBounds ( Transform handle , Vector3 visualSize )
116
123
{
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 ;
118
126
GetVisual ( handle ) . transform . localScale = new Vector3 ( invScale , invScale , invScale ) ;
119
127
BoxCollider collider = handle . gameObject . GetComponent < BoxCollider > ( ) ;
120
128
Vector3 colliderSize = visualSize * invScale ;
121
129
collider . size = colliderSize ;
122
130
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 ;
124
132
}
125
133
126
134
/// <summary>
127
135
/// Creates the corner visual and returns the bounds of the created visual
128
136
/// </summary>
137
+ /// <param name="handleIndex">cornerIndex</param>
129
138
/// <param name="parent">parent of visual</param>
130
139
/// <param name="isFlattened">instantiate in flattened mode - slate</param>
131
140
/// <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 )
133
142
{
134
143
// figure out which prefab to instantiate
135
- GameObject cornerVisual = null ;
144
+ GameObject handleVisual = null ;
136
145
areHandlesFlattened = isFlattened ;
137
146
GameObject prefabType = isFlattened ? config . HandleSlatePrefab : config . HandlePrefab ;
138
147
if ( prefabType == null )
139
148
{
140
149
// 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
+
144
152
// deactivate collider on visuals and register for deletion - actual collider
145
153
// of handle is attached to the handle gameobject, not the visual
146
- var collider = cornerVisual . GetComponent < BoxCollider > ( ) ;
154
+ var collider = handleVisual . GetComponent < BoxCollider > ( ) ;
147
155
collider . enabled = false ;
148
- Object . Destroy ( collider ) ;
156
+ UnityEngine . Object . Destroy ( collider ) ;
149
157
}
150
158
else
151
159
{
152
- cornerVisual = GameObject . Instantiate ( prefabType , parent . transform ) ;
160
+ handleVisual = GameObject . Instantiate ( prefabType , parent . transform ) ;
153
161
}
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 ;
154
173
155
174
if ( isFlattened )
156
175
{
157
176
// 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 ;
159
183
}
160
184
161
- cornerVisual . name = visualsName ;
185
+ if ( config . HandleMaterial != null )
186
+ {
187
+ VisualUtils . ApplyMaterialToAllRenderers ( handleVisual , config . HandleMaterial ) ;
188
+ }
162
189
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 ) ) ;
167
197
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 ] ) ;
171
202
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 ) ;
173
205
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 ;
175
211
}
176
212
177
213
internal void Reset ( bool areHandlesActive , FlattenModeType flattenAxis )
@@ -191,13 +227,7 @@ internal void Reset(bool areHandlesActive, FlattenModeType flattenAxis)
191
227
192
228
protected override Transform GetVisual ( Transform handle )
193
229
{
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 ) ;
201
231
if ( visual != null && visual . name == visualsName )
202
232
{
203
233
return visual ;
0 commit comments