Unity SDK Docs 1.5.0-beta.6
Loading...
Searching...
No Matches
Glasses.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 */
16
17using System;
18using System.Collections.Generic;
19using UnityEngine;
20
21#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
22using UnityEngine.InputSystem;
23using UnityEngine.InputSystem.Controls;
24#endif
25
26using TiltFive;
28
29namespace 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 Classes
67
68 public class DisplayInfo
69 {
70 public DisplayInfo(Vector2Int framebufferDimensions)
71 {
72 this.framebufferDimensions = framebufferDimensions;
73 }
74
76 public int monoWidth => framebufferDimensions.x;
78 public int stereoWidth => monoWidth * 2;
80 public int height => framebufferDimensions.y;
82 public float monoWidthToHeightRatio => (float)monoWidth / height;
86 public readonly int depthBuffer = 24;
87
88 // Provide a texture format compatible with the glasses.
89 public readonly RenderTextureFormat nativeTextureFormat = RenderTextureFormat.ARGB32;
90
91 // Provide a hardcoded default resolution if the plugin is somehow unavailable.
92 private readonly Vector2Int framebufferDimensions = new Vector2Int(1216, 768);
93 }
94
95 #endregion // Public Classes
96
97
98 #region Public Fields
99
105 [Obsolete("Glasses.updated is deprecated. Please use Glasses.IsTracked() instead.", true)]
106 public static bool updated => headPoseUpdated(PlayerIndex.One);
112 [Obsolete("Glasses.configured is deprecated. Please use Player.IsConnected() instead.", true)]
113 public static bool configured => GetPlayerOneGlassesCore()?.configured ?? false;
118 [Obsolete("Glasses.position is deprecated. Please use Glasses.TryGetPose() and Pose.position instead.", true)]
119 public static Vector3 position => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.position ?? Vector3.zero;
124 [Obsolete("Glasses.rotation is deprecated. Please use Glasses.TryGetPose() and Pose.rotation instead.", true)]
125 public static Quaternion rotation => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.rotation ?? Quaternion.identity;
130 [Obsolete("Glasses.forward is deprecated. Please use Glasses.TryGetPose() and Pose.forward instead.", true)]
131 public static Vector3 forward => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.forward ?? Vector3.forward;
136 [Obsolete("Glasses.right is deprecated. Please use Glasses.TryGetPose() and Pose.right instead.", true)]
137 public static Vector3 right => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.right ?? Vector3.right;
142 [Obsolete("Glasses.up is deprecated. Please use Glasses.TryGetPose() and Pose.up instead.", true)]
143 public static Vector3 up => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.up ?? Vector3.up;
144
149 [Obsolete("Glasses.leftEyePosition is deprecated. Please use Glasses.GetLeftEye() and Transform.position", true)]
150 public static Vector3 leftEyePosition => GetPlayerOneGlassesCore()?.eyePositions[AREyes.EYE_LEFT] ?? Vector3.zero;
155 [Obsolete("Glasses.rightEyePosition is deprecated. Please use Glasses.GetRightEye() and Transform.position", true)]
156 public static Vector3 rightEyePosition => GetPlayerOneGlassesCore()?.eyePositions[AREyes.EYE_RIGHT] ?? Vector3.zero;
157
161 [Obsolete("Glasses.glassesAvailable is deprecated. Please use Player.IsConnected() instead.", true)]
162 public static bool glassesAvailable { get; private set; }
163
164 #endregion Public Fields
165
166
167 #region Public Functions
168
174 public static bool IsTracked(PlayerIndex playerIndex = PlayerIndex.One)
175 {
176 return Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
177 && IsTracked(glassesHandle);
178 }
179
180#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
190 public static bool TryGetGlassesDevice(PlayerIndex playerIndex, out GlassesDevice glassesDevice)
191 {
192 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
193 {
194 glassesDevice = null;
195 return false;
196 }
197
198 return TryGetGlassesDevice(glassesHandle, out glassesDevice);
199 }
200#endif // UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
201
210 public static bool TryGetDisplayInfo(PlayerIndex playerIndex, out DisplayInfo displayInfo)
211 {
212 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
213 {
214 displayInfo = null;
215 return false;
216 }
217
218 return TryGetDisplayInfo(glassesHandle, out displayInfo);
219 }
220
229 public static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
230 {
231 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
232 {
233 friendlyName = null;
234 return false;
235 }
236
237 return TryGetFriendlyName(glassesHandle, out friendlyName);
238 }
239
240#if TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
241 public static bool TrySetProjectorExtrinsicsAdjustment(PlayerIndex playerIndex, float[] args)
242 {
243 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
244 {
245 return false;
246 }
247 return TrySetProjectorExtrinsicsAdjustment(glassesHandle, args);
248 }
249
250 public static bool SetSingleEyeMode(PlayerIndex playerIndex, AREyes eye)
251 {
252 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
253 {
254 return false;
255 }
256 return SetSingleEyeMode(glassesHandle, eye);
257 }
258#endif // TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
259
266 public static bool TryGetPose(PlayerIndex playerIndex, out Pose pose)
267 {
268 if(Player.TryGetGlassesHandle(playerIndex, out var glassesHandle) && Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
269 {
270 pose = glassesCore.Pose_UnityWorldSpace;
271 return true;
272 }
273 pose = new Pose();
274 return false;
275 }
276
284 public static bool TryGetPreviewPose(PlayerIndex playerIndex, out Pose pose)
285 {
286 if (Player.TryGetGlassesHandle(playerIndex, out var glassesHandle) && Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
287 {
288 pose = glassesCore.previewCore.Pose_UnityWorldSpace;
289 return true;
290 }
291 pose = new Pose();
292 return false;
293 }
294
305 public static GameObject GetPoseRoot(PlayerIndex playerIndex)
306 {
307 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
308 {
309 return null;
310 }
311 return GetPoseRoot(glassesHandle);
312 }
313
319 public static Camera GetLeftEye(PlayerIndex playerIndex)
320 {
321 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
322 {
323 return null;
324 }
325 return GetLeftEye(glassesHandle);
326 }
327
333 public static Camera GetRightEye(PlayerIndex playerIndex)
334 {
335 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
336 {
337 return null;
338 }
339 return GetRightEye(glassesHandle);
340 }
341
342 #region Deprecated Public Functions
343
350 [Obsolete("Glasses.headPoseUpdated is deprecated. Please use Glasses.IsTracked() instead", true)]
351 public static bool headPoseUpdated(PlayerIndex playerIndex = PlayerIndex.One)
352 {
353 if (playerIndex == PlayerIndex.None || !Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
354 {
355 return false;
356 }
357
358 return Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
359 && glassesCore.IsTracked;
360 }
361
367 [Obsolete("Converted to be an internal function.", true)]
368 public static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null, PlayerIndex playerIndex = PlayerIndex.None)
369 {
370 Reset(playerIndex, glassesSettings, spectatorSettings);
371 }
372
382 [Obsolete("Converted to be an internal function.", true)]
383 public static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null, PlayerIndex playerIndex = PlayerIndex.One)
384 {
385 return Validate(playerIndex, glassesSettings, spectatorSettings);
386 }
387
392 [Obsolete("Converted to be an internal function.", true)]
393 public static void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
394 {
395 UpdateAllGlassesCores(glassesSettings, scaleSettings, gameBoardSettings);
396 }
397
403 [Obsolete("Deprecated in favor of Player.IsConnected().", true)]
404 public static bool IsConnected(PlayerIndex playerIndex = PlayerIndex.One)
405 {
406 return Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
407 && IsConnected(glassesHandle);
408 }
409
410 [Obsolete("Converted to be an internal function.", true)]
411 public static void ScanForGlasses() { Scan(); }
412
413 #endregion Deprecated Public Functions
414
415
416 #endregion Public Functions
417
418
419 #region Internal Functions
420
421 internal static void Scan()
422 {
423 // Enumerate the available glasses provided by the native plugin
424 UInt64[] glassesHandles = new UInt64[GlassesSettings.MAX_SUPPORTED_GLASSES_COUNT];
425 Debug.Assert(glassesHandles.Length <= Byte.MaxValue);
426 byte glassesCount = (byte)glassesHandles.Length;
427 int result = NativePlugin.T5_RESULT_SUCCESS;
428
429 try
430 {
431 var refreshResult = NativePlugin.RefreshGlassesAvailable();
432 result = NativePlugin.GetGlassesHandles(ref glassesCount, glassesHandles);
433 }
434 catch (System.Exception e)
435 {
436 Log.Error($"Error getting glasses handles: {e.Message}");
437 }
438
439 // Add/Remove entries from the glassesCores dictionary
440 var glassesCores = Instance.glassesCores;
441 var incomingHandles = Instance.incomingHandles;
442 var lostHandles = Instance.lostHandles;
443
444 incomingHandles.Clear();
445 lostHandles.Clear();
446
447 // If we ran into a problem getting the glasses handles, all bets are off — just tear down the now-useless glasses cores.
448 if (result != NativePlugin.T5_RESULT_SUCCESS)
449 {
450 foreach (var keyValuePair in glassesCores)
451 {
452 var lostHandle = keyValuePair.Key;
453 var glassesCore = keyValuePair.Value;
454
455 glassesCore.Dispose();
456 lostHandles.Add(lostHandle);
457 }
458 glassesCores.Clear();
459 }
460 else
461 {
462 // Add newly connected glasses
463 for (int i = 0; i < glassesCount; i++)
464 {
465 incomingHandles.Add(glassesHandles[i]);
466
467 // If we don't already have a glassesCore for this handle,
468 // and we still have an available player slot, then create a glassesCore.
469 if (!glassesCores.ContainsKey(glassesHandles[i]) && !Player.AllSupportedPlayersConnected())
470 {
471#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
472 glassesCores[glassesHandles[i]] = new GlassesDeviceCore(glassesHandles[i]);
473#else
474 glassesCores[glassesHandles[i]] = new GlassesCore(glassesHandles[i]);
475#endif
476 }
477 }
478
479 // Prune disconnected glasses
480 foreach (var currentHandle in glassesCores.Keys)
481 {
482 if (!incomingHandles.Contains(currentHandle))
483 {
484 lostHandles.Add(currentHandle);
485 }
486 }
487 foreach (var lostHandle in lostHandles)
488 {
489 glassesCores[lostHandle].Dispose();
490 glassesCores.Remove(lostHandle);
491 }
492 }
493
494 // If we don't have a default glasses ID yet, assign the first one we got from the native plugin as the default.
495 if (!Instance.defaultGlassesHandle.HasValue && result == 0 && glassesCount > 0)
496 {
497 Instance.defaultGlassesHandle = glassesHandles[0];
498 }
499
500 // If defaultGlassesId isn't valid anymore, reset its value.
501 if (Instance.defaultGlassesHandle.HasValue && !glassesCores.ContainsKey(Instance.defaultGlassesHandle.Value))
502 {
503 Instance.defaultGlassesHandle = null;
504 }
505 }
506
507 internal static void Reset(PlayerIndex playerIndex, GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null)
508 {
509 if (spectatorSettings == null && !TryGetSpectatorSettings(out spectatorSettings))
510 {
511 Log.Error("Glasses.Reset() could not find any spectator settings.");
512 return;
513 }
514
515 // If playerIndex is none, reset all glasses
516 if (playerIndex == PlayerIndex.None)
517 {
518 foreach (var glassesCore in Instance.glassesCores.Values)
519 {
520 glassesCore.Reset(glassesSettings, spectatorSettings);
521 }
522 return;
523 }
524
525 if (Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
526 {
527 Reset(glassesSettings, spectatorSettings, glassesHandle);
528 }
529 }
530
531 internal static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings, GlassesHandle glassesHandle)
532 {
533 if (Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore) && spectatorSettings != null)
534 {
535 glassesCore.Reset(glassesSettings, spectatorSettings);
536 }
537 }
538
539 internal static bool Validate(PlayerIndex playerIndex, GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null)
540 {
541 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
542 {
543 return false;
544 }
545
546 if (spectatorSettings == null && !TryGetSpectatorSettings(out spectatorSettings))
547 {
548 return false;
549 }
550
551 return Validate(glassesSettings, spectatorSettings, glassesHandle);
552 }
553
554 internal static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings, GlassesHandle glassesHandle)
555 {
556 return Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
557 && glassesCore.Validate(glassesSettings, spectatorSettings);
558 }
559
560 internal static void UpdateAllGlassesCores(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
561 {
562 if (!TryGetSpectatorSettings(out var spectatorSettings))
563 {
564 return;
565 }
566
567 // Update the glasses cores
568 foreach (var glassesCore in Instance.glassesCores.Values)
569 {
570 glassesCore.Update(glassesSettings, scaleSettings, gameBoardSettings, spectatorSettings);
571 }
572 }
573
574 internal static void Update(GlassesHandle glassesHandle, GlassesSettings glassesSettings, ScaleSettings scaleSettings,
575 GameBoardSettings gameBoardSettings, SpectatorSettings spectatorSettings)
576 {
577 if (Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
578 {
579 glassesCore.Update(glassesSettings, scaleSettings, gameBoardSettings, spectatorSettings);
580 }
581 }
582
583 internal static bool IsTracked(GlassesHandle glassesHandle)
584 {
585 return Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
586 && glassesCore.IsTracked;
587 }
588
589 internal static bool IsConnected(GlassesHandle glassesHandle)
590 {
591 return Instance.glassesCores.ContainsKey(glassesHandle);
592 }
593
594#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
595 internal static bool TryGetGlassesDevice(GlassesHandle glassesHandle, out GlassesDevice glassesDevice)
596 {
597 if(!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
598 || !(glassesCore is GlassesDeviceCore glassesDeviceCore))
599 {
600 glassesDevice = null;
601 return false;
602 }
603
604 glassesDevice = glassesDeviceCore.glassesDevice;
605 return true;
606 }
607#endif // UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
608
609 internal static bool TryGetDisplayInfo(GlassesHandle glassesHandle, out DisplayInfo displayInfo)
610 {
611 if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
612 {
613 displayInfo = null;
614 return false;
615 }
616
617 displayInfo = glassesCore.displayInfo;
618 return true;
619 }
620
621 internal static bool TryGetFriendlyName(GlassesHandle glassesHandle, out string friendlyName)
622 {
623 if(!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
624 {
625 friendlyName = null;
626 return false;
627 }
628
629 return glassesCore.TryGetFriendlyName(out friendlyName);
630 }
631
632#if TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
633 internal static bool TrySetProjectorExtrinsicsAdjustment(GlassesHandle glassesHandle, float[] args)
634 {
635 if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
636 {
637 return false;
638 }
639 return glassesCore.TrySetProjectorExtrinsicsAdjustment(args);
640 }
641
642 internal static bool SetSingleEyeMode(GlassesHandle glassesHandle, AREyes eye)
643 {
644 if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
645 {
646 return false;
647 }
648 return glassesCore.SetSingleEyeMode(eye);
649 }
650#endif // TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
651
658 internal static GlassesHandle? GetDefaultGlassesHandle()
659 {
660 var defaultGlassesHandle = Instance.defaultGlassesHandle;
661 return defaultGlassesHandle.HasValue && Instance.glassesCores.ContainsKey(defaultGlassesHandle.Value)
662 ? defaultGlassesHandle
663 : null;
664 }
665
666 internal static void SetDefaultGlassesHandle(GlassesHandle glassesHandle)
667 {
668 Instance.defaultGlassesHandle = glassesHandle;
669 }
670
671 internal static GlassesHandle[] GetAllConnectedGlassesHandles()
672 {
673 var keys = Instance.glassesCores.Keys;
674 GlassesHandle[] glassesHandles = new GlassesHandle[keys.Count];
675 keys.CopyTo(glassesHandles, 0);
676 return glassesHandles;
677 }
678
679 internal static void OnDisable()
680 {
681 foreach (var glassesCore in Instance.glassesCores.Values)
682 {
683 glassesCore.Dispose();
684 }
685 Instance.glassesCores.Clear();
686 }
687
688 #endregion Internal Functions
689
690
691 #region Private Functions
692
693 private static GameObject GetPoseRoot(GlassesHandle glassesHandle)
694 {
695 if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
696 {
697 return null;
698 }
699 return glassesCore.headPose.gameObject;
700 }
701
702 private static Camera GetLeftEye(GlassesHandle glassesHandle)
703 {
704 if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
705 {
706 return null;
707 }
708 return glassesCore.leftEye;
709 }
710
711 private static Camera GetRightEye(GlassesHandle glassesHandle)
712 {
713 if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
714 {
715 return null;
716 }
717
718 return glassesCore.rightEye;
719 }
720
721 private static GlassesCore GetPlayerOneGlassesCore()
722 {
723 if(Player.TryGetGlassesHandle(PlayerIndex.One, out var glassesHandle)
724 && Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
725 {
726 return glassesCore;
727 }
728 return null;
729 }
730
731 private static bool TryGetSpectatorSettings(out SpectatorSettings spectatorSettings)
732 {
733 if (TiltFiveSingletonHelper.TryGetISceneInfo(out var sceneInfo))
734 {
735 if (sceneInfo is TiltFiveManager2 tiltFiveManager2)
736 {
737 spectatorSettings = tiltFiveManager2.spectatorSettings;
738 return true;
739 }
740
741 if (sceneInfo is TiltFiveManager tiltFiveManager)
742 {
743 spectatorSettings = tiltFiveManager.spectatorSettings;
744 return true;
745 }
746 }
747
748 spectatorSettings = null;
749 return false;
750 }
751
752 #endregion
753
754
755 #region Private Classes
756
757 private class BaseGlassesCore : TrackableCore<GlassesSettings, T5_GlassesPose>, IDisposable
758 {
759 public GlassesHandle glassesHandle;
760
761 public GameObject headPoseRoot;
762 public Transform headPose => headPoseRoot.transform;
763
764 private T5_GlassesPoseUsage glassesPoseUsage;
765
766 private DisplayInfo _displayInfo;
768 public DisplayInfo displayInfo => _displayInfo;
769
777 private readonly Vector3 DEFAULT_GLASSES_POSITION_GAME_BOARD_SPACE = new Vector3(0f, 0.5f, -0.5f);
778
786 private readonly Quaternion DEFAULT_GLASSES_ROTATION_GAME_BOARD_SPACE = Quaternion.Euler(new Vector3(-45f, 0f, 0f));
787
788 public BaseGlassesCore(
789 GlassesHandle glassesHandle,
790 T5_GlassesPoseUsage glassesPoseUsage,
791 string name)
792 {
793 this.glassesHandle = glassesHandle;
794 this.glassesPoseUsage = glassesPoseUsage;
795 headPoseRoot = new GameObject(name);
796
797 var displayDimensions = new Vector2Int();
798 if (!Display.GetDisplayDimensions(this.glassesHandle, ref displayDimensions))
799 {
800 Log.Warn("Could not retrieve display settings from the plugin; display dimensions may be incorrect.");
801 }
802 else
803 {
804 _displayInfo = new DisplayInfo(displayDimensions);
805 }
806 }
807
808 public virtual void Dispose()
809 {
810 GameObject.Destroy(headPoseRoot);
811 }
812
819 protected override void Update(
820 GlassesSettings glassesSettings,
821 ScaleSettings scaleSettings,
822 GameBoardSettings gameBoardSettings)
823 {
824 // Obtain the latest glasses pose.
825 base.Update(glassesSettings, scaleSettings, gameBoardSettings);
826 }
827
828 protected override void SetDefaultPoseGameboardSpace(GlassesSettings settings)
829 {
830 pose_USTAGE = new Pose(
831 DEFAULT_GLASSES_POSITION_GAME_BOARD_SPACE,
832 DEFAULT_GLASSES_ROTATION_GAME_BOARD_SPACE);
833 }
834
835 protected override void SetPoseUnityWorldSpace(
836 ScaleSettings scaleSettings,
837 GameBoardSettings gameBoardSettings)
838 {
839 pose_UWRLD = GameboardToWorldSpace(pose_USTAGE, scaleSettings, gameBoardSettings);
840 }
841
842 protected override bool TryCheckConnected(out bool connected)
843 {
844 connected = IsConnected(glassesHandle);
845 return connected;
846 }
847
848 protected override bool TryGetStateFromPlugin(out T5_GlassesPose glassesPose, out bool poseIsValid)
849 {
850 T5_GlassesPose glassesPoseResult = new T5_GlassesPose { };
851
852 int result = NativePlugin.T5_RESULT_UNKNOWN_ERROR;
853 try
854 {
855 result = NativePlugin.GetGlassesPose(
856 glassesHandle,
857 ref glassesPoseResult,
858 glassesPoseUsage);
859 }
860 catch (System.Exception e)
861 {
862 Log.Error(e.Message);
863 }
864
865 poseIsValid = result == NativePlugin.T5_RESULT_SUCCESS && glassesPoseResult.GameboardType != GameboardType.GameboardType_None;
866 glassesPose = glassesPoseResult;
867 return result == NativePlugin.T5_RESULT_SUCCESS;
868 }
869
870 protected override void SetPoseGameboardSpace(
871 in T5_GlassesPose glassesPose,
872 GlassesSettings glassesSettings,
873 ScaleSettings scaleSettings,
874 GameBoardSettings gameboardSettings)
875 {
876 pose_USTAGE = GetPoseGameboardSpace(glassesPose);
877 }
878
879 protected override void SetInvalidPoseGameboardSpace(
880 in T5_GlassesPose glassesPose,
881 GlassesSettings settings,
882 ScaleSettings scaleSettings,
883 GameBoardSettings gameboardSettings)
884 {
885 var newPose_GameboardSpace = GetPoseGameboardSpace(glassesPose);
886
887 if (!isTracked && settings.RejectUntrackedPositionData)
888 {
889 switch (settings.FailureMode)
890 {
891 case TrackableSettings.TrackingFailureMode.FreezePosition:
892 pose_USTAGE = new Pose(pose_USTAGE.position, newPose_GameboardSpace.rotation);
893 break;
894 // If we want to freeze both position and rotation when tracking is lost, things are easy - just do nothing.
895 case TrackableSettings.TrackingFailureMode.FreezePositionAndRotation:
896 break;
897 // Otherwise, we may want to keep the legacy behavior of snapping to a default position when tracking is lost.
898 case TrackableSettings.TrackingFailureMode.SnapToDefault:
899 // TODO: Rethink the existence of the preview pose? Is that a separate tracking failure mode?
900 if (settings.usePreviewPose)
901 {
902 pose_USTAGE = new Pose(WorldToGameboardSpace(settings.previewPose.position, scaleSettings, gameboardSettings),
903 WorldToGameboardSpace(settings.previewPose.rotation, gameboardSettings));
904 }
905 // Otherwise do nothing and let the developer control the head pose camera themselves.
906 // It will be up to them to let go once head tracking kicks in again.
907 break;
908 }
909 }
910 else // Either things are working well and we're tracked, or we don't care about invalid data and want to display it regardless.
911 {
912 pose_USTAGE = newPose_GameboardSpace;
913 }
914 }
915
916 protected override void SetDrivenObjectTransform(
917 GlassesSettings settings,
918 ScaleSettings scaleSettings,
919 GameBoardSettings gameBoardSettings)
920 {
921 if (!isTracked && settings.RejectUntrackedPositionData)
922 {
923 switch (settings.FailureMode)
924 {
925 case TrackableSettings.TrackingFailureMode.FreezePosition:
926 headPose.SetPositionAndRotation(headPose.position, pose_UWRLD.rotation);
927 break;
928 // If we want to freeze both position and rotation when tracking is lost, things are easy - just do nothing.
929 case TrackableSettings.TrackingFailureMode.FreezePositionAndRotation:
930 break;
931 // Otherwise, we may want to keep the legacy behavior of snapping to a default position when tracking is lost.
932 case TrackableSettings.TrackingFailureMode.SnapToDefault:
933 // TODO: Rethink the existence of the preview pose? Is that a separate tracking failure mode?
934 if (settings.usePreviewPose)
935 {
936 headPose.SetPositionAndRotation(
937 settings.previewPose.position,
938 settings.previewPose.rotation);
939 }
940 // Otherwise do nothing and let the developer control the head pose camera themselves.
941 // It will be up to them to let go once head tracking kicks in again.
942 break;
943 }
944 }
945 else // Either things are working well and we're tracked, or we don't care about invalid data and want to display it regardless.
946 {
947 headPose.SetPositionAndRotation(pose_UWRLD.position, pose_UWRLD.rotation);
948 }
949 }
950
951 private Pose GetPoseGameboardSpace(T5_GlassesPose glassesPose)
952 {
953 // Unity reference frames:
954 //
955 // UGLS - Unity GLaSses local space.
956 // +x right, +y up, +z forward
957 // USTAGE - Unity Stage space.
958 // +x right, +y up, +z forward
959 //
960 // Tilt Five reference frames:
961 //
962 // DC - Our right-handed version of Unity's default camera space.
963 // +x right, +y up, +z backward
964 // STAGE - Stage space.
965 // +x right, +y forward, +z up
966
967 Quaternion rotToDC_STAGE = Quaternion.AngleAxis((-Mathf.PI / 2f) * Mathf.Rad2Deg, Vector3.right);
968
969 Quaternion rotToSTAGE_DC = Quaternion.Inverse(rotToDC_STAGE);
970
971 Quaternion rotToGLS_STAGE = glassesPose.RotationToGLS_STAGE;
972
973 Quaternion rotToGLS_DC = rotToGLS_STAGE * rotToSTAGE_DC;
974
975 Quaternion rotToUSTAGE_UGLS = new Quaternion(rotToGLS_DC.x, rotToGLS_DC.y, -rotToGLS_DC.z, rotToGLS_DC.w);
976
977 // Swap from right-handed (T5 internal) to left-handed (Unity) coord space.
978 Vector3 posOfUGLS_USTAGE = ConvertPosSTAGEToUSTAGE(glassesPose.PosOfGLS_STAGE);
979
980 return new Pose(posOfUGLS_USTAGE, rotToUSTAGE_UGLS);
981 }
982 }
983
991 private class GlassesPreviewCore : BaseGlassesCore
992 {
993 public GlassesPreviewCore(GlassesHandle glassesHandle) :
994 base(glassesHandle, T5_GlassesPoseUsage.SpectatorPresentation, $"Glasses {glassesHandle} preview")
995 { }
996
1003 public new void Update(
1004 GlassesSettings glassesSettings,
1005 ScaleSettings scaleSettings,
1006 GameBoardSettings gameBoardSettings)
1007 {
1008 // Obtain the latest glasses pose.
1009 base.Update(glassesSettings, scaleSettings, gameBoardSettings);
1010 }
1011
1012 protected override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
1013 {
1014 base.SetDrivenObjectTransform(settings, scaleSettings, gameBoardSettings);
1015 }
1016 }
1017
1018
1022 private class GlassesCore : BaseGlassesCore
1023 {
1024 public PlayerIndex playerIndex;
1025 public GameObject baseObject;
1026 public string friendlyName;
1027
1031 public bool configured = false;
1032
1033 public Dictionary<AREyes, Vector3> eyePositions = new Dictionary<AREyes, Vector3>()
1034 {
1035 { AREyes.EYE_LEFT, new Vector3() },
1036 { AREyes.EYE_RIGHT, new Vector3() }
1037 };
1038
1039 public Dictionary<AREyes, Quaternion> eyeRotations = new Dictionary<AREyes, Quaternion>()
1040 {
1041 { AREyes.EYE_LEFT, new Quaternion() },
1042 { AREyes.EYE_RIGHT, new Quaternion() }
1043 };
1044
1045 public GlassesPreviewCore previewCore;
1046
1047 public GlassesCore(GlassesHandle glassesHandle) :
1048 base(glassesHandle, T5_GlassesPoseUsage.GlassesPresentation, $"Glasses {glassesHandle}")
1049 {
1050 if(!Player.TryGetPlayerIndex(glassesHandle, out playerIndex))
1051 {
1052 Player.TryAddPlayer(glassesHandle, out playerIndex);
1053 }
1054 previewCore = new GlassesPreviewCore(glassesHandle);
1055 CameraImage.Initialize(glassesHandle);
1056
1057 Log.Info($"Glasses {glassesHandle} connected.");
1058 }
1059
1060 public override void Dispose()
1061 {
1062 GameObject.Destroy(splitStereoCamera);
1063 GameObject.Destroy(baseObject);
1064 CameraImage.RemoveCore(glassesHandle);
1065 previewCore.Dispose();
1066
1067 base.Dispose();
1068
1069 Log.Info($"Glasses {glassesHandle} (\"{friendlyName}\") disconnected");
1070 }
1071
1075 private SplitStereoCamera splitStereoCamera = null;
1076
1077 internal Camera leftEye => splitStereoCamera?.leftEyeCamera;
1078 internal Camera rightEye => splitStereoCamera?.rightEyeCamera;
1079
1085 public virtual void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
1086 {
1087 base.Reset(glassesSettings);
1088
1089 configured = false;
1090
1091 if(null == spectatorSettings.spectatorCamera)
1092 {
1093 Log.Error($"Required Camera assignment missing from { spectatorSettings.GetType() }. Check Spectator settings in Tilt Five Manager");
1094 return;
1095 }
1096
1097#if UNITY_EDITOR
1098 if (glassesSettings.tiltFiveXR)
1099 {
1100#endif
1101 //if the splitScreenCamera does not exist already.
1102 if (null == splitStereoCamera)
1103 {
1104 //get the head pose camera's GameObject
1105 GameObject spectatorCameraObject = spectatorSettings.spectatorCamera.gameObject;
1106
1107 // Initialize the SplitStereoCamera
1108 splitStereoCamera = spectatorCameraObject.AddComponent<SplitStereoCamera>();
1109 if (null == headPoseRoot)
1110 {
1111
1112 headPoseRoot = new GameObject($"Glasses {glassesHandle}");
1113 previewCore = new GlassesPreviewCore(glassesHandle);
1114 }
1115 if (glassesSettings.objectTemplate && null == baseObject)
1116 {
1117#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1118 if(glassesSettings.objectTemplate.TryGetComponent<PlayerInput>(out var playerInput))
1119 {
1120 Log.Warn("Attaching a PlayerInput component to the Object Template is not recommended," +
1121 " as the Object Template does not persist across scene loads." +
1122 "Consider using the Input Template instead.");
1123 }
1124#endif //UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1125 baseObject = GameObject.Instantiate(glassesSettings.objectTemplate, headPoseRoot.transform);
1126 baseObject.name = $"{baseObject.transform.parent.name} - Prefab {playerIndex}";
1127 }
1128 splitStereoCamera.Initialize(headPoseRoot, glassesSettings, spectatorSettings, displayInfo);
1129 }
1130#if UNITY_EDITOR
1131 }
1132#endif
1133
1134 TryGetFriendlyName(out friendlyName);
1135
1136 configured = true;
1137 }
1138
1145 public bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
1146 {
1147 bool valid = true;
1148
1149 valid &= splitStereoCamera != null
1150 && glassesSettings.cameraTemplate == splitStereoCamera.cameraTemplate
1151 && spectatorSettings == splitStereoCamera.spectatorSettings;
1152
1153 return valid;
1154 }
1155
1156 public bool TryGetFriendlyName(out string friendlyName)
1157 {
1158 T5_StringUTF8 friendlyNameResult = "";
1159 int result = NativePlugin.T5_RESULT_UNKNOWN_ERROR;
1160
1161 try
1162 {
1163 result = NativePlugin.GetGlassesFriendlyName(glassesHandle, ref friendlyNameResult);
1164 }
1165 catch (System.Exception e)
1166 {
1167 Log.Error($"Error getting friendly name: {e.Message}");
1168 }
1169 finally
1170 {
1171 friendlyName = (result == NativePlugin.T5_RESULT_SUCCESS)
1172 ? friendlyNameResult
1173 : null;
1174
1175 // Unfortunately we can't use a "using" block for friendlyNameResult
1176 // since "using" parameters are readonly, preventing us from passing it via "ref".
1177 // We do the next best thing with try-finally and dispose of it here.
1178 friendlyNameResult.Dispose();
1179 }
1180
1181 return result == NativePlugin.T5_RESULT_SUCCESS;
1182 }
1183
1184#if TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
1185 public bool TrySetProjectorExtrinsicsAdjustment(float[] args)
1186 {
1187 int result = 1;
1188
1189 try
1190 {
1191 result = NativePlugin.SetProjectorExtrinsicsAdjustment(glassesHandle, args);
1192 }
1193 catch (System.Exception e)
1194 {
1195 Log.Error($"Error setting projector extrinsics adjustment: ${e.Message}");
1196 }
1197 return result == NativePlugin.T5_RESULT_SUCCESS;
1198 }
1199
1200 public bool SetSingleEyeMode(AREyes eye)
1201 {
1202 Camera leftEyeCamera = splitStereoCamera.leftEyeCamera;
1203 Camera rightEyeCamera = splitStereoCamera.rightEyeCamera;
1204 if (leftEyeCamera == null || rightEyeCamera == null)
1205 {
1206 Log.Error("Eye camera null during call to SetSingleEyeMode()");
1207 return false;
1208 }
1209
1210 switch (eye)
1211 {
1212 case AREyes.EYE_LEFT:
1213 {
1214 // Enable only the left eye
1215 leftEyeCamera.enabled = true;
1216 rightEyeCamera.enabled = false;
1217 // Clear the right eye texture
1218 RenderTexture rt = RenderTexture.active;
1219 RenderTexture.active = rightEyeCamera.targetTexture;
1220 GL.Viewport(rightEyeCamera.pixelRect);
1221 GL.Clear(true, true, Color.clear);
1222 RenderTexture.active = rt;
1223 break;
1224 }
1225 case AREyes.EYE_RIGHT:
1226 {
1227 // Enable only the right eye
1228 leftEyeCamera.enabled = false;
1229 rightEyeCamera.enabled = true;
1230 // Clear the left eye texture
1231 RenderTexture rt = RenderTexture.active;
1232 RenderTexture.active = leftEyeCamera.targetTexture;
1233 GL.Viewport(leftEyeCamera.pixelRect);
1234 GL.Clear(true, true, Color.clear);
1235 RenderTexture.active = rt;
1236 break;
1237 }
1238 case AREyes.EYE_MAX:
1239 leftEyeCamera.enabled = true;
1240 rightEyeCamera.enabled = true;
1241 break;
1242 }
1243 return true;
1244 }
1245#endif // TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
1246
1251 public void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, SpectatorSettings spectatorSettings)
1252 {
1253 if (null == glassesSettings)
1254 {
1255 Log.Error("GlassesSettings configuration required for Glasses tracking Update.");
1256 return;
1257 }
1258
1259 if (null == splitStereoCamera)
1260 {
1261 Log.Error($"Stereo camera(s) missing from Glasses {glassesHandle} - aborting Update.");
1262 return;
1263 }
1264
1265 if (glassesSettings.cameraTemplate != splitStereoCamera.cameraTemplate)
1266 {
1267 Log.Warn("Found mismatched template Cameras in GlassesCore Update - should call Reset.");
1268 return;
1269 }
1270
1271 if (spectatorSettings != splitStereoCamera.spectatorSettings)
1272 {
1273 Log.Warn("Found mismatched spectator settings in GlassesCore Update - should call Reset.");
1274 return;
1275 }
1276
1277 // Obtain the latest glasses poses.
1278 base.Update(glassesSettings, scaleSettings, gameBoardSettings);
1279 previewCore.Update(glassesSettings, scaleSettings, gameBoardSettings);
1280
1281 // Check whether the glasses are plugged in and available.
1282 splitStereoCamera.glassesHandle = glassesHandle;
1283 splitStereoCamera.enabled = isConnected;
1284
1285 // Sync settings with splitStereoCamera
1286 splitStereoCamera.glassesSettings = glassesSettings;
1287 splitStereoCamera.spectatorSettings = spectatorSettings;
1288
1289 // Enable spectating for player specified in the spectator settings, if they're available.
1290 splitStereoCamera.UseSpectatorCamera = Player.IsConnected(spectatorSettings.spectatedPlayer)
1291 && Player.TryGetGlassesHandle(spectatorSettings.spectatedPlayer, out var spectatorGlassesHandle)
1292 && spectatorGlassesHandle == glassesHandle;
1293
1294 // Get the glasses pose in Unity world-space.
1295 float scaleToUWRLD_USTAGE = scaleSettings.GetScaleToWorldSpaceFromGameboardSpace(gameBoardSettings.gameBoardScale);
1296 float scaleToUSTAGE_UWRLD = 1.0f / scaleToUWRLD_USTAGE;
1297
1298 splitStereoCamera.UpdateStageSpaceData(gameboardPose_UWRLD, scaleToUSTAGE_UWRLD);
1299
1300 // TODO: Revisit native XR support.
1301
1302 // NOTE: We do this because "Mock HMD" in UNITY_2017_0_2_OR_NEWER
1303 // the fieldOfView is locked to 111.96 degrees (Vive emulation),
1304 // so setting custom projection matrices is broken. If Unity
1305 // opens the API to custom settings, we can go back to native XR
1306 // support.
1307
1308 // Manual split screen 'new glasses' until the day Unity lets
1309 // me override their Mock HMD settings.
1310
1311 // compute half ipd translation
1312 float ipd_USTAGE = GlassesSettings.DEFAULT_IPD_STAGE;
1313 if(!Display.GetGlassesIPD(glassesHandle, ref ipd_USTAGE) && isConnected)
1314 {
1315 Log.Error("Failed to obtain Glasses IPD");
1316 }
1317 float ipd_UWRLD = scaleToUWRLD_USTAGE * ipd_USTAGE;
1318 Vector3 eyeOffset = (headPose.right.normalized * (ipd_UWRLD * 0.5f));
1319
1320 // set the left eye camera offset from the head by the half ipd amount (-)
1321 eyePositions[AREyes.EYE_LEFT] = headPose.position - eyeOffset;
1322 eyeRotations[AREyes.EYE_LEFT] = headPose.rotation;
1323
1324 // set the right eye camera offset from the head by the half ipd amount (+)
1325 eyePositions[AREyes.EYE_RIGHT] = headPose.position + eyeOffset;
1326 eyeRotations[AREyes.EYE_RIGHT] = headPose.rotation;
1327
1328 Camera leftEyeCamera = splitStereoCamera.leftEyeCamera;
1329 if (null != leftEyeCamera)
1330 {
1331 GameObject leftEye = leftEyeCamera.gameObject;
1332 leftEye.transform.position = eyePositions[AREyes.EYE_LEFT];
1333 leftEye.transform.rotation = eyeRotations[AREyes.EYE_LEFT];
1334
1335 //make sure projection fields are synchronized to the head camera.
1336 leftEyeCamera.nearClipPlane = glassesSettings.nearClipPlane / scaleToUSTAGE_UWRLD;
1337 leftEyeCamera.farClipPlane = glassesSettings.farClipPlane / scaleToUSTAGE_UWRLD;
1338 leftEyeCamera.fieldOfView = glassesSettings.fieldOfView;
1339 leftEyeCamera.cullingMask = glassesSettings.cullingMask;
1340 }
1341
1342 Camera rightEyeCamera = splitStereoCamera.rightEyeCamera;
1343 if (null != rightEyeCamera)
1344 {
1345 GameObject rightEye = rightEyeCamera.gameObject;
1346 rightEye.transform.position = eyePositions[AREyes.EYE_RIGHT];
1347 rightEye.transform.rotation = eyeRotations[AREyes.EYE_RIGHT];
1348
1349 //make sure projection fields are synchronized to the head camera.
1350 rightEyeCamera.nearClipPlane = glassesSettings.nearClipPlane / scaleToUSTAGE_UWRLD;
1351 rightEyeCamera.farClipPlane = glassesSettings.farClipPlane / scaleToUSTAGE_UWRLD;
1352 rightEyeCamera.fieldOfView = glassesSettings.fieldOfView;
1353 rightEyeCamera.cullingMask = glassesSettings.cullingMask;
1354 }
1355
1356 // TODO: Poll less frequently by plumbing t5_hmdGetChangedParams up to Unity.
1357 if (!TryGetFriendlyName(out glassesSettings.friendlyName))
1358 {
1359 glassesSettings.friendlyName = GlassesSettings.DEFAULT_FRIENDLY_NAME;
1360 }
1361 }
1362
1363 protected override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
1364 {
1365 base.SetDrivenObjectTransform(settings, scaleSettings, gameBoardSettings);
1366 }
1367 }
1368
1369#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1370 private class GlassesDeviceCore : GlassesCore, IDisposable
1371 {
1372 internal GlassesDevice glassesDevice;
1373 internal GameObject playerTemplateObject;
1374
1375 private enum TrackingState : int
1376 {
1377 None,
1378 Limited,
1379 Tracking
1380 }
1381
1382 public GlassesDeviceCore(GlassesHandle glassesHandle) : base(glassesHandle)
1383 {
1384 Input.AddGlassesDevice(playerIndex);
1385 glassesDevice = Input.GetGlassesDevice(playerIndex);
1386
1387 if(glassesDevice != null && glassesDevice.added)
1388 {
1389 InputSystem.EnableDevice(glassesDevice);
1390 }
1391 if(TiltFiveManager2.IsInstantiated)
1392 {
1393 TiltFiveManager2.Instance.RefreshInputDevicePairings();
1394 }
1395 }
1396
1397 public override void Dispose()
1398 {
1399 base.Dispose();
1400
1401 if(glassesDevice != null && glassesDevice.added)
1402 {
1403 if (glassesDevice.LeftWand != null)
1404 {
1405 Input.RemoveWandDevice(playerIndex, ControllerIndex.Left);
1406 }
1407 if (glassesDevice.RightWand != null)
1408 {
1409 Input.RemoveWandDevice(playerIndex, ControllerIndex.Right);
1410 }
1411 InputSystem.DisableDevice(glassesDevice);
1412 Input.RemoveGlassesDevice(playerIndex);
1413 }
1414 GameObject.Destroy(playerTemplateObject);
1415 }
1416
1417 public override void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
1418 {
1419 base.Reset(glassesSettings, spectatorSettings);
1420
1421 if(glassesSettings.playerTemplate)
1422 {
1423 playerTemplateObject = GameObject.Instantiate(glassesSettings.playerTemplate);
1424 if(TiltFiveManager2.IsInstantiated)
1425 {
1426 TiltFiveManager2.Instance.RefreshInputDevicePairings();
1427 }
1428 }
1429 }
1430
1431 protected override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
1432 {
1433 base.SetDrivenObjectTransform(settings, scaleSettings, gameboardSettings);
1434
1435 if(glassesDevice == null || !glassesDevice.added)
1436 {
1437 return;
1438 }
1439
1440 // World space positions
1441 QueueDeltaStateEvent(glassesDevice.devicePosition, pose_UWRLD.position);
1442 QueueDeltaStateEvent(glassesDevice.centerEyePosition, pose_UWRLD.position);
1443 QueueDeltaStateEvent(glassesDevice.leftEyePosition, eyePositions[AREyes.EYE_LEFT]);
1444 QueueDeltaStateEvent(glassesDevice.rightEyePosition, eyePositions[AREyes.EYE_RIGHT]);
1445
1446 // World space rotations
1447 QueueDeltaStateEvent(glassesDevice.deviceRotation, pose_UWRLD.rotation);
1448 QueueDeltaStateEvent(glassesDevice.centerEyeRotation, pose_UWRLD.rotation);
1449 QueueDeltaStateEvent(glassesDevice.leftEyeRotation, pose_UWRLD.rotation);
1450 QueueDeltaStateEvent(glassesDevice.rightEyeRotation, pose_UWRLD.rotation);
1451
1452 // Gameboard space positions
1453 QueueDeltaStateEvent(glassesDevice.RawPosition, pose_USTAGE.position);
1454 QueueDeltaStateEvent(glassesDevice.RawLeftEyePosition, WorldToGameboardSpace(eyePositions[AREyes.EYE_LEFT], scaleSettings, gameboardSettings));
1455 QueueDeltaStateEvent(glassesDevice.RawRightEyePosition, WorldToGameboardSpace(eyePositions[AREyes.EYE_RIGHT], scaleSettings, gameboardSettings));
1456
1457 // Gameboard space rotations
1458 QueueDeltaStateEvent(glassesDevice.RawRotation, pose_USTAGE.rotation);
1459 QueueDeltaStateEvent(glassesDevice.leftEyeRotation, pose_USTAGE.rotation);
1460 QueueDeltaStateEvent(glassesDevice.rightEyeRotation, pose_USTAGE.rotation);
1461
1462 InputSystem.QueueDeltaStateEvent(glassesDevice.isTracked, isTracked);
1463
1464 var trackingState = TrackingState.Tracking;
1465 if (!isTracked)
1466 {
1467 trackingState = settings.FailureMode == TrackableSettings.TrackingFailureMode.FreezePosition
1468 ? TrackingState.Limited
1469 : TrackingState.None;
1470 }
1471
1472 InputSystem.QueueDeltaStateEvent(glassesDevice.trackingState, (int)trackingState);
1473 }
1474
1475 private static void QueueDeltaStateEvent(Vector3Control vector3Control, Vector3 delta)
1476 {
1477 InputSystem.QueueDeltaStateEvent(vector3Control.x, delta.x);
1478 InputSystem.QueueDeltaStateEvent(vector3Control.y, delta.y);
1479 InputSystem.QueueDeltaStateEvent(vector3Control.z, delta.z);
1480 }
1481
1482 // For some reason, using QueueDeltaStateEvent on a QuaternionControl with a Quaternion as the delta state doesn't work.
1483 // As a workaround, let's do it component-wise, since we know floats seem fine.
1484 private static void QueueDeltaStateEvent(QuaternionControl quaternionControl, Quaternion delta)
1485 {
1486 InputSystem.QueueDeltaStateEvent(quaternionControl.w, delta.w);
1487 InputSystem.QueueDeltaStateEvent(quaternionControl.x, delta.x);
1488 InputSystem.QueueDeltaStateEvent(quaternionControl.y, delta.y);
1489 InputSystem.QueueDeltaStateEvent(quaternionControl.z, delta.z);
1490 }
1491 }
1492#endif // UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1493
1494 #endregion Private Classes
1495
1496 }
1497}
float gameBoardScale
The gameboard's scale multiplies the perceived size of objects in the scene.
int monoWidth
The display width for a single eye.
Definition Glasses.cs:76
float stereoWidthToHeightRatio
The double-width display aspect ratio.
Definition Glasses.cs:84
float monoWidthToHeightRatio
The display aspect ratio.
Definition Glasses.cs:82
int stereoWidth
The display width for two eyes.
Definition Glasses.cs:78
int height
The display height.
Definition Glasses.cs:80
readonly int depthBuffer
The depth buffer's precision.
Definition Glasses.cs:86
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:319
static Vector3 up
Gets the head orientation's up vector.
Definition Glasses.cs:143
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:284
static bool updated
Gets a value indicating whether this T:TiltFive.Glasses is updated.
Definition Glasses.cs:106
static Vector3 leftEyePosition
Gets the left eye position.
Definition Glasses.cs:150
static bool IsTracked(PlayerIndex playerIndex=PlayerIndex.One)
Indicate if the specified glasses are tracked.
Definition Glasses.cs:174
static Vector3 rightEyePosition
Gets the right eye position.
Definition Glasses.cs:156
static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings=null, PlayerIndex playerIndex=PlayerIndex.None)
Reset this T:TiltFive.Glasses.
Definition Glasses.cs:368
static Vector3 position
Gets the head pose position.
Definition Glasses.cs:119
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:229
static Vector3 forward
Gets the head orientation's forward vector.
Definition Glasses.cs:131
static GameObject GetPoseRoot(PlayerIndex playerIndex)
Gets the pose root GameObject for the specified player.
Definition Glasses.cs:305
static Vector3 right
Gets the head orientation's right vector.
Definition Glasses.cs:137
static void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Updates this T:TiltFive.Glasses.
Definition Glasses.cs:393
static bool TryGetDisplayInfo(PlayerIndex playerIndex, out DisplayInfo displayInfo)
Gets the display info associated with the specified player's glasses.
Definition Glasses.cs:210
static bool TryGetPose(PlayerIndex playerIndex, out Pose pose)
Attempts to get the position and orientation of the specified player's glasses.
Definition Glasses.cs:266
static Quaternion rotation
Gets the head pose rotation.
Definition Glasses.cs:125
static bool glassesAvailable
Indicates whether the glasses are plugged in and functioning.
Definition Glasses.cs:162
static bool IsConnected(PlayerIndex playerIndex=PlayerIndex.One)
Indicate if the specified glasses are connected.
Definition Glasses.cs:404
static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings=null, PlayerIndex playerIndex=PlayerIndex.One)
Validates the specified glassesSettings with the specified glasses core.
Definition Glasses.cs:383
static bool configured
Gets a value indicating whether this T:TiltFive.Glasses is configured.
Definition Glasses.cs:113
static bool headPoseUpdated(PlayerIndex playerIndex=PlayerIndex.One)
Returns a boolean indication that the head pose was successfully updated.
Definition Glasses.cs:351
static Camera GetRightEye(PlayerIndex playerIndex)
Gets the right eye camera for the specified player's glasses.
Definition Glasses.cs:333
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.
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.
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...
PlayerIndex spectatedPlayer
The player that will have their perspective mirrored on screen.
Camera spectatorCamera
The camera used for rendering the onscreen preview.
bool IsConnected
Whether or not the trackable is connected.
Pose gameboardPose_UWRLD
The pose of the gameboard reference frame w.r.t. the Unity world-space reference frame.
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)