Tanoda
InteractionManagerEditor.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
9using System.Collections;
10using System.Collections.Generic;
11using UnityEngine;
12using UnityEditor;
13using Leap.Unity.Query;
16
17namespace Leap.Unity.Interaction {
18
19 [CustomEditor(typeof(InteractionManager))]
20 public class InteractionManagerEditor : CustomEditorBase<InteractionManager> {
21
22 protected override void OnEnable() {
23 base.OnEnable();
24
25 // Interaction Controllers
26 specifyCustomDrawer("_interactionControllers", drawControllersStatusEditor);
27
28 // Layers
29 SerializedProperty autoGenerateLayerProperty = serializedObject.FindProperty("_autoGenerateLayers");
30 specifyConditionalDrawing(() => autoGenerateLayerProperty.boolValue,
31 "_templateLayer");
32 specifyConditionalDrawing(() => !autoGenerateLayerProperty.boolValue,
33 "_interactionLayer",
34 "_interactionNoContactLayer",
35 "_contactBoneLayer");
36 specifyCustomDecorator("_interactionLayer", drawInteractionLayerDecorator);
37
38 specifyCustomDecorator("_drawControllerRuntimeGizmos", drawControllerRuntimeGizmoDecorator);
39 specifyCustomPostDecorator("_drawControllerRuntimeGizmos", drawPostControllerRuntimeGizmoDecorator);
40 }
41
42 public override void OnInspectorGUI() {
43 base.OnInspectorGUI();
44 }
45
46 public override bool RequiresConstantRepaint() {
47 return Application.isPlaying;
48 }
49
50 private void drawInteractionLayerDecorator(SerializedProperty property) {
51 if (!Physics.GetIgnoreLayerCollision(target.interactionNoContactLayer.layerIndex,
52 target.contactBoneLayer.layerIndex)) {
53 EditorGUILayout.HelpBox("The No Contact layer should NOT collide with the Contact "
54 + "Bone layer. (Check your layer collision settings in Edit "
55 + "/Project Settings/Physics.)", MessageType.Error);
56 }
57
58 if (Physics.GetIgnoreLayerCollision(target.interactionLayer.layerIndex,
59 target.contactBoneLayer.layerIndex)) {
60 EditorGUILayout.HelpBox("The Interaction layer should collide with the Contact "
61 + "Bone layer. (Check your layer collision settings in Edit "
62 + "/Project Settings/Physics.)", MessageType.Error);
63 }
64 }
65
66 private RuntimeGizmoManager _runtimeGizmoManager;
67
68 private void drawControllerRuntimeGizmoDecorator(SerializedProperty property) {
69 if (property.boolValue && _runtimeGizmoManager == null) {
70 _runtimeGizmoManager = FindObjectOfType<RuntimeGizmoManager>();
71
72 if (_runtimeGizmoManager == null) {
73 EditorGUILayout.Space();
74 EditorGUILayout.Space();
75 EditorGUILayout.HelpBox("Draw Controller Runtime Gizmos is checked, but there "
76 + "is no RuntimeGizmoManager in your scene, or it is "
77 + "disabled.", MessageType.Warning);
78 }
79 }
80 }
81
82 private void drawPostControllerRuntimeGizmoDecorator(SerializedProperty property) {
83 if (property.boolValue && _runtimeGizmoManager != null) {
85 }
86 }
87
89 EditorGUILayout.Space();
90 EditorGUILayout.LabelField("Controller Gizmos Legend", EditorStyles.boldLabel);
91
92 EditorGUI.BeginDisabledGroup(true);
93
94 EditorGUILayout.ColorField(new GUIContent("Contact Bone Colliders",
95 "The gizmo color for contact bone colliders "
96 + "when soft contact is disabled."),
98 EditorGUILayout.ColorField(new GUIContent("Soft Contact Bone Colliders",
99 "The gizmo color for contact bones colliders "
100 + "when soft contact is enabled."),
101 InteractionController.GizmoColors.SoftContactBone);
102
103 EditorGUILayout.Space();
104 EditorGUILayout.ColorField(new GUIContent("Hover Points",
105 "The gizmo color for hover points. Gizmo "
106 + "does not reflect the actual hover radius."),
108 EditorGUILayout.ColorField(new GUIContent("Primary Hover Points",
109 "The gizmo color for primary hover points."),
110 InteractionController.GizmoColors.PrimaryHoverPoint);
111
112 EditorGUILayout.Space();
113 EditorGUILayout.ColorField(new GUIContent("Grasp Points",
114 "The gizmo color for grasp points. "
115 + "InteractionHands do not use grasp points, "
116 + "so no gizmo is drawn for them."),
118 EditorGUILayout.ColorField(new GUIContent("Graspable Objects",
119 "The gizmo color for the wire sphere "
120 + "that appears at objects when they are "
121 + "graspable by an interaction controller. "),
123
124 EditorGUI.EndDisabledGroup();
125 }
126
127 private void drawControllersStatusEditor(SerializedProperty property) {
128 EditorGUILayout.Space();
129 EditorGUILayout.LabelField("Interaction Controller Status", EditorStyles.boldLabel);
130
131 if (target.interactionControllers.Count == 0) {
132 EditorGUILayout.HelpBox("This Interaction Manager has no interaction controllers "
133 + "assigned to it. Please add at least one InteractionHand "
134 + "or an InteractionVRController as a child of this object.",
135 MessageType.Warning);
136
137 return;
138 }
139
140 EditorGUILayout.BeginVertical();
141
142 _leftVRNodeController = null;
143 _rightVRNodeController = null;
144 foreach (var controller in target.interactionControllers) {
145 EditorGUILayout.BeginHorizontal();
146
147 drawControllerStatusEditor(controller);
148
149 EditorGUILayout.EndHorizontal();
150 }
151
152 EditorGUILayout.EndVertical();
153 }
154
155 public static class Colors {
156 public static Color DarkGray { get { return new Color(0.4F, 0.4F, 0.4F); } }
157 public static Color LightGray { get { return new Color(0.7F, 0.7F, 0.7F); } }
158
159 public static Color Good { get { return Color.Lerp(Color.green, LightGray, 0.2F); } }
160 public static Color Caution { get { return Color.Lerp(Good, Color.yellow, 0.8F); } }
161 public static Color Warning { get { return Color.Lerp(Color.yellow, Problem, 0.5F); } }
162 public static Color Problem { get { return Color.Lerp(Color.red, Color.yellow, 0.3F); } }
163 }
164
165 private struct ControllerStatusMessage {
166 public string message;
167 public string tooltip;
168 public Color color;
169 }
170 private List<ControllerStatusMessage> statusMessagesBuffer = new List<ControllerStatusMessage>();
171
172 private void drawControllerStatusEditor(InteractionController controller) {
173 // Controller object
174 EditorGUI.BeginDisabledGroup(true);
175 EditorGUILayout.ObjectField(controller, typeof(InteractionController), true);
176 EditorGUI.EndDisabledGroup();
177
178 // Status
179 var messages = statusMessagesBuffer;
180 messages.Clear();
181
182 // Check various states and add messages to the messages buffer.
183 checkTrackingStatus(controller, messages);
184
185 if (controller.intHand != null) {
186 checkInteractionHandStatus(controller.intHand, messages);
187 }
188 else if (controller is InteractionXRController) {
189 checkInteractionVRControllerStatus(controller as InteractionXRController, messages);
190 }
191
192 // Render the status messages.
193 Rect statusMessagesRect = EditorGUILayout.BeginVertical(GUILayout.MinHeight(20));
194
195 EditorGUI.DrawRect(statusMessagesRect, Colors.DarkGray);
196 statusMessagesRect = statusMessagesRect.ShrinkOne();
197 EditorGUI.DrawRect(statusMessagesRect, Colors.LightGray);
198 statusMessagesRect = statusMessagesRect.ShrinkOne();
199 EditorGUI.DrawRect(statusMessagesRect, Colors.DarkGray);
200
201 if (messages.Count == 0) {
202 messages.Add(new ControllerStatusMessage() {
203 message = "No Status Messages",
204 tooltip = "",
205 color = Colors.Good
206 });
207 }
208
209 foreach (var statusMessage in messages) {
210 var messageColorStyle = new GUIStyle(EditorStyles.label);
211 messageColorStyle.normal.textColor = statusMessage.color;
212
213 EditorGUILayout.LabelField(new GUIContent("[" + statusMessage.message + "]",
214 statusMessage.tooltip),
215 messageColorStyle);
216 GUILayout.Space(1);
217 }
218 GUILayout.Space(1);
219
220 EditorGUILayout.EndVertical();
221 }
222
223 private void checkTrackingStatus(InteractionController controller,
224 List<ControllerStatusMessage> messages) {
225 if (Application.isPlaying) {
226 if (controller.isTracked) {
227 if (controller.isBeingMoved) {
228 messages.Add(new ControllerStatusMessage() {
229 message = "Tracked",
230 tooltip = "This interaction controller is currently being tracked.",
231 color = Colors.Good
232 });
233 } else {
234 messages.Add(new ControllerStatusMessage() {
235 message = "Not Moving",
236 tooltip = "This interaction controller is currently not being moved.",
237 color = Colors.Caution
238 });
239 }
240 } else {
241 messages.Add(new ControllerStatusMessage() {
242 message = "Untracked",
243 tooltip = "This interaction controller is not currently being tracked.",
244 color = Colors.Warning
245 });
246 }
247 }
248 }
249
250 private LeapProvider _provider = null;
251
252 private void checkInteractionHandStatus(InteractionHand intHand,
253 List<ControllerStatusMessage> messages) {
254 if (!Application.isPlaying) {
255 // Check for valid InteractionHand data state.
256 if (intHand.handDataMode == HandDataMode.Custom) {
257 messages.Add(new ControllerStatusMessage() {
258 message = "HandDataMode: Custom",
259 tooltip = "This interaction hand has its data mode set to Custom. "
260 + "A custom script will be required to ensure hand data gets to "
261 + "the interaction hand properly. Upon pressing play, an error will "
262 + "be raised by the hand itself if it is misconfigured.",
263 color = Colors.Caution
264 });
265 }
266 else {
267 // Check for a LeapProvider in the scene somewhere.
268 if (_provider == null) {
269 _provider = FindObjectOfType<LeapProvider>();
270 }
271 if (_provider == null) {
272 messages.Add(new ControllerStatusMessage() {
273 message = "No LeapProvider",
274 tooltip = "No LeapProvider object was found in your scene. "
275 + "InteractionHands require a LeapProvider to function; consider "
276 + "dragging in the LeapHeadMountedRig prefab or creating and "
277 + "configuring a LeapServiceProvider.",
278 color = Colors.Warning
279 });
280 }
281 }
282 }
283
284 // Check if the player has multiple left hands or multiple right hands.
285 if (intHand.handDataMode != HandDataMode.Custom) {
286 int index = target.interactionControllers.Query().IndexOf(intHand);
287
288 if (target.interactionControllers.Query().
289 Take(index).
290 OfType<InteractionHand>().
291 Where(h => h.handDataMode == intHand.handDataMode).
292 Where(h => h.leapProvider == intHand.leapProvider).
293 Any()) {
294 messages.Add(new ControllerStatusMessage() {
295 message = "Duplicate Hand",
296 tooltip = "You already have a hand with this data mode in your scene. "
297 + "You should remove one of the duplicates.",
298 color = Colors.Problem
299 });
300 }
301 }
302 }
303
304 private InteractionXRController _leftVRNodeController;
305 private InteractionXRController _rightVRNodeController;
306
307 private void checkInteractionVRControllerStatus(InteractionXRController controller,
308 List<ControllerStatusMessage> messages) {
309 // Check if the controller is configured correctly if it is set up with a custom
310 // tracking provider.
311 if (controller.isUsingCustomTracking) {
312 messages.Add(new ControllerStatusMessage() {
313 message = "Custom Tracking Provider",
314 tooltip = "You are using a custom tracking provider for this VR controller.",
315 color = Colors.Caution
316 });
317 }
318
319 // Check if the player has duplicate VRNode left controllers or right controllers.
320 bool isLeftVRNodeController = controller.trackingProvider is DefaultXRNodeTrackingProvider
321 && controller.chirality == Chirality.Left;
322 bool isRightVRNodeController = controller.trackingProvider is DefaultXRNodeTrackingProvider
323 && controller.chirality == Chirality.Right;
324
325 if (isLeftVRNodeController && _leftVRNodeController != null
326 || isRightVRNodeController && _rightVRNodeController != null) {
327
328 var alreadyExistsController = isLeftVRNodeController ? _leftVRNodeController : _rightVRNodeController;
329
330 string message;
331 string tooltip;
332 Color color;
333
334 if (controller.deviceJoystickTokens.Equals(alreadyExistsController.deviceJoystickTokens)) {
335 message = "Duplicate VR Controller";
336 tooltip = "You already have a VRNode controller with this chirality and device "
337 + "string in your scene. You should remove one of the duplicates.";
338 color = Colors.Problem;
339 } else {
340 message = "Multiple VR Controllers";
341 tooltip = "You have multiple VR controllers of the same chirality enabled with "
342 + "different device strings. If both device strings match attached "
343 + "Unity joysticks, you may get duplicate controllers.";
344 color = Colors.Caution;
345 }
346
347 messages.Add(new ControllerStatusMessage() {
348 message = message,
349 tooltip = tooltip,
350 color = color
351 });
352 }
353
354 if (isLeftVRNodeController) {
355 _leftVRNodeController = controller;
356 }
357 if (isRightVRNodeController) {
358 _rightVRNodeController = controller;
359 }
360
361 string wrongChiralityToken = controller.isLeft ? "right" : "left";
362 if (controller.deviceJoystickTokens.Contains(wrongChiralityToken)) {
363 messages.Add(new ControllerStatusMessage() {
364 message = "Wrong Chirality?",
365 tooltip = "This VR controller's device joystick string specifies a chirality "
366 + "that is different from the chirality of the controller itself. You "
367 + "should confirm this controller's device string or chirality setting.",
368 color = Colors.Warning
369 });
370 }
371
372 }
373
374 }
375
376 public static class Extensions {
377
378 public static Rect ShrinkOne(this Rect rect) {
379 rect.x += 1;
380 rect.y += 1;
381 rect.width -= 2;
382 rect.height -= 2;
383 return rect;
384 }
385
386 }
387
388}
UnityEngine.Color Color
Definition: TestScript.cs:32
void specifyConditionalDrawing(string conditionalName, params string[] dependantProperties)
Specify a list of properties that should only be displayed if the conditional property has a value of...
void specifyCustomPostDecorator(string propertyName, Action< SerializedProperty > decoratorDrawer)
Specify a callback to be used to draw a decorator AFTER a specific named property.
void specifyCustomDrawer(string propertyName, Action< SerializedProperty > propertyDrawer)
Specify a callback to be used to draw a specific named property. Should be called in OnEnable.
void specifyCustomDecorator(string propertyName, Action< SerializedProperty > decoratorDrawer)
Specify a callback to be used to draw a decorator for a specific named property. Should be called in ...