Tilt Five™ Unity API  1.4.1
Glasses.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 
17 using System;
18 using System.Collections.Generic;
19 using UnityEngine;
20 
21 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
22 using UnityEngine.InputSystem;
23 using UnityEngine.InputSystem.Controls;
24 #endif
25 
26 using TiltFive;
27 using TiltFive.Logging;
28 
29 namespace TiltFive
30 {
34  public sealed class Glasses : Singleton<Glasses>
35  {
36  #region Private Fields
37 
41  private Dictionary<GlassesHandle, GlassesCore> glassesCores = new Dictionary<GlassesHandle, GlassesCore>();
42 
43  private HashSet<GlassesHandle> incomingHandles = new HashSet<GlassesHandle>();
44  private HashSet<GlassesHandle> lostHandles = new HashSet<GlassesHandle>();
45 
49  private GlassesHandle? defaultGlassesHandle;
50 
51  #endregion
52 
53 
54  #region public Enums
55 
56  public enum AREyes
57  {
58  EYE_LEFT = 0,
59  EYE_RIGHT,
60  EYE_MAX,
61  }
62 
63  #endregion
64 
65 
66  #region Public Fields
67 
73  [Obsolete("Glasses.updated is deprecated. Please use Glasses.IsTracked() instead.")]
74  public static bool updated => headPoseUpdated(PlayerIndex.One);
80  [Obsolete("Glasses.configured is deprecated. Please use Player.IsConnected() instead.")]
81  public static bool configured => GetPlayerOneGlassesCore()?.configured ?? false;
86  [Obsolete("Glasses.position is deprecated. Please use Glasses.TryGetPose() and Pose.position instead.")]
87  public static Vector3 position => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.position ?? Vector3.zero;
92  [Obsolete("Glasses.rotation is deprecated. Please use Glasses.TryGetPose() and Pose.rotation instead.")]
93  public static Quaternion rotation => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.rotation ?? Quaternion.identity;
98  [Obsolete("Glasses.forward is deprecated. Please use Glasses.TryGetPose() and Pose.forward instead.")]
99  public static Vector3 forward => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.forward ?? Vector3.forward;
104  [Obsolete("Glasses.right is deprecated. Please use Glasses.TryGetPose() and Pose.right instead.")]
105  public static Vector3 right => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.right ?? Vector3.right;
110  [Obsolete("Glasses.up is deprecated. Please use Glasses.TryGetPose() and Pose.up instead.")]
111  public static Vector3 up => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.up ?? Vector3.up;
112 
117  [Obsolete("Glasses.leftEyePosition is deprecated. Please use Glasses.GetLeftEye() and Transform.position")]
118  public static Vector3 leftEyePosition => GetPlayerOneGlassesCore()?.eyePositions[AREyes.EYE_LEFT] ?? Vector3.zero;
123  [Obsolete("Glasses.rightEyePosition is deprecated. Please use Glasses.GetRightEye() and Transform.position")]
124  public static Vector3 rightEyePosition => GetPlayerOneGlassesCore()?.eyePositions[AREyes.EYE_RIGHT] ?? Vector3.zero;
125 
129  [Obsolete("Glasses.glassesAvailable is deprecated. Please use Player.IsConnected() instead.")]
130  public static bool glassesAvailable { get; private set; }
131 
132  #endregion Public Fields
133 
134 
135  #region Public Functions
136 
142  public static bool IsTracked(PlayerIndex playerIndex = PlayerIndex.One)
143  {
144  return Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
145  && IsTracked(glassesHandle);
146  }
147 
148 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
158  public static bool TryGetGlassesDevice(PlayerIndex playerIndex, out GlassesDevice glassesDevice)
159  {
160  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
161  {
162  glassesDevice = null;
163  return false;
164  }
165 
166  return TryGetGlassesDevice(glassesHandle, out glassesDevice);
167  }
168 #endif // UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
169 
178  public static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
179  {
180  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
181  {
182  friendlyName = null;
183  return false;
184  }
185 
186  return TryGetFriendlyName(glassesHandle, out friendlyName);
187  }
188 
189 #if TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
190  public static bool TrySetProjectorExtrinsicsAdjustment(PlayerIndex playerIndex, float[] args)
191  {
192  if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
193  {
194  return false;
195  }
196  return TrySetProjectorExtrinsicsAdjustment(glassesHandle, args);
197  }
198 
199  public static bool SetSingleEyeMode(PlayerIndex playerIndex, AREyes eye)
200  {
201  if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
202  {
203  return false;
204  }
205  return SetSingleEyeMode(glassesHandle, eye);
206  }
207 #endif // TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
208 
215  public static bool TryGetPose(PlayerIndex playerIndex, out Pose pose)
216  {
217  if(Player.TryGetGlassesHandle(playerIndex, out var glassesHandle) && Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
218  {
219  pose = glassesCore.Pose_UnityWorldSpace;
220  return true;
221  }
222  pose = new Pose();
223  return false;
224  }
225 
233  public static bool TryGetPreviewPose(PlayerIndex playerIndex, out Pose pose)
234  {
235  if (Player.TryGetGlassesHandle(playerIndex, out var glassesHandle) && Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
236  {
237  pose = glassesCore.previewCore.Pose_UnityWorldSpace;
238  return true;
239  }
240  pose = new Pose();
241  return false;
242  }
243 
254  public static GameObject GetPoseRoot(PlayerIndex playerIndex)
255  {
256  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
257  {
258  return null;
259  }
260  return GetPoseRoot(glassesHandle);
261  }
262 
268  public static Camera GetLeftEye(PlayerIndex playerIndex)
269  {
270  if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
271  {
272  return null;
273  }
274  return GetLeftEye(glassesHandle);
275  }
276 
282  public static Camera GetRightEye(PlayerIndex playerIndex)
283  {
284  if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
285  {
286  return null;
287  }
288  return GetRightEye(glassesHandle);
289  }
290 
291  #region Deprecated Public Functions
292 
299  [Obsolete("Glasses.headPoseUpdated is deprecated. Please use Glasses.IsTracked() instead")]
300  public static bool headPoseUpdated(PlayerIndex playerIndex = PlayerIndex.One)
301  {
302  if (playerIndex == PlayerIndex.None || !Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
303  {
304  return false;
305  }
306 
307  return Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
308  && glassesCore.IsTracked;
309  }
310 
316  [Obsolete("Converted to be an internal function.")]
317  public static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null, PlayerIndex playerIndex = PlayerIndex.None)
318  {
319  Reset(playerIndex, glassesSettings, spectatorSettings);
320  }
321 
331  [Obsolete("Converted to be an internal function.")]
332  public static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null, PlayerIndex playerIndex = PlayerIndex.One)
333  {
334  return Validate(playerIndex, glassesSettings, spectatorSettings);
335  }
336 
341  [Obsolete("Converted to be an internal function.")]
342  public static void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
343  {
344  UpdateAllGlassesCores(glassesSettings, scaleSettings, gameBoardSettings);
345  }
346 
352  [Obsolete("Deprecated in favor of Player.IsConnected().")]
353  public static bool IsConnected(PlayerIndex playerIndex = PlayerIndex.One)
354  {
355  return Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
356  && IsConnected(glassesHandle);
357  }
358 
359  [Obsolete("Converted to be an internal function.")]
360  public static void ScanForGlasses() { Scan(); }
361 
362  #endregion Deprecated Public Functions
363 
364 
365  #endregion Public Functions
366 
367 
368  #region Internal Functions
369 
370  internal static void Scan()
371  {
372  // Enumerate the available glasses provided by the native plugin
373  UInt64[] glassesHandles = new UInt64[GlassesSettings.MAX_SUPPORTED_GLASSES_COUNT];
374  Debug.Assert(glassesHandles.Length <= Byte.MaxValue);
375  byte glassesCount = (byte)glassesHandles.Length;
376  int result = NativePlugin.T5_RESULT_SUCCESS;
377 
378  try
379  {
380  var refreshResult = NativePlugin.RefreshGlassesAvailable();
381  result = NativePlugin.GetGlassesHandles(ref glassesCount, glassesHandles);
382  }
383  catch (System.Exception e)
384  {
385  Log.Error($"Error getting glasses handles: {e.Message}");
386  }
387 
388  // Add/Remove entries from the glassesCores dictionary
389  var glassesCores = Instance.glassesCores;
390  var incomingHandles = Instance.incomingHandles;
391  var lostHandles = Instance.lostHandles;
392 
393  incomingHandles.Clear();
394  lostHandles.Clear();
395 
396  // If we ran into a problem getting the glasses handles, all bets are off — just tear down the now-useless glasses cores.
397  if (result != NativePlugin.T5_RESULT_SUCCESS)
398  {
399  foreach (var keyValuePair in glassesCores)
400  {
401  var lostHandle = keyValuePair.Key;
402  var glassesCore = keyValuePair.Value;
403 
404  glassesCore.Dispose();
405  lostHandles.Add(lostHandle);
406  }
407  glassesCores.Clear();
408  }
409  else
410  {
411  // Add newly connected glasses
412  for (int i = 0; i < glassesCount; i++)
413  {
414  incomingHandles.Add(glassesHandles[i]);
415 
416  // If we don't already have a glassesCore for this handle,
417  // and we still have an available player slot, then create a glassesCore.
418  if (!glassesCores.ContainsKey(glassesHandles[i]) && !Player.AllSupportedPlayersConnected())
419  {
420 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
421  glassesCores[glassesHandles[i]] = new GlassesDeviceCore(glassesHandles[i]);
422 #else
423  glassesCores[glassesHandles[i]] = new GlassesCore(glassesHandles[i]);
424 #endif
425  }
426  }
427 
428  // Prune disconnected glasses
429  foreach (var currentHandle in glassesCores.Keys)
430  {
431  if (!incomingHandles.Contains(currentHandle))
432  {
433  lostHandles.Add(currentHandle);
434  }
435  }
436  foreach (var lostHandle in lostHandles)
437  {
438  glassesCores[lostHandle].Dispose();
439  glassesCores.Remove(lostHandle);
440  }
441  }
442 
443  // If we don't have a default glasses ID yet, assign the first one we got from the native plugin as the default.
444  if (!Instance.defaultGlassesHandle.HasValue && result == 0 && glassesCount > 0)
445  {
446  Instance.defaultGlassesHandle = glassesHandles[0];
447  }
448 
449  // If defaultGlassesId isn't valid anymore, reset its value.
450  if (Instance.defaultGlassesHandle.HasValue && !glassesCores.ContainsKey(Instance.defaultGlassesHandle.Value))
451  {
452  Instance.defaultGlassesHandle = null;
453  }
454  }
455 
456  internal static void Reset(PlayerIndex playerIndex, GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null)
457  {
458  if (spectatorSettings == null && !TryGetSpectatorSettings(out spectatorSettings))
459  {
460  Log.Error("Glasses.Reset() could not find any spectator settings.");
461  return;
462  }
463 
464  // If playerIndex is none, reset all glasses
465  if (playerIndex == PlayerIndex.None)
466  {
467  foreach (var glassesCore in Instance.glassesCores.Values)
468  {
469  glassesCore.Reset(glassesSettings, spectatorSettings);
470  }
471  return;
472  }
473 
474  if (Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
475  {
476  Reset(glassesSettings, spectatorSettings, glassesHandle);
477  }
478  }
479 
480  internal static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings, GlassesHandle glassesHandle)
481  {
482  if (Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore) && spectatorSettings != null)
483  {
484  glassesCore.Reset(glassesSettings, spectatorSettings);
485  }
486  }
487 
488  internal static bool Validate(PlayerIndex playerIndex, GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null)
489  {
490  if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
491  {
492  return false;
493  }
494 
495  if (spectatorSettings == null && !TryGetSpectatorSettings(out spectatorSettings))
496  {
497  return false;
498  }
499 
500  return Validate(glassesSettings, spectatorSettings, glassesHandle);
501  }
502 
503  internal static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings, GlassesHandle glassesHandle)
504  {
505  return Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
506  && glassesCore.Validate(glassesSettings, spectatorSettings);
507  }
508 
509  internal static void UpdateAllGlassesCores(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
510  {
511  if (!TryGetSpectatorSettings(out var spectatorSettings))
512  {
513  return;
514  }
515 
516  // Update the glasses cores
517  foreach (var glassesCore in Instance.glassesCores.Values)
518  {
519  glassesCore.Update(glassesSettings, scaleSettings, gameBoardSettings, spectatorSettings);
520  }
521  }
522 
523  internal static void Update(GlassesHandle glassesHandle, GlassesSettings glassesSettings, ScaleSettings scaleSettings,
524  GameBoardSettings gameBoardSettings, SpectatorSettings spectatorSettings)
525  {
526  if (Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
527  {
528  glassesCore.Update(glassesSettings, scaleSettings, gameBoardSettings, spectatorSettings);
529  }
530  }
531 
532  internal static bool IsTracked(GlassesHandle glassesHandle)
533  {
534  return Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
535  && glassesCore.IsTracked;
536  }
537 
538  internal static bool IsConnected(GlassesHandle glassesHandle)
539  {
540  return Instance.glassesCores.ContainsKey(glassesHandle);
541  }
542 
543 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
544  internal static bool TryGetGlassesDevice(GlassesHandle glassesHandle, out GlassesDevice glassesDevice)
545  {
546  if(!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
547  || !(glassesCore is GlassesDeviceCore glassesDeviceCore))
548  {
549  glassesDevice = null;
550  return false;
551  }
552 
553  glassesDevice = glassesDeviceCore.glassesDevice;
554  return true;
555  }
556 #endif // UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
557 
558  internal static bool TryGetFriendlyName(GlassesHandle glassesHandle, out string friendlyName)
559  {
560  if(!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
561  {
562  friendlyName = null;
563  return false;
564  }
565 
566  return glassesCore.TryGetFriendlyName(out friendlyName);
567  }
568 
569 #if TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
570  internal static bool TrySetProjectorExtrinsicsAdjustment(GlassesHandle glassesHandle, float[] args)
571  {
572  if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
573  {
574  return false;
575  }
576  return glassesCore.TrySetProjectorExtrinsicsAdjustment(args);
577  }
578 
579  internal static bool SetSingleEyeMode(GlassesHandle glassesHandle, AREyes eye)
580  {
581  if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
582  {
583  return false;
584  }
585  return glassesCore.SetSingleEyeMode(eye);
586  }
587 #endif // TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
588 
595  internal static GlassesHandle? GetDefaultGlassesHandle()
596  {
597  var defaultGlassesHandle = Instance.defaultGlassesHandle;
598  return defaultGlassesHandle.HasValue && Instance.glassesCores.ContainsKey(defaultGlassesHandle.Value)
599  ? defaultGlassesHandle
600  : null;
601  }
602 
603  internal static void SetDefaultGlassesHandle(GlassesHandle glassesHandle)
604  {
605  Instance.defaultGlassesHandle = glassesHandle;
606  }
607 
608  internal static GlassesHandle[] GetAllConnectedGlassesHandles()
609  {
610  var keys = Instance.glassesCores.Keys;
611  GlassesHandle[] glassesHandles = new GlassesHandle[keys.Count];
612  keys.CopyTo(glassesHandles, 0);
613  return glassesHandles;
614  }
615 
616  internal static void OnDisable()
617  {
618  foreach (var glassesCore in Instance.glassesCores.Values)
619  {
620  glassesCore.Dispose();
621  }
622  Instance.glassesCores.Clear();
623  }
624 
625  #endregion Internal Functions
626 
627  #region Private Functions
628 
629  private static GameObject GetPoseRoot(GlassesHandle glassesHandle)
630  {
631  if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
632  {
633  return null;
634  }
635  return glassesCore.headPose.gameObject;
636  }
637 
638  private static Camera GetLeftEye(GlassesHandle glassesHandle)
639  {
640  if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
641  {
642  return null;
643  }
644  return glassesCore.leftEye;
645  }
646 
647  private static Camera GetRightEye(GlassesHandle glassesHandle)
648  {
649  if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
650  {
651  return null;
652  }
653 
654  return glassesCore.rightEye;
655  }
656 
658  {
659  if(Player.TryGetGlassesHandle(PlayerIndex.One, out var glassesHandle)
660  && Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
661  {
662  return glassesCore;
663  }
664  return null;
665  }
666 
667  private static bool TryGetSpectatorSettings(out SpectatorSettings spectatorSettings)
668  {
669  if (TiltFiveSingletonHelper.TryGetISceneInfo(out var sceneInfo))
670  {
671  if (sceneInfo is TiltFiveManager2 tiltFiveManager2)
672  {
673  spectatorSettings = tiltFiveManager2.spectatorSettings;
674  return true;
675  }
676 
677  if (sceneInfo is TiltFiveManager tiltFiveManager)
678  {
679  spectatorSettings = tiltFiveManager.spectatorSettings;
680  return true;
681  }
682  }
683 
684  spectatorSettings = null;
685  return false;
686  }
687 
688  #endregion
689 
690  private class BaseGlassesCore : TrackableCore<GlassesSettings, T5_GlassesPose>, IDisposable
691  {
692  public GlassesHandle glassesHandle;
693 
694  public GameObject headPoseRoot;
695  public Transform headPose => headPoseRoot.transform;
696 
697  private T5_GlassesPoseUsage glassesPoseUsage;
698 
706  private readonly Vector3 DEFAULT_GLASSES_POSITION_GAME_BOARD_SPACE = new Vector3(0f, 0.5f, -0.5f);
707 
715  private readonly Quaternion DEFAULT_GLASSES_ROTATION_GAME_BOARD_SPACE = Quaternion.Euler(new Vector3(-45f, 0f, 0f));
716 
718  GlassesHandle glassesHandle,
719  T5_GlassesPoseUsage glassesPoseUsage,
720  string name)
721  {
722  this.glassesHandle = glassesHandle;
723  this.glassesPoseUsage = glassesPoseUsage;
724  headPoseRoot = new GameObject(name);
725  }
726 
727  public virtual void Dispose()
728  {
729  GameObject.Destroy(headPoseRoot);
730  }
731 
738  protected override void Update(
739  GlassesSettings glassesSettings,
740  ScaleSettings scaleSettings,
741  GameBoardSettings gameBoardSettings)
742  {
743  // Obtain the latest glasses pose.
744  base.Update(glassesSettings, scaleSettings, gameBoardSettings);
745  }
746 
747  protected override void SetDefaultPoseGameboardSpace(GlassesSettings settings)
748  {
749  pose_UGBD = new Pose(
750  DEFAULT_GLASSES_POSITION_GAME_BOARD_SPACE,
751  DEFAULT_GLASSES_ROTATION_GAME_BOARD_SPACE);
752  }
753 
754  protected override void SetPoseUnityWorldSpace(
755  ScaleSettings scaleSettings,
756  GameBoardSettings gameBoardSettings)
757  {
758  pose_UWRLD = GameboardToWorldSpace(pose_UGBD, scaleSettings, gameBoardSettings);
759  }
760 
761  protected override bool TryCheckConnected(out bool connected)
762  {
763  connected = IsConnected(glassesHandle);
764  return connected;
765  }
766 
767  protected override bool TryGetStateFromPlugin(
768  out T5_GlassesPose glassesPose,
769  out bool poseIsValid,
770  GameBoardSettings gameBoardSettings)
771  {
772  T5_GlassesPose glassesPoseResult = new T5_GlassesPose { };
773 
774  int result = NativePlugin.T5_RESULT_UNKNOWN_ERROR;
775  try
776  {
777  result = NativePlugin.GetGlassesPose(
778  glassesHandle,
779  ref glassesPoseResult,
780  glassesPoseUsage);
781  }
782  catch (System.Exception e)
783  {
784  Log.Error(e.Message);
785  }
786 
787  if (result == NativePlugin.T5_RESULT_SUCCESS)
788  {
789  GameBoard.SetGameboardType(glassesHandle, glassesPoseResult.GameboardType);
790  }
791  else
792  {
793  GameBoard.SetGameboardType(glassesHandle, GameboardType.GameboardType_None);
794  }
795 
796  poseIsValid = result == NativePlugin.T5_RESULT_SUCCESS && glassesPoseResult.GameboardType != GameboardType.GameboardType_None;
797  glassesPose = glassesPoseResult;
798  return result == NativePlugin.T5_RESULT_SUCCESS;
799  }
800 
801  protected override void SetPoseGameboardSpace(
802  in T5_GlassesPose glassesPose,
803  GlassesSettings glassesSettings,
804  ScaleSettings scaleSettings,
805  GameBoardSettings gameboardSettings)
806  {
807  pose_UGBD = GetPoseGameboardSpace(glassesPose);
808  }
809 
810  protected override void SetInvalidPoseGameboardSpace(
811  in T5_GlassesPose glassesPose,
812  GlassesSettings settings,
813  ScaleSettings scaleSettings,
814  GameBoardSettings gameboardSettings)
815  {
816  var newPose_GameboardSpace = GetPoseGameboardSpace(glassesPose);
817 
818  if (!isTracked && settings.RejectUntrackedPositionData)
819  {
820  switch (settings.FailureMode)
821  {
822  case TrackableSettings.TrackingFailureMode.FreezePosition:
823  pose_UGBD = new Pose(pose_UGBD.position, newPose_GameboardSpace.rotation);
824  break;
825  // If we want to freeze both position and rotation when tracking is lost, things are easy - just do nothing.
826  case TrackableSettings.TrackingFailureMode.FreezePositionAndRotation:
827  break;
828  // Otherwise, we may want to keep the legacy behavior of snapping to a default position when tracking is lost.
829  case TrackableSettings.TrackingFailureMode.SnapToDefault:
830  // TODO: Rethink the existence of the preview pose? Is that a separate tracking failure mode?
831  if (settings.usePreviewPose)
832  {
833  pose_UGBD = new Pose(WorldToGameboardSpace(settings.previewPose.position, scaleSettings, gameboardSettings),
834  WorldToGameboardSpace(settings.previewPose.rotation, gameboardSettings));
835  }
836  // Otherwise do nothing and let the developer control the head pose camera themselves.
837  // It will be up to them to let go once head tracking kicks in again.
838  break;
839  }
840  }
841  else // Either things are working well and we're tracked, or we don't care about invalid data and want to display it regardless.
842  {
843  pose_UGBD = newPose_GameboardSpace;
844  }
845  }
846 
847  protected override void SetDrivenObjectTransform(
848  GlassesSettings settings,
849  ScaleSettings scaleSettings,
850  GameBoardSettings gameBoardSettings)
851  {
852  if (!isTracked && settings.RejectUntrackedPositionData)
853  {
854  switch (settings.FailureMode)
855  {
856  case TrackableSettings.TrackingFailureMode.FreezePosition:
857  headPose.SetPositionAndRotation(headPose.position, pose_UWRLD.rotation);
858  break;
859  // If we want to freeze both position and rotation when tracking is lost, things are easy - just do nothing.
860  case TrackableSettings.TrackingFailureMode.FreezePositionAndRotation:
861  break;
862  // Otherwise, we may want to keep the legacy behavior of snapping to a default position when tracking is lost.
863  case TrackableSettings.TrackingFailureMode.SnapToDefault:
864  // TODO: Rethink the existence of the preview pose? Is that a separate tracking failure mode?
865  if (settings.usePreviewPose)
866  {
867  headPose.SetPositionAndRotation(
868  settings.previewPose.position,
869  settings.previewPose.rotation);
870  }
871  // Otherwise do nothing and let the developer control the head pose camera themselves.
872  // It will be up to them to let go once head tracking kicks in again.
873  break;
874  }
875  }
876  else // Either things are working well and we're tracked, or we don't care about invalid data and want to display it regardless.
877  {
878  headPose.SetPositionAndRotation(pose_UWRLD.position, pose_UWRLD.rotation);
879  }
880  }
881 
882  private Pose GetPoseGameboardSpace(T5_GlassesPose glassesPose)
883  {
884  // Unity reference frames:
885  //
886  // UGLS - Unity GLaSses local space.
887  // +x right, +y up, +z forward
888  // UGBD - Unity Gameboard space.
889  // +x right, +y up, +z forward
890  //
891  // Tilt Five reference frames:
892  //
893  // DC - Our right-handed version of Unity's default camera space.
894  // +x right, +y up, +z backward
895  // GBD - Gameboard space.
896  // +x right, +y forward, +z up
897 
898  Quaternion rotToDC_GBD = Quaternion.AngleAxis((-Mathf.PI / 2f) * Mathf.Rad2Deg, Vector3.right);
899 
900  Quaternion rotToGBD_DC = Quaternion.Inverse(rotToDC_GBD);
901 
902  Quaternion rotToGLS_GBD = glassesPose.RotationToGLS_GBD;
903 
904  Quaternion rotToGLS_DC = rotToGLS_GBD * rotToGBD_DC;
905 
906  Quaternion rotToUGBD_UGLS = new Quaternion(rotToGLS_DC.x, rotToGLS_DC.y, -rotToGLS_DC.z, rotToGLS_DC.w);
907 
908  // Swap from right-handed (T5 internal) to left-handed (Unity) coord space.
909  Vector3 posOfUGLS_UGBD = ConvertPosGBDToUGBD(glassesPose.PosOfGLS_GBD);
910 
911  return new Pose(posOfUGLS_UGBD, rotToUGBD_UGLS);
912  }
913  }
914 
923  {
924  public GlassesPreviewCore(GlassesHandle glassesHandle) :
925  base(glassesHandle, T5_GlassesPoseUsage.SpectatorPresentation, $"Glasses {glassesHandle} preview")
926  { }
927 
934  public new void Update(
935  GlassesSettings glassesSettings,
936  ScaleSettings scaleSettings,
937  GameBoardSettings gameBoardSettings)
938  {
939  // Obtain the latest glasses pose.
940  base.Update(glassesSettings, scaleSettings, gameBoardSettings);
941  }
942  }
943 
947  private class GlassesCore : BaseGlassesCore
948  {
950  public GameObject baseObject;
951  public string friendlyName;
952 
956  public bool configured = false;
957 
958  public Dictionary<AREyes, Vector3> eyePositions = new Dictionary<AREyes, Vector3>()
959  {
960  { AREyes.EYE_LEFT, new Vector3() },
961  { AREyes.EYE_RIGHT, new Vector3() }
962  };
963 
964  public Dictionary<AREyes, Quaternion> eyeRotations = new Dictionary<AREyes, Quaternion>()
965  {
966  { AREyes.EYE_LEFT, new Quaternion() },
967  { AREyes.EYE_RIGHT, new Quaternion() }
968  };
969 
971 
972  public GlassesCore(GlassesHandle glassesHandle) :
973  base(glassesHandle, T5_GlassesPoseUsage.GlassesPresentation, $"Glasses {glassesHandle}")
974  {
975  if(!Player.TryGetPlayerIndex(glassesHandle, out playerIndex))
976  {
977  Player.TryAddPlayer(glassesHandle, out playerIndex);
978  }
979  previewCore = new GlassesPreviewCore(glassesHandle);
980  CameraImage.Initialize(glassesHandle);
981 
982  Log.Info($"Glasses {glassesHandle} connected.");
983  }
984 
985  public override void Dispose()
986  {
987  GameObject.Destroy(splitStereoCamera);
988  GameObject.Destroy(baseObject);
989  CameraImage.RemoveCore(glassesHandle);
990  previewCore.Dispose();
991 
992  base.Dispose();
993 
994  Log.Info($"Glasses {glassesHandle} (\"{friendlyName}\") disconnected");
995  }
996 
1000  private SplitStereoCamera splitStereoCamera = null;
1001 
1002  internal Camera leftEye => splitStereoCamera?.leftEyeCamera;
1003  internal Camera rightEye => splitStereoCamera?.rightEyeCamera;
1004 
1010  public virtual void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
1011  {
1012  base.Reset(glassesSettings);
1013 
1014  configured = false;
1015 
1016  if(null == spectatorSettings.spectatorCamera)
1017  {
1018  Log.Error($"Required Camera assignment missing from { spectatorSettings.GetType() }. Check Spectator settings in Tilt Five Manager");
1019  return;
1020  }
1021 
1022 #if UNITY_EDITOR
1023  if (glassesSettings.tiltFiveXR)
1024  {
1025 #endif
1026  //if the splitScreenCamera does not exist already.
1027  if (null == splitStereoCamera)
1028  {
1029  //get the head pose camera's GameObject
1030  GameObject spectatorCameraObject = spectatorSettings.spectatorCamera.gameObject;
1031 
1032  // Initialize the SplitStereoCamera
1033  splitStereoCamera = spectatorCameraObject.AddComponent<SplitStereoCamera>();
1034  if (null == headPoseRoot)
1035  {
1036 
1037  headPoseRoot = new GameObject($"Glasses {glassesHandle}");
1038  previewCore = new GlassesPreviewCore(glassesHandle);
1039  }
1040  if (glassesSettings.objectTemplate && null == baseObject)
1041  {
1042 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1043  if(glassesSettings.objectTemplate.TryGetComponent<PlayerInput>(out var playerInput))
1044  {
1045  Log.Warn("Attaching a PlayerInput component to the Object Template is not recommended," +
1046  " as the Object Template does not persist across scene loads." +
1047  "Consider using the Input Template instead.");
1048  }
1049 #endif //UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1050  baseObject = GameObject.Instantiate(glassesSettings.objectTemplate, headPoseRoot.transform);
1051  baseObject.name = $"{baseObject.transform.parent.name} - Prefab {playerIndex}";
1052  }
1053  splitStereoCamera.Initialize(headPoseRoot, glassesSettings, spectatorSettings);
1054  }
1055 #if UNITY_EDITOR
1056  }
1057 #endif
1058 
1059  TryGetFriendlyName(out friendlyName);
1060 
1061  configured = true;
1062  }
1063 
1070  public bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
1071  {
1072  bool valid = true;
1073 
1074  valid &= splitStereoCamera != null
1075  && glassesSettings.cameraTemplate == splitStereoCamera.cameraTemplate
1076  && spectatorSettings == splitStereoCamera.spectatorSettings;
1077 
1078  return valid;
1079  }
1080 
1081  public bool TryGetFriendlyName(out string friendlyName)
1082  {
1083  T5_StringUTF8 friendlyNameResult = "";
1084  int result = NativePlugin.T5_RESULT_UNKNOWN_ERROR;
1085 
1086  try
1087  {
1088  result = NativePlugin.GetGlassesFriendlyName(glassesHandle, ref friendlyNameResult);
1089  }
1090  catch (System.Exception e)
1091  {
1092  Log.Error($"Error getting friendly name: {e.Message}");
1093  }
1094  finally
1095  {
1096  friendlyName = (result == NativePlugin.T5_RESULT_SUCCESS)
1097  ? friendlyNameResult
1098  : null;
1099 
1100  // Unfortunately we can't use a "using" block for friendlyNameResult
1101  // since "using" parameters are readonly, preventing us from passing it via "ref".
1102  // We do the next best thing with try-finally and dispose of it here.
1103  friendlyNameResult.Dispose();
1104  }
1105 
1106  return result == NativePlugin.T5_RESULT_SUCCESS;
1107  }
1108 
1109 #if TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
1110  public bool TrySetProjectorExtrinsicsAdjustment(float[] args)
1111  {
1112  int result = 1;
1113 
1114  try
1115  {
1116  result = NativePlugin.SetProjectorExtrinsicsAdjustment(glassesHandle, args);
1117  }
1118  catch (System.Exception e)
1119  {
1120  Log.Error($"Error setting projector extrinsics adjustment: ${e.Message}");
1121  }
1122  return result == NativePlugin.T5_RESULT_SUCCESS;
1123  }
1124 
1125  public bool SetSingleEyeMode(AREyes eye)
1126  {
1127  Camera leftEyeCamera = splitStereoCamera.leftEyeCamera;
1128  Camera rightEyeCamera = splitStereoCamera.rightEyeCamera;
1129  if (leftEyeCamera == null || rightEyeCamera == null)
1130  {
1131  Log.Error("Eye camera null during call to SetSingleEyeMode()");
1132  return false;
1133  }
1134 
1135  switch (eye)
1136  {
1137  case AREyes.EYE_LEFT:
1138  {
1139  // Enable only the left eye
1140  leftEyeCamera.enabled = true;
1141  rightEyeCamera.enabled = false;
1142  // Clear the right eye texture
1143  RenderTexture rt = RenderTexture.active;
1144  RenderTexture.active = rightEyeCamera.targetTexture;
1145  GL.Viewport(rightEyeCamera.pixelRect);
1146  GL.Clear(true, true, Color.clear);
1147  RenderTexture.active = rt;
1148  break;
1149  }
1150  case AREyes.EYE_RIGHT:
1151  {
1152  // Enable only the right eye
1153  leftEyeCamera.enabled = false;
1154  rightEyeCamera.enabled = true;
1155  // Clear the left eye texture
1156  RenderTexture rt = RenderTexture.active;
1157  RenderTexture.active = leftEyeCamera.targetTexture;
1158  GL.Viewport(leftEyeCamera.pixelRect);
1159  GL.Clear(true, true, Color.clear);
1160  RenderTexture.active = rt;
1161  break;
1162  }
1163  case AREyes.EYE_MAX:
1164  leftEyeCamera.enabled = true;
1165  rightEyeCamera.enabled = true;
1166  break;
1167  }
1168  return true;
1169  }
1170 #endif // TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
1171 
1176  public virtual void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, SpectatorSettings spectatorSettings)
1177  {
1178  if (null == glassesSettings)
1179  {
1180  Log.Error("GlassesSettings configuration required for Glasses tracking Update.");
1181  return;
1182  }
1183 
1184  if (null == splitStereoCamera)
1185  {
1186  Log.Error($"Stereo camera(s) missing from Glasses {glassesHandle} - aborting Update.");
1187  return;
1188  }
1189 
1190  if (glassesSettings.cameraTemplate != splitStereoCamera.cameraTemplate)
1191  {
1192  Log.Warn("Found mismatched template Cameras in GlassesCore Update - should call Reset.");
1193  return;
1194  }
1195 
1196  if (spectatorSettings != splitStereoCamera.spectatorSettings)
1197  {
1198  Log.Warn("Found mismatched spectator settings in GlassesCore Update - should call Reset.");
1199  return;
1200  }
1201 
1202  // Obtain the latest glasses poses.
1203  base.Update(glassesSettings, scaleSettings, gameBoardSettings);
1204  previewCore.Update(glassesSettings, scaleSettings, gameBoardSettings);
1205 
1206  // Check whether the glasses are plugged in and available.
1207  splitStereoCamera.glassesHandle = glassesHandle;
1208  splitStereoCamera.enabled = isConnected;
1209 
1210  // Sync settings with splitStereoCamera
1211  splitStereoCamera.glassesSettings = glassesSettings;
1212  splitStereoCamera.spectatorSettings = spectatorSettings;
1213 
1214  // Enable spectating for player specified in the spectator settings, if they're available.
1215  splitStereoCamera.UseSpectatorCamera = Player.IsConnected(spectatorSettings.spectatedPlayer)
1216  && Player.TryGetGlassesHandle(spectatorSettings.spectatedPlayer, out var spectatorGlassesHandle)
1217  && spectatorGlassesHandle == glassesHandle;
1218 
1219  // Get the glasses pose in Unity world-space.
1220  float scaleToUWRLD_UGBD = scaleSettings.GetScaleToUWRLD_UGBD(gameBoardSettings.gameBoardScale);
1221  float scaleToUGBD_UWRLD = 1.0f / scaleToUWRLD_UGBD;
1222 
1223  // Set the game board transform on the SplitStereoCamera.
1224  splitStereoCamera.posUGBD_UWRLD = gameboardPos_UWRLD.position;
1225  splitStereoCamera.rotToUGBD_UWRLD = gameboardPos_UWRLD.rotation;
1226  splitStereoCamera.scaleToUGBD_UWRLD = scaleToUGBD_UWRLD;
1227 
1228  // TODO: Revisit native XR support.
1229 
1230  // NOTE: We do this because "Mock HMD" in UNITY_2017_0_2_OR_NEWER
1231  // the fieldOfView is locked to 111.96 degrees (Vive emulation),
1232  // so setting custom projection matrices is broken. If Unity
1233  // opens the API to custom settings, we can go back to native XR
1234  // support.
1235 
1236  // Manual split screen 'new glasses' until the day Unity lets
1237  // me override their Mock HMD settings.
1238 
1239  // compute half ipd translation
1240  float ipd_UGBD = GlassesSettings.DEFAULT_IPD_UGBD;
1241  if(!Display.GetGlassesIPD(glassesHandle, ref ipd_UGBD) && isConnected)
1242  {
1243  Log.Error("Failed to obtain Glasses IPD");
1244  }
1245  float ipd_UWRLD = scaleToUWRLD_UGBD * ipd_UGBD;
1246  Vector3 eyeOffset = (headPose.right.normalized * (ipd_UWRLD * 0.5f));
1247 
1248  // set the left eye camera offset from the head by the half ipd amount (-)
1249  eyePositions[AREyes.EYE_LEFT] = headPose.position - eyeOffset;
1250  eyeRotations[AREyes.EYE_LEFT] = headPose.rotation;
1251 
1252  // set the right eye camera offset from the head by the half ipd amount (+)
1253  eyePositions[AREyes.EYE_RIGHT] = headPose.position + eyeOffset;
1254  eyeRotations[AREyes.EYE_RIGHT] = headPose.rotation;
1255 
1256  Camera leftEyeCamera = splitStereoCamera.leftEyeCamera;
1257  if (null != leftEyeCamera)
1258  {
1259  GameObject leftEye = leftEyeCamera.gameObject;
1260  leftEye.transform.position = eyePositions[AREyes.EYE_LEFT];
1261  leftEye.transform.rotation = eyeRotations[AREyes.EYE_LEFT];
1262 
1263  //make sure projection fields are synchronized to the head camera.
1264  leftEyeCamera.nearClipPlane = glassesSettings.nearClipPlane / scaleToUGBD_UWRLD;
1265  leftEyeCamera.farClipPlane = glassesSettings.farClipPlane / scaleToUGBD_UWRLD;
1266  leftEyeCamera.fieldOfView = glassesSettings.fieldOfView;
1267  leftEyeCamera.cullingMask = glassesSettings.cullingMask;
1268  }
1269 
1270  Camera rightEyeCamera = splitStereoCamera.rightEyeCamera;
1271  if (null != rightEyeCamera)
1272  {
1273  GameObject rightEye = rightEyeCamera.gameObject;
1274  rightEye.transform.position = eyePositions[AREyes.EYE_RIGHT];
1275  rightEye.transform.rotation = eyeRotations[AREyes.EYE_RIGHT];
1276 
1277  //make sure projection fields are synchronized to the head camera.
1278  rightEyeCamera.nearClipPlane = glassesSettings.nearClipPlane / scaleToUGBD_UWRLD;
1279  rightEyeCamera.farClipPlane = glassesSettings.farClipPlane / scaleToUGBD_UWRLD;
1280  rightEyeCamera.fieldOfView = glassesSettings.fieldOfView;
1281  rightEyeCamera.cullingMask = glassesSettings.cullingMask;
1282  }
1283 
1284  // TODO: Poll less frequently by plumbing t5_hmdGetChangedParams up to Unity.
1285  if (!TryGetFriendlyName(out glassesSettings.friendlyName))
1286  {
1288  }
1289  }
1290 
1291  protected override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
1292  {
1293  base.SetDrivenObjectTransform(settings, scaleSettings, gameBoardSettings);
1294  }
1295  }
1296 
1297 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1298  private class GlassesDeviceCore : GlassesCore, IDisposable
1299  {
1300  internal GlassesDevice glassesDevice;
1301  internal GameObject playerTemplateObject;
1302 
1303  private enum TrackingState : int
1304  {
1305  None,
1306  Limited,
1307  Tracking
1308  }
1309 
1310  public GlassesDeviceCore(GlassesHandle glassesHandle) : base(glassesHandle)
1311  {
1312  Input.AddGlassesDevice(playerIndex);
1313  glassesDevice = Input.GetGlassesDevice(playerIndex);
1314 
1315  if(glassesDevice != null && glassesDevice.added)
1316  {
1317  InputSystem.EnableDevice(glassesDevice);
1318  }
1319  if(TiltFiveManager2.IsInstantiated)
1320  {
1321  TiltFiveManager2.Instance.RefreshInputDevicePairings();
1322  }
1323  }
1324 
1325  public override void Dispose()
1326  {
1327  base.Dispose();
1328 
1329  if(glassesDevice != null && glassesDevice.added)
1330  {
1331  if (glassesDevice.LeftWand != null)
1332  {
1333  Input.RemoveWandDevice(playerIndex, ControllerIndex.Left);
1334  }
1335  if (glassesDevice.RightWand != null)
1336  {
1337  Input.RemoveWandDevice(playerIndex, ControllerIndex.Right);
1338  }
1339  InputSystem.DisableDevice(glassesDevice);
1340  Input.RemoveGlassesDevice(playerIndex);
1341  }
1342  GameObject.Destroy(playerTemplateObject);
1343  }
1344 
1345  public override void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
1346  {
1347  base.Reset(glassesSettings, spectatorSettings);
1348 
1349  if(glassesSettings.playerTemplate)
1350  {
1351  playerTemplateObject = GameObject.Instantiate(glassesSettings.playerTemplate);
1352  if(TiltFiveManager2.IsInstantiated)
1353  {
1354  TiltFiveManager2.Instance.RefreshInputDevicePairings();
1355  }
1356  }
1357  }
1358 
1359  protected override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
1360  {
1361  base.SetDrivenObjectTransform(settings, scaleSettings, gameboardSettings);
1362 
1363  if(glassesDevice == null || !glassesDevice.added)
1364  {
1365  return;
1366  }
1367 
1368  // World space positions
1369  QueueDeltaStateEvent(glassesDevice.devicePosition, pose_UWRLD.position);
1370  QueueDeltaStateEvent(glassesDevice.centerEyePosition, pose_UWRLD.position);
1371  QueueDeltaStateEvent(glassesDevice.leftEyePosition, eyePositions[AREyes.EYE_LEFT]);
1372  QueueDeltaStateEvent(glassesDevice.rightEyePosition, eyePositions[AREyes.EYE_RIGHT]);
1373 
1374  // World space rotations
1375  QueueDeltaStateEvent(glassesDevice.deviceRotation, pose_UWRLD.rotation);
1376  QueueDeltaStateEvent(glassesDevice.centerEyeRotation, pose_UWRLD.rotation);
1377  QueueDeltaStateEvent(glassesDevice.leftEyeRotation, pose_UWRLD.rotation);
1378  QueueDeltaStateEvent(glassesDevice.rightEyeRotation, pose_UWRLD.rotation);
1379 
1380  // Gameboard space positions
1381  QueueDeltaStateEvent(glassesDevice.RawPosition, pose_UGBD.position);
1382  QueueDeltaStateEvent(glassesDevice.RawLeftEyePosition, WorldToGameboardSpace(eyePositions[AREyes.EYE_LEFT], scaleSettings, gameboardSettings));
1383  QueueDeltaStateEvent(glassesDevice.RawRightEyePosition, WorldToGameboardSpace(eyePositions[AREyes.EYE_RIGHT], scaleSettings, gameboardSettings));
1384 
1385  // Gameboard space rotations
1386  QueueDeltaStateEvent(glassesDevice.RawRotation, pose_UGBD.rotation);
1387  QueueDeltaStateEvent(glassesDevice.leftEyeRotation, pose_UGBD.rotation);
1388  QueueDeltaStateEvent(glassesDevice.rightEyeRotation, pose_UGBD.rotation);
1389 
1390  InputSystem.QueueDeltaStateEvent(glassesDevice.isTracked, isTracked);
1391 
1392  var trackingState = TrackingState.Tracking;
1393  if (!isTracked)
1394  {
1395  trackingState = settings.FailureMode == TrackableSettings.TrackingFailureMode.FreezePosition
1396  ? TrackingState.Limited
1397  : TrackingState.None;
1398  }
1399 
1400  InputSystem.QueueDeltaStateEvent(glassesDevice.trackingState, (int)trackingState);
1401  }
1402 
1403  private static void QueueDeltaStateEvent(Vector3Control vector3Control, Vector3 delta)
1404  {
1405  InputSystem.QueueDeltaStateEvent(vector3Control.x, delta.x);
1406  InputSystem.QueueDeltaStateEvent(vector3Control.y, delta.y);
1407  InputSystem.QueueDeltaStateEvent(vector3Control.z, delta.z);
1408  }
1409 
1410  // For some reason, using QueueDeltaStateEvent on a QuaternionControl with a Quaternion as the delta state doesn't work.
1411  // As a workaround, let's do it component-wise, since we know floats seem fine.
1412  private static void QueueDeltaStateEvent(QuaternionControl quaternionControl, Quaternion delta)
1413  {
1414  InputSystem.QueueDeltaStateEvent(quaternionControl.w, delta.w);
1415  InputSystem.QueueDeltaStateEvent(quaternionControl.x, delta.x);
1416  InputSystem.QueueDeltaStateEvent(quaternionControl.y, delta.y);
1417  InputSystem.QueueDeltaStateEvent(quaternionControl.z, delta.z);
1418  }
1419  }
1420 #endif // UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1421 
1422  }
1423 }
TiltFive.Glasses.AREyes AREyes
The Camera Frame API and runtime.
Definition: CameraImage.cs:31
static bool GetGlassesIPD(UInt64 glassesHandle, ref float glassesIPD)
Definition: Display.cs:352
Represents the game board.
Definition: GameBoard.cs:29
float gameBoardScale
The game board's scale multiplies the perceived size of objects in the scene.
T5_GlassesPoseUsage glassesPoseUsage
Definition: Glasses.cs:697
BaseGlassesCore(GlassesHandle glassesHandle, T5_GlassesPoseUsage glassesPoseUsage, string name)
Definition: Glasses.cs:717
override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Definition: Glasses.cs:847
override bool TryGetStateFromPlugin(out T5_GlassesPose glassesPose, out bool poseIsValid, GameBoardSettings gameBoardSettings)
Definition: Glasses.cs:767
override bool TryCheckConnected(out bool connected)
Determines whether the tracked object is still connected.
Definition: Glasses.cs:761
override void SetInvalidPoseGameboardSpace(in T5_GlassesPose glassesPose, GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
Definition: Glasses.cs:810
override void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Updates this T:TiltFive.Glasses.BaseGlassesCore
Definition: Glasses.cs:738
override void SetPoseUnityWorldSpace(ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Sets the pose values of the tracked object in Unity World Space
Definition: Glasses.cs:754
Pose GetPoseGameboardSpace(T5_GlassesPose glassesPose)
Definition: Glasses.cs:882
override void SetPoseGameboardSpace(in T5_GlassesPose glassesPose, GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
Definition: Glasses.cs:801
override void SetDefaultPoseGameboardSpace(GlassesSettings settings)
Definition: Glasses.cs:747
Internal Glasses core runtime.
Definition: Glasses.cs:948
bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
Tests this T:TiltFive.Glasses.GlassesCore for validity with the parameterized T:TiltFive....
Definition: Glasses.cs:1070
override void Dispose()
Definition: Glasses.cs:985
GlassesCore(GlassesHandle glassesHandle)
Definition: Glasses.cs:972
virtual void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, SpectatorSettings spectatorSettings)
Updates this T:TiltFive.Glasses.GlassesCore
Definition: Glasses.cs:1176
bool TryGetFriendlyName(out string friendlyName)
Definition: Glasses.cs:1081
virtual void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
Reset this T:TiltFive.Glasses.GlassesCore
Definition: Glasses.cs:1010
Dictionary< AREyes, Vector3 > eyePositions
Definition: Glasses.cs:958
GlassesPreviewCore previewCore
Definition: Glasses.cs:970
override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Definition: Glasses.cs:1291
bool configured
Configuration ready indicator.
Definition: Glasses.cs:956
Trackable core for the smoothed glasses preview pose.
Definition: Glasses.cs:923
new void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Updates this T:TiltFive.Glasses.GlassesPreviewCore
Definition: Glasses.cs:934
GlassesPreviewCore(GlassesHandle glassesHandle)
Definition: Glasses.cs:924
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
HashSet< GlassesHandle > incomingHandles
Definition: Glasses.cs:43
static Vector3 up
Gets the head orientation's up vector.
Definition: Glasses.cs:111
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 bool updated
Gets a value indicating whether this T:TiltFive.Glasses is updated.
Definition: Glasses.cs:74
static Vector3 leftEyePosition
Gets the left eye position.
Definition: Glasses.cs:118
static Camera GetRightEye(GlassesHandle glassesHandle)
Definition: Glasses.cs:647
static bool IsTracked(PlayerIndex playerIndex=PlayerIndex.One)
Indicate if the specified glasses are tracked.
Definition: Glasses.cs:142
GlassesHandle? defaultGlassesHandle
The identifier for the first detected pair of glasses.
Definition: Glasses.cs:49
static Vector3 rightEyePosition
Gets the right eye position.
Definition: Glasses.cs:124
static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings=null, PlayerIndex playerIndex=PlayerIndex.None)
Reset this T:TiltFive.Glasses.
Definition: Glasses.cs:317
static Vector3 position
Gets the head pose position.
Definition: Glasses.cs:87
static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
Gets the friendly name associated with the specified player's glasses in the Tilt Five Control Panel.
Definition: Glasses.cs:178
static Vector3 forward
Gets the head orientation's forward vector.
Definition: Glasses.cs:99
Dictionary< GlassesHandle, GlassesCore > glassesCores
The glasses core runtimes.
Definition: Glasses.cs:41
static GameObject GetPoseRoot(PlayerIndex playerIndex)
Gets the pose root GameObject for the specified player.
Definition: Glasses.cs:254
static Vector3 right
Gets the head orientation's right vector.
Definition: Glasses.cs:105
static void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Updates this T:TiltFive.Glasses.
Definition: Glasses.cs:342
static GlassesCore GetPlayerOneGlassesCore()
Definition: Glasses.cs:657
static GameObject GetPoseRoot(GlassesHandle glassesHandle)
Definition: Glasses.cs:629
static Camera GetLeftEye(GlassesHandle glassesHandle)
Definition: Glasses.cs:638
static bool TryGetPose(PlayerIndex playerIndex, out Pose pose)
Attempts to get the position and orientation of the specified player's glasses.
Definition: Glasses.cs:215
static Quaternion rotation
Gets the head pose rotation.
Definition: Glasses.cs:93
static bool glassesAvailable
Indicates whether the glasses are plugged in and functioning.
Definition: Glasses.cs:130
static bool IsConnected(PlayerIndex playerIndex=PlayerIndex.One)
Indicate if the specified glasses are connected.
Definition: Glasses.cs:353
static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings=null, PlayerIndex playerIndex=PlayerIndex.One)
Validates the specified glassesSettings with the specified glasses core.
Definition: Glasses.cs:332
static bool configured
Gets a value indicating whether this T:TiltFive.Glasses is configured.
Definition: Glasses.cs:81
static void ScanForGlasses()
Definition: Glasses.cs:360
HashSet< GlassesHandle > lostHandles
Definition: Glasses.cs:44
static bool headPoseUpdated(PlayerIndex playerIndex=PlayerIndex.One)
Returns a boolean indication that the head pose was successfully updated.
Definition: Glasses.cs:300
static Camera GetRightEye(PlayerIndex playerIndex)
Gets the right eye camera for the specified player's glasses.
Definition: Glasses.cs:282
static bool TryGetSpectatorSettings(out SpectatorSettings spectatorSettings)
Definition: Glasses.cs:667
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.
static readonly string DEFAULT_FRIENDLY_NAME
float farClipPlane
The far clip plane in physical space (meters), to adjust for content scale and gameboard size
LayerMask cullingMask
The culling mask to be used by the eye cameras for this pair of glasses.
GameObject objectTemplate
The object used as a template for creating the base Game Object when a specific playerIndex connects.
float nearClipPlane
The near clip plane in physical space (meters), to adjust for content scale and gameboard size
Provides access to Wand inputs
Definition: Input.cs:31
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
Provides access to player settings and functionality.
Definition: Player.cs:31
static bool IsConnected(PlayerIndex playerIndex)
Determines whether the specified player has an associated pair of glasses connected.
Definition: Player.cs:48
ScaleSettings contains the scale data used to translate between Unity units and the user's physical s...
float GetScaleToUWRLD_UGBD(float gameboardScale)
PlayerIndex spectatedPlayer
The player that will have their perspective mirrored on screen.
Camera spectatorCamera
The camera used for rendering the onscreen preview
void Initialize(GameObject headPoseRoot, GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
Quaternion rotToUGBD_UWRLD
The rotation taking points from the Unity world-space reference frame to the game board reference fra...
Camera leftEyeCamera
The left eye Camera property.
Camera cameraTemplate
The Camera used as a template when creating the eye cameras.
Vector3 posUGBD_UWRLD
The position of the game board reference frame w.r.t. the Unity world-space reference frame.
Camera rightEyeCamera
The right eye Camera property.
float scaleToUGBD_UWRLD
The uniform scale factor that takes points from the Unity world-space to the game board reference fra...
The Tilt Five manager.
The Tilt Five manager.
static bool TryGetISceneInfo(out ISceneInfo sceneInfo)
Pose Pose_UnityWorldSpace
The Pose of the trackable in Unity world space.
TrackingFailureMode FailureMode
Definition: Log.cs:21
ControllerIndex
Since wands are all physically identical (they have no "handedness"), it doesn't make sense to addres...
GameboardType
The type of Gameboard being tracked by the glasses
PlayerIndex
The Player index (e.g. Player One, Player Two, etc)