Interhaptics SDK for Unity 1.6
Loading...
Searching...
No Matches
HapticManager.cs
Go to the documentation of this file.
1/* ​
2* Copyright (c) 2023 Go Touch VR SAS. All rights reserved. ​
3* ​
4*/
5
6using UnityEngine;
7#if UNITY_2019_3_OR_NEWER
8using UnityEngine.LowLevel;
9using UnityEngine.PlayerLoop;
10#else
11using UnityEngine.Experimental.LowLevel;
12using UnityEngine.Experimental.PlayerLoop;
13#endif
14
15
16namespace Interhaptics
17{
22
23 internal struct InterhapticsPlayerLoop
24 {
25 // Replace the rendering loop cycle used previously
26 internal struct UpdateHaptic { }
27 }
28
29 public static class HapticManager
30 {
31
32 private const float FIXED_TIMEFRAME = 0.03f;
36 public static bool DebugSwitch { get; set; } = false;
40 public static bool MonoScriptingBackend { get; internal set; } = false;
44 public static bool StopHapticsOnFocusLoss { get; set; } = true;
45 private static float lastCall = 0;
46
47 [RuntimeInitializeOnLoadMethod]
48 private static void AppStart()
49 {
50 GeneratePlayerLoopNodes();
51 Application.quitting += OnApplicationQuit;
52 Application.focusChanged += OnFocusChanged;
53#if UNITY_EDITOR
54 UnityEditor.EditorApplication.pauseStateChanged += OnPauseStateChanged;
55#endif
56
57#if UNITY_EDITOR_OSX
58 UnityEngine.Debug.LogWarning("Unity Editor in macOS will have dummy implementation of the Interhaptics Engine. To correctly debug in Unity use a Windows version of the Unity Editor.");
59#endif
60
61#if !UNITY_EDITOR_OSX
62 Core.HAR.Init();
63 Internal.HapticDeviceManager.DeviceInitLoop();
64#if UNITY_ANDROID && !ENABLE_METAQUEST && !ENABLE_OPENXR && !UNITY_EDITOR
65 Platforms.Mobile.GenericAndroidHapticAbstraction.Initialize();
66#elif UNITY_IPHONE
68#endif
69#endif
70 }
71
72 private static void GeneratePlayerLoopNodes()
73 {
74#if UNITY_2019_3_OR_NEWER
75 PlayerLoopSystem system = PlayerLoop.GetCurrentPlayerLoop();
76#else
77 PlayerLoopSystem system = PlayerLoop.GetDefaultPlayerLoop();
78#endif
79 PlayerLoopSystem updateHaptic = new PlayerLoopSystem()
80 {
81 updateDelegate = UpdateHaptic,
82 type = typeof(InterhapticsPlayerLoop.UpdateHaptic)
83 };
84
85 if (!InsertPlayerLoopNodeAfter<PostLateUpdate.UpdateAudio>(ref system, updateHaptic) &&
86 !InsertPlayerLoopNodeAfter<PostLateUpdate>(ref system, updateHaptic))
87 {
88 Debug.LogError("[Interhaptics] Impossible to initialize the haptic rendering loop");
89 return;
90 }
91
92 PlayerLoop.SetPlayerLoop(system);
93 }
94
95 private static bool InsertPlayerLoopNodeAfter<T>(ref PlayerLoopSystem system, PlayerLoopSystem elementToInsert)
96 {
97 if (system.subSystemList == null)
98 {
99 return false;
100 }
101
102 // We search the life cycle identified by the type T
103 for (var i = 0; i < system.subSystemList.Length; i++)
104 {
105 if (system.subSystemList[i].type == typeof(T))
106 {
107 i++; // i was the index of the tick identify by T. By incrementing it, it become the index of the life cycle we want to insert
108 System.Array.Resize(ref system.subSystemList, system.subSystemList.Length + 1); // increase the subsystem length of 1
109 System.Array.Copy(system.subSystemList, i, system.subSystemList, i + 1, system.subSystemList.Length - i - 1); // shift the elements after i
110 system.subSystemList[i] = elementToInsert; // assign the index i to the life cycle we want to introduce
111 return true;
112 }
113
114 // if the subsystem isn't of type T, we search inside its children
115 if (InsertPlayerLoopNodeAfter<T>(ref system.subSystemList[i], elementToInsert))
116 {
117 return true;
118 }
119 }
120
121 // arrive here only if no subsystem was of type T
122 return false;
123 }
124
125 private static void UpdateHaptic()
126 {
127 if (!Application.isPlaying)
128 {
129 return;
130 }
131 if (Time.realtimeSinceStartup - lastCall < FIXED_TIMEFRAME)
132 {
133 return;
134 }
135
136 //Compute all haptics event
137#if !UNITY_IOS && (!UNITY_ANDROID || ENABLE_METAQUEST || ENABLE_OPENXR || UNITY_EDITOR)
138 Core.HAR.ComputeAllEvents(Time.realtimeSinceStartup);
139#endif
140 //Insert device rendering loop here
141 Internal.HapticDeviceManager.DeviceRenderLoop();
142 lastCall = Time.realtimeSinceStartup;
143 }
144
145 private static void OnFocusChanged(bool hasFocus)
146 {
147 // Check if haptics should be stopped when out of focus TODO: pause the haptic playback
148 if ((!hasFocus) && (StopHapticsOnFocusLoss))
149 {
150 Core.HAR.ClearActiveEvents();
151 }
152 }
153
154#if UNITY_EDITOR
155 private static void OnPauseStateChanged(UnityEditor.PauseState state)
156 {
157 // TODO pause the haptic playback
158 if ((UnityEditor.PauseState.Paused == state) && (StopHapticsOnFocusLoss))
159 {
160 Core.HAR.ClearActiveEvents();
161 }
162 }
163#endif
164
165 private static void OnApplicationQuit()
166 {
167#if !UNITY_EDITOR_OSX
168 Internal.HapticDeviceManager.DeviceCleanLoop();
169 Core.HAR.ClearActiveEvents();
170 Core.HAR.ClearInactiveEvents();
171 Core.HAR.Quit();
172#endif
173
174#if UNITY_EDITOR
175 UnityEditor.EditorApplication.pauseStateChanged -= OnPauseStateChanged;
176#endif
177 Application.focusChanged -= OnFocusChanged;
178 Application.quitting -= OnApplicationQuit;
179 }
180
181 }
182
183}