ImmersalSDK.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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. switch (DeviceType.type)
  171. {
  172. case "A01":
  173. Application.targetFrameRate = 300;
  174. break;
  175. default:
  176. Application.targetFrameRate = targetFrameRate ;
  177. break;
  178. }
  179. }
  180. private void SetDownsample()
  181. {
  182. if (downsample)
  183. {
  184. Immersal.Core.SetInteger("LocalizationMaxPixels", 1280*720);
  185. }
  186. else
  187. {
  188. Immersal.Core.SetInteger("LocalizationMaxPixels", 0);
  189. }
  190. }
  191. private void Update()
  192. {
  193. bool trackingQualityAvailable = false;
  194. if (ImmersalSDK.isHWAR)
  195. {
  196. #if HWAR
  197. trackingQualityAvailable = HWARHelper.TryGetTrackingQuality(out m_SLAMTrackingQuality);
  198. #endif
  199. }
  200. else
  201. {
  202. trackingQualityAvailable = ARHelper.TryGetTrackingQuality(out m_SLAMTrackingQuality);
  203. }
  204. if (trackingQualityAvailable && Localizer != null)
  205. {
  206. LocalizerStats stats = Localizer.stats;
  207. if (stats.localizationAttemptCount > 0)
  208. {
  209. q = CurrentResults(stats.localizationSuccessCount);
  210. if (!m_HasPose && q > 1)
  211. {
  212. m_HasPose = true;
  213. onPoseFound?.Invoke();
  214. }
  215. if (m_HasPose && q < 1 && m_SLAMTrackingQuality <= 1)
  216. {
  217. m_HasPose = false;
  218. Localizer.Reset();
  219. m_PreviousResults = 0;
  220. m_CurrentResults = 0;
  221. onPoseLost?.Invoke();
  222. }
  223. TrackingQuality = q;
  224. }
  225. }
  226. if (!isHWAR)
  227. {
  228. if (!m_bCamConfigDone && cameraManager != null)
  229. ConfigureCamera();
  230. }
  231. }
  232. private void ConfigureCamera()
  233. {
  234. #if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS)
  235. var cameraSubsystem = cameraManager.subsystem;
  236. if (cameraSubsystem == null || !cameraSubsystem.running)
  237. return;
  238. var configurations = cameraSubsystem.GetConfigurations(Allocator.Temp);
  239. if (!configurations.IsCreated || (configurations.Length <= 0))
  240. return;
  241. int bestError = int.MaxValue;
  242. var currentConfig = cameraSubsystem.currentConfiguration;
  243. int dw = (int)currentConfig?.width;
  244. int dh = (int)currentConfig?.height;
  245. if (dw == 0 && dh == 0)
  246. return;
  247. #if UNITY_ANDROID
  248. CameraResolution reso = androidResolution;
  249. #else
  250. CameraResolution reso = iOSResolution;
  251. #endif
  252. if (!m_bCamConfigDone)
  253. {
  254. m_InitialConfig = currentConfig;
  255. }
  256. switch (reso)
  257. {
  258. case CameraResolution.Default:
  259. dw = (int)currentConfig?.width;
  260. dh = (int)currentConfig?.height;
  261. break;
  262. case CameraResolution.HD:
  263. dw = 1280;
  264. dh = 720;
  265. break;
  266. case CameraResolution.FullHD:
  267. dw = 1920;
  268. dh = 1080;
  269. break;
  270. case CameraResolution.Max:
  271. dw = 80000;
  272. dh = 80000;
  273. break;
  274. }
  275. foreach (var config in configurations)
  276. {
  277. int perror = config.width * config.height - dw * dh;
  278. if (Math.Abs(perror) < bestError)
  279. {
  280. bestError = Math.Abs(perror);
  281. currentConfig = config;
  282. }
  283. }
  284. if (reso != CameraResolution.Default) {
  285. Debug.Log("resolution = " + (int)currentConfig?.width + "x" + (int)currentConfig?.height);
  286. cameraSubsystem.currentConfiguration = currentConfig;
  287. }
  288. else
  289. {
  290. cameraSubsystem.currentConfiguration = m_InitialConfig;
  291. }
  292. #endif
  293. m_bCamConfigDone = true;
  294. }
  295. int CurrentResults(int localizationResults) {
  296. int diffResults = localizationResults - m_PreviousResults;
  297. m_PreviousResults = localizationResults;
  298. if (diffResults > 0)
  299. {
  300. m_LatestPoseUpdated = Time.time;
  301. m_CurrentResults += diffResults;
  302. if (m_CurrentResults > 3)
  303. {
  304. m_CurrentResults = 3;
  305. }
  306. }
  307. else if (Time.time - m_LatestPoseUpdated > secondsToDecayPose)
  308. {
  309. m_LatestPoseUpdated = Time.time;
  310. if (m_CurrentResults > 0)
  311. {
  312. m_CurrentResults--;
  313. }
  314. }
  315. return m_CurrentResults;
  316. }
  317. public void RegisterLocalizer(LocalizerBase localizer)
  318. {
  319. Localizer = localizer;
  320. Localizer.OnReset += OnLocalizerReset;
  321. }
  322. public void UnRegisterLocalizer()
  323. {
  324. Localizer.OnReset -= OnLocalizerReset;
  325. Localizer = null;
  326. }
  327. private void OnLocalizerReset()
  328. {
  329. m_CurrentResults = m_PreviousResults = 0;
  330. m_HasPose = false;
  331. }
  332. }
  333. }