/*=============================================================================== Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved. This file is part of the Immersal SDK. The Immersal SDK cannot be copied, distributed, or made available to third-parties for commercial purposes without written permission of Immersal Ltd. Contact sales@immersal.com for licensing requests. ===============================================================================*/ using UnityEngine; using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Immersal.XR; #if UNITY_EDITOR || UNITY_STANDALONE using System.Diagnostics; #endif namespace Immersal { [StructLayout(LayoutKind.Sequential)] public struct CaptureInfo { public int captureSize; public int connected; } [StructLayout(LayoutKind.Sequential)] public struct LocalizeInfo { public int mapId; public Vector3 position; public Quaternion rotation; public int confidence; }; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void LogCallback(IntPtr msg); public static class Core { #region On-Device Mapping public static int MapAddImage(IntPtr pixels, int width, int height, int channels, ref Vector4 intrinsics, ref Vector3 pos, ref Quaternion rot) => Native.icvMapAddImage(pixels, width, height, channels, ref intrinsics, ref pos, ref rot); public static int MapImageGetCount() => Native.icvMapImageGetCount(); public static int MapPrepare(string path) => Native.icvMapPrepare(path); public static int MapGet(byte[] map) => Native.icvMapGet(map); public static int MapPointsGetCount() => Native.icvMapPointsGetCount(); public static int MapPointsGet(Vector3[] points) { GCHandle pointsHandle = GCHandle.Alloc(points, GCHandleType.Pinned); int n = Native.icvMapPointsGet(pointsHandle.AddrOfPinnedObject(), points.Length); pointsHandle.Free(); return n; } public static int MapResourcesFree() => Native.icvMapResourcesFree(); #endregion /// /// Get a Vector3 point cloud representation of the map data. /// /// An integer map id /// A preallocated Vector3 array for the points /// Returns the number of points if succeeded, 0 otherwise. public static int GetPointCloud(int mapId, Vector3[] points) { if (MapHandleMapping.TryGetHandle(mapId, out int handle)) { GCHandle vector3ArrayHandle = GCHandle.Alloc(points, GCHandleType.Pinned); int n = Native.icvPointsGet(handle, vector3ArrayHandle.AddrOfPinnedObject(), points.Length); vector3ArrayHandle.Free(); return n; } return -1; } /// /// Get point count of the map's point cloud. /// /// An integer map id /// Returns the number of points. public static int GetPointCloudSize(int mapId) { if (MapHandleMapping.TryGetHandle(mapId, out int handle)) { return Native.icvPointsGetCount(handle); } return -1; } /// /// Load map data from a .bytes file. /// /// Map data as a byte array /// An integer map id /// An integer map id public static int LoadMap(int mapId, byte[] buffer) { // if there is already a loaded map with the same id, return the handle pointing to that if (MapHandleMapping.TryGetHandle(mapId, out int handle)) { ImmersalLogger.Log($"Trying to load map {mapId}({handle}) into plugin, but it's already mapped."); return handle; } int mapHandle = Native.icvLoadMap(buffer); MapHandleMapping.AddMapping(mapId, mapHandle); ImmersalLogger.Log($"Loaded map {mapId}({mapHandle}) into plugin"); return mapHandle; } /// /// Free the map data from memory. /// /// An integer map handle /// Returns 1 if succeeded, 0 otherwise. public static int FreeMap(int mapId) { if (MapHandleMapping.TryGetHandle(mapId, out int mapHandle)) { ImmersalLogger.Log($"Freeing map {mapId}({mapHandle}) from plugin"); MapHandleMapping.RemoveMappingByMapId(mapId); return Native.icvFreeMap(mapHandle); } return 0; } /// /// Capture image into the current map. /// /// A preallocated byte array for the captured PNG image /// Int size of the array /// Raw pixel buffer data from the camera /// Image width /// Image height /// 1 or 3, monochromatic or RGB capture /// Int size of the captured PNG bytes public static CaptureInfo CaptureImage(byte[] capture, int captureSizeMax, byte[] pixels, int width, int height, int channels, int useMatching = 0) { GCHandle captureHandle = GCHandle.Alloc(capture, GCHandleType.Pinned); GCHandle pixelsHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned); CaptureInfo info = Native.icvCaptureImage(captureHandle.AddrOfPinnedObject(), captureSizeMax, pixelsHandle.AddrOfPinnedObject(), width, height, channels, useMatching); captureHandle.Free(); pixelsHandle.Free(); return info; } /// /// Gets the position and orientation of the image within the map. /// /// Output Vector3 for the position /// Output Quaternion for the orientation /// Image width /// Image height /// Camera intrinsics /// Raw pixel buffer data from the camera /// A LocalizeInfo struct with mapId. mapId will be -1 on failure. public static LocalizeInfo LocalizeImage(int n, int[] mapIds, int width, int height, ref Vector4 intrinsics, IntPtr pixels, int channels, int solverType, ref Quaternion cameraRotation) { if (MapHandleMapping.IdsToHandles(mapIds, out int[] handles)) { GCHandle intHandle = GCHandle.Alloc(handles, GCHandleType.Pinned); LocalizeInfo result = Native.icvLocalize(n, intHandle.AddrOfPinnedObject(), width, height, ref intrinsics, pixels, channels, solverType, ref cameraRotation); intHandle.Free(); // result.mapId is a handle at this point -> convert if (MapHandleMapping.TryGetMapId(result.mapId, out int mapId)) { result.mapId = mapId; return result; } } return new LocalizeInfo { mapId = -1 }; } /// /// Gets the position and orientation of the image within the map. /// /// Output Vector3 for the position /// Output Quaternion for the orientation /// Image width /// Image height /// Camera intrinsics /// Raw pixel buffer data from the camera /// A LocalizeInfo struct with mapId. mapId will be -1 on failure. public static LocalizeInfo LocalizeImage(int width, int height, ref Vector4 intrinsics, IntPtr pixels) { int n = 0; int[] mapIds = new int[1]; int channels = 1; Quaternion cameraRotation = Quaternion.identity; return LocalizeImage(n, mapIds, width, height, ref intrinsics, pixels, channels, 0, ref cameraRotation); } public static LocalizeInfo LocalizeImage(int width, int height, ref Vector4 intrinsics, IntPtr pixels, int channels, int solverType, ref Quaternion cameraRotation) { int n = 0; int[] mapIds = new int[1]; return LocalizeImage(n, mapIds, width, height, ref intrinsics, pixels, channels, solverType, ref cameraRotation); } /// /// Gets the position and orientation of the image within the map. /// /// ICameraData containing necessary data /// A LocalizeInfo struct with mapId. mapId will be -1 on failure. public static LocalizeInfo LocalizeImage(ICameraData cameraData, IntPtr pixelBuffer, int solverType = 0) { int channels = cameraData.Channels == 0 ? 1 : cameraData.Channels; // default to 1 Vector4 intrinsics = cameraData.Intrinsics; Quaternion r = cameraData.CameraRotationOnCapture * cameraData.Orientation; r.SwitchHandedness(); return LocalizeImage(cameraData.Width, cameraData.Height, ref intrinsics, pixelBuffer, channels, solverType, ref r); } public static LocalizeInfo icvLocalizeImageWithPrior(ICameraData cameraData, IntPtr pixelBuffer, ref Vector3 priorPos, int priorNNCount, float priorRadius) { int channels = cameraData.Channels == 0 ? 1 : cameraData.Channels; // default to 1 Vector4 intrinsics = cameraData.Intrinsics; int n = 0; int[] mapIds = new int[1]; if (MapHandleMapping.IdsToHandles(mapIds, out int[] handles)) { GCHandle intHandle = GCHandle.Alloc(handles, GCHandleType.Pinned); LocalizeInfo result = Native.icvLocalizePrior(n, intHandle.AddrOfPinnedObject(), cameraData.Width, cameraData.Height, ref intrinsics, pixelBuffer, channels, ref priorPos, priorNNCount, priorRadius); intHandle.Free(); // result.mapId is a handle at this point -> convert if (MapHandleMapping.TryGetMapId(result.mapId, out int mapId)) { result.mapId = mapId; return result; } } return new LocalizeInfo { mapId = -1 }; } /// /// /// /// /// /// /// public static int PosMapToEcef(double[] ecef, Vector3 map, double[] mapToEcef) { GCHandle ecefHandle = GCHandle.Alloc(ecef, GCHandleType.Pinned); GCHandle mapToEcefHandle = GCHandle.Alloc(mapToEcef, GCHandleType.Pinned); int r = Native.icvPosMapToEcef(ecefHandle.AddrOfPinnedObject(), ref map, mapToEcefHandle.AddrOfPinnedObject()); mapToEcefHandle.Free(); ecefHandle.Free(); return r; } /// /// /// /// /// /// public static int PosEcefToWgs84(double[] wgs84, double[] ecef) { GCHandle wgs84Handle = GCHandle.Alloc(wgs84, GCHandleType.Pinned); GCHandle ecefHandle = GCHandle.Alloc(ecef, GCHandleType.Pinned); int r = Native.icvPosEcefToWgs84(wgs84Handle.AddrOfPinnedObject(), ecefHandle.AddrOfPinnedObject()); ecefHandle.Free(); wgs84Handle.Free(); return r; } /// /// /// /// /// /// public static int PosWgs84ToEcef(double[] ecef, double[] wgs84) { GCHandle ecefHandle = GCHandle.Alloc(ecef, GCHandleType.Pinned); GCHandle wgs84Handle = GCHandle.Alloc(wgs84, GCHandleType.Pinned); int r = Native.icvPosWgs84ToEcef(ecefHandle.AddrOfPinnedObject(), wgs84Handle.AddrOfPinnedObject()); wgs84Handle.Free(); ecefHandle.Free(); return r; } /// /// /// /// /// /// /// public static int PosEcefToMap(out Vector3 map, double[] ecef, double[] mapToEcef) { GCHandle ecefHandle = GCHandle.Alloc(ecef, GCHandleType.Pinned); GCHandle mapToEcefHandle = GCHandle.Alloc(mapToEcef, GCHandleType.Pinned); int r = Native.icvPosEcefToMap(out map, ecefHandle.AddrOfPinnedObject(), mapToEcefHandle.AddrOfPinnedObject()); mapToEcefHandle.Free(); ecefHandle.Free(); return r; } /// /// /// /// /// /// /// public static int PosMapToWgs84(double[] wgs84, Vector3 map, double[] mapToEcef) { double[] ecef = new double[3]; int err = PosMapToEcef(ecef, map, mapToEcef); if (err != 0) return err; return PosEcefToWgs84(wgs84, ecef); } /// /// /// /// /// /// /// public static int RotMapToEcef(out Quaternion ecef, Quaternion map, double[] mapToEcef) { GCHandle mapToEcefHandle = GCHandle.Alloc(mapToEcef, GCHandleType.Pinned); int r = Native.icvRotMapToEcef(out ecef, ref map, mapToEcefHandle.AddrOfPinnedObject()); mapToEcefHandle.Free(); return r; } /// /// /// /// /// /// /// public static int RotEcefToMap(out Quaternion map, Quaternion ecef, double[] mapToEcef) { GCHandle mapToEcefHandle = GCHandle.Alloc(mapToEcef, GCHandleType.Pinned); int r = Native.icvRotEcefToMap(out map, ref ecef, mapToEcefHandle.AddrOfPinnedObject()); mapToEcefHandle.Free(); return r; } /// /// Get internal plugin parameters. /// /// Parameter name /// Returns an integer value if set, -1 otherwise. public static int GetInteger(string parameter) => Native.icvGetInteger(parameter); /// /// Set internal plugin parameters. /// /// Available parameters: /// "LocalizationMaxPixels" - 0 is no limit (the default), 960*720 or higher. /// "NumThreads" - how many CPU cores to use; -1 (system default) or a positive integer. /// "ImageCompressionLevel" - 0 (no compression, fastest) to 9 (slowest). Defaults to 4. /// /// Parameter name /// An integer parameter value /// Returns 1 if succeeded, -1 otherwise. public static int SetInteger(string parameter, int value) => Native.icvSetInteger(parameter, value); public static int ValidateUser(string token) => Native.icvValidateUser(token); } public static class MapHandleMapping { private static Dictionary mapIdToHandleMapping = new Dictionary(); private static Dictionary handleToMapIdMapping = new Dictionary(); public static void AddMapping(int mapId, int pluginHandle) { if (mapIdToHandleMapping.ContainsKey(mapId) || handleToMapIdMapping.ContainsKey(pluginHandle)) { return; } mapIdToHandleMapping[mapId] = pluginHandle; handleToMapIdMapping[pluginHandle] = mapId; } public static bool TryGetHandle(int mapId, out int pluginHandle) { return mapIdToHandleMapping.TryGetValue(mapId, out pluginHandle); } public static bool TryGetMapId(int pluginHandle, out int mapId) { mapId = -1; return handleToMapIdMapping.TryGetValue(pluginHandle, out mapId); } public static bool RemoveMappingByMapId(int mapId) { if (mapIdToHandleMapping.TryGetValue(mapId, out int pluginHandle)) { mapIdToHandleMapping.Remove(mapId); handleToMapIdMapping.Remove(pluginHandle); return true; } return false; } public static bool RemoveMappingByHandle(int pluginHandle) { if (handleToMapIdMapping.TryGetValue(pluginHandle, out int mapId)) { handleToMapIdMapping.Remove(pluginHandle); mapIdToHandleMapping.Remove(mapId); return true; } return false; } public static bool Clear() { foreach (KeyValuePair keyValuePair in mapIdToHandleMapping) { if (RemoveMappingByMapId(keyValuePair.Key) == false) return false; } return true; } public static bool IdsToHandles(int[] mapIds, out int[] handles) { handles = new int[mapIds.Length]; // single zero id case if (mapIds.Length == 1 && mapIds[0] == 0) { handles[0] = 0; return true; } for (int i = 0; i < handles.Length; i++) { // Dictionary lookups are O(1) complexity, no need for caching? if (!TryGetHandle(mapIds[i], out int handle)) return false; handles[i] = handle; } return true; } } public static class Native { private const string Assembly = #if UNITY_IOS && !UNITY_EDITOR "__Internal"; #else "PosePlugin"; #endif [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern void PP_RegisterLogCallback(IntPtr callbackDelegate); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvPointsGet(int mapHandle, IntPtr array, int maxCount); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvPointsGetCount(int mapHandle); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvLoadMap(byte[] buffer); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvFreeMap(int mapHandle); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern CaptureInfo icvCaptureImage(IntPtr capture, int captureSizeMax, IntPtr pixels, int width, int height, int channels, int useMatching); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern LocalizeInfo icvLocalize(int n, IntPtr handles, int width, int height, ref Vector4 intrinsics, IntPtr pixels, int channels, int solverType, ref Quaternion cameraRotation); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern LocalizeInfo icvLocalizePrior(int n, IntPtr handles, int width, int height, ref Vector4 intrinsics, IntPtr pixels, int channels, ref Vector3 priorPos, int priorNNCount, float priorRadius); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvPosMapToEcef(IntPtr ecef, ref Vector3 map, IntPtr mapToEcef); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvPosEcefToWgs84(IntPtr wgs84, IntPtr ecef); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvPosWgs84ToEcef(IntPtr ecef, IntPtr wgs84); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvPosEcefToMap(out Vector3 map, IntPtr ecef, IntPtr mapToEcef); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvRotMapToEcef(out Quaternion ecef, ref Quaternion map, IntPtr mapToEcef); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvRotEcefToMap(out Quaternion map, ref Quaternion ecef, IntPtr mapToEcef); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvSetInteger([MarshalAs(UnmanagedType.LPStr)] string parameter, int value); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvGetInteger([MarshalAs(UnmanagedType.LPStr)] string parameter); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvValidateUser([MarshalAs(UnmanagedType.LPStr)] string token); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvMapAddImage(IntPtr pixels, int width, int height, int channels, ref Vector4 intrinsics, ref Vector3 pos, ref Quaternion rot); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvMapImageGetCount(); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvMapPrepare([MarshalAs(UnmanagedType.LPStr)] string path); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvMapGet(byte[] map); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvMapPointsGetCount(); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvMapPointsGet(IntPtr points, int countMax); [DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)] public static extern int icvMapResourcesFree(); } }