Skip to content

Commit ea8dc5d

Browse files
authored
Merge pull request #7377 from Troy-Ferrell/users/trferrel/rotation-theme-fix
Fix visual rotation theme for toggling local vs world space rotation
2 parents 8d4a997 + 3fae4c0 commit ea8dc5d

File tree

4 files changed

+117
-9
lines changed

4 files changed

+117
-9
lines changed

Assets/MixedRealityToolkit.SDK/Features/UX/Interactable/Themes/Model_BuckyTheme.asset

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,27 @@ MonoBehaviour:
145145
Quaternion: {x: 0, y: 0, z: 0, w: 0}
146146
AudioClip: {fileID: 0}
147147
Animation: {fileID: 0}
148+
- Name: Local Rotation
149+
Tooltip: Should the theme manipulate the target's local rotation or world space
150+
rotation
151+
Type: 15
152+
Value:
153+
Name:
154+
String:
155+
Bool: 0
156+
Int: 0
157+
Float: 0
158+
Texture: {fileID: 0}
159+
Material: {fileID: 0}
160+
Shader: {fileID: 0}
161+
GameObject: {fileID: 0}
162+
Vector2: {x: 0, y: 0}
163+
Vector3: {x: 0, y: 0, z: 0}
164+
Vector4: {x: 0, y: 0, z: 0, w: 0}
165+
Color: {r: 0, g: 0, b: 0, a: 0}
166+
Quaternion: {x: 0, y: 0, z: 0, w: 0}
167+
AudioClip: {fileID: 0}
168+
Animation: {fileID: 0}
148169
easing:
149170
Enabled: 1
150171
Curve:

Assets/MixedRealityToolkit.SDK/Features/UX/Scripts/VisualThemes/ThemeEngines/InteractableRotationTheme.cs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,22 @@ namespace Microsoft.MixedReality.Toolkit.UI
1212
/// </summary>
1313
public class InteractableRotationTheme : InteractableThemeBase
1414
{
15+
/// <summary>
16+
/// If true, the theme rotation value will be added to the initial Gameobject's rotation. Otherwise, it will set directly as absolute euler angles.
17+
/// </summary>
18+
public bool IsRelativeRotation => GetThemeProperty(RelativeRotationPropertyIndex).Value.Bool;
19+
20+
/// <summary>
21+
/// If true, the theme manipulate the target's local rotation. Otherwise, the theme will control the world space rotation.
22+
/// </summary>
23+
public bool IsLocalRotation => GetThemeProperty(LocalRotationPropertyIndex).Value.Bool;
24+
1525
protected Vector3 originalRotation;
1626
protected Transform hostTransform;
1727

28+
private const int RelativeRotationPropertyIndex = 0;
29+
private const int LocalRotationPropertyIndex = 1;
30+
1831
public InteractableRotationTheme()
1932
{
2033
Types = new Type[] { typeof(Transform) };
@@ -42,10 +55,17 @@ public override ThemeDefinition GetDefaultThemeDefinition()
4255
new ThemeProperty()
4356
{
4457
Name = "Relative Rotation",
45-
Tooltip = "Should the rotation be added to initial Gameobject rotation, or absolute",
58+
Tooltip = "Should the theme rotation value be added to the initial Gameobject's rotation, or set directly as absolute euler angles",
4659
Type = ThemePropertyTypes.Bool,
4760
Value = new ThemePropertyValue() { Bool = false }
4861
},
62+
new ThemeProperty()
63+
{
64+
Name = "Local Rotation",
65+
Tooltip = "Should the theme manipulate the target's local rotation or world space rotation",
66+
Type = ThemePropertyTypes.Bool,
67+
Value = new ThemePropertyValue() { Bool = true }
68+
},
4969
},
5070
};
5171
}
@@ -56,14 +76,14 @@ public override void Init(GameObject host, ThemeDefinition settings)
5676
base.Init(host, settings);
5777

5878
hostTransform = Host.transform;
59-
originalRotation = hostTransform.localEulerAngles;
79+
originalRotation = IsLocalRotation ? hostTransform.localEulerAngles : hostTransform.eulerAngles;
6080
}
6181

6282
/// <inheritdoc />
6383
public override ThemePropertyValue GetProperty(ThemeStateProperty property)
6484
{
6585
ThemePropertyValue start = new ThemePropertyValue();
66-
start.Vector3 = hostTransform.eulerAngles;
86+
start.Vector3 = IsLocalRotation ? hostTransform.localEulerAngles : hostTransform.eulerAngles;
6787
return start;
6888
}
6989

@@ -72,13 +92,21 @@ public override void SetValue(ThemeStateProperty property, int index, float perc
7292
{
7393
Vector3 lerpTarget = property.Values[index].Vector3;
7494

75-
bool relative = Properties[0].Value.Bool;
76-
if (relative)
95+
if (IsRelativeRotation)
7796
{
7897
lerpTarget = originalRotation + lerpTarget;
7998
}
8099

81-
hostTransform.localRotation = Quaternion.Euler(Vector3.Lerp(property.StartValue.Vector3, lerpTarget, percentage));
100+
var setValue = Quaternion.Euler(Vector3.Lerp(property.StartValue.Vector3, lerpTarget, percentage));
101+
102+
if (IsLocalRotation)
103+
{
104+
hostTransform.localRotation = setValue;
105+
}
106+
else
107+
{
108+
hostTransform.rotation = setValue;
109+
}
82110
}
83111
}
84112
}

Assets/MixedRealityToolkit.SDK/Features/UX/Scripts/VisualThemes/ThemeEngines/InteractableThemeBase.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,5 +222,22 @@ protected int LerpInt(int s, int e, float t)
222222
{
223223
return Mathf.RoundToInt((e - s) * t) + s;
224224
}
225+
226+
protected ThemeProperty GetThemeProperty(int index)
227+
{
228+
if (index >= 0)
229+
{
230+
if (Properties != null && Properties.Count > index)
231+
{
232+
return Properties[index];
233+
}
234+
235+
// If Theme's data does not have desired property, return defaults.
236+
// If index is not in range, throw exception to fail fast.
237+
return GetDefaultThemeDefinition().CustomProperties[index];
238+
}
239+
240+
return null;
241+
}
225242
}
226243
}

Assets/MixedRealityToolkit.Tests/PlayModeTests/VisualThemeTests.cs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,16 @@ public IEnumerator TestOffsetTheme()
105105
(theme) => { Assert.AreEqual(state1, theme.Host.transform.position); });
106106
}
107107

108+
/// <summary>
109+
/// Test InteractableRotationTheme applied not as local, non-relative rotation to host target
110+
/// </summary>
108111
[UnityTest]
109112
public IEnumerator TestRotationTheme()
110113
{
114+
// Point Cube diagonally to right
115+
var hostGameObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
116+
hostGameObject.transform.rotation = Quaternion.LookRotation(Vector3.forward + Vector3.right);
117+
111118
Vector3 state0 = Quaternion.LookRotation(Vector3.up).eulerAngles;
112119
Vector3 state1 = Quaternion.LookRotation(Vector3.down).eulerAngles;
113120

@@ -120,9 +127,44 @@ public IEnumerator TestRotationTheme()
120127
}
121128
};
122129

123-
yield return TestTheme<InteractableRotationTheme, MeshRenderer>(defaultStateValues,
124-
(theme) => { Assert.AreEqual(state0, theme.Host.transform.rotation.eulerAngles); },
125-
(theme) => { Assert.AreEqual(state1, theme.Host.transform.rotation.eulerAngles); });
130+
yield return TestTheme<InteractableRotationTheme, MeshRenderer>(hostGameObject, defaultStateValues,
131+
(theme) => { Assert.AreEqual(state0, theme.Host.transform.localEulerAngles); },
132+
(theme) => { Assert.AreEqual(state1, theme.Host.transform.localEulerAngles); });
133+
}
134+
135+
/// <summary>
136+
/// Test InteractableRotationTheme applied not as world space, relative rotation to host target
137+
/// </summary>
138+
[UnityTest]
139+
public IEnumerator TestRotationThemeWorldSpace()
140+
{
141+
// Point Cube diagonally to right
142+
var hostGameObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
143+
var initRotation = Quaternion.LookRotation(Vector3.forward + Vector3.right); ;
144+
hostGameObject.transform.rotation = initRotation;
145+
146+
Vector3 state0 = Quaternion.LookRotation(Vector3.up).eulerAngles;
147+
Vector3 state1 = Quaternion.LookRotation(Vector3.down).eulerAngles;
148+
149+
var defaultStateValues = new List<List<ThemePropertyValue>>()
150+
{
151+
new List<ThemePropertyValue>()
152+
{
153+
new ThemePropertyValue() { Vector3 = state0 },
154+
new ThemePropertyValue() { Vector3 = state1 },
155+
}
156+
};
157+
158+
// Generate default theme properties for InteractableRotationTheme.
159+
// Set Relative Rotation property (index=0) to true so theme values are applied in addition instead of absolutely set
160+
// Set Local Rotation property (index=1) to false so euler angles are world space
161+
var defaultThemeProperties = (new InteractableRotationTheme()).GetDefaultThemeDefinition().CustomProperties;
162+
defaultThemeProperties[0].Value.Bool = true;
163+
defaultThemeProperties[1].Value.Bool = false;
164+
165+
yield return TestTheme<InteractableRotationTheme, MeshRenderer>(hostGameObject, defaultStateValues, defaultThemeProperties,
166+
(theme) => { Assert.AreEqual(initRotation.eulerAngles + state0, theme.Host.transform.eulerAngles); },
167+
(theme) => { Assert.AreEqual(initRotation.eulerAngles + state1, theme.Host.transform.eulerAngles); });
126168
}
127169

128170
/// <summary>

0 commit comments

Comments
 (0)