Unity SDK Docs 1.5.0-beta.6
Loading...
Searching...
No Matches
TiltFiveManager.cs
1/*
2 * Copyright (C) 2020-2023 Tilt Five, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16using System;
17using System.Collections;
18using UnityEngine;
19
20#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
21using UnityEngine.InputSystem;
22using UnityEngine.InputSystem.Users;
23#endif
24
25using TiltFive;
27
28namespace TiltFive
29{
30
34 [DisallowMultipleComponent]
35#if !UNITY_2019_1_OR_NEWER || !INPUTSYSTEM_AVAILABLE
36 // Workaround to enable inputs to be collected before other scripts execute their Update() functions.
37 // This is unnecessary if we're using the Input System's OnBeforeUpdate() to collect fresh inputs.
38 [DefaultExecutionOrder(-500)]
39#else
40 // If the Input System's OnBeforeUpdate is available, set TiltFiveManager's execution order to be very late.
41 // This is desirable in two similar scenarios:
42 // - Our Update() executes last, providing the freshest pose data possible to any scripts using LateUpdate().
43 // - Our LateUpdate() executes last, providing the freshest pose data possible before we render to the glasses.
44 [DefaultExecutionOrder(500)]
45#endif
46 public class TiltFiveManager : TiltFive.SingletonComponent<TiltFiveManager>, ISceneInfo
47 {
52
57
62
63 // TODO: Make {left,right}WandSettings into the members actually holding the data. These are
64 // kept for prefab compatibility reasons, but will eventually switch. Please start using
65 // the new names from your own code.
66 [Obsolete("primaryWandSettings is deprecated, please update to use left/right based on user preference instead.")]
67 public WandSettings primaryWandSettings;
68 [Obsolete("secondaryWandSettings is deprecated, please update to use left/right based on user preference instead.")]
69 public WandSettings secondaryWandSettings;
70
75 #pragma warning disable 618 // this is for compatibility; disable obsolete warning
76 get => secondaryWandSettings;
77 set => secondaryWandSettings = value;
78 #pragma warning restore 618
79 }
80
85 #pragma warning disable 618 // this is for compatibility; disable obsolete warning
86 get => primaryWandSettings;
87 set => primaryWandSettings = value;
88 #pragma warning restore 618
89 }
90
95
100
105
106#if UNITY_EDITOR
110 public EditorSettings editorSettings = new EditorSettings();
111
112#endif
113
114 private bool needsDriverUpdateNotifiedOnce = false;
115 private bool needsDriverUpdateErroredOnce = false;
116
117 internal PlayerSettings playerSettings = new PlayerSettings();
118
122 protected override void Awake()
123 {
124 base.Awake();
125
126 // Apply log settings
128 Log.TAG = logSettings.TAG;
129
130 // Store graphics settings
131 graphicsSettings.applicationTargetFramerate = Application.targetFrameRate;
132 graphicsSettings.applicationVSyncCount = QualitySettings.vSyncCount;
133
134 if (!SystemControl.SetPlatformContext())
135 {
136 Log.Warn("Failed to set application context.");
137 enabled = false;
138 }
139
140 if (!SystemControl.SetApplicationInfo())
141 {
142 Log.Warn("Failed to send application info to the T5 Service.");
143 enabled = false;
144 }
145
146 RefreshPlayerSettings();
147
148 spectatorSettings.spectatorCamera = glassesSettings.cameraTemplate;
149 spectatorSettings.glassesMirrorMode = glassesSettings.glassesMirrorMode;
150 spectatorSettings.spectatedPlayer = playerSettings.PlayerIndex;
151 }
152
153#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
157 private void OnBeforeUpdate()
158 {
159#if UNITY_EDITOR
160 if (UnityEditor.EditorApplication.isPaused)
161 {
162 return;
163 }
164#endif
165 if (Player.scanningForPlayers)
166 {
167 return;
168 }
170 Player.ScanForNewPlayers();
171 Wand.GetLatestInputs(); // Should only be executed once per frame
172
173 // OnBeforeUpdate can get called multiple times per frame. Unity seems to not properly utilize the camera positions for rendering
174 // if they are updated after Late Update and before render, causing a disparity between render pose and camera position leading to
175 // shaky displays in the headset. To avoid this, we prevent updating the camera positions during the BeforeRender Input State.
176 if (UnityEngine.InputSystem.LowLevel.InputState.currentUpdateType != UnityEngine.InputSystem.LowLevel.InputUpdateType.BeforeRender)
177 {
178 Update();
179 }
180 }
181#endif
182
186 void Update()
187 {
188#if !UNITY_2019_1_OR_NEWER || !INPUTSYSTEM_AVAILABLE
190 Player.ScanForNewPlayers();
191 Wand.GetLatestInputs(); // Should only be executed once per frame
192#endif
193 RefreshPlayerSettings();
194 RefreshSpectatorSettings();
195 Display.ApplyGraphicsSettings(graphicsSettings);
196 Player.Update(playerSettings, spectatorSettings);
197
198 var spectatedPlayer = spectatorSettings.spectatedPlayer;
199 if (Glasses.TryGetPreviewPose(spectatedPlayer, out var spectatedPlayerPose))
200 {
201 spectatorSettings.spectatorCamera?.transform.SetPositionAndRotation(
202 spectatedPlayerPose.position,
203 spectatedPlayerPose.rotation);
204 }
205
206#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
207 var devices = InputUser.GetUnpairedInputDevices();
208 if (devices.Count > 0)
209 {
210 foreach (InputDevice dev in devices)
211 {
212 if (dev is WandDevice)
213 {
214 var headPoseRoot = Glasses.GetPoseRoot(((WandDevice)dev).playerIndex);
215
216 if (headPoseRoot != null)
217 {
218 var playerInput = headPoseRoot.GetComponentInChildren<PlayerInput>();
219
220 if (playerInput != null && playerInput.user.valid)
221 {
222 Log.Warn($"Unpaired Wand Device [{((WandDevice)dev).ControllerIndex}] found and paired to Player [{((WandDevice)dev).playerIndex}].");
223 InputUser.PerformPairingWithDevice(dev, playerInput.user);
224 playerInput.user.ActivateControlScheme("XR");
225 }
226 }
227 }
228 }
229 }
230#endif
231 }
232
233
237 void LateUpdate()
238 {
239 // Trackables should be updated just before rendering occurs,
240 // after all Update() calls are completed.
241 // This allows any Game Board movements to be finished before we base the
242 // Glasses/Wand poses off of its pose, preventing perceived jittering.
243 Player.Update(playerSettings, spectatorSettings);
244 }
245
249 private void GetLatestPoseData()
250 {
251 Glasses.UpdateAllGlassesCores(glassesSettings, scaleSettings, gameBoardSettings);
254 }
255
267 public bool NeedsDriverUpdate()
268 {
269 if (!needsDriverUpdateErroredOnce)
270 {
271 try
272 {
273 ServiceCompatibility compatibility = SystemControl.GetServiceCompatibility();
274 bool needsUpdate = compatibility == ServiceCompatibility.Incompatible;
275
276 if (needsUpdate)
277 {
278 if (!needsDriverUpdateNotifiedOnce)
279 {
280 Log.Warn("Incompatible Tilt Five service. Please update driver package.");
281 needsDriverUpdateNotifiedOnce = true;
282 }
283 }
284 else
285 {
286 // Not incompatible. Reset the incompatibility warning.
287 needsDriverUpdateNotifiedOnce = false;
288 }
289 return needsUpdate;
290 }
291 catch (System.DllNotFoundException e)
292 {
293 Log.Info(
294 "Could not connect to Tilt Five plugin for compatibility check: {0}",
295 e.Message);
296 needsDriverUpdateErroredOnce = true;
297 }
298 catch (System.Exception e)
299 {
300 Log.Error(e.Message);
301 needsDriverUpdateErroredOnce = true;
302 }
303 }
304
305 // Failed to communicate with Tilt Five plugin at some point, so don't know whether
306 // an update is needed or not. Just say no.
307 return false;
308 }
309
313 private void OnEnable()
314 {
315 try
316 {
317 NativePlugin.SetMaxDesiredGlasses((byte)GetSupportedPlayerCount());
318 }
319 catch (System.DllNotFoundException e)
320 {
321 Log.Info(
322 "Could not connect to Tilt Five plugin for setting max glasses: {0}",
323 e.Message);
324 }
325 catch (System.Exception e)
326 {
327 Log.Error(e.Message);
328 }
329
330 Glasses.Reset(PlayerIndex.None, glassesSettings);
331
332#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
333 InputSystem.onBeforeUpdate += OnBeforeUpdate;
334#endif
335 }
336
337 private void OnDisable()
338 {
339#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
340 InputSystem.onBeforeUpdate -= OnBeforeUpdate;
341#endif
342 Player.OnDisable();
343 }
344
345 private void OnDestroy()
346 {
347 Player.OnDisable();
348 }
349
350 private void OnApplicationQuit()
351 {
352 OnDisable();
353 }
354
355 // There's a longstanding bug where UnityPluginUnload isn't called.
356 // - https://forum.unity.com/threads/unitypluginunload-never-called.414066/
357 // - https://gamedev.stackexchange.com/questions/200118/unity-native-plugin-unitypluginload-is-called-but-unitypluginunload-is-not
358 // - https://issuetracker.unity3d.com/issues/unitypluginunload-is-never-called-in-a-standalone-build
359 // Work around this by invoking it via Application.quitting.
360 private static void Quit()
361 {
362 try
363 {
364 NativePlugin.UnloadWorkaround();
365 }
366 catch (System.DllNotFoundException)
367 {
368 // Nothing to report on quit if the plugin isn't present
369 }
370 catch (System.Exception e)
371 {
372 Log.Error(e.Message);
373 }
374 }
375
376 [RuntimeInitializeOnLoadMethod]
377 private static void RunOnStart()
378 {
379 Application.quitting += Quit;
380 }
381
382 private void RefreshSpectatorSettings()
383 {
384 // Warn developers if they've left the glassesSettings camera template field empty, since it's still required for the original TiltFiveManager
385 if(glassesSettings.cameraTemplate == null)
386 {
387 Log.Warn("No camera template detected in TiltFiveManager's glassesSettings. A camera template is required.");
388 }
389
390 // We don't expose any global settings like SpectatorSettings in TiltFiveManager's custom inspector,
391 // though they're still accessible from scripts.
392 // Just synchronize spectatorSettings from TiltFiveManager's glassesSettings as needed.
393 spectatorSettings.spectatorCamera = glassesSettings.cameraTemplate;
394 spectatorSettings.glassesMirrorMode = glassesSettings.glassesMirrorMode;
395
396 // Make sure that the spectated player isn't set to a player index higher than what TiltFiveManager supports
397 var highestSupportedPlayer = (PlayerIndex)GetSupportedPlayerCount();
398 if (spectatorSettings.spectatedPlayer > highestSupportedPlayer)
399 {
400 Log.Warn($"Invalid spectatorSettings.spectatedPlayer [{spectatorSettings.spectatedPlayer}]. TiltFiveManager only supports one player.");
401 spectatorSettings.spectatedPlayer = highestSupportedPlayer;
402 }
403 }
404
405 private void RefreshPlayerSettings()
406 {
407 /* In an initial implementation of TiltFiveManager's internal PlayerSettings object, we initialized
408 * a new PlayerSettings in Awake() and set its internal settings objects to TiltFiveManager's internal settings objects.
409 *
410 * However, this introduced a bug. The settings values in TiltFiveManager's custom inspector couldn't be
411 * modified when the editor was in play mode, which would be a fairly significant quality of life issue for developers.
412 *
413 * I'm a bit fuzzy on the underlying mechanism, but the issue seemed to be that in the TiltFiveManager's
414 * custom inspector code, the SerializedProperty API (for GlassesSettings, WandSettings, ScaleSettings, etc)
415 * couldn't apply edits to the underlying settings objects if they were owned/shared by multiple parent classes
416 * (e.g. the same GlassesSettings can't be owned by both a TiltFiveManager and a PlayerSettings without
417 * breaking SerializedProperty's ability to modify the GlassesSettings).
418 *
419 * So the fix was to stop sharing.
420 * Instead of building a PlayerSettings internally on Awake() that uses TiltFiveManager's existing settings objects,
421 * we build one that has its own unique internal settings objects, and any time an edit gets made,
422 * OnValidate() does a shallow copy to those objects using RefreshPlayerSettings(). */
423
424 if(playerSettings == null)
425 {
426 return;
427 }
428 playerSettings.PlayerIndex = PlayerIndex.One;
429
430 if (glassesSettings != null)
431 {
432 playerSettings.glassesSettings = glassesSettings.Copy();
433 }
434 if (rightWandSettings != null)
435 {
436 playerSettings.rightWandSettings = rightWandSettings.Copy();
437 }
438 if (leftWandSettings != null)
439 {
440 playerSettings.leftWandSettings = leftWandSettings.Copy();
441 }
442 if (gameBoardSettings != null)
443 {
444 playerSettings.gameboardSettings = gameBoardSettings.Copy();
445 }
446 if (scaleSettings != null)
447 {
448 playerSettings.scaleSettings = scaleSettings.Copy();
449 }
450 }
451
452#if UNITY_EDITOR
453
457 void OnValidate()
458 {
459 Log.LogLevel = logSettings.level;
460 Log.TAG = logSettings.TAG;
461
462 if (scaleSettings != null)
463 {
464 scaleSettings.contentScaleRatio = Mathf.Clamp(scaleSettings.contentScaleRatio, ScaleSettings.MIN_CONTENT_SCALE_RATIO, float.MaxValue);
465 }
466
467 if (leftWandSettings != null)
468 {
469 leftWandSettings.controllerIndex = ControllerIndex.Left;
470 }
471 if (rightWandSettings != null)
472 {
473 rightWandSettings.controllerIndex = ControllerIndex.Right;
474 }
475
476 if (playerSettings != null)
477 {
478 RefreshPlayerSettings();
479 }
480
481 if (spectatorSettings != null)
482 {
483 RefreshSpectatorSettings();
484 }
485 }
486
490 void OnDrawGizmos()
491 {
492 if (enabled && gameBoardSettings.currentGameBoard != null)
493 {
494 gameBoardSettings.currentGameBoard.DrawGizmo(scaleSettings, gameBoardSettings);
495 }
496 }
497
498#endif
499
500 #region ISceneInfo Implementation
501
502 [Obsolete("TiltFiveManager.GetScaleToUWRLD_UGBD is deprecated. Please use TiltFiveManager.GetScaleToWorldSpaceFromGameboardSpace instead.")]
503 public float GetScaleToUWRLD_UGBD() { return GetScaleToWorldSpaceFromGameboardSpace(); }
504
505 public float GetScaleToWorldSpaceFromGameboardSpace()
506 {
507 return scaleSettings.GetScaleToWorldSpaceFromGameboardSpace(gameBoardSettings.gameBoardScale);
508 }
509
510 public Pose GetGameboardPose()
511 {
512 return new Pose(gameBoardSettings.gameBoardCenter, Quaternion.Euler(gameBoardSettings.gameBoardRotation));
513 }
514
515 public Camera GetEyeCamera()
516 {
517 return Glasses.GetLeftEye(PlayerIndex.One);
518 }
519
520 public uint GetSupportedPlayerCount()
521 {
522 return 1; // TODO: Change this if we decide to include spectators with TiltFiveManager
523 }
524
525 public bool IsActiveAndEnabled()
526 {
527 return isActiveAndEnabled;
528 }
529
530 #endregion ISceneInfo Implementation
531 }
532
533}
GlassesSettings encapsulates all configuration data used by the Glasses' tracking runtime to compute ...
GraphicsSettings encapsulates configuration data related to the project's graphics settings,...
The Logger.
Definition Log.cs:42
static void Warn(string m, params object[] list)
WARN logging function call.
Definition Log.cs:166
static void Info(string m, params object[] list)
INFO logging function call.
Definition Log.cs:140
static void Error(string m, params object[] list)
ERROR logging function call.
Definition Log.cs:127
static int LogLevel
Gets or sets the logging level.
Definition Log.cs:68
static string TAG
Gets or sets the logging tag.
Definition Log.cs:58
Provides access to player settings and functionality.
Definition Player.cs:31
ScaleSettings contains the scale data used to translate between Unity units and the user's physical s...
The Tilt Five manager.
LogSettings logSettings
The log settings.
SpectatorSettings spectatorSettings
The spectator camera's runtime configuration data.
GraphicsSettings graphicsSettings
Project-wide graphics settings related to Tilt Five.
GameBoardSettings gameBoardSettings
The game board runtime configuration data.
override void Awake()
Awake this instance.
WandSettings rightWandSettings
The wand runtime configuration data for the right hand wand.
ScaleSettings scaleSettings
The scale conversion runtime configuration data.
bool NeedsDriverUpdate()
Check if a driver update is needed.
GlassesSettings glassesSettings
The glasses runtime configuration data.
WandSettings leftWandSettings
The wand runtime configuration data for the left hand wand.
Wand Settings encapsulates all configuration data used by the Wand's tracking runtime to compute the ...
Definition Wand.cs:38
ServiceCompatibility
Whether the running service is compatible.
ControllerIndex
Since wands are all physically identical (they have no "handedness"), it doesn't make sense to addres...
PlayerIndex
The Player index (e.g. Player One, Player Two, etc)