Tilt Five™ Unity API  1.3.0
 
Loading...
Searching...
No Matches
Glasses.cs
Go to the documentation of this file.
1/*
2 * Copyright (C) 2020-2022 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,
60 EYE_MAX,
61 }
62
63 #endregion
64
65
66 #region Public Fields
67
73 public static bool updated => headPoseUpdated(PlayerIndex.One);
79 public static bool configured => GetPlayerOneGlassesCore()?.configured ?? false;
84 public static Vector3 position => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.position ?? Vector3.zero;
89 public static Quaternion rotation => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.rotation ?? Quaternion.identity;
94 public static Vector3 forward => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.forward ?? Vector3.forward;
99 public static Vector3 right => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.right ?? Vector3.right;
104 public static Vector3 up => GetPlayerOneGlassesCore()?.Pose_UnityWorldSpace.up ?? Vector3.up;
105
110 public static Vector3 leftEyePosition => GetPlayerOneGlassesCore()?.eyePositions[AREyes.EYE_LEFT] ?? Vector3.zero;
115 public static Vector3 rightEyePosition => GetPlayerOneGlassesCore()?.eyePositions[AREyes.EYE_RIGHT] ?? Vector3.zero;
116
120 public static bool glassesAvailable { get; private set; }
121
122 #endregion Public Fields
123
124
125 #region Public Functions
126
133 public static bool headPoseUpdated(PlayerIndex playerIndex = PlayerIndex.One)
134 {
135 if(playerIndex == PlayerIndex.None || !Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
136 {
137 return false;
138 }
139
140 return Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
141 && glassesCore.TrackingUpdated;
142 }
143
149 public static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null, PlayerIndex playerIndex = PlayerIndex.None)
150 {
151 if(spectatorSettings == null && !TryGetSpectatorSettings(out spectatorSettings))
152 {
153 Log.Error("Glasses.Reset() could not find any spectator settings.");
154 return;
155 }
156
157 // If playerIndex is none, reset all glasses
158 if(playerIndex == PlayerIndex.None)
159 {
160 foreach (var glassesCore in Instance.glassesCores.Values)
161 {
162 glassesCore.Reset(glassesSettings, spectatorSettings);
163 }
164 return;
165 }
166
167 if(Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
168 {
169 Reset(glassesSettings, spectatorSettings, glassesHandle);
170 }
171 }
172
182 public static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings = null, PlayerIndex playerIndex = PlayerIndex.One)
183 {
184 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
185 {
186 return false;
187 }
188
189 if(spectatorSettings == null && !TryGetSpectatorSettings(out spectatorSettings))
190 {
191 return false;
192 }
193
194 return Validate(glassesSettings, spectatorSettings, glassesHandle);
195 }
196
201 public static void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
202 {
203 if(!TryGetSpectatorSettings(out var spectatorSettings))
204 {
205 return;
206 }
207
208 // Update the glasses cores
209 foreach (var glassesCore in Instance.glassesCores.Values)
210 {
211 glassesCore.Update(glassesSettings, scaleSettings, gameBoardSettings, spectatorSettings);
212 }
213 }
214
220 public static bool IsTracked(PlayerIndex playerIndex = PlayerIndex.One)
221 {
222 return Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
223 && IsTracked(glassesHandle);
224 }
225
231 public static bool IsConnected(PlayerIndex playerIndex = PlayerIndex.One)
232 {
233 return Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
234 && IsConnected(glassesHandle);
235 }
236
237#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
238 public static bool TryGetGlassesDevice(PlayerIndex playerIndex, out GlassesDevice glassesDevice)
239 {
240 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
241 {
242 glassesDevice = null;
243 return false;
244 }
245
246 return TryGetGlassesDevice(glassesHandle, out glassesDevice);
247 }
248#endif
249
250 public static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
251 {
252 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
253 {
254 friendlyName = null;
255 return false;
256 }
257
258 return TryGetFriendlyName(glassesHandle, out friendlyName);
259 }
260
267 public static bool TryGetPose(PlayerIndex playerIndex, out Pose pose)
268 {
269 if(Player.TryGetGlassesHandle(playerIndex, out var glassesHandle) && Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
270 {
271 pose = glassesCore.Pose_UnityWorldSpace;
272 return true;
273 }
274 pose = new Pose();
275 return false;
276 }
277
285 public static bool TryGetPreviewPose(PlayerIndex playerIndex, out Pose pose)
286 {
287 if (Player.TryGetGlassesHandle(playerIndex, out var glassesHandle) && Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
288 {
289 pose = glassesCore.previewCore.Pose_UnityWorldSpace;
290 return true;
291 }
292 pose = new Pose();
293 return false;
294 }
295
296 public static GameObject GetPoseRoot(PlayerIndex playerIndex)
297 {
298 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
299 {
300 return null;
301 }
302 return GetPoseRoot(glassesHandle);
303 }
304
305 public static Camera GetLeftEye(PlayerIndex playerIndex)
306 {
307 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
308 {
309 return null;
310 }
311 return GetLeftEye(glassesHandle);
312 }
313
314 public static Camera GetRightEye(PlayerIndex playerIndex)
315 {
316 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
317 {
318 return null;
319 }
320 return GetRightEye(glassesHandle);
321 }
322
323 public static void ScanForGlasses()
324 {
325 // Enumerate the available glasses provided by the native plugin
326 UInt64[] glassesHandles = new UInt64[GlassesSettings.MAX_SUPPORTED_GLASSES_COUNT];
327 Debug.Assert(glassesHandles.Length <= Byte.MaxValue);
328 byte glassesCount = (byte)glassesHandles.Length;
329 int result = 0;
330
331 try
332 {
333 var refreshResult = NativePlugin.RefreshGlassesAvailable();
334 result = NativePlugin.GetGlassesHandles(ref glassesCount, glassesHandles);
335 }
336 catch (System.Exception e)
337 {
338 Log.Error($"Error getting glasses handles: {e.Message}");
339 }
340
341 // Add/Remove entries from the glassesCores dictionary
342 var glassesCores = Instance.glassesCores;
343 var incomingHandles = Instance.incomingHandles;
344 var lostHandles = Instance.lostHandles;
345
346 incomingHandles.Clear();
347 lostHandles.Clear();
348
349 // If we ran into a problem getting the glasses handles, all bets are off — just tear down the now-useless glasses cores.
350 if (result != 0)
351 {
352 foreach (var keyValuePair in glassesCores)
353 {
354 var lostHandle = keyValuePair.Key;
355 var glassesCore = keyValuePair.Value;
356
357 glassesCore.Dispose();
358 lostHandles.Add(lostHandle);
359 }
360 glassesCores.Clear();
361 }
362 else
363 {
364 // Add newly connected glasses
365 for (int i = 0; i < glassesCount; i++)
366 {
367 incomingHandles.Add(glassesHandles[i]);
368
369 // If we don't already have a glassesCore for this handle,
370 // and we still have an available player slot, then create a glassesCore.
371 if (!glassesCores.ContainsKey(glassesHandles[i]) && !Player.AllSupportedPlayersConnected())
372 {
373#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
374 glassesCores[glassesHandles[i]] = new GlassesDeviceCore(glassesHandles[i]);
375#else
376 glassesCores[glassesHandles[i]] = new GlassesCore(glassesHandles[i]);
377#endif
378 }
379 }
380
381 // Prune disconnected glasses
382 foreach (var currentHandle in glassesCores.Keys)
383 {
384 if (!incomingHandles.Contains(currentHandle))
385 {
386 lostHandles.Add(currentHandle);
387 }
388 }
389 foreach (var lostHandle in lostHandles)
390 {
391 glassesCores[lostHandle].Dispose();
392 glassesCores.Remove(lostHandle);
393 }
394 }
395
396 // If we don't have a default glasses ID yet, assign the first one we got from the native plugin as the default.
397 if (!Instance.defaultGlassesHandle.HasValue && result == 0 && glassesCount > 0)
398 {
399 Instance.defaultGlassesHandle = glassesHandles[0];
400 }
401
402 // If defaultGlassesId isn't valid anymore, reset its value.
403 if (Instance.defaultGlassesHandle.HasValue && !glassesCores.ContainsKey(Instance.defaultGlassesHandle.Value))
404 {
405 Instance.defaultGlassesHandle = null;
406 }
407 }
408
409 #endregion Public Functions
410
411
412 #region Internal Functions
413
414 internal static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings, GlassesHandle glassesHandle)
415 {
416 if (Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore) && spectatorSettings != null)
417 {
418 glassesCore.Reset(glassesSettings, spectatorSettings);
419 }
420 }
421
422 internal static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings, GlassesHandle glassesHandle)
423 {
424 return Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
425 && glassesCore.Validate(glassesSettings, spectatorSettings);
426 }
427
428 internal static void Update(GlassesHandle glassesHandle, GlassesSettings glassesSettings, ScaleSettings scaleSettings,
429 GameBoardSettings gameBoardSettings, SpectatorSettings spectatorSettings)
430 {
431 if (Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
432 {
433 glassesCore.Update(glassesSettings, scaleSettings, gameBoardSettings, spectatorSettings);
434 }
435 }
436
437 internal static bool IsTracked(GlassesHandle glassesHandle)
438 {
439 return Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
440 && glassesCore.IsTracked;
441 }
442
443 internal static bool IsConnected(GlassesHandle glassesHandle)
444 {
445 return Instance.glassesCores.ContainsKey(glassesHandle);
446 }
447
448#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
449 internal static bool TryGetGlassesDevice(GlassesHandle glassesHandle, out GlassesDevice glassesDevice)
450 {
451 if(!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore)
452 || !(glassesCore is GlassesDeviceCore glassesDeviceCore))
453 {
454 glassesDevice = null;
455 return false;
456 }
457
458 glassesDevice = glassesDeviceCore.glassesDevice;
459 return true;
460 }
461#endif
462
463 internal static bool TryGetFriendlyName(GlassesHandle glassesHandle, out string friendlyName)
464 {
465 if(!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
466 {
467 friendlyName = null;
468 return false;
469 }
470
471 return glassesCore.TryGetFriendlyName(out friendlyName);
472 }
473
480 internal static GlassesHandle? GetDefaultGlassesHandle()
481 {
482 var defaultGlassesHandle = Instance.defaultGlassesHandle;
483 return defaultGlassesHandle.HasValue && Instance.glassesCores.ContainsKey(defaultGlassesHandle.Value)
484 ? defaultGlassesHandle
485 : null;
486 }
487
488 internal static void SetDefaultGlassesHandle(GlassesHandle glassesHandle)
489 {
490 Instance.defaultGlassesHandle = glassesHandle;
491 }
492
493 internal static GlassesHandle[] GetAllConnectedGlassesHandles()
494 {
495 var keys = Instance.glassesCores.Keys;
496 GlassesHandle[] glassesHandles = new GlassesHandle[keys.Count];
497 keys.CopyTo(glassesHandles, 0);
498 return glassesHandles;
499 }
500
501 internal static void OnDisable()
502 {
503 foreach (var glassesCore in Instance.glassesCores.Values)
504 {
505 glassesCore.Dispose();
506 }
507 Instance.glassesCores.Clear();
508 }
509
510 #endregion Internal Functions
511
512 #region Private Functions
513
514 private static GameObject GetPoseRoot(GlassesHandle glassesHandle)
515 {
516 if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
517 {
518 return null;
519 }
520 return glassesCore.headPose.gameObject;
521 }
522
523 private static Camera GetLeftEye(GlassesHandle glassesHandle)
524 {
525 if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
526 {
527 return null;
528 }
529 return glassesCore.leftEye;
530 }
531
532 private static Camera GetRightEye(GlassesHandle glassesHandle)
533 {
534 if (!Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
535 {
536 return null;
537 }
538
539 return glassesCore.rightEye;
540 }
541
543 {
544 if(Player.TryGetGlassesHandle(PlayerIndex.One, out var glassesHandle)
545 && Instance.glassesCores.TryGetValue(glassesHandle, out var glassesCore))
546 {
547 return glassesCore;
548 }
549 return null;
550 }
551
552 private static bool TryGetSpectatorSettings(out SpectatorSettings spectatorSettings)
553 {
554 if (TiltFiveSingletonHelper.TryGetISceneInfo(out var sceneInfo))
555 {
556 if (sceneInfo is TiltFiveManager2 tiltFiveManager2)
557 {
558 spectatorSettings = tiltFiveManager2.spectatorSettings;
559 return true;
560 }
561
562 if (sceneInfo is TiltFiveManager tiltFiveManager)
563 {
564 spectatorSettings = tiltFiveManager.spectatorSettings;
565 return true;
566 }
567 }
568
569 spectatorSettings = null;
570 return false;
571 }
572
573 #endregion
574
575 private class BaseGlassesCore : TrackableCore<GlassesSettings, T5_GlassesPose>, IDisposable
576 {
577 public GlassesHandle glassesHandle;
578
579 public GameObject headPoseRoot;
580 public Transform headPose => headPoseRoot.transform;
581
583
591 private readonly Vector3 DEFAULT_GLASSES_POSITION_GAME_BOARD_SPACE = new Vector3(0f, 0.5f, -0.5f);
592
600 private readonly Quaternion DEFAULT_GLASSES_ROTATION_GAME_BOARD_SPACE = Quaternion.Euler(new Vector3(-45f, 0f, 0f));
601
603 GlassesHandle glassesHandle,
604 T5_GlassesPoseUsage glassesPoseUsage,
605 string name)
606 {
607 this.glassesHandle = glassesHandle;
608 this.glassesPoseUsage = glassesPoseUsage;
609 headPoseRoot = new GameObject(name);
610 }
611
612 public virtual void Dispose()
613 {
614 GameObject.Destroy(headPoseRoot);
615 }
616
623 protected override void Update(
624 GlassesSettings glassesSettings,
625 ScaleSettings scaleSettings,
626 GameBoardSettings gameBoardSettings)
627 {
628 // Obtain the latest glasses pose.
629 base.Update(glassesSettings, scaleSettings, gameBoardSettings);
630 }
631
632 protected override void SetDefaultPoseGameboardSpace(GlassesSettings settings)
633 {
634 pose_UGBD = new Pose(
635 DEFAULT_GLASSES_POSITION_GAME_BOARD_SPACE,
636 DEFAULT_GLASSES_ROTATION_GAME_BOARD_SPACE);
637 }
638
639 protected override void SetPoseUnityWorldSpace(
640 ScaleSettings scaleSettings,
641 GameBoardSettings gameBoardSettings)
642 {
643 pose_UWRLD = GameboardToWorldSpace(pose_UGBD, scaleSettings, gameBoardSettings);
644 }
645
646 protected override bool TryCheckConnected(out bool connected)
647 {
648 connected = IsConnected(glassesHandle);
649 return connected;
650 }
651
652 protected override bool TryGetStateFromPlugin(
653 out T5_GlassesPose glassesPose,
654 out bool poseIsValid,
655 GameBoardSettings gameBoardSettings)
656 {
657 T5_GlassesPose glassesPoseResult = new T5_GlassesPose { };
658
659 int result = 1;
660 try
661 {
663 glassesHandle,
664 ref glassesPoseResult,
665 glassesPoseUsage);
666 }
667 catch (System.Exception e)
668 {
669 Log.Error(e.Message);
670 }
671
672 GameBoard.SetGameboardType(glassesHandle, glassesPoseResult.GameboardType);
673 poseIsValid = result == 0 && glassesPoseResult.GameboardType != GameboardType.GameboardType_None;
674 glassesPose = glassesPoseResult;
675 return result == 0;
676 }
677
678 protected override void SetPoseGameboardSpace(
679 in T5_GlassesPose glassesPose,
680 GlassesSettings glassesSettings,
681 ScaleSettings scaleSettings,
682 GameBoardSettings gameboardSettings)
683 {
684 pose_UGBD = GetPoseGameboardSpace(glassesPose);
685 }
686
687 protected override void SetInvalidPoseGameboardSpace(
688 in T5_GlassesPose glassesPose,
689 GlassesSettings settings,
690 ScaleSettings scaleSettings,
691 GameBoardSettings gameboardSettings)
692 {
693 var newPose_GameboardSpace = GetPoseGameboardSpace(glassesPose);
694
695 if (!isTracked && settings.RejectUntrackedPositionData)
696 {
697 switch (settings.FailureMode)
698 {
699 case TrackableSettings.TrackingFailureMode.FreezePosition:
700 pose_UGBD = new Pose(pose_UGBD.position, newPose_GameboardSpace.rotation);
701 break;
702 // If we want to freeze both position and rotation when tracking is lost, things are easy - just do nothing.
703 case TrackableSettings.TrackingFailureMode.FreezePositionAndRotation:
704 break;
705 // Otherwise, we may want to keep the legacy behavior of snapping to a default position when tracking is lost.
706 case TrackableSettings.TrackingFailureMode.SnapToDefault:
707 // TODO: Rethink the existence of the preview pose? Is that a separate tracking failure mode?
708 if (settings.usePreviewPose)
709 {
710 pose_UGBD = new Pose(WorldToGameboardSpace(settings.previewPose.position, scaleSettings, gameboardSettings),
711 WorldToGameboardSpace(settings.previewPose.rotation, gameboardSettings));
712 }
713 // Otherwise do nothing and let the developer control the head pose camera themselves.
714 // It will be up to them to let go once head tracking kicks in again.
715 break;
716 }
717 }
718 else // Either things are working well and we're tracked, or we don't care about invalid data and want to display it regardless.
719 {
720 pose_UGBD = newPose_GameboardSpace;
721 }
722 }
723
724 protected override void SetDrivenObjectTransform(
725 GlassesSettings settings,
726 ScaleSettings scaleSettings,
727 GameBoardSettings gameBoardSettings)
728 {
729 if (!isTracked && settings.RejectUntrackedPositionData)
730 {
731 switch (settings.FailureMode)
732 {
733 case TrackableSettings.TrackingFailureMode.FreezePosition:
734 headPose.SetPositionAndRotation(headPose.position, pose_UWRLD.rotation);
735 break;
736 // If we want to freeze both position and rotation when tracking is lost, things are easy - just do nothing.
737 case TrackableSettings.TrackingFailureMode.FreezePositionAndRotation:
738 break;
739 // Otherwise, we may want to keep the legacy behavior of snapping to a default position when tracking is lost.
740 case TrackableSettings.TrackingFailureMode.SnapToDefault:
741 // TODO: Rethink the existence of the preview pose? Is that a separate tracking failure mode?
742 if (settings.usePreviewPose)
743 {
744 headPose.SetPositionAndRotation(
745 settings.previewPose.position,
746 settings.previewPose.rotation);
747 }
748 // Otherwise do nothing and let the developer control the head pose camera themselves.
749 // It will be up to them to let go once head tracking kicks in again.
750 break;
751 }
752 }
753 else // Either things are working well and we're tracked, or we don't care about invalid data and want to display it regardless.
754 {
755 headPose.SetPositionAndRotation(pose_UWRLD.position, pose_UWRLD.rotation);
756 }
757 }
758
759 private Pose GetPoseGameboardSpace(T5_GlassesPose glassesPose)
760 {
761 // Unity reference frames:
762 //
763 // UGLS - Unity GLaSses local space.
764 // +x right, +y up, +z forward
765 // UGBD - Unity Gameboard space.
766 // +x right, +y up, +z forward
767 //
768 // Tilt Five reference frames:
769 //
770 // DC - Our right-handed version of Unity's default camera space.
771 // +x right, +y up, +z backward
772 // GBD - Gameboard space.
773 // +x right, +y forward, +z up
774
775 Quaternion rotToDC_GBD = Quaternion.AngleAxis((-Mathf.PI / 2f) * Mathf.Rad2Deg, Vector3.right);
776
777 Quaternion rotToGBD_DC = Quaternion.Inverse(rotToDC_GBD);
778
779 Quaternion rotToGLS_GBD = glassesPose.RotationToGLS_GBD;
780
781 Quaternion rotToGLS_DC = rotToGLS_GBD * rotToGBD_DC;
782
783 Quaternion rotToUGBD_UGLS = new Quaternion(rotToGLS_DC.x, rotToGLS_DC.y, -rotToGLS_DC.z, rotToGLS_DC.w);
784
785 // Swap from right-handed (T5 internal) to left-handed (Unity) coord space.
786 Vector3 posOfUGLS_UGBD = ConvertPosGBDToUGBD(glassesPose.PosOfGLS_GBD);
787
788 return new Pose(posOfUGLS_UGBD, rotToUGBD_UGLS);
789 }
790 }
791
800 {
801 public GlassesPreviewCore(GlassesHandle glassesHandle) :
802 base(glassesHandle, T5_GlassesPoseUsage.SpectatorPresentation, $"Glasses {glassesHandle} preview")
803 { }
804
811 public new void Update(
812 GlassesSettings glassesSettings,
813 ScaleSettings scaleSettings,
814 GameBoardSettings gameBoardSettings)
815 {
816 // Obtain the latest glasses pose.
817 base.Update(glassesSettings, scaleSettings, gameBoardSettings);
818 }
819 }
820
825 {
827 public GameObject baseObject;
828 public string friendlyName;
829
833 public bool configured = false;
834
835 public Dictionary<AREyes, Vector3> eyePositions = new Dictionary<AREyes, Vector3>()
836 {
837 { AREyes.EYE_LEFT, new Vector3() },
838 { AREyes.EYE_RIGHT, new Vector3() }
839 };
840
841 public Dictionary<AREyes, Quaternion> eyeRotations = new Dictionary<AREyes, Quaternion>()
842 {
843 { AREyes.EYE_LEFT, new Quaternion() },
844 { AREyes.EYE_RIGHT, new Quaternion() }
845 };
846
848
849 public GlassesCore(GlassesHandle glassesHandle) :
850 base(glassesHandle, T5_GlassesPoseUsage.GlassesPresentation, $"Glasses {glassesHandle}")
851 {
852 if(!Player.TryGetPlayerIndex(glassesHandle, out playerIndex))
853 {
854 Player.TryAddPlayer(glassesHandle, out playerIndex);
855 }
856 previewCore = new GlassesPreviewCore(glassesHandle);
857 CameraImage.Initialize(glassesHandle);
858
859 Log.Info($"Glasses {glassesHandle} connected.");
860 }
861
862 public override void Dispose()
863 {
864 GameObject.Destroy(splitStereoCamera);
865 GameObject.Destroy(baseObject);
866 CameraImage.RemoveCore(glassesHandle);
867 previewCore.Dispose();
868
869 base.Dispose();
870
871 Log.Info($"Glasses {glassesHandle} (\"{friendlyName}\") disconnected");
872 }
873
878 public bool TrackingUpdated { get; private set; } = false;
879
883 private SplitStereoCamera splitStereoCamera = null;
884
885 internal Camera leftEye => splitStereoCamera?.leftEyeCamera;
886 internal Camera rightEye => splitStereoCamera?.rightEyeCamera;
887
893 public void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
894 {
895 base.Reset(glassesSettings);
896
897 configured = false;
898
899 if(null == spectatorSettings.spectatorCamera)
900 {
901 Log.Error($"Required Camera assignment missing from { spectatorSettings.GetType() }. Check Spectator settings in Tilt Five Manager");
902 return;
903 }
904
905#if UNITY_EDITOR
906 if (glassesSettings.tiltFiveXR)
907 {
908#endif
909 //if the splitScreenCamera does not exist already.
910 if (null == splitStereoCamera)
911 {
912 //get the head pose camera's GameObject
913 GameObject spectatorCameraObject = spectatorSettings.spectatorCamera.gameObject;
914
915 // Initialize the SplitStereoCamera
916 splitStereoCamera = spectatorCameraObject.AddComponent<SplitStereoCamera>();
917 if (null == headPoseRoot)
918 {
919
920 headPoseRoot = new GameObject($"Glasses {glassesHandle}");
921 previewCore = new GlassesPreviewCore(glassesHandle);
922 }
923 if (glassesSettings.objectTemplate && null == baseObject)
924 {
925 baseObject = GameObject.Instantiate(glassesSettings.objectTemplate);
926 baseObject.transform.parent = headPoseRoot.transform;
927 baseObject.name = $"{baseObject.transform.parent.name} - Prefab {playerIndex}";
928
929#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
930 // If the provided prefab includes a playerInput, we assume the developer
931 // is trying to use the input system to manage/enumerate their players.
932 // Warn the developer that they should include a PlayerInputManager
933 // if there isn't one already.
934 var playerInput = baseObject.GetComponentInChildren<PlayerInput>();
935 var playerInputManager = GameObject.FindObjectOfType<PlayerInputManager>();
936 if (playerInput != null && playerInputManager == null)
937 {
938 Log.Warn("No PlayerInputManager detected. " +
939 "Add a PlayerInputManager to the scene to enable the Input System " +
940 "to enumerate players as T5 devices connect/disconnect.");
941 }
942
943 if (playerInput != null && playerInput.actions != null && playerInput.actions.controlSchemes.Count < 1)
944 {
945 // Control Schemes appear to be very important when creating an InputUser.
946 // This isn't well-documented, but this can be a problem when working with a brand new empty Action Map.
947 // If the action map doesn't include any defined schemes, User #1 (the second user)
948 // will not be created in the Input System, and it isn't an obvious thing to debug.
949 // Warn the developer if the action map they provide doesn't have any schemes defined yet.
950 Log.Warn("The prefab provided to TiltFiveManager2 appears to have a PlayerInput component " +
951 "with an action map assigned, but the provided action map has no defined control schemes. " +
952 "This may cause the input system to fail to create a new InputUser for players 2 and beyond. " +
953 "Consider defining a control scheme in the provided action map.");
954 }
955#endif
956 }
957
958 splitStereoCamera.Initialize(headPoseRoot, glassesSettings, spectatorSettings);
959 }
960#if UNITY_EDITOR
961 }
962#endif //UNITY_EDITOR
963
964 TryGetFriendlyName(out friendlyName);
965
966 configured = true;
967 }
968
975 public bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
976 {
977 bool valid = true;
978
979 valid &= splitStereoCamera != null
980 && glassesSettings.cameraTemplate == splitStereoCamera.cameraTemplate
981 && spectatorSettings == splitStereoCamera.spectatorSettings;
982
983 return valid;
984 }
985
986 public bool TryGetFriendlyName(out string friendlyName)
987 {
988 T5_StringUTF8 friendlyNameResult = "";
989 int result = 1;
990
991 try
992 {
993 result = NativePlugin.GetGlassesFriendlyName(glassesHandle, ref friendlyNameResult);
994 }
995 catch (System.Exception e)
996 {
997 Log.Error($"Error getting friendly name: {e.Message}");
998 }
999 finally
1000 {
1001 friendlyName = (result == 0)
1002 ? friendlyNameResult
1003 : null;
1004
1005 // Unfortunately we can't use a "using" block for friendlyNameResult
1006 // since "using" parameters are readonly, preventing us from passing it via "ref".
1007 // We do the next best thing with try-finally and dispose of it here.
1008 friendlyNameResult.Dispose();
1009 }
1010
1011 return result == 0;
1012 }
1013
1018 public virtual void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, SpectatorSettings spectatorSettings)
1019 {
1020 TrackingUpdated = false;
1021
1022 if (null == glassesSettings)
1023 {
1024 Log.Error("GlassesSettings configuration required for Glasses tracking Update.");
1025 return;
1026 }
1027
1028 if (null == splitStereoCamera)
1029 {
1030 Log.Error($"Stereo camera(s) missing from Glasses {glassesHandle} - aborting Update.");
1031 return;
1032 }
1033
1034 if (glassesSettings.cameraTemplate != splitStereoCamera.cameraTemplate)
1035 {
1036 Log.Warn("Found mismatched template Cameras in GlassesCore Update - should call Reset.");
1037 return;
1038 }
1039
1040 if (spectatorSettings != splitStereoCamera.spectatorSettings)
1041 {
1042 Log.Warn("Found mismatched spectator settings in GlassesCore Update - should call Reset.");
1043 return;
1044 }
1045
1046 // Obtain the latest glasses poses.
1047 base.Update(glassesSettings, scaleSettings, gameBoardSettings);
1048 previewCore.Update(glassesSettings, scaleSettings, gameBoardSettings);
1049
1050 // Check whether the glasses are plugged in and available.
1051 glassesAvailable = isConnected;
1052 splitStereoCamera.glassesHandle = glassesHandle;
1053 splitStereoCamera.enabled = glassesAvailable;
1054
1055 // Sync settings with splitStereoCamera
1056 splitStereoCamera.glassesSettings = glassesSettings;
1057 splitStereoCamera.spectatorSettings = spectatorSettings;
1058
1059 // Enable spectating for player specified in the spectator settings, if they're available.
1060 splitStereoCamera.UseSpectatorCamera = Player.IsConnected(spectatorSettings.spectatedPlayer)
1061 && Player.TryGetGlassesHandle(spectatorSettings.spectatedPlayer, out var spectatorGlassesHandle)
1062 && spectatorGlassesHandle == glassesHandle;
1063
1064 // Get the glasses pose in Unity world-space.
1065 float scaleToUGBD_UWRLD = scaleSettings.physicalMetersPerWorldSpaceUnit * gameBoardSettings.gameBoardScale;
1066 float scaleToUWRLD_UGBD = scaleSettings.GetScaleToUWRLD_UGBD(gameBoardSettings.gameBoardScale);
1067
1068 // Set the game board transform on the SplitStereoCamera.
1069 splitStereoCamera.posUGBD_UWRLD = gameboardPos_UWRLD.position;
1070 splitStereoCamera.rotToUGBD_UWRLD = gameboardPos_UWRLD.rotation;
1071 splitStereoCamera.scaleToUGBD_UWRLD = scaleToUGBD_UWRLD;
1072
1073 // TODO: Revisit native XR support.
1074
1075 // NOTE: We do this because "Mock HMD" in UNITY_2017_0_2_OR_NEWER
1076 // the fieldOfView is locked to 111.96 degrees (Vive emulation),
1077 // so setting custom projection matrices is broken. If Unity
1078 // opens the API to custom settings, we can go back to native XR
1079 // support.
1080
1081 // Manual split screen 'new glasses' until the day Unity lets
1082 // me override their Mock HMD settings.
1083
1084 // compute half ipd translation
1085 float ipd_UGBD = GlassesSettings.DEFAULT_IPD_UGBD;
1086 if(!Display.GetGlassesIPD(glassesHandle, ref ipd_UGBD) && glassesAvailable)
1087 {
1088 Log.Error("Failed to obtain Glasses IPD");
1089 }
1090 float ipd_UWRLD = scaleToUWRLD_UGBD * ipd_UGBD;
1091 Vector3 eyeOffset = (headPose.right.normalized * (ipd_UWRLD * 0.5f));
1092
1093 // set the left eye camera offset from the head by the half ipd amount (-)
1094 eyePositions[AREyes.EYE_LEFT] = headPose.position - eyeOffset;
1095 eyeRotations[AREyes.EYE_LEFT] = headPose.rotation;
1096
1097 // set the right eye camera offset from the head by the half ipd amount (+)
1098 eyePositions[AREyes.EYE_RIGHT] = headPose.position + eyeOffset;
1099 eyeRotations[AREyes.EYE_RIGHT] = headPose.rotation;
1100
1101 Camera leftEyeCamera = splitStereoCamera.leftEyeCamera;
1102 if (null != leftEyeCamera)
1103 {
1104 GameObject leftEye = leftEyeCamera.gameObject;
1105 leftEye.transform.position = eyePositions[AREyes.EYE_LEFT];
1106 leftEye.transform.rotation = eyeRotations[AREyes.EYE_LEFT];
1107
1108 //make sure projection fields are synchronized to the head camera.
1109 leftEyeCamera.nearClipPlane = glassesSettings.nearClipPlane / scaleToUGBD_UWRLD;
1110 leftEyeCamera.farClipPlane = glassesSettings.farClipPlane / scaleToUGBD_UWRLD;
1111 leftEyeCamera.fieldOfView = glassesSettings.fieldOfView;
1112 leftEyeCamera.cullingMask = glassesSettings.cullingMask;
1113 }
1114
1115 Camera rightEyeCamera = splitStereoCamera.rightEyeCamera;
1116 if (null != rightEyeCamera)
1117 {
1118 GameObject rightEye = rightEyeCamera.gameObject;
1119 rightEye.transform.position = eyePositions[AREyes.EYE_RIGHT];
1120 rightEye.transform.rotation = eyeRotations[AREyes.EYE_RIGHT];
1121
1122 //make sure projection fields are synchronized to the head camera.
1123 rightEyeCamera.nearClipPlane = glassesSettings.nearClipPlane / scaleToUGBD_UWRLD;
1124 rightEyeCamera.farClipPlane = glassesSettings.farClipPlane / scaleToUGBD_UWRLD;
1125 rightEyeCamera.fieldOfView = glassesSettings.fieldOfView;
1126 rightEyeCamera.cullingMask = glassesSettings.cullingMask;
1127 }
1128
1129 // TODO: Poll less frequently by plumbing t5_hmdGetChangedParams up to Unity.
1130 if (!TryGetFriendlyName(out glassesSettings.friendlyName))
1131 {
1132 glassesSettings.friendlyName = GlassesSettings.DEFAULT_FRIENDLY_NAME;
1133 }
1134 }
1135
1136 protected override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
1137 {
1138 base.SetDrivenObjectTransform(settings, scaleSettings, gameBoardSettings);
1139 }
1140 }
1141
1142#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1143 private class GlassesDeviceCore : GlassesCore, IDisposable
1144 {
1145 internal GlassesDevice glassesDevice;
1146
1147 private enum TrackingState : int
1148 {
1149 None,
1150 Limited,
1151 Tracking
1152 }
1153
1154 public GlassesDeviceCore(GlassesHandle glassesHandle) : base(glassesHandle)
1155 {
1156 glassesDevice = Input.GetGlassesDevice(playerIndex);
1157
1158 if(glassesDevice != null && glassesDevice.added)
1159 {
1160 InputSystem.EnableDevice(glassesDevice);
1161 }
1162 }
1163
1164 public override void Dispose()
1165 {
1166 base.Dispose();
1167
1168 if(glassesDevice != null && glassesDevice.added)
1169 {
1170 InputSystem.DisableDevice(glassesDevice);
1171 }
1172 }
1173
1174 protected override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
1175 {
1176 base.SetDrivenObjectTransform(settings, scaleSettings, gameboardSettings);
1177
1178 if(glassesDevice == null || !glassesDevice.added)
1179 {
1180 return;
1181 }
1182
1183 // World space positions
1184 QueueDeltaStateEvent(glassesDevice.devicePosition, pose_UWRLD.position);
1185 QueueDeltaStateEvent(glassesDevice.centerEyePosition, pose_UWRLD.position);
1186 QueueDeltaStateEvent(glassesDevice.leftEyePosition, eyePositions[AREyes.EYE_LEFT]);
1187 QueueDeltaStateEvent(glassesDevice.rightEyePosition, eyePositions[AREyes.EYE_RIGHT]);
1188
1189 // World space rotations
1190 QueueDeltaStateEvent(glassesDevice.deviceRotation, pose_UWRLD.rotation);
1191 QueueDeltaStateEvent(glassesDevice.centerEyeRotation, pose_UWRLD.rotation);
1192 QueueDeltaStateEvent(glassesDevice.leftEyeRotation, pose_UWRLD.rotation);
1193 QueueDeltaStateEvent(glassesDevice.rightEyeRotation, pose_UWRLD.rotation);
1194
1195 // Gameboard space positions
1196 QueueDeltaStateEvent(glassesDevice.RawPosition, pose_UGBD.position);
1197 QueueDeltaStateEvent(glassesDevice.RawLeftEyePosition, WorldToGameboardSpace(eyePositions[AREyes.EYE_LEFT], scaleSettings, gameboardSettings));
1198 QueueDeltaStateEvent(glassesDevice.RawRightEyePosition, WorldToGameboardSpace(eyePositions[AREyes.EYE_RIGHT], scaleSettings, gameboardSettings));
1199
1200 // Gameboard space rotations
1201 QueueDeltaStateEvent(glassesDevice.RawRotation, pose_UGBD.rotation);
1202 QueueDeltaStateEvent(glassesDevice.leftEyeRotation, pose_UGBD.rotation);
1203 QueueDeltaStateEvent(glassesDevice.rightEyeRotation, pose_UGBD.rotation);
1204
1205 InputSystem.QueueDeltaStateEvent(glassesDevice.isTracked, isTracked);
1206
1207 var trackingState = TrackingState.Tracking;
1208 if (!isTracked)
1209 {
1210 trackingState = settings.FailureMode == TrackableSettings.TrackingFailureMode.FreezePosition
1211 ? TrackingState.Limited
1212 : TrackingState.None;
1213 }
1214
1215 InputSystem.QueueDeltaStateEvent(glassesDevice.trackingState, (int)trackingState);
1216 }
1217
1218 private static void QueueDeltaStateEvent(Vector3Control vector3Control, Vector3 delta)
1219 {
1220 InputSystem.QueueDeltaStateEvent(vector3Control.x, delta.x);
1221 InputSystem.QueueDeltaStateEvent(vector3Control.y, delta.y);
1222 InputSystem.QueueDeltaStateEvent(vector3Control.z, delta.z);
1223 }
1224
1225 // For some reason, using QueueDeltaStateEvent on a QuaternionControl with a Quaternion as the delta state doesn't work.
1226 // As a workaround, let's do it component-wise, since we know floats seem fine.
1227 private static void QueueDeltaStateEvent(QuaternionControl quaternionControl, Quaternion delta)
1228 {
1229 InputSystem.QueueDeltaStateEvent(quaternionControl.w, delta.w);
1230 InputSystem.QueueDeltaStateEvent(quaternionControl.x, delta.x);
1231 InputSystem.QueueDeltaStateEvent(quaternionControl.y, delta.y);
1232 InputSystem.QueueDeltaStateEvent(quaternionControl.z, delta.z);
1233 }
1234 }
1235#endif
1236
1237 }
1238}
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:398
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:582
BaseGlassesCore(GlassesHandle glassesHandle, T5_GlassesPoseUsage glassesPoseUsage, string name)
Definition: Glasses.cs:602
override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Definition: Glasses.cs:724
override bool TryGetStateFromPlugin(out T5_GlassesPose glassesPose, out bool poseIsValid, GameBoardSettings gameBoardSettings)
Definition: Glasses.cs:652
override bool TryCheckConnected(out bool connected)
Determines whether the tracked object is still connected.
Definition: Glasses.cs:646
override void SetInvalidPoseGameboardSpace(in T5_GlassesPose glassesPose, GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
Definition: Glasses.cs:687
override void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Updates this T:TiltFive.Glasses.BaseGlassesCore
Definition: Glasses.cs:623
override void SetPoseUnityWorldSpace(ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Sets the pose values of the tracked object in Unity World Space.
Definition: Glasses.cs:639
Pose GetPoseGameboardSpace(T5_GlassesPose glassesPose)
Definition: Glasses.cs:759
override void SetPoseGameboardSpace(in T5_GlassesPose glassesPose, GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
Definition: Glasses.cs:678
override void SetDefaultPoseGameboardSpace(GlassesSettings settings)
Definition: Glasses.cs:632
Internal Glasses core runtime.
Definition: Glasses.cs:825
bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
Tests this T:TiltFive.Glasses.GlassesCore for validity with the parameterized T:TiltFive....
Definition: Glasses.cs:975
void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings)
Reset this T:TiltFive.Glasses.GlassesCore
Definition: Glasses.cs:893
override void Dispose()
Definition: Glasses.cs:862
GlassesCore(GlassesHandle glassesHandle)
Definition: Glasses.cs:849
virtual void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, SpectatorSettings spectatorSettings)
Updates this T:TiltFive.Glasses.GlassesCore
Definition: Glasses.cs:1018
bool TryGetFriendlyName(out string friendlyName)
Definition: Glasses.cs:986
Dictionary< AREyes, Vector3 > eyePositions
Definition: Glasses.cs:835
GlassesPreviewCore previewCore
Definition: Glasses.cs:847
override void SetDrivenObjectTransform(GlassesSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Definition: Glasses.cs:1136
bool configured
Configuration ready indicator.
Definition: Glasses.cs:833
Trackable core for the smoothed glasses preview pose.
Definition: Glasses.cs:800
new void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Updates this T:TiltFive.Glasses.GlassesPreviewCore
Definition: Glasses.cs:811
GlassesPreviewCore(GlassesHandle glassesHandle)
Definition: Glasses.cs:801
The Glasses API and runtime.
Definition: Glasses.cs:35
static Camera GetLeftEye(PlayerIndex playerIndex)
Definition: Glasses.cs:305
HashSet< GlassesHandle > incomingHandles
Definition: Glasses.cs:43
static Vector3 up
Gets the head orientation's up vector.
Definition: Glasses.cs:104
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:285
static bool updated
Gets a value indicating whether this T:TiltFive.Glasses is updated.
Definition: Glasses.cs:73
static Vector3 leftEyePosition
Gets the left eye position.
Definition: Glasses.cs:110
static Camera GetRightEye(GlassesHandle glassesHandle)
Definition: Glasses.cs:532
static bool IsTracked(PlayerIndex playerIndex=PlayerIndex.One)
Indicate if the specified glasses are tracked.
Definition: Glasses.cs:220
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:115
static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings=null, PlayerIndex playerIndex=PlayerIndex.None)
Reset this T:TiltFive.Glasses.
Definition: Glasses.cs:149
static Vector3 position
Gets the head pose position.
Definition: Glasses.cs:84
static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
Definition: Glasses.cs:250
static Vector3 forward
Gets the head orientation's forward vector.
Definition: Glasses.cs:94
Dictionary< GlassesHandle, GlassesCore > glassesCores
The glasses core runtimes.
Definition: Glasses.cs:41
static GameObject GetPoseRoot(PlayerIndex playerIndex)
Definition: Glasses.cs:296
static Vector3 right
Gets the head orientation's right vector.
Definition: Glasses.cs:99
static void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Updates this T:TiltFive.Glasses.
Definition: Glasses.cs:201
static GlassesCore GetPlayerOneGlassesCore()
Definition: Glasses.cs:542
static GameObject GetPoseRoot(GlassesHandle glassesHandle)
Definition: Glasses.cs:514
static Camera GetLeftEye(GlassesHandle glassesHandle)
Definition: Glasses.cs:523
static bool TryGetPose(PlayerIndex playerIndex, out Pose pose)
Attempts to get the position and orientation of the specified player's glasses.
Definition: Glasses.cs:267
static Quaternion rotation
Gets the head pose rotation.
Definition: Glasses.cs:89
static bool glassesAvailable
Indicates whether the glasses are plugged in and functioning.
Definition: Glasses.cs:120
static bool IsConnected(PlayerIndex playerIndex=PlayerIndex.One)
Indicate if the specified glasses are connected.
Definition: Glasses.cs:231
static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings=null, PlayerIndex playerIndex=PlayerIndex.One)
Validates the specified glassesSettings with the specified glasses core.
Definition: Glasses.cs:182
static bool configured
Gets a value indicating whether this T:TiltFive.Glasses is configured.
Definition: Glasses.cs:79
static void ScanForGlasses()
Definition: Glasses.cs:323
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:133
static Camera GetRightEye(PlayerIndex playerIndex)
Definition: Glasses.cs:314
static bool TryGetSpectatorSettings(out SpectatorSettings spectatorSettings)
Definition: Glasses.cs:552
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
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 pair of glasses connects.
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
static int GetGlassesPose(UInt64 glassesHandle, ref T5_GlassesPose glassesPose, [MarshalAs(UnmanagedType.I4)] T5_GlassesPoseUsage glassesPoseUsage)
static int GetGlassesFriendlyName(UInt64 glassesHandle, ref T5_StringUTF8 glassesFriendlyName)
static int RefreshGlassesAvailable()
static int GetGlassesHandles(ref byte handleCount, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] UInt64[] glassesHandle)
Provides access to player settings and functionality.
Definition: Player.cs:16
static bool IsConnected(PlayerIndex playerIndex)
Determines whether the specified player has an associated pair of glasses connected.
Definition: Player.cs:32
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)
Camera leftEyeCamera
The left eye Camera property.
Camera cameraTemplate
The Camera used as a template when creating the eye cameras.
Camera rightEyeCamera
The right eye Camera property.
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
T5_GlassesPoseUsage
Glasses pose usage indicator.
@ SpectatorPresentation
The pose will be used to render images to be presented on a device other than the glasses,...
@ GlassesPresentation
The pose will be used to render images to be presented on the glasses.
GameboardType
The type of Gameboard being tracked by the glasses.
PlayerIndex
The Player index (e.g. Player One, Player Two, etc)
Headset pose information to be retrieved with NativePlugin.GetGlassesPose(ref T5_GlassesPose)
GameboardType GameboardType
Quaternion RotationToGLS_GBD
Represents a string value.
void Dispose()
Safely disposes of this T5_StringUTF8 and any unmanaged memory allocated during its construction.