ImmersalSDK.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /*===============================================================================
  2. Copyright (C) 2022 Immersal - Part of Hexagon. All Rights Reserved.
  3. This file is part of the Immersal SDK.
  4. The Immersal SDK cannot be copied, distributed, or made available to
  5. third-parties for commercial purposes without written permission of Immersal Ltd.
  6. Contact sdk@immersal.com for licensing requests.
  7. ===============================================================================*/
  8. using UnityEngine;
  9. using UnityEngine.Events;
  10. using Unity.Collections;
  11. using UnityEngine.XR.ARFoundation;
  12. using System;
  13. using System.Net.Http;
  14. using Immersal.AR;
  15. using UnityEngine.XR.ARSubsystems;
  16. namespace Immersal
  17. {
  18. public class ImmersalSDK : MonoBehaviour
  19. {
  20. public static string sdkVersion = "1.18.1";
  21. public static bool isHWAR = false;
  22. public static readonly string DefaultServer = "https://api.immersal.com";
  23. public enum CameraResolution { Default, HD, FullHD, Max }; // With Huawei AR Engine SDK, only Default (640x480) and Max (1440x1080) are supported.
  24. private static ImmersalSDK instance = null;
  25. [Tooltip("SDK developer token")]
  26. public string developerToken;
  27. [SerializeField]
  28. [Tooltip("Application target frame rate")]
  29. private int m_TargetFrameRate = 60;
  30. [SerializeField]
  31. [Tooltip("Android resolution")]
  32. private CameraResolution m_AndroidResolution = CameraResolution.FullHD;
  33. [SerializeField]
  34. [Tooltip("iOS resolution")]
  35. private CameraResolution m_iOSResolution = CameraResolution.Default;
  36. [Tooltip("Downsample image to HD resolution")]
  37. [SerializeField]
  38. private bool m_Downsample = true;
  39. public UnityEvent onPoseLost = null;
  40. public UnityEvent onPoseFound = null;
  41. public int secondsToDecayPose = 10;
  42. public LocalizerBase Localizer { get; private set; }
  43. public int TrackingQuality { get; private set; }
  44. private ARCameraManager m_CameraManager;
  45. private ARSession m_ARSession;
  46. private bool m_bCamConfigDone = false;
  47. private string m_LocalizationServer = DefaultServer;
  48. private int m_PreviousResults = 0;
  49. private int m_CurrentResults = 0;
  50. private int q = 0;
  51. private float m_LatestPoseUpdated = 0f;
  52. private int m_SLAMTrackingQuality = 0;
  53. private bool m_HasPose = false;
  54. private XRCameraConfiguration? m_InitialConfig;
  55. public static HttpClient client;
  56. public int targetFrameRate
  57. {
  58. get { return m_TargetFrameRate; }
  59. set
  60. {
  61. m_TargetFrameRate = value;
  62. SetFrameRate();
  63. }
  64. }
  65. public CameraResolution androidResolution
  66. {
  67. get { return m_AndroidResolution; }
  68. set
  69. {
  70. m_AndroidResolution = value;
  71. ConfigureCamera();
  72. }
  73. }
  74. public CameraResolution iOSResolution
  75. {
  76. get { return m_iOSResolution; }
  77. set
  78. {
  79. m_iOSResolution = value;
  80. ConfigureCamera();
  81. }
  82. }
  83. public bool downsample
  84. {
  85. get { return m_Downsample; }
  86. set
  87. {
  88. m_Downsample = value;
  89. SetDownsample();
  90. }
  91. }
  92. public string localizationServer
  93. {
  94. get { return m_LocalizationServer; }
  95. set
  96. {
  97. m_LocalizationServer = value;
  98. }
  99. }
  100. public ARCameraManager cameraManager
  101. {
  102. get
  103. {
  104. if (m_CameraManager == null)
  105. {
  106. m_CameraManager = UnityEngine.Object.FindObjectOfType<ARCameraManager>();
  107. }
  108. return m_CameraManager;
  109. }
  110. }
  111. public ARSession arSession
  112. {
  113. get
  114. {
  115. if (m_ARSession == null)
  116. {
  117. m_ARSession = UnityEngine.Object.FindObjectOfType<ARSession>();
  118. }
  119. return m_ARSession;
  120. }
  121. }
  122. public static ImmersalSDK Instance
  123. {
  124. get
  125. {
  126. #if UNITY_EDITOR
  127. if (instance == null && !Application.isPlaying)
  128. {
  129. instance = UnityEngine.Object.FindObjectOfType<ImmersalSDK>();
  130. }
  131. #endif
  132. if (instance == null)
  133. {
  134. Debug.LogError("No ImmersalSDK instance found. Ensure one exists in the scene.");
  135. }
  136. return instance;
  137. }
  138. }
  139. void Awake()
  140. {
  141. if (instance == null)
  142. {
  143. instance = this;
  144. }
  145. if (instance != this)
  146. {
  147. Debug.LogError("There must be only one ImmersalSDK object in a scene.");
  148. UnityEngine.Object.DestroyImmediate(this);
  149. return;
  150. }
  151. HttpClientHandler handler = new HttpClientHandler();
  152. handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
  153. client = new HttpClient(handler);
  154. client.DefaultRequestHeaders.ExpectContinue = false;
  155. if (developerToken != null && developerToken.Length > 0)
  156. {
  157. PlayerPrefs.SetString("token", developerToken);
  158. }
  159. }
  160. void Start()
  161. {
  162. SetFrameRate();
  163. #if !UNITY_EDITOR
  164. SetDownsample();
  165. #endif
  166. onPoseLost?.Invoke();
  167. }
  168. private void SetFrameRate()
  169. {
  170. Application.targetFrameRate = targetFrameRate;
  171. }
  172. private void SetDownsample()
  173. {
  174. if (downsample)
  175. {
  176. Immersal.Core.SetInteger("LocalizationMaxPixels", 1280*720);
  177. }
  178. else
  179. {
  180. Immersal.Core.SetInteger("LocalizationMaxPixels", 0);
  181. }
  182. }
  183. private void Update()
  184. {
  185. bool trackingQualityAvailable = false;
  186. if (ImmersalSDK.isHWAR)
  187. {
  188. #if HWAR
  189. trackingQualityAvailable = HWARHelper.TryGetTrackingQuality(out m_SLAMTrackingQuality);
  190. #endif
  191. }
  192. else
  193. {
  194. trackingQualityAvailable = ARHelper.TryGetTrackingQuality(out m_SLAMTrackingQuality);
  195. }
  196. if (trackingQualityAvailable && Localizer != null)
  197. {
  198. LocalizerStats stats = Localizer.stats;
  199. if (stats.localizationAttemptCount > 0)
  200. {
  201. q = CurrentResults(stats.localizationSuccessCount);
  202. if (!m_HasPose && q > 1)
  203. {
  204. m_HasPose = true;
  205. onPoseFound?.Invoke();
  206. }
  207. if (m_HasPose && q < 1 && m_SLAMTrackingQuality <= 1)
  208. {
  209. m_HasPose = false;
  210. Localizer.Reset();
  211. m_PreviousResults = 0;
  212. m_CurrentResults = 0;
  213. onPoseLost?.Invoke();
  214. }
  215. TrackingQuality = q;
  216. }
  217. }
  218. if (!isHWAR)
  219. {
  220. if (!m_bCamConfigDone && cameraManager != null)
  221. ConfigureCamera();
  222. }
  223. }
  224. private void ConfigureCamera()
  225. {
  226. #if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS)
  227. var cameraSubsystem = cameraManager.subsystem;
  228. if (cameraSubsystem == null || !cameraSubsystem.running)
  229. return;
  230. var configurations = cameraSubsystem.GetConfigurations(Allocator.Temp);
  231. if (!configurations.IsCreated || (configurations.Length <= 0))
  232. return;
  233. int bestError = int.MaxValue;
  234. var currentConfig = cameraSubsystem.currentConfiguration;
  235. int dw = (int)currentConfig?.width;
  236. int dh = (int)currentConfig?.height;
  237. if (dw == 0 && dh == 0)
  238. return;
  239. #if UNITY_ANDROID
  240. CameraResolution reso = androidResolution;
  241. #else
  242. CameraResolution reso = iOSResolution;
  243. #endif
  244. if (!m_bCamConfigDone)
  245. {
  246. m_InitialConfig = currentConfig;
  247. }
  248. switch (reso)
  249. {
  250. case CameraResolution.Default:
  251. dw = (int)currentConfig?.width;
  252. dh = (int)currentConfig?.height;
  253. break;
  254. case CameraResolution.HD:
  255. dw = 1280;
  256. dh = 720;
  257. break;
  258. case CameraResolution.FullHD:
  259. dw = 1920;
  260. dh = 1080;
  261. break;
  262. case CameraResolution.Max:
  263. dw = 80000;
  264. dh = 80000;
  265. break;
  266. }
  267. foreach (var config in configurations)
  268. {
  269. int perror = config.width * config.height - dw * dh;
  270. if (Math.Abs(perror) < bestError)
  271. {
  272. bestError = Math.Abs(perror);
  273. currentConfig = config;
  274. }
  275. }
  276. if (reso != CameraResolution.Default) {
  277. Debug.Log("resolution = " + (int)currentConfig?.width + "x" + (int)currentConfig?.height);
  278. cameraSubsystem.currentConfiguration = currentConfig;
  279. }
  280. else
  281. {
  282. cameraSubsystem.currentConfiguration = m_InitialConfig;
  283. }
  284. #endif
  285. m_bCamConfigDone = true;
  286. }
  287. int CurrentResults(int localizationResults) {
  288. int diffResults = localizationResults - m_PreviousResults;
  289. m_PreviousResults = localizationResults;
  290. if (diffResults > 0)
  291. {
  292. m_LatestPoseUpdated = Time.time;
  293. m_CurrentResults += diffResults;
  294. if (m_CurrentResults > 3)
  295. {
  296. m_CurrentResults = 3;
  297. }
  298. }
  299. else if (Time.time - m_LatestPoseUpdated > secondsToDecayPose)
  300. {
  301. m_LatestPoseUpdated = Time.time;
  302. if (m_CurrentResults > 0)
  303. {
  304. m_CurrentResults--;
  305. }
  306. }
  307. return m_CurrentResults;
  308. }
  309. public void RegisterLocalizer(LocalizerBase localizer)
  310. {
  311. Localizer = localizer;
  312. Localizer.OnReset += OnLocalizerReset;
  313. }
  314. public void UnRegisterLocalizer()
  315. {
  316. Localizer.OnReset -= OnLocalizerReset;
  317. Localizer = null;
  318. }
  319. private void OnLocalizerReset()
  320. {
  321. m_CurrentResults = m_PreviousResults = 0;
  322. m_HasPose = false;
  323. }
  324. }
  325. }