Skip to content

Commit 1eaa37a

Browse files
authored
[2.8] Fix pointer profile layer mask syncing with prefabs (#10592)
* Keep prioritizedLayerMasks in sync * Indent the gaze provider inspector * Update layermasks in grab pointers * Update serialization paths to properly sync * Now that the profile does better syncing, only overwrite if needed * Auto-save if a prefab changed * Reserialize profiles
1 parent b77876a commit 1eaa37a

File tree

13 files changed

+238
-98
lines changed

13 files changed

+238
-98
lines changed

Assets/MRTK/Core/Definitions/InputSystem/MixedRealityPointerProfile.cs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the MIT License.
33

44
using Microsoft.MixedReality.Toolkit.Utilities;
5+
using System;
6+
using System.Linq;
57
using UnityEngine;
68

79
namespace Microsoft.MixedReality.Toolkit.Input
@@ -11,7 +13,7 @@ namespace Microsoft.MixedReality.Toolkit.Input
1113
/// </summary>
1214
[CreateAssetMenu(menuName = "Mixed Reality/Toolkit/Profiles/Mixed Reality Pointer Profile", fileName = "MixedRealityInputPointerProfile", order = (int)CreateProfileMenuItemIndices.Pointer)]
1315
[HelpURL("https://docs.microsoft.com/windows/mixed-reality/mrtk-unity/features/input/pointers")]
14-
public class MixedRealityPointerProfile : BaseMixedRealityProfile
16+
public class MixedRealityPointerProfile : BaseMixedRealityProfile, ISerializationCallbackReceiver
1517
{
1618
[SerializeField]
1719
[Tooltip("Maximum distance at which all pointers can collide with a GameObject, unless it has an override extent.")]
@@ -123,5 +125,50 @@ public bool IsEyeTrackingEnabled
123125
/// Primary pointer selector implementation to use. This is used by the focus provider to choose the primary pointer.
124126
/// </summary>
125127
public SystemType PrimaryPointerSelector => primaryPointerSelector;
128+
129+
void ISerializationCallbackReceiver.OnBeforeSerialize()
130+
{
131+
for (int i = 0; i < pointerOptions.Length; i++)
132+
{
133+
ref PointerOption pointerOption = ref pointerOptions[i];
134+
IMixedRealityPointer pointer = pointerOption.PointerPrefab != null ? pointerOption.PointerPrefab.GetComponent<IMixedRealityPointer>() : null;
135+
136+
if (pointer.IsNull()
137+
|| (pointer.PrioritizedLayerMasksOverride != null
138+
&& pointer.PrioritizedLayerMasksOverride.Length > 0
139+
&& pointerOption.PrioritizedLayerMasks != null
140+
&& pointerOption.PrioritizedLayerMasks.SequenceEqual(pointer.PrioritizedLayerMasksOverride))
141+
|| (pointingRaycastLayerMasks != null
142+
&& pointingRaycastLayerMasks.Length > 0
143+
&& pointerOption.PrioritizedLayerMasks != null
144+
&& pointerOption.PrioritizedLayerMasks.SequenceEqual(pointingRaycastLayerMasks)))
145+
{
146+
continue;
147+
}
148+
149+
// If the prefab has new LayerMasks, sync with prioritizedLayerMasks
150+
int pointerPrioritizedLayerMasksOverrideCount = pointer.PrioritizedLayerMasksOverride?.Length ?? 0;
151+
if (pointerPrioritizedLayerMasksOverrideCount != 0)
152+
{
153+
if (pointerOption.PrioritizedLayerMasks?.Length != pointerPrioritizedLayerMasksOverrideCount)
154+
{
155+
pointerOption.PrioritizedLayerMasks = new LayerMask[pointerPrioritizedLayerMasksOverrideCount];
156+
}
157+
Array.Copy(pointer.PrioritizedLayerMasksOverride, pointerOption.PrioritizedLayerMasks, pointerPrioritizedLayerMasksOverrideCount);
158+
}
159+
// If the prefab doesn't have any LayerMasks, initialize with the global default
160+
else
161+
{
162+
int pointingRaycastLayerMasksCount = pointingRaycastLayerMasks.Length;
163+
if (pointerOption.PrioritizedLayerMasks?.Length != pointingRaycastLayerMasksCount)
164+
{
165+
pointerOption.PrioritizedLayerMasks = new LayerMask[pointingRaycastLayerMasksCount];
166+
}
167+
Array.Copy(pointingRaycastLayerMasks, pointerOption.PrioritizedLayerMasks, pointingRaycastLayerMasksCount);
168+
}
169+
}
170+
}
171+
172+
void ISerializationCallbackReceiver.OnAfterDeserialize() { }
126173
}
127174
}

Assets/MRTK/Core/Definitions/InputSystem/PointerOption.cs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using Microsoft.MixedReality.Toolkit.Utilities;
55
using System;
6+
using System.Linq;
67
using UnityEngine;
78

89
namespace Microsoft.MixedReality.Toolkit.Input
@@ -11,7 +12,7 @@ namespace Microsoft.MixedReality.Toolkit.Input
1112
/// Defines a pointer option to assign to a controller.
1213
/// </summary>
1314
[Serializable]
14-
public struct PointerOption
15+
public struct PointerOption : ISerializationCallbackReceiver
1516
{
1617
/// <summary>
1718
/// Constructor.
@@ -60,6 +61,36 @@ public PointerOption(SupportedControllerType controllerType, Handedness handedne
6061
/// <summary>
6162
/// The LayerMasks, in prioritized order, which are used to determine the target
6263
/// </summary>
63-
public LayerMask[] PrioritizedLayerMasks => prioritizedLayerMasks;
64+
public LayerMask[] PrioritizedLayerMasks
65+
{
66+
get => prioritizedLayerMasks;
67+
internal set => prioritizedLayerMasks = value;
68+
}
69+
70+
void ISerializationCallbackReceiver.OnBeforeSerialize()
71+
{
72+
if (pointerPrefab == null)
73+
{
74+
return;
75+
}
76+
77+
IMixedRealityPointer pointer = pointerPrefab.GetComponent<IMixedRealityPointer>();
78+
if (pointer.IsNull()
79+
|| pointer.PrioritizedLayerMasksOverride == null
80+
|| pointer.PrioritizedLayerMasksOverride.Length == 0
81+
|| (prioritizedLayerMasks != null && pointer.PrioritizedLayerMasksOverride.SequenceEqual(prioritizedLayerMasks)))
82+
{
83+
return;
84+
}
85+
86+
int pointerPrioritizedLayerMasksOverrideCount = pointer.PrioritizedLayerMasksOverride.Length;
87+
if (prioritizedLayerMasks?.Length != pointerPrioritizedLayerMasksOverrideCount)
88+
{
89+
prioritizedLayerMasks = new LayerMask[pointerPrioritizedLayerMasksOverrideCount];
90+
}
91+
Array.Copy(pointer.PrioritizedLayerMasksOverride, prioritizedLayerMasks, pointerPrioritizedLayerMasksOverrideCount);
92+
}
93+
94+
void ISerializationCallbackReceiver.OnAfterDeserialize() { }
6495
}
65-
}
96+
}

Assets/MRTK/Core/Inspectors/Profiles/MixedRealityPointerProfileInspector.cs

Lines changed: 32 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class MixedRealityPointerProfileInspector : BaseMixedRealityToolkitConfig
1818
private static readonly GUIContent GazeCursorPrefabContent = new GUIContent("Gaze Cursor Prefab");
1919
private static readonly GUIContent UseEyeTrackingDataContent = new GUIContent("Use Eye Tracking Data");
2020
private static readonly GUIContent RaycastLayerMaskContent = new GUIContent("Default Raycast LayerMasks");
21+
private static readonly GUIContent PointerRaycastLayerMaskContent = new GUIContent("Pointer Raycast LayerMasks");
2122

2223
#if UNITY_2019_3_OR_NEWER
2324
private const string EnableGazeCapabilityContent = "To use eye tracking with UWP, the GazeInput capability needs to be set in the manifest." +
@@ -28,7 +29,7 @@ public class MixedRealityPointerProfileInspector : BaseMixedRealityToolkitConfig
2829
private const string ProfileDescription = "Pointers attach themselves onto controllers as they are initialized.";
2930

3031
private SerializedProperty pointingExtent;
31-
private SerializedProperty pointingRaycastLayerMasks;
32+
private SerializedProperty defaultRaycastLayerMasks;
3233
private static bool showPointerOptionProperties = true;
3334
private SerializedProperty pointerOptions;
3435

@@ -50,7 +51,7 @@ protected override void OnEnable()
5051
base.OnEnable();
5152

5253
pointingExtent = serializedObject.FindProperty("pointingExtent");
53-
pointingRaycastLayerMasks = serializedObject.FindProperty("pointingRaycastLayerMasks");
54+
defaultRaycastLayerMasks = serializedObject.FindProperty("pointingRaycastLayerMasks");
5455
pointerOptions = serializedObject.FindProperty("pointerOptions");
5556
debugDrawPointingRays = serializedObject.FindProperty("debugDrawPointingRays");
5657
debugDrawPointingRayColors = serializedObject.FindProperty("debugDrawPointingRayColors");
@@ -75,7 +76,7 @@ public override void OnInspectorGUI()
7576

7677
EditorGUILayout.Space();
7778
EditorGUILayout.PropertyField(pointingExtent);
78-
EditorGUILayout.PropertyField(pointingRaycastLayerMasks, RaycastLayerMaskContent, true);
79+
EditorGUILayout.PropertyField(defaultRaycastLayerMasks, RaycastLayerMaskContent, true);
7980
EditorGUILayout.PropertyField(pointerMediator);
8081
EditorGUILayout.PropertyField(primaryPointerSelector);
8182

@@ -117,8 +118,11 @@ public override void OnInspectorGUI()
117118
// Provide a convenient way to toggle the gaze provider as enabled/disabled via editor
118119
gazeProvider.Enabled = EditorGUILayout.Toggle("Enable Gaze Provider", gazeProvider.Enabled);
119120

120-
// Draw out the rest of the Gaze Provider's settings
121-
gazeProviderEditor.OnInspectorGUI();
121+
using (new EditorGUI.IndentLevelScope())
122+
{
123+
// Draw out the rest of the Gaze Provider's settings
124+
gazeProviderEditor.OnInspectorGUI();
125+
}
122126
}
123127
}
124128

@@ -133,7 +137,6 @@ public override void OnInspectorGUI()
133137
}
134138
}
135139

136-
137140
EditorGUILayout.Space();
138141
EditorGUILayout.LabelField("Debug Settings", EditorStyles.boldLabel);
139142
{
@@ -156,8 +159,6 @@ protected override bool IsProfileInActiveInstance()
156159

157160
private void RenderPointerList(SerializedProperty list)
158161
{
159-
var profile = target as MixedRealityPointerProfile;
160-
161162
if (InspectorUIUtility.RenderIndentedButton(AddButtonContent, EditorStyles.miniButton))
162163
{
163164
pointerOptions.arraySize += 1;
@@ -181,11 +182,10 @@ private void RenderPointerList(SerializedProperty list)
181182
return;
182183
}
183184

185+
bool anyPrefabChanged = false;
186+
184187
for (int i = 0; i < list.arraySize; i++)
185188
{
186-
IMixedRealityPointer pointer = null;
187-
Object pointerPrefab = null;
188-
189189
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
190190
{
191191
Color prevColor = GUI.color;
@@ -196,47 +196,15 @@ private void RenderPointerList(SerializedProperty list)
196196
var prefab = pointerOption.FindPropertyRelative("pointerPrefab");
197197
var prioritizedLayerMasks = pointerOption.FindPropertyRelative("prioritizedLayerMasks");
198198

199-
pointerPrefab = prefab.objectReferenceValue;
200-
pointer = pointerPrefab.IsNull() ? null : ((GameObject)pointerPrefab).GetComponent<IMixedRealityPointer>();
199+
GameObject pointerPrefab = prefab.objectReferenceValue as GameObject;
200+
IMixedRealityPointer pointer = pointerPrefab != null ? pointerPrefab.GetComponent<IMixedRealityPointer>() : null;
201201

202202
// Display an error if the prefab doesn't have a IMixedRealityPointer Component
203-
if (pointer == null)
203+
if (pointer.IsNull())
204204
{
205205
InspectorUIUtility.DrawError($"The prefab associated with this pointer option needs an {typeof(IMixedRealityPointer).Name} component");
206-
207206
GUI.color = MixedRealityInspectorUtility.ErrorColor;
208207
}
209-
// if the prefab does have the component, provide a field to display and edit it's PrioritzedLayerMaskOverrides if it specifies a way to get it
210-
else
211-
{
212-
// sync the pointer option with the prefab
213-
if (pointer.PrioritizedLayerMasksOverride != null)
214-
{
215-
if (prioritizedLayerMasks.arraySize != pointer.PrioritizedLayerMasksOverride.Length)
216-
{
217-
prioritizedLayerMasks.arraySize = pointer.PrioritizedLayerMasksOverride.Length;
218-
}
219-
foreach (LayerMask mask in pointer.PrioritizedLayerMasksOverride)
220-
{
221-
SerializedProperty item = prioritizedLayerMasks.GetArrayElementAtIndex(prioritizedLayerMasks.arraySize - 1);
222-
item.intValue = mask;
223-
}
224-
}
225-
226-
// if after syncing the the pointer option list is still empty, initialize with the global default
227-
// sync the pointer option with the prefab
228-
if (prioritizedLayerMasks.arraySize == 0)
229-
{
230-
for (int j = 0; j < pointingRaycastLayerMasks.arraySize; j++)
231-
{
232-
var mask = pointingRaycastLayerMasks.GetArrayElementAtIndex(j).intValue;
233-
234-
prioritizedLayerMasks.InsertArrayElementAtIndex(prioritizedLayerMasks.arraySize);
235-
SerializedProperty item = prioritizedLayerMasks.GetArrayElementAtIndex(prioritizedLayerMasks.arraySize - 1);
236-
item.intValue = mask;
237-
}
238-
}
239-
}
240208

241209
using (new EditorGUILayout.HorizontalScope())
242210
{
@@ -253,23 +221,36 @@ private void RenderPointerList(SerializedProperty list)
253221

254222
// Ultimately sync the pointer prefab's value with the pointer option's
255223
EditorGUI.BeginChangeCheck();
256-
EditorGUILayout.PropertyField(prioritizedLayerMasks, new GUIContent("Pointer Raycast LayerMasks"), true);
257-
if (EditorGUI.EndChangeCheck() && pointer.PrioritizedLayerMasksOverride != null)
224+
EditorGUILayout.PropertyField(prioritizedLayerMasks, PointerRaycastLayerMaskContent, true);
225+
if (EditorGUI.EndChangeCheck() && pointer.IsNotNull())
258226
{
259227
Undo.RecordObject(pointerPrefab, "Sync Pointer Prefab");
260-
pointer.PrioritizedLayerMasksOverride = new LayerMask[prioritizedLayerMasks.arraySize];
261-
for (int j = 0; j < prioritizedLayerMasks.arraySize; j++)
228+
229+
int prioritizedLayerMasksCount = prioritizedLayerMasks.arraySize;
230+
if (pointer.PrioritizedLayerMasksOverride?.Length != prioritizedLayerMasksCount)
231+
{
232+
pointer.PrioritizedLayerMasksOverride = new LayerMask[prioritizedLayerMasksCount];
233+
}
234+
235+
for (int j = 0; j < prioritizedLayerMasksCount; j++)
262236
{
263237
pointer.PrioritizedLayerMasksOverride[j] = prioritizedLayerMasks.GetArrayElementAtIndex(j).intValue;
264238
}
265-
239+
266240
PrefabUtility.RecordPrefabInstancePropertyModifications(pointerPrefab);
241+
EditorUtility.SetDirty(pointerPrefab);
242+
anyPrefabChanged = true;
267243
}
268244

269245
GUI.color = prevColor;
270246
}
271247
EditorGUILayout.Space();
272248
}
249+
250+
if (anyPrefabChanged)
251+
{
252+
AssetDatabase.SaveAssets();
253+
}
273254
}
274255
}
275256
}

Assets/MRTK/Core/Providers/BaseInputDeviceManager.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,11 @@ private IMixedRealityPointer CreatePointer(ref PointerOption option)
312312
GameObjectExtensions.DestroyGameObject(pointerObject);
313313
}
314314

315-
// make sure we init the pointer with the correct raycast LayerMasks
316-
pointer.PrioritizedLayerMasksOverride = option.PrioritizedLayerMasks;
315+
// Make sure we init the pointer with the correct raycast LayerMasks, if needed
316+
if (pointer.PrioritizedLayerMasksOverride == null || pointer.PrioritizedLayerMasksOverride.Length == 0)
317+
{
318+
pointer.PrioritizedLayerMasksOverride = option.PrioritizedLayerMasks;
319+
}
317320

318321
return pointer;
319322
}

Assets/MRTK/Examples/Demos/EyeTracking/General/Profiles/EyeTrackingDemoPointerProfile.asset

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,37 @@ MonoBehaviour:
3131
handedness: 7
3232
pointerPrefab: {fileID: 1247086986094436, guid: 51e60b8742bc47640923ac9e75ea74e9,
3333
type: 3}
34-
prioritizedLayerMasks: []
34+
prioritizedLayerMasks:
35+
- serializedVersion: 2
36+
m_Bits: 4294967291
3537
- controllerType: 512
3638
handedness: 7
3739
pointerPrefab: {fileID: 1247086986094436, guid: 31d81f88cf3f71d4b8392ded50df3f05,
3840
type: 3}
39-
prioritizedLayerMasks: []
41+
prioritizedLayerMasks:
42+
- serializedVersion: 2
43+
m_Bits: 4294967291
4044
- controllerType: 1024
4145
handedness: 7
4246
pointerPrefab: {fileID: 1507865967819406, guid: 38b548c6a2c270545a383296ad2bc4d5,
4347
type: 3}
44-
prioritizedLayerMasks: []
48+
prioritizedLayerMasks:
49+
- serializedVersion: 2
50+
m_Bits: 4294967291
4551
- controllerType: 1024
4652
handedness: 7
4753
pointerPrefab: {fileID: 1507865967819406, guid: 526b854247016cf47bc5c58e01d82407,
4854
type: 3}
49-
prioritizedLayerMasks: []
55+
prioritizedLayerMasks:
56+
- serializedVersion: 2
57+
m_Bits: 51
5058
- controllerType: 3072
5159
handedness: 7
5260
pointerPrefab: {fileID: 1247086986094436, guid: 039b325c9e8fd0545a0475fd4aa35b10,
5361
type: 3}
54-
prioritizedLayerMasks: []
62+
prioritizedLayerMasks:
63+
- serializedVersion: 2
64+
m_Bits: 4294967291
5565
pointerMediator:
5666
reference: Microsoft.MixedReality.Toolkit.Input.DefaultPointerMediator, Microsoft.MixedReality.Toolkit.SDK
5767
primaryPointerSelector:

0 commit comments

Comments
 (0)