Tilt Five™ Unity API  1.4.1
NativePlugin.cs
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2020-2023 Tilt Five, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 using System;
17 using System.Runtime.InteropServices;
18 using UnityEngine;
19 
20 namespace TiltFive
21 {
22  public class NativePlugin
23  {
24 
25 #if (UNITY_IPHONE || UNITY_WEBGL) && !UNITY_EDITOR
26  public const string PLUGIN_LIBRARY = @"__Internal";
27 #else
28  public const string PLUGIN_LIBRARY = @"TiltFiveUnity";
29 #endif
30 
31  internal const int T5_RESULT_SUCCESS = 0;
32  internal const int T5_RESULT_UNKNOWN_ERROR = 1;
33 
34  #region Native Functions
35 
36  // Init
37  [DllImport(PLUGIN_LIBRARY)]
38  internal static extern int SetApplicationInfo(
39  T5_StringUTF8 appName,
40  T5_StringUTF8 appId,
41  T5_StringUTF8 appVersion);
42 
43  [DllImport(PLUGIN_LIBRARY)]
44  internal static extern int SetPlatformContext(
45  IntPtr platformContext);
46 
47  [DllImport(PLUGIN_LIBRARY)]
48  [return: MarshalAs(UnmanagedType.I4)]
49  internal static extern ServiceCompatibility GetServiceCompatibility();
50 
51  [DllImport(PLUGIN_LIBRARY)]
52  internal static extern int IsTiltFiveUIRequestingAttention(ref T5_Bool attentionRequested);
53 
54  // Glasses Acquisition
55  [DllImport(PLUGIN_LIBRARY)]
56  internal static extern int RefreshGlassesAvailable();
57 
58  // Set Maximum Desired Glasses. Count starts at 0.
59  //
60  // Note that using this to reduce the count will result in glasses being released back to
61  // the set of available glasses, which can invalidate handles currently in use. Generally,
62  // this should only be called once on load to indicate how many glasses your application
63  // wants.
64  [DllImport(PLUGIN_LIBRARY)]
65  internal static extern void SetMaxDesiredGlasses(byte maxCount);
66 
67  // Glasses Availability
68  [DllImport(PLUGIN_LIBRARY)]
69  internal static extern int GetGlassesHandles(
70  ref byte handleCount,
71  [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] UInt64[] glassesHandle);
72 
73  // Glasses Friendly Name
74  [DllImport(PLUGIN_LIBRARY)]
75  internal static extern int GetGlassesFriendlyName(UInt64 glassesHandle, ref T5_StringUTF8 glassesFriendlyName);
76 
77  // Head Pose
78  [DllImport(PLUGIN_LIBRARY)]
79  internal static extern int GetGlassesPose(
80  UInt64 glassesHandle,
81  ref T5_GlassesPose glassesPose,
82  [MarshalAs(UnmanagedType.I4)] T5_GlassesPoseUsage glassesPoseUsage);
83 
84  [DllImport(PLUGIN_LIBRARY)]
85  internal static extern int ConfigureCameraStream(UInt64 glassesHandle, T5_CameraStreamConfig cameraConfig);
86 
87  [DllImport(PLUGIN_LIBRARY)]
88  internal static extern int GetFilledCamImageBuffer(UInt64 glassesHandle, ref T5_CamImage camImageBuffer);
89 
90  [DllImport(PLUGIN_LIBRARY)]
91  internal static extern int SubmitEmptyCamImageBuffer(UInt64 glassesHandle, IntPtr camImageBuffer, UInt32 bufferSize);
92 
93  [DllImport(PLUGIN_LIBRARY)]
94  internal static extern int CancelCamImageBuffer(UInt64 glassesHandle, IntPtr buffer);
95 
96 #if TILT_FIVE_ENABLE_PROJECTOR_EXTRINSICS_ADJUSTMENTS
97  [DllImport(PLUGIN_LIBRARY)]
98  internal static extern int SetProjectorExtrinsicsAdjustment(UInt64 glassesHandle,
99  [MarshalAs(UnmanagedType.LPArray, SizeConst = 14)] float[] args);
100 #endif
101 
102  // Gameboard dimensions
103  [DllImport(PLUGIN_LIBRARY)]
104  internal static extern int GetGameboardDimensions(
105  [MarshalAs(UnmanagedType.I4)] GameboardType gameboardType,
106  ref T5_GameboardSize playableSpaceInMeters);
107 
108  // Wand Availability
109  [DllImport(PLUGIN_LIBRARY)]
110  internal static extern int GetWandAvailability(
111  UInt64 glassesHandle,
112  ref T5_Bool wandAvailable,
113  [MarshalAs(UnmanagedType.I4)] ControllerIndex wandTarget);
114 
115  // Scan for Wands
116  [DllImport(PLUGIN_LIBRARY)]
117  internal static extern int ScanForWands();
118 
119  // Wand Controls State
120  [DllImport(PLUGIN_LIBRARY)]
121  internal static extern int GetControllerState(
122  UInt64 glassesHandle,
123  [MarshalAs(UnmanagedType.I4)] ControllerIndex controllerIndex,
124  ref T5_ControllerState controllerState);
125 
126  [DllImport(PLUGIN_LIBRARY)]
127  internal static extern int SendImpulse(UInt64 glassesHandle,
128  [MarshalAs(UnmanagedType.I4)] ControllerIndex controllerIndex, float amplitude, ushort duration);
129 
130  // Submit Render Textures
131  [DllImport(PLUGIN_LIBRARY)]
132  internal static extern int QueueStereoImages(UInt64 glassesHandle, T5_FrameInfo frameInfo);
133 
134  [DllImport(PLUGIN_LIBRARY)]
135  internal static extern IntPtr GetSendFrameCallback();
136 
137  [DllImport(PLUGIN_LIBRARY)]
138  internal static extern int GetMaxDisplayDimensions(
139  [MarshalAs(UnmanagedType.LPArray, SizeConst = 2)] int[] displayDimensions);
140 
141  [DllImport(PLUGIN_LIBRARY)]
142  internal static extern int GetGlassesIPD(UInt64 glassesHandle, ref float glassesIPD);
143 
144  [DllImport(PLUGIN_LIBRARY)]
145  internal static extern void UnloadWorkaround();
146 
147 #if UNITY_WEBGL && !UNITY_EDITOR
148  [DllImport ("__Internal")]
149  internal static extern void RegisterPlugin();
150 #endif
151 
152  #endregion Native Functions
153  }
154 
160  internal struct T5_Bool
161  {
162  private readonly byte booleanByte;
163 
164  public T5_Bool(bool boolean)
165  {
166  booleanByte = Convert.ToByte(boolean);
167  }
168 
169  public static implicit operator bool(T5_Bool t5_boolean)
170  => Convert.ToBoolean(t5_boolean.booleanByte);
171  public static implicit operator T5_Bool(bool boolean) => new T5_Bool(boolean);
172  }
173 
179  [StructLayout(LayoutKind.Sequential)]
180  internal struct T5_Position
181  {
182  public float X, Y, Z;
183 
184  public T5_Position(Vector3 position)
185  {
186  X = position.x;
187  Y = position.y;
188  Z = position.z;
189  }
190 
191  public static implicit operator Vector3(T5_Position t5_position)
192  => new Vector3(t5_position.X, t5_position.Y, t5_position.Z);
193  public static implicit operator T5_Position(Vector3 position) => new T5_Position(position);
194  }
195 
201  [StructLayout(LayoutKind.Sequential)]
202  internal struct T5_Rotation
203  {
204  public float W, X, Y, Z;
205 
206  public T5_Rotation(Quaternion rotation)
207  {
208  W = rotation.w;
209  X = rotation.x;
210  Y = rotation.y;
211  Z = rotation.z;
212  }
213 
214  public static implicit operator Quaternion(T5_Rotation t5_rotation)
215  => new Quaternion(t5_rotation.X, t5_rotation.Y, t5_rotation.Z, t5_rotation.W);
216  public static implicit operator T5_Rotation(Quaternion rotation) => new T5_Rotation(rotation);
217  }
218 
219  internal struct GlassesHandle : IEquatable<GlassesHandle>
220  {
221  private UInt64 glassesHandle;
222  public static implicit operator UInt64(GlassesHandle handle) => handle.glassesHandle;
223  public static implicit operator GlassesHandle(UInt64 handle) => new GlassesHandle() { glassesHandle = handle };
224  public override int GetHashCode() => glassesHandle.GetHashCode();
225  public bool Equals(GlassesHandle other) => glassesHandle == other.glassesHandle;
226  public override string ToString()
227  {
228  return glassesHandle.ToString();
229  }
230  }
231 
235  [StructLayout(LayoutKind.Sequential)]
236  internal struct T5_GlassesPose
237  {
238  public UInt64 TimestampNanos;
239 
240  private T5_Position posOfGLS_GBD;
241  private T5_Rotation rotationToGLS_GBD;
242 
244 
245  public Vector3 PosOfGLS_GBD { get => posOfGLS_GBD; set => posOfGLS_GBD = value; }
246  public Quaternion RotationToGLS_GBD { get => rotationToGLS_GBD; set => rotationToGLS_GBD = value; }
247  }
248 
256  [StructLayout(LayoutKind.Sequential)]
257  public struct T5_CamImage
258  {
259  public UInt16 ImageBufferWidth_PIX;
260  public UInt16 ImageBufferHeight_PIX;
261  public UInt16 ImageBufferStride_PIX;
262  public UInt32 ImageBufferSize_PIX;
263  public IntPtr ImageBuffer;
264  private T5_Position posOfCAM_GBD;
265  private T5_Rotation rotToCAM_GBD;
266  public Vector3 PosOfCAM_GBD { get => posOfCAM_GBD; set => posOfCAM_GBD = value; }
267  public Quaternion RotToCAM_GBD { get => rotToCAM_GBD; set => rotToCAM_GBD = value; }
268 
269  public T5_CamImage(UInt16 bufferWidth, UInt16 bufferHeight, UInt16 bufferStride, UInt32 bufferSize)
270  {
271  ImageBuffer = IntPtr.Zero;
272  ImageBufferWidth_PIX = bufferWidth;
273  ImageBufferHeight_PIX = bufferHeight;
274  ImageBufferStride_PIX = bufferStride;
275  ImageBufferSize_PIX = bufferSize;
276  posOfCAM_GBD = Vector3.zero;
277  rotToCAM_GBD = Quaternion.identity;
278  }
279  }
280 
284  [StructLayout(LayoutKind.Sequential)]
285  public struct T5_CameraStreamConfig
286  {
287  public byte cameraIndex;
288  private T5_Bool streamEnabled;
289  public bool enabled { get => streamEnabled; set => streamEnabled = value; }
290  }
291 
295  [StructLayout(LayoutKind.Sequential)]
296  internal struct T5_ControllerState
297  {
298  [StructLayout(LayoutKind.Sequential)]
299  public struct Joystick
300  {
301  public float X, Y;
302 
303  public static implicit operator Vector2(Joystick joystick) => new Vector2(joystick.X, joystick.Y);
304  }
305 
306  [StructLayout(LayoutKind.Sequential)]
307  public struct Buttons
308  {
309  private T5_Bool t5,
313  a,
314  b,
315  x,
316  y;
317 
318  public bool T5 { get => t5; set => t5 = value; }
319  public bool One { get => one; set => one = value; }
320  public bool Two { get => two; set => two = value; }
321  public bool Three { get => three; set => three = value; }
322  public bool A { get => a; set => a = value; }
323  public bool B { get => b; set => b = value; }
324  public bool X { get => x; set => x = value; }
325  public bool Y { get => y; set => y = value; }
326  [Obsolete("Buttons.System is deprecated, please use Wandbutton.T5 instead.")]
327  public bool System => T5;
328  [Obsolete("Buttons.Z is deprecated, please use Wandbutton.Three instead.")]
329  public bool Z => Three;
330  }
331 
332  public UInt64 TimestampNanos;
333 
334  private T5_Bool analogValid;
335  private T5_Bool batteryValid;
336  private T5_Bool buttonsValid;
337  private T5_Bool poseValid;
338 
339  public float Trigger;
340  public Joystick Stick;
341  public byte Battery;
342  public Buttons ButtonsState;
343 
344  private T5_Rotation rotToWND_GBD;
345  private T5_Position aimPos_GBD;
346  private T5_Position fingertipsPos_GBD;
347  private T5_Position gripPos_GBD;
348 
349  public T5_Hand Hand;
350 
351  public bool AnalogValid { get => analogValid; set => analogValid = value; }
352  public bool BatteryValid { get => batteryValid; set => batteryValid = value; }
353  public bool ButtonsValid { get => buttonsValid; set => buttonsValid = value; }
354  public bool PoseValid { get => poseValid; set => poseValid = value; }
355 
356  public Quaternion RotToWND_GBD { get => rotToWND_GBD; set => rotToWND_GBD = value; }
357  public Vector3 AimPos_GBD { get => aimPos_GBD; set => aimPos_GBD = value; }
358  public Vector3 FingertipsPos_GBD { get => fingertipsPos_GBD; set => fingertipsPos_GBD = value; }
359  public Vector3 GripPos_GBD { get => gripPos_GBD; set => gripPos_GBD = value; }
360  }
361 
367  [StructLayout(LayoutKind.Sequential)]
368  internal struct T5_VCI
369  {
370  public float StartX_VCI;
371  public float StartY_VCI;
372  public float Width_VCI;
373  public float Height_VCI;
374 
375  public T5_VCI(Rect rect)
376  {
377  StartX_VCI = rect.x;
378  StartY_VCI = rect.y;
379  Width_VCI = rect.width;
380  Height_VCI = rect.height;
381  }
382 
383  public static implicit operator Rect(T5_VCI vci)
384  => new Rect(vci.StartX_VCI, vci.StartY_VCI, vci.Width_VCI, vci.Height_VCI);
385  public static implicit operator T5_VCI(Rect rect) => new T5_VCI(rect);
386  }
387 
391  [StructLayout(LayoutKind.Sequential)]
392  internal struct T5_FrameInfo
393  {
394  public IntPtr LeftTexHandle;
395  public IntPtr RightTexHandle;
396  public UInt16 TexWidth_PIX;
397  public UInt16 TexHeight_PIX;
398 
399  private T5_Bool isSrgb;
400  private T5_Bool isUpsideDown;
401 
402  private T5_VCI vci;
403 
404  private T5_Rotation rotToLVC_GBD;
405  private T5_Position posOfLVC_GBD;
406 
407  private T5_Rotation rotToRVC_GBD;
408  private T5_Position posOfRVC_GBD;
409 
410  public bool IsSrgb { get => isSrgb; set => isSrgb = value; }
411  public bool IsUpsideDown { get => isUpsideDown; set => isUpsideDown = value; }
412  public Rect VCI { get => vci; set => vci = value; }
413  public Quaternion RotToLVC_GBD { get => rotToLVC_GBD; set => rotToLVC_GBD = value; }
414  public Vector3 PosOfLVC_GBD { get => posOfLVC_GBD; set => posOfLVC_GBD = value; }
415  public Quaternion RotToRVC_GBD { get => rotToRVC_GBD; set => rotToRVC_GBD = value; }
416  public Vector3 PosOfRVC_GBD { get => posOfRVC_GBD; set => posOfRVC_GBD = value; }
417  }
418 
426  [StructLayout(LayoutKind.Explicit, Pack = 4)]
427  internal struct T5_StringUTF8 : IDisposable
428  {
429  [FieldOffset(0)] private UInt32 maxBufferSize;
430  [FieldOffset(4)] private UInt32 length;
431  [FieldOffset(8)] private IntPtr pStringBytesUTF8;
432 
433  public T5_StringUTF8(string text)
434  {
435  pStringBytesUTF8 = IntPtr.Zero;
436  length = 0;
437  maxBufferSize = 16 * 1024;
438 
439  if (text != null)
440  {
441  // Allocate enough unmanaged memory to store the string
442  byte[] textBytesUTF8 = System.Text.Encoding.UTF8.GetBytes(text);
443 
444  // If the string is too long to fit in the unmanaged buffer, truncate it.
445  length = (UInt32)Math.Min(textBytesUTF8.Length, maxBufferSize);
446  pStringBytesUTF8 = Marshal.AllocHGlobal((int)maxBufferSize);
447 
448  // Store the string data
449  Marshal.Copy(textBytesUTF8, 0, pStringBytesUTF8, (int)length);
450  }
451  }
452 
453  public static implicit operator string(T5_StringUTF8 t5_StringUTF8)
454  => ToString(t5_StringUTF8);
455 
456  public static implicit operator T5_StringUTF8(string text) => new T5_StringUTF8(text);
457 
458  private static string ToString(T5_StringUTF8 t5_StringUTF8)
459  {
460  if (t5_StringUTF8.pStringBytesUTF8 == IntPtr.Zero)
461  {
462  return null;
463  }
464 
465  var managedBytes = new byte[t5_StringUTF8.length];
466  try
467  {
468  Marshal.Copy(t5_StringUTF8.pStringBytesUTF8, managedBytes, 0, (int)t5_StringUTF8.length);
469  return System.Text.Encoding.UTF8.GetString(managedBytes);
470  }
471  catch (Exception e)
472  {
473  Debug.LogError($"Failed to copy string from unmanaged memory: {e}");
474  return null;
475  }
476  }
477 
481  public void Dispose()
482  {
483  // Don't forget to free that unmanaged memory we allocated.
484  // Marshal.FreeHGlobal() will safely do nothing if IntPtr.Zero is passed in.
485  Marshal.FreeHGlobal(pStringBytesUTF8);
486  pStringBytesUTF8 = IntPtr.Zero;
487  }
488  }
489 
493  public enum ServiceCompatibility : Int32
494  {
495  // <summary>
496  // The running service is incompatible with this client.
497  // </summary>
498  Incompatible = 0,
499 
500  // <summary>
501  // The running service is compatible with this client.
502  // </summary>
503  Compatible = 1,
504 
505  // <summary>
506  // Don't know yet whether the running service is compatible with this client.
507  // </summary>
508  Unknown = 2,
509  }
510 
514  public enum PlayerIndex
515  {
516  [HideInInspector]
517  None = 0,
518  One = 1,
519  Two = 2,
520  Three = 3,
521  Four = 4
522  }
523 
528  public enum ControllerIndex : Int32
529  {
533  Right = 0,
534 
538  Left = 1,
539 
540  [Obsolete("ControllerIndex.Primary is obsolete, please update to use left/right based on user preference instead.", true)]
541  Primary = -1,
542 
543  [Obsolete("ControllerIndex.Secondary is obsolete, please update to use left/right based on user preference instead.", true)]
544  Secondary = -2,
545  }
546 
550  public enum GameboardType : Int32
551  {
558  GameboardType_None = 1,
559 
563  GameboardType_LE = 2,
564 
568  GameboardType_XE = 3,
569 
574  }
575 
579  [StructLayout(LayoutKind.Sequential)]
580  public struct T5_GameboardSize
581  {
586 
591 
596 
601 
606 
607  public T5_GameboardSize(float viewableExtentPositiveX, float viewableExtentNegativeX,
608  float viewableExtentPositiveZ, float viewableExtentNegativeZ,
609  float viewableExtentPositiveY)
610  {
611  ViewableExtentPositiveX = viewableExtentPositiveX;
612  ViewableExtentNegativeX = viewableExtentNegativeX;
613  ViewableExtentPositiveZ = viewableExtentPositiveZ;
614  ViewableExtentNegativeZ = viewableExtentNegativeZ;
615  ViewableExtentPositiveY = viewableExtentPositiveY;
616  }
617 
618  [Obsolete("This version of the T5_GameboardSize constructor is obsolete. " +
619  "Please use T5_GameboardSize(float viewableExtentPositiveX, float viewableExtentNegativeX, " +
620  "float viewableExtentPositiveZ, float viewableExtentNegativeZ, float viewableExtentPositiveY) instead.")]
621  public T5_GameboardSize(float playableSpaceX, float playableSpaceZ, float borderWidth)
622  {
623  ViewableExtentPositiveX = playableSpaceX / 2f;
624  ViewableExtentNegativeX = playableSpaceX / 2f;
625  ViewableExtentPositiveZ = playableSpaceZ / 2f;
626  ViewableExtentNegativeZ = playableSpaceZ / 2f;
628  }
629  }
630 
634  public enum ControllerPosition : Int32
635  {
639  Grip = 0,
640 
644  Fingertips = 1,
645 
649  Aim = 2
650  }
651 
655  public enum T5_Hand : byte
656  {
660  Unknown = 0,
661 
665  Left = 1,
666 
670  Right = 2,
671  }
672 
676  internal enum T5_GlassesPoseUsage : Int32
677  {
688  GlassesPresentation = 1,
689 
698  SpectatorPresentation = 2,
699  }
700 }
const string PLUGIN_LIBRARY
Definition: NativePlugin.cs:28
Definition: Log.cs:21
ServiceCompatibility
Whether the running service is compatible.
ControllerPosition
Points of interest along the wand controller, such as the handle position or wand tip.
@ Fingertips
The typical resting position of the player's fingertips, near the wand joystick and trigger.
@ Aim
The tip of the wand.
@ Grip
The center of the wand handle.
ControllerIndex
Since wands are all physically identical (they have no "handedness"), it doesn't make sense to addres...
@ Right
The wand held in the player's right hand.
@ Left
The wand held in the player's left hand.
GameboardType
The type of Gameboard being tracked by the glasses
@ GameboardType_None
No Gameboard at all.
@ GameboardType_LE
The LE Gameboard.
@ GameboardType_XE
The XE Gameboard, laid out flat.
@ GameboardType_XE_Raised
The XE Gameboard, folded upward using its kickstand.
T5_Hand
Reported wand hand
PlayerIndex
The Player index (e.g. Player One, Player Two, etc)
Represents a wrapper on a buffer comtaining a camera image.
T5_Position posOfCAM_GBD
T5_CamImage(UInt16 bufferWidth, UInt16 bufferHeight, UInt16 bufferStride, UInt32 bufferSize)
T5_Rotation rotToCAM_GBD
Quaternion RotToCAM_GBD
Camera Stream Configuration.
Physical dimensions of a gameboard, in meters.
T5_GameboardSize(float playableSpaceX, float playableSpaceZ, float borderWidth)
T5_GameboardSize(float viewableExtentPositiveX, float viewableExtentNegativeX, float viewableExtentPositiveZ, float viewableExtentNegativeZ, float viewableExtentPositiveY)
float ViewableExtentNegativeX
The distance in meters from the gameboard origin to the edge of the viewable area in the negative X d...
float ViewableExtentPositiveY
The distance in meters above the gameboard origin that the viewable area extends in the positive Y di...
float ViewableExtentPositiveX
The distance in meters from the gameboard origin to the edge of the viewable area in the positive X d...
float ViewableExtentPositiveZ
The distance in meters from the gameboard origin to the edge of the viewable area in the positive Z d...
float ViewableExtentNegativeZ
The distance in meters from the gameboard origin to the edge of the viewable area in the negative Z d...