Tilt Five™ Unity API  1.4.1
TiltFiveManager.cs
Go to the documentation of this file.
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  */
16 using System;
17 using System.Collections;
18 using UnityEngine;
19 
20 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
21 using UnityEngine.InputSystem;
22 using UnityEngine.InputSystem.Users;
23 #endif
24 
25 using TiltFive;
26 using TiltFive.Logging;
27 
28 namespace 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.")]
68  [Obsolete("secondaryWandSettings is deprecated, please update to use left/right based on user preference instead.")]
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
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 
147 
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
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  {
270  {
271  try
272  {
273  ServiceCompatibility compatibility = SystemControl.GetServiceCompatibility();
274  bool needsUpdate = compatibility == ServiceCompatibility.Incompatible;
275 
276  if (needsUpdate)
277  {
279  {
280  Log.Warn("Incompatible Tilt Five service. Please update driver package.");
282  }
283  }
284  else
285  {
286  // Not incompatible. Reset the incompatibility warning.
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);
297  }
298  catch (System.Exception e)
299  {
300  Log.Error(e.Message);
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 
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 
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.
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  {
461 
462  if (scaleSettings != null)
463  {
465  }
466 
467  if (leftWandSettings != null)
468  {
470  }
471  if (rightWandSettings != null)
472  {
474  }
475 
476  if (playerSettings != null)
477  {
479  }
480 
481  if (spectatorSettings != null)
482  {
484  }
485  }
486 
490  void OnDrawGizmos()
491  {
492  if (enabled && gameBoardSettings.currentGameBoard != null)
493  {
495  }
496  }
497 
498 #endif
499 
500  #region ISceneInfo Implementation
501 
502  public float GetScaleToUWRLD_UGBD()
503  {
505  }
506 
507  public Pose GetGameboardPose()
508  {
509  return new Pose(gameBoardSettings.gameBoardCenter, Quaternion.Euler(gameBoardSettings.gameBoardRotation));
510  }
511 
512  public Camera GetEyeCamera()
513  {
514  return Glasses.GetLeftEye(PlayerIndex.One);
515  }
516 
518  {
519  return 1; // TODO: Change this if we decide to include spectators with TiltFiveManager
520  }
521 
522  public bool IsActiveAndEnabled()
523  {
524  return isActiveAndEnabled;
525  }
526 
527  #endregion ISceneInfo Implementation
528  }
529 
530 }
Vector3 gameBoardRotation
The game board rotation or focal rotational offset.
Vector3 gameBoardCenter
The game board position or focal position offset.
GameBoard currentGameBoard
The game board is the window into the game world, as well as the origin about which the glasses/wand ...
float gameBoardScale
The game board's scale multiplies the perceived size of objects in the scene.
The Glasses API and runtime.
Definition: Glasses.cs:35
static Camera GetLeftEye(PlayerIndex playerIndex)
Gets the left eye camera for the specified player's glasses.
Definition: Glasses.cs:268
static bool TryGetPreviewPose(PlayerIndex playerIndex, out Pose pose)
Attempts to get the position and orientation of the specified player's glasses, smoothed for on-scree...
Definition: Glasses.cs:233
static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings=null, PlayerIndex playerIndex=PlayerIndex.None)
Reset this T:TiltFive.Glasses.
Definition: Glasses.cs:317
static GameObject GetPoseRoot(PlayerIndex playerIndex)
Gets the pose root GameObject for the specified player.
Definition: Glasses.cs:254
GlassesSettings encapsulates all configuration data used by the Glasses' tracking runtime to compute ...
Camera cameraTemplate
The camera used as a template for creating the eye cameras at runtime.
GlassesMirrorMode glassesMirrorMode
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 scaleSettings
GameBoardSettings gameboardSettings
WandSettings leftWandSettings
GlassesSettings glassesSettings
WandSettings rightWandSettings
ScaleSettings contains the scale data used to translate between Unity units and the user's physical s...
const float MIN_CONTENT_SCALE_RATIO
float contentScaleRatio
The scaling ratio relates physical distances to world-space units.
float GetScaleToUWRLD_UGBD(float gameboardScale)
PlayerIndex spectatedPlayer
The player that will have their perspective mirrored on screen.
GlassesMirrorMode glassesMirrorMode
The spectator camera will display content on screen depending on the mirroring mode....
Camera spectatorCamera
The camera used for rendering the onscreen preview
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.
void OnEnable()
Called when the GameObject is enabled.
override void Awake()
Awake this instance.
WandSettings rightWandSettings
The wand runtime configuration data for the right hand wand.
void Update()
Update this instance.
WandSettings secondaryWandSettings
void LateUpdate()
Update this instance after all components have finished executing their Update() functions.
void GetLatestPoseData()
Obtains the latest pose for all trackable objects.
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.
WandSettings primaryWandSettings
The Wand API and runtime.
Definition: Wand.cs:56
static void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, PlayerIndex playerIndex=PlayerIndex.One)
Definition: Wand.cs:305
Wand Settings encapsulates all configuration data used by the Wand's tracking runtime to compute the ...
Definition: Wand.cs:38
ControllerIndex controllerIndex
Definition: Wand.cs:39
Definition: Log.cs:21
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)