111
This commit is contained in:
8
Assets/Plugins/ImmersalSDK/Runtime.meta
Normal file
8
Assets/Plugins/ImmersalSDK/Runtime.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c0884dab4c8bbc4d8a979f9838b8cbe
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/ImmersalSDK/Runtime/Core.meta
Normal file
8
Assets/Plugins/ImmersalSDK/Runtime/Core.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9779e2a208874279abf2acd33869873
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Plugins.meta
Normal file
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Plugins.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4e1b058addf948b589443857632c8e7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f192b64343523994e999fa8a5c964120
|
||||
folderAsset: yes
|
||||
timeCreated: 1466076008
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3f6095e1f6cb45b399b9c5a3def17f9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Plugins/ImmersalSDK/Runtime/Core/Plugins/Android/arm64-v8a/.DS_Store
vendored
Normal file
BIN
Assets/Plugins/ImmersalSDK/Runtime/Core/Plugins/Android/arm64-v8a/.DS_Store
vendored
Normal file
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9571c8196841b422bb87ee1f1f937e39
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Android: 0
|
||||
Exclude Editor: 1
|
||||
Exclude Linux64: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
Exclude iOS: 1
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: ARM64
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec9137557106b48d8995616414845b71
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Android: 0
|
||||
Exclude Editor: 1
|
||||
Exclude Linux64: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
Exclude iOS: 1
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: ARM64
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0353ebb74a494fc38f3854047d3a2cd
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,129 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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.
|
||||
===============================================================================*/
|
||||
|
||||
#if (UNITY_IOS || PLATFORM_ANDROID) && !UNITY_EDITOR
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Immersal
|
||||
{
|
||||
public class NativeBindings
|
||||
{
|
||||
#if UNITY_IOS
|
||||
[DllImport("__Internal")]
|
||||
public static extern void startLocation();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern void stopLocation();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern double getLatitude();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern double getLongitude();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern double getAltitude();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern double getHorizontalAccuracy();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern double getVerticalAccuracy();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern bool locationServicesEnabled();
|
||||
|
||||
#elif PLATFORM_ANDROID
|
||||
static AndroidJavaClass obj = new AndroidJavaClass("com.immersal.nativebindings.Main");
|
||||
#endif
|
||||
|
||||
public static bool StartLocation()
|
||||
{
|
||||
if (!Input.location.isEnabledByUser)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_IOS
|
||||
startLocation();
|
||||
#elif PLATFORM_ANDROID
|
||||
obj.CallStatic("startLocation");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void StopLocation()
|
||||
{
|
||||
#if UNITY_IOS
|
||||
stopLocation();
|
||||
#elif PLATFORM_ANDROID
|
||||
obj.CallStatic("stopLocation");
|
||||
#endif
|
||||
}
|
||||
|
||||
public static double GetLatitude()
|
||||
{
|
||||
#if UNITY_IOS
|
||||
return getLatitude();
|
||||
#elif PLATFORM_ANDROID
|
||||
return obj.CallStatic<double>("getLatitude");
|
||||
#endif
|
||||
}
|
||||
|
||||
public static double GetLongitude()
|
||||
{
|
||||
#if UNITY_IOS
|
||||
return getLongitude();
|
||||
#elif PLATFORM_ANDROID
|
||||
return obj.CallStatic<double>("getLongitude");
|
||||
#endif
|
||||
}
|
||||
|
||||
public static double GetAltitude()
|
||||
{
|
||||
#if UNITY_IOS
|
||||
return getAltitude();
|
||||
#elif PLATFORM_ANDROID
|
||||
return obj.CallStatic<double>("getAltitude");
|
||||
#endif
|
||||
}
|
||||
|
||||
public static double GetHorizontalAccuracy()
|
||||
{
|
||||
#if UNITY_IOS
|
||||
return getHorizontalAccuracy();
|
||||
#elif PLATFORM_ANDROID
|
||||
return obj.CallStatic<double>("getHorizontalAccuracy");
|
||||
#endif
|
||||
}
|
||||
|
||||
public static double GetVerticalAccuracy()
|
||||
{
|
||||
#if UNITY_IOS
|
||||
return getVerticalAccuracy();
|
||||
#elif PLATFORM_ANDROID
|
||||
return obj.CallStatic<double>("getVerticalAccuracy");
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool LocationServicesEnabled()
|
||||
{
|
||||
#if UNITY_IOS
|
||||
return locationServicesEnabled();
|
||||
#elif PLATFORM_ANDROID
|
||||
return obj.CallStatic<bool>("locationServicesEnabled");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f141c749ad8c4a4dbdd7b392e91a9c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 867bc362f511875419b00825c35adb62
|
||||
folderAsset: yes
|
||||
timeCreated: 1469455304
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a60313de0e704fd682e181e56ca94a7
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 1
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Android: 1
|
||||
Exclude Editor: 0
|
||||
Exclude Linux: 1
|
||||
Exclude Linux64: 0
|
||||
Exclude LinuxUniversal: 0
|
||||
Exclude Lumin: 1
|
||||
Exclude OSXIntel: 1
|
||||
Exclude OSXIntel64: 0
|
||||
Exclude OSXUniversal: 0
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 0
|
||||
Exclude iOS: 1
|
||||
- first:
|
||||
: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: x86_64
|
||||
OS: OSX
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: ARMv7
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: OSX
|
||||
- first:
|
||||
Facebook: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Facebook: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Linux
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: LinuxUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: OSXIntel
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: OSXIntel64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>22D68</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>PosePlugin</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.immersal.PosePlugin</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>PosePlugin</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string></string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>13.3</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>22E245</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx13.3</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1430</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>14E222b</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2022 Immersal - Part of Hexagon. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict/>
|
||||
<key>files2</key>
|
||||
<dict/>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^Resources/</key>
|
||||
<true/>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^[^/]+$</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -0,0 +1,116 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3ad48f0b80387d47808cea0b43f73fb
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 1
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Android: 1
|
||||
Exclude Editor: 0
|
||||
Exclude Linux64: 0
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 0
|
||||
Exclude iOS: 1
|
||||
- first:
|
||||
: Linux
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
: LinuxUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
: OSXIntel
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
: OSXIntel64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: ARMv7
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
DefaultValueInitialized: true
|
||||
OS: Windows
|
||||
- first:
|
||||
Facebook: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Facebook: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,81 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9af32ce34e3c09740a2296f38904f864
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Android: 1
|
||||
Exclude Editor: 0
|
||||
Exclude Linux64: 0
|
||||
Exclude Lumin: 1
|
||||
Exclude OSXUniversal: 0
|
||||
Exclude Win: 0
|
||||
Exclude Win64: 0
|
||||
Exclude iOS: 1
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: ARMv7
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
DefaultValueInitialized: true
|
||||
OS: Windows
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Prefabs.meta
Normal file
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Prefabs.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bba6e78353c194c489388f40a4f2bd6c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &1434895627878410
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4682760393691964}
|
||||
- component: {fileID: 8869940821398010349}
|
||||
- component: {fileID: 7715055325084305990}
|
||||
m_Layer: 0
|
||||
m_Name: ImmersalSDK
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4682760393691964
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1434895627878410}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &8869940821398010349
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1434895627878410}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bc609bf82f8e346d593bc1a41c2b7cb8, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_AndroidResolution: 2
|
||||
m_Downsample: 1
|
||||
onPoseLost:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
onPoseFound:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
secondsToDecayPose: 10
|
||||
--- !u!114 &7715055325084305990
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1434895627878410}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: da24111645ab24d549a37ea7da86304b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_AutoStart: 1
|
||||
localizationInterval: 2
|
||||
m_UseFiltering: 1
|
||||
m_ResetOnMapChange: 0
|
||||
m_BurstMode: 1
|
||||
m_EnableLogging: 0
|
||||
MultipleLocalizationsCount: 20
|
||||
OnMultipleLocalizations:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 181e478384bef4862b94db5a060df1b6
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 100100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Resources.meta
Normal file
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Resources.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e40124e0b172426ba7c8c93d75c788b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30df7aac5e0027a46a0a8d0a3afc1f6a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
Shader "Immersal/Point Cloud"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Cull Off
|
||||
Tags{ "RenderType" = "Opaque" }
|
||||
LOD 100
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma target 3.0
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
float _PointSize;
|
||||
fixed _PerspectiveEnabled;
|
||||
fixed4 _PointColor;
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
float3 vertex : POSITION;
|
||||
};
|
||||
|
||||
struct VertexOut
|
||||
{
|
||||
float psize : PSIZE;
|
||||
float4 center : TEXCOORD0;
|
||||
half size : TEXCOORD1;
|
||||
UNITY_FOG_COORDS(0)
|
||||
};
|
||||
|
||||
VertexOut vert(Vertex vertex, out float4 outpos : SV_POSITION)
|
||||
{
|
||||
VertexOut o;
|
||||
outpos = UnityObjectToClipPos(vertex.vertex);
|
||||
|
||||
o.psize = lerp(_PointSize, _PointSize / outpos.w * _ScreenParams.y, step(0.5, _PerspectiveEnabled));
|
||||
o.size = o.psize;
|
||||
|
||||
o.center = ComputeScreenPos(outpos);
|
||||
UNITY_TRANSFER_FOG(o, o.position);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(VertexOut i, UNITY_VPOS_TYPE vpos : VPOS) : SV_Target
|
||||
{
|
||||
fixed4 c = _PointColor;
|
||||
float4 center = i.center;
|
||||
center.xy /= center.w;
|
||||
center.xy *= _ScreenParams.xy;
|
||||
float d = distance(vpos.xy, center.xy);
|
||||
|
||||
if (d > i.size * 0.5) {
|
||||
discard;
|
||||
}
|
||||
|
||||
UNITY_APPLY_FOG(input.fogCoord, c);
|
||||
return c;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80bb3c180be837f4daba8ccfbd06b6eb
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts.meta
Normal file
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eee6937ee2c707d42a0b601979052c50
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/AR.meta
Normal file
8
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/AR.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09263de2dbfc4a743bdd4c5ff73574ef
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
216
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/AR/ARHelper.cs
Normal file
216
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/AR/ARHelper.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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 UnityEngine.XR.ARFoundation;
|
||||
using UnityEngine.XR.ARSubsystems;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace Immersal.AR
|
||||
{
|
||||
public class ARHelper {
|
||||
public static Matrix4x4 SwitchHandedness(Matrix4x4 b)
|
||||
{
|
||||
Matrix4x4 D = Matrix4x4.identity;
|
||||
D.m00 = -1;
|
||||
return D * b * D;
|
||||
}
|
||||
|
||||
public static Quaternion SwitchHandedness(Quaternion b)
|
||||
{
|
||||
Matrix4x4 m = SwitchHandedness(Matrix4x4.Rotate(b));
|
||||
return m.rotation;
|
||||
}
|
||||
|
||||
public static Vector3 SwitchHandedness(Vector3 b)
|
||||
{
|
||||
Matrix4x4 m = SwitchHandedness(Matrix4x4.TRS(b, Quaternion.identity, Vector3.one));
|
||||
return m.GetColumn(3);
|
||||
}
|
||||
|
||||
public static void DoubleQuaternionToDoubleMatrix3x3(out double[] m, double[] q)
|
||||
{
|
||||
m = new double [] {1, 0, 0, 0, 1, 0, 0, 0, 1}; //identity matrix
|
||||
|
||||
// input quaternion should be in WXYZ order
|
||||
double w = q[0];
|
||||
double x = q[1];
|
||||
double y = q[2];
|
||||
double z = q[3];
|
||||
|
||||
double ww = w * w;
|
||||
double xx = x * x;
|
||||
double yy = y * y;
|
||||
double zz = z * z;
|
||||
|
||||
double xy = x * y;
|
||||
double zw = z * w;
|
||||
double xz = x * z;
|
||||
double yw = y * w;
|
||||
double yz = y * z;
|
||||
double xw = x * w;
|
||||
|
||||
double inv = 1.0 / (xx + yy + zz + ww);
|
||||
|
||||
m[0] = ( xx - yy - zz + ww) * inv;
|
||||
m[1] = 2.0 * (xy - zw) * inv;
|
||||
m[2] = 2.0 * (xz + yw) * inv;
|
||||
m[3] = 2.0 * (xy + zw) * inv;
|
||||
m[4] = (-xx + yy - zz + ww) * inv;
|
||||
m[5] = 2.0 * (yz - xw) * inv;
|
||||
m[6] = 2.0 * (xz - yw) * inv;
|
||||
m[7] = 2.0 * (yz + xw) * inv;
|
||||
m[8] = (-xx - yy + zz + ww) * inv;
|
||||
}
|
||||
|
||||
public static void GetIntrinsics(out Vector4 intrinsics)
|
||||
{
|
||||
intrinsics = Vector4.zero;
|
||||
XRCameraIntrinsics intr;
|
||||
ARCameraManager manager = ImmersalSDK.Instance?.cameraManager;
|
||||
|
||||
if (manager != null && manager.TryGetIntrinsics(out intr))
|
||||
{
|
||||
intrinsics.x = intr.focalLength.x;
|
||||
intrinsics.y = intr.focalLength.y;
|
||||
intrinsics.z = intr.principalPoint.x;
|
||||
intrinsics.w = intr.principalPoint.y;
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetRotation(ref Quaternion rot)
|
||||
{
|
||||
float angle = 0f;
|
||||
switch (Screen.orientation)
|
||||
{
|
||||
case ScreenOrientation.Portrait:
|
||||
angle = 90f;
|
||||
break;
|
||||
case ScreenOrientation.LandscapeLeft:
|
||||
angle = 180f;
|
||||
break;
|
||||
case ScreenOrientation.LandscapeRight:
|
||||
angle = 0f;
|
||||
break;
|
||||
case ScreenOrientation.PortraitUpsideDown:
|
||||
angle = -90f;
|
||||
break;
|
||||
default:
|
||||
angle = 0f;
|
||||
break;
|
||||
}
|
||||
|
||||
rot *= Quaternion.Euler(0f, 0f, angle);
|
||||
}
|
||||
|
||||
public static void GetPlaneDataFast(ref IntPtr pixels, XRCpuImage image)
|
||||
{
|
||||
XRCpuImage.Plane plane = image.GetPlane(0); // use the Y plane
|
||||
int width = image.width, height = image.height;
|
||||
|
||||
if (width == plane.rowStride)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
pixels = (IntPtr)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(plane.data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] data = new byte[width * height];
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* dstPtr = data)
|
||||
{
|
||||
byte* srcPtr = (byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(plane.data);
|
||||
if (width > 0 && height > 0) {
|
||||
UnsafeUtility.MemCpyStride(dstPtr, width, srcPtr, plane.rowStride, width, height);
|
||||
}
|
||||
pixels = (IntPtr)dstPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetPlaneData(out byte[] pixels, XRCpuImage image)
|
||||
{
|
||||
XRCpuImage.Plane plane = image.GetPlane(0); // use the Y plane
|
||||
int width = image.width, height = image.height;
|
||||
pixels = new byte[width * height];
|
||||
|
||||
if (width == plane.rowStride)
|
||||
{
|
||||
plane.data.CopyTo(pixels);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* dstPtr = pixels)
|
||||
{
|
||||
byte* srcPtr = (byte*)NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(plane.data);
|
||||
if (width > 0 && height > 0) {
|
||||
UnsafeUtility.MemCpyStride(dstPtr, width, srcPtr, plane.rowStride, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetPlaneDataRGB(out byte[] pixels, XRCpuImage image)
|
||||
{
|
||||
var conversionParams = new XRCpuImage.ConversionParams
|
||||
{
|
||||
inputRect = new RectInt(0, 0, image.width, image.height),
|
||||
outputDimensions = new Vector2Int(image.width, image.height),
|
||||
outputFormat = TextureFormat.RGB24,
|
||||
transformation = XRCpuImage.Transformation.None
|
||||
};
|
||||
|
||||
int size = image.GetConvertedDataSize(conversionParams);
|
||||
pixels = new byte[size];
|
||||
GCHandle bufferHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
|
||||
image.Convert(conversionParams, bufferHandle.AddrOfPinnedObject(), pixels.Length);
|
||||
bufferHandle.Free();
|
||||
}
|
||||
|
||||
public static bool TryGetTrackingQuality(out int quality)
|
||||
{
|
||||
quality = default;
|
||||
|
||||
if (ImmersalSDK.Instance?.arSession == null)
|
||||
return false;
|
||||
|
||||
var arSubsystem = ImmersalSDK.Instance?.arSession.subsystem;
|
||||
|
||||
if (arSubsystem != null && arSubsystem.running)
|
||||
{
|
||||
switch (arSubsystem.trackingState)
|
||||
{
|
||||
case TrackingState.Tracking:
|
||||
quality = 4;
|
||||
break;
|
||||
case TrackingState.Limited:
|
||||
quality = 1;
|
||||
break;
|
||||
case TrackingState.None:
|
||||
quality = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f78d5a866c99746cdb1556667e77111c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
613
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/AR/ARMap.cs
Normal file
613
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/AR/ARMap.cs
Normal file
@@ -0,0 +1,613 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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 UnityEngine.Events;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine.Rendering;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Immersal.AR
|
||||
{
|
||||
[System.Serializable]
|
||||
public class MapLocalizedEvent : UnityEvent<int>
|
||||
{
|
||||
}
|
||||
|
||||
[ExecuteAlways]
|
||||
public class ARMap : MonoBehaviour
|
||||
{
|
||||
public static readonly Color[] pointCloudColors = new Color[] { new Color(0.22f, 1f, 0.46f),
|
||||
new Color(0.96f, 0.14f, 0.14f),
|
||||
new Color(0.16f, 0.69f, 0.95f),
|
||||
new Color(0.93f, 0.84f, 0.12f),
|
||||
new Color(0.57f, 0.93f, 0.12f),
|
||||
new Color(1f, 0.38f, 0.78f),
|
||||
new Color(0.4f, 0f, 0.9f),
|
||||
new Color(0.89f, 0.4f, 0f)
|
||||
};
|
||||
|
||||
public enum RenderMode { DoNotRender, EditorOnly, EditorAndRuntime }
|
||||
|
||||
public static Dictionary<int, ARMap> mapHandleToMap = new Dictionary<int, ARMap>();
|
||||
public static bool pointCloudVisible = true;
|
||||
|
||||
public RenderMode renderMode = RenderMode.EditorOnly;
|
||||
public TextAsset mapFile;
|
||||
|
||||
[SerializeField]
|
||||
private Color m_PointColor = new Color(0.57f, 0.93f, 0.12f);
|
||||
|
||||
[SerializeField]
|
||||
public string mapLicense;
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[Space(10)]
|
||||
[Header("Map License Detail")]
|
||||
[SerializeField][ReadOnly]
|
||||
private string m_LicenceId = "0";
|
||||
[SerializeField][ReadOnly]
|
||||
private string m_ExpireTime = "0";
|
||||
#endif
|
||||
|
||||
[Space(10)]
|
||||
[Header("Map Metadata")]
|
||||
|
||||
[SerializeField][ReadOnly]
|
||||
private int m_MapId = -1;
|
||||
[SerializeField][ReadOnly]
|
||||
private string m_MapName = null;
|
||||
[ReadOnly]
|
||||
public int privacy;
|
||||
[ReadOnly]
|
||||
public MapAlignment mapAlignment;
|
||||
[ReadOnly]
|
||||
public WGS84 wgs84;
|
||||
|
||||
[Space(10)]
|
||||
[Header("Events")]
|
||||
|
||||
public MapLocalizedEvent OnFirstLocalization = null;
|
||||
protected ARSpace m_ARSpace = null;
|
||||
private bool m_LocalizedOnce = false;
|
||||
|
||||
public Color pointColor
|
||||
{
|
||||
get { return m_PointColor; }
|
||||
set { m_PointColor = value; }
|
||||
}
|
||||
|
||||
public static float pointSize = 0.33f;
|
||||
// public static bool isRenderable = true;
|
||||
public static bool renderAs3dPoints = true;
|
||||
|
||||
[System.Serializable]
|
||||
public struct MapAlignment
|
||||
{
|
||||
public double tx;
|
||||
public double ty;
|
||||
public double tz;
|
||||
public double qx;
|
||||
public double qy;
|
||||
public double qz;
|
||||
public double qw;
|
||||
public double scale;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public struct WGS84
|
||||
{
|
||||
public double latitude;
|
||||
public double longitude;
|
||||
public double altitude;
|
||||
}
|
||||
|
||||
private Shader m_Shader;
|
||||
private Material m_Material;
|
||||
private Mesh m_Mesh;
|
||||
private MeshFilter m_MeshFilter;
|
||||
private MeshRenderer m_MeshRenderer;
|
||||
|
||||
public Transform root { get; protected set; }
|
||||
public int mapHandle { get; private set; } = -1;
|
||||
|
||||
public int mapId
|
||||
{
|
||||
get => m_MapId;
|
||||
private set => m_MapId = value;
|
||||
}
|
||||
|
||||
public string mapName
|
||||
{
|
||||
get => m_MapName;
|
||||
set => m_MapName = value;
|
||||
}
|
||||
|
||||
public static int MapHandleToId(int handle)
|
||||
{
|
||||
if (mapHandleToMap.ContainsKey(handle))
|
||||
{
|
||||
return mapHandleToMap[handle].mapId;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int MapIdToHandle(int id)
|
||||
{
|
||||
if (ARSpace.mapIdToMap.ContainsKey(id))
|
||||
{
|
||||
return ARSpace.mapIdToMap[id].mapHandle;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public double[] MapToEcefGet()
|
||||
{
|
||||
double[] q = new double[] {this.mapAlignment.qw, this.mapAlignment.qx, this.mapAlignment.qy, this.mapAlignment.qz};
|
||||
double[] m = new double[9];
|
||||
ARHelper.DoubleQuaternionToDoubleMatrix3x3(out m, q);
|
||||
|
||||
double[] mapToEcef = new double[] {this.mapAlignment.tx, this.mapAlignment.ty, this.mapAlignment.tz, m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], this.mapAlignment.scale};
|
||||
|
||||
return mapToEcef;
|
||||
}
|
||||
|
||||
public virtual void FreeMap(bool destroy = false)
|
||||
{
|
||||
if (mapHandle >= 0)
|
||||
{
|
||||
Immersal.Core.FreeMap(mapHandle);
|
||||
|
||||
if (mapHandleToMap.ContainsKey(mapHandle))
|
||||
{
|
||||
mapHandleToMap.Remove(mapHandle);
|
||||
}
|
||||
}
|
||||
|
||||
mapHandle = -1;
|
||||
ClearMesh();
|
||||
Reset();
|
||||
|
||||
if (this.mapId > 0)
|
||||
{
|
||||
ARSpace.UnregisterSpace(root, this.mapId);
|
||||
this.mapId = -1;
|
||||
}
|
||||
|
||||
if (destroy)
|
||||
{
|
||||
GameObject.Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
m_LocalizedOnce = false;
|
||||
}
|
||||
|
||||
public virtual async Task<int> LoadMap(byte[] mapBytes = null, int mapId = -1)
|
||||
{
|
||||
if (mapBytes == null)
|
||||
{
|
||||
mapBytes = (mapFile != null) ? mapFile.bytes : null;
|
||||
}
|
||||
|
||||
if (mapBytes != null)
|
||||
{
|
||||
Task<int> t = Task.Run(() =>
|
||||
{
|
||||
var bytes = System.Convert.FromBase64String(mapLicense);
|
||||
var str = System.Text.Encoding.UTF8.GetString(bytes);
|
||||
return Immersal.Core.LoadMap(mapBytes, str);
|
||||
});
|
||||
|
||||
await t;
|
||||
if (mapHandle != -1)
|
||||
{
|
||||
FreeMap();
|
||||
}
|
||||
|
||||
mapHandle = t.Result;
|
||||
|
||||
if (this == null)
|
||||
{
|
||||
FreeMap();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mapId > 0)
|
||||
{
|
||||
this.mapId = mapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseMapIdAndName();
|
||||
}
|
||||
|
||||
if (mapHandle >= 0)
|
||||
{
|
||||
int pointCloudSize = Immersal.Core.GetPointCloudSize(mapHandle);
|
||||
Vector3[] points = new Vector3[pointCloudSize];
|
||||
Immersal.Core.GetPointCloud(mapHandle, points);
|
||||
for (int i = 0; i < pointCloudSize; i++)
|
||||
{
|
||||
points[i] = ARHelper.SwitchHandedness(points[i]);
|
||||
}
|
||||
mapHandleToMap[mapHandle] = this;
|
||||
InitializeMesh(points);
|
||||
}
|
||||
|
||||
if (this.mapId > 0 && m_ARSpace != null)
|
||||
{
|
||||
root = m_ARSpace.transform;
|
||||
ARSpace.RegisterSpace(root, this, transform.localPosition, transform.localRotation, transform.localScale);
|
||||
}
|
||||
|
||||
return mapHandle;
|
||||
}
|
||||
|
||||
private void InitializeMesh(Vector3[] pointPositions)
|
||||
{
|
||||
if (this == null) return;
|
||||
|
||||
if (m_Shader == null)
|
||||
{
|
||||
m_Shader = Shader.Find("Immersal/Point Cloud");
|
||||
}
|
||||
|
||||
if (m_Material == null)
|
||||
{
|
||||
m_Material = new Material(m_Shader);
|
||||
m_Material.hideFlags = HideFlags.DontSave;
|
||||
}
|
||||
|
||||
if (m_Mesh == null)
|
||||
{
|
||||
m_Mesh = new Mesh();
|
||||
m_Mesh.indexFormat = IndexFormat.UInt32;
|
||||
}
|
||||
|
||||
int numPoints = pointPositions.Length;
|
||||
|
||||
int[] indices = new int[numPoints];
|
||||
Vector3[] pts = new Vector3[numPoints];
|
||||
Color32[] col = new Color32[numPoints];
|
||||
|
||||
for (int i = 0; i < numPoints; ++i)
|
||||
{
|
||||
indices[i] = i;
|
||||
pts[i] = pointPositions[i];
|
||||
}
|
||||
|
||||
m_Mesh.Clear();
|
||||
m_Mesh.vertices = pts;
|
||||
m_Mesh.colors32 = col;
|
||||
m_Mesh.SetIndices(indices, MeshTopology.Points, 0);
|
||||
m_Mesh.RecalculateBounds();
|
||||
|
||||
if (m_MeshFilter == null)
|
||||
{
|
||||
m_MeshFilter = gameObject.GetComponent<MeshFilter>();
|
||||
if (m_MeshFilter == null)
|
||||
{
|
||||
m_MeshFilter = gameObject.AddComponent<MeshFilter>();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_MeshRenderer == null)
|
||||
{
|
||||
m_MeshRenderer = gameObject.GetComponent<MeshRenderer>();
|
||||
if (m_MeshRenderer == null)
|
||||
{
|
||||
m_MeshRenderer = gameObject.AddComponent<MeshRenderer>();
|
||||
}
|
||||
}
|
||||
|
||||
m_MeshFilter.mesh = m_Mesh;
|
||||
m_MeshRenderer.material = m_Material;
|
||||
|
||||
m_MeshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
m_MeshRenderer.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off;
|
||||
m_MeshRenderer.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off;
|
||||
}
|
||||
|
||||
private void InitializeMesh()
|
||||
{
|
||||
InitializeMesh(new Vector3[0]);
|
||||
}
|
||||
|
||||
private void ClearMesh()
|
||||
{
|
||||
if (m_Mesh != null)
|
||||
{
|
||||
m_Mesh.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void NotifySuccessfulLocalization(int mapId)
|
||||
{
|
||||
if (m_LocalizedOnce)
|
||||
return;
|
||||
|
||||
OnFirstLocalization?.Invoke(mapId);
|
||||
m_LocalizedOnce = true;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
m_ARSpace = gameObject.GetComponentInParent<ARSpace>();
|
||||
if (!m_ARSpace)
|
||||
{
|
||||
GameObject go = new GameObject("AR Space");
|
||||
m_ARSpace = go.AddComponent<ARSpace>();
|
||||
transform.SetParent(go.transform);
|
||||
}
|
||||
|
||||
ParseMapIdAndName();
|
||||
InitializeMesh();
|
||||
}
|
||||
|
||||
private void ParseMapIdAndName()
|
||||
{
|
||||
int id;
|
||||
if (GetMapId(out id))
|
||||
{
|
||||
this.mapId = id;
|
||||
this.mapName = mapFile.name.Substring(id.ToString().Length + 1);
|
||||
}
|
||||
|
||||
if (Application.isEditor)
|
||||
{
|
||||
if (mapFile != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string destinationFolder = Path.Combine("Assets", "Map Data");
|
||||
string jsonFilePath = Path.Combine(destinationFolder, string.Format("{0}-metadata.json", mapFile.name));
|
||||
|
||||
MetadataFile metadataFile = JsonUtility.FromJson<MetadataFile>(File.ReadAllText(jsonFilePath));
|
||||
|
||||
this.mapAlignment.tx = metadataFile.tx;
|
||||
this.mapAlignment.ty = metadataFile.ty;
|
||||
this.mapAlignment.tz = metadataFile.tz;
|
||||
|
||||
this.mapAlignment.qx = metadataFile.qx;
|
||||
this.mapAlignment.qy = metadataFile.qy;
|
||||
this.mapAlignment.qz = metadataFile.qz;
|
||||
this.mapAlignment.qw = metadataFile.qw;
|
||||
|
||||
this.mapAlignment.scale = metadataFile.scale;
|
||||
|
||||
this.wgs84.latitude = metadataFile.latitude;
|
||||
this.wgs84.longitude = metadataFile.longitude;
|
||||
this.wgs84.altitude = metadataFile.altitude;
|
||||
|
||||
this.privacy = metadataFile.privacy;
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
Debug.LogWarningFormat("{0}\nCould not find {1}-metadata.json", e.Message, mapFile.name);
|
||||
// set default values in case metadata is not available
|
||||
|
||||
this.mapAlignment.tx = 0.0;
|
||||
this.mapAlignment.ty = 0.0;
|
||||
this.mapAlignment.tz = 0.0;
|
||||
|
||||
this.mapAlignment.qx = 0.0;
|
||||
this.mapAlignment.qy = 0.0;
|
||||
this.mapAlignment.qz = 0.0;
|
||||
this.mapAlignment.qw = 1.0;
|
||||
|
||||
this.mapAlignment.scale = 1.0;
|
||||
|
||||
this.wgs84.latitude = 0.0;
|
||||
this.wgs84.longitude = 0.0;
|
||||
this.wgs84.altitude = 0.0;
|
||||
|
||||
this.privacy = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public struct MetadataFile
|
||||
{
|
||||
public string error;
|
||||
public int id;
|
||||
public int type;
|
||||
public string created;
|
||||
public string version;
|
||||
public int user;
|
||||
public int creator;
|
||||
public string name;
|
||||
public int size;
|
||||
public string status;
|
||||
public int privacy;
|
||||
public double latitude;
|
||||
public double longitude;
|
||||
public double altitude;
|
||||
public double tx;
|
||||
public double ty;
|
||||
public double tz;
|
||||
public double qw;
|
||||
public double qx;
|
||||
public double qy;
|
||||
public double qz;
|
||||
public double scale;
|
||||
public string sha256_al;
|
||||
public string sha256_sparse;
|
||||
public string sha256_dense;
|
||||
public string sha256_tex;
|
||||
}
|
||||
|
||||
private bool GetMapId(out int mapId)
|
||||
{
|
||||
if (mapFile == null)
|
||||
{
|
||||
mapId = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
string mapFileName = mapFile.name;
|
||||
Regex rx = new Regex(@"^\d+");
|
||||
Match match = rx.Match(mapFileName);
|
||||
if (match.Success)
|
||||
{
|
||||
mapId = Int32.Parse(match.Value);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapId = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnEnable()
|
||||
{
|
||||
if (mapFile != null)
|
||||
{
|
||||
await LoadMap();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
FreeMap();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
FreeMap();
|
||||
|
||||
if (m_Material != null)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Destroy(m_Mesh);
|
||||
Destroy(m_Material);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyImmediate(m_Mesh);
|
||||
DestroyImmediate(m_Material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsRenderable()
|
||||
{
|
||||
if (pointCloudVisible)
|
||||
{
|
||||
switch (renderMode)
|
||||
{
|
||||
case RenderMode.DoNotRender:
|
||||
return false;
|
||||
case RenderMode.EditorOnly:
|
||||
if (Application.isEditor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case RenderMode.EditorAndRuntime:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void OnRenderObject()
|
||||
{
|
||||
if (IsRenderable() && m_Material != null)
|
||||
{
|
||||
m_MeshRenderer.enabled = true;
|
||||
|
||||
if (renderAs3dPoints)
|
||||
{
|
||||
m_Material.SetFloat("_PerspectiveEnabled", 1f);
|
||||
m_Material.SetFloat("_PointSize", Mathf.Lerp(0.002f, 0.14f, Mathf.Max(0, Mathf.Pow(pointSize, 3f))));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Material.SetFloat("_PerspectiveEnabled", 0f);
|
||||
m_Material.SetFloat("_PointSize", Mathf.Lerp(1.5f, 40f, Mathf.Max(0, pointSize)));
|
||||
}
|
||||
m_Material.SetColor("_PointColor", m_PointColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_MeshRenderer.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
private string _prevMapLicense = null;
|
||||
private TextAsset _prevMapFile = null;
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (mapLicense != _prevMapLicense)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bytes = System.Convert.FromBase64String(mapLicense);
|
||||
var str = System.Text.Encoding.UTF8.GetString(bytes);
|
||||
|
||||
MapLicense obj = JsonUtility.FromJson<MapLicense>(str);
|
||||
|
||||
m_LicenceId = obj.certificateID;
|
||||
m_ExpireTime = obj.expireTime;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Debug.LogWarningFormat("Incorrect map license!\n{0}", e.Message);
|
||||
|
||||
m_LicenceId = "0";
|
||||
m_ExpireTime = "0";
|
||||
}
|
||||
|
||||
_prevMapLicense = mapLicense;
|
||||
}
|
||||
|
||||
if (mapFile != null)
|
||||
{
|
||||
if (mapFile != _prevMapFile)
|
||||
{
|
||||
ParseMapIdAndName();
|
||||
}
|
||||
|
||||
_prevMapFile = mapFile;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[Serializable]
|
||||
public class MapLicense
|
||||
{
|
||||
public string certificateID;
|
||||
public string applyTime;
|
||||
public string expireTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a7ab799a64cb7941b7b41a8e1211b12
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- mapFile: {instanceID: 0}
|
||||
- m_Shader: {fileID: 4800000, guid: 9cee2ca6ff2bbdc458a11fe108bb2e5b, type: 3}
|
||||
- m_Sorter: {fileID: 7200000, guid: 333809191cc59694fb5772e17740acfe, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
165
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/AR/ARSpace.cs
Normal file
165
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/AR/ARSpace.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Immersal.AR
|
||||
{
|
||||
public class SpaceContainer
|
||||
{
|
||||
public int mapCount = 0;
|
||||
public Vector3 targetPosition = Vector3.zero;
|
||||
public Quaternion targetRotation = Quaternion.identity;
|
||||
public PoseFilter filter = new PoseFilter();
|
||||
}
|
||||
|
||||
public class MapOffset
|
||||
{
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
public Vector3 scale;
|
||||
public SpaceContainer space;
|
||||
}
|
||||
|
||||
public class ARSpace : MonoBehaviour
|
||||
{
|
||||
public static bool _inited = false;
|
||||
|
||||
public static Dictionary<Transform, SpaceContainer> transformToSpace = new Dictionary<Transform, SpaceContainer>();
|
||||
public static Dictionary<SpaceContainer, Transform> spaceToTransform = new Dictionary<SpaceContainer, Transform>();
|
||||
public static Dictionary<int, MapOffset> mapIdToOffset = new Dictionary<int, MapOffset>();
|
||||
public static Dictionary<int, ARMap> mapIdToMap = new Dictionary<int, ARMap>();
|
||||
|
||||
private Matrix4x4 m_InitialOffset = Matrix4x4.identity;
|
||||
|
||||
public Matrix4x4 initialOffset
|
||||
{
|
||||
get { return m_InitialOffset; }
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (!_inited)
|
||||
{
|
||||
#if !UNITY_EDITOR
|
||||
init();
|
||||
#endif
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
Vector3 pos = transform.position;
|
||||
Quaternion rot = transform.rotation;
|
||||
Matrix4x4 offset = Matrix4x4.TRS(pos, rot, Vector3.one);
|
||||
|
||||
m_InitialOffset = offset;
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
transformToSpace.Clear();
|
||||
spaceToTransform.Clear();
|
||||
mapIdToOffset.Clear();
|
||||
mapIdToMap.Clear();
|
||||
}
|
||||
|
||||
public Pose ToCloudSpace(Vector3 camPos, Quaternion camRot)
|
||||
{
|
||||
Matrix4x4 trackerSpace = Matrix4x4.TRS(camPos, camRot, Vector3.one);
|
||||
Matrix4x4 trackerToCloudSpace = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
|
||||
Matrix4x4 cloudSpace = trackerToCloudSpace.inverse * trackerSpace;
|
||||
|
||||
return new Pose(cloudSpace.GetColumn(3), cloudSpace.rotation);
|
||||
}
|
||||
|
||||
public Pose FromCloudSpace(Vector3 camPos, Quaternion camRot)
|
||||
{
|
||||
Matrix4x4 cloudSpace = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
|
||||
Matrix4x4 trackerSpace = Matrix4x4.TRS(camPos, camRot, Vector3.one);
|
||||
Matrix4x4 m = trackerSpace * (cloudSpace.inverse);
|
||||
|
||||
return new Pose(m.GetColumn(3), m.rotation);
|
||||
}
|
||||
|
||||
public static void RegisterSpace(Transform tr, ARMap map, Vector3 offsetPosition, Quaternion offsetRotation, Vector3 offsetScale)
|
||||
{
|
||||
if (tr == null)
|
||||
return;
|
||||
|
||||
SpaceContainer sc;
|
||||
|
||||
if (!transformToSpace.ContainsKey(tr))
|
||||
{
|
||||
sc = new SpaceContainer();
|
||||
transformToSpace[tr] = sc;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc = transformToSpace[tr];
|
||||
}
|
||||
|
||||
spaceToTransform[sc] = tr;
|
||||
|
||||
sc.mapCount++;
|
||||
|
||||
MapOffset mo = new MapOffset();
|
||||
mo.position = offsetPosition;
|
||||
mo.rotation = offsetRotation;
|
||||
mo.scale = offsetScale;
|
||||
mo.space = sc;
|
||||
|
||||
mapIdToOffset[map.mapId] = mo;
|
||||
mapIdToMap[map.mapId] = map;
|
||||
}
|
||||
|
||||
public static void RegisterSpace(Transform tr, ARMap map)
|
||||
{
|
||||
RegisterSpace(tr, map, Vector3.zero, Quaternion.identity, Vector3.one);
|
||||
}
|
||||
|
||||
public static void UnregisterSpace(Transform tr, int mapId)
|
||||
{
|
||||
if (tr == null)
|
||||
return;
|
||||
|
||||
if (transformToSpace.ContainsKey(tr))
|
||||
{
|
||||
SpaceContainer sc = transformToSpace[tr];
|
||||
if (--sc.mapCount == 0)
|
||||
{
|
||||
transformToSpace.Remove(tr);
|
||||
spaceToTransform.Remove(sc);
|
||||
}
|
||||
if (mapIdToOffset.ContainsKey(mapId))
|
||||
mapIdToOffset.Remove(mapId);
|
||||
if (mapIdToMap.ContainsKey(mapId))
|
||||
mapIdToMap.Remove(mapId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateSpace(SpaceContainer space, Vector3 pos, Quaternion rot)
|
||||
{
|
||||
if (space == null)
|
||||
return;
|
||||
|
||||
if (spaceToTransform.ContainsKey(space))
|
||||
{
|
||||
Transform tr = spaceToTransform[space];
|
||||
tr.SetPositionAndRotation(pos, rot);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("RokidLocalizer", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void init();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53a086897bd294848aa39db1a6b2e6ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,290 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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;
|
||||
|
||||
namespace Immersal.AR
|
||||
{
|
||||
public class LocalizerStats
|
||||
{
|
||||
public int localizationAttemptCount = 0;
|
||||
public int localizationSuccessCount = 0;
|
||||
}
|
||||
|
||||
public struct LocalizerPose
|
||||
{
|
||||
public bool valid;
|
||||
public double[] mapToEcef;
|
||||
public Matrix4x4 matrix;
|
||||
public Pose lastUpdatedPose;
|
||||
public double vLatitude;
|
||||
public double vLongitude;
|
||||
public double vAltitude;
|
||||
}
|
||||
|
||||
|
||||
public abstract class LocalizerBase : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Start localizing at app startup")]
|
||||
[SerializeField]
|
||||
protected bool m_AutoStart = true;
|
||||
[Tooltip("Time between localization requests in seconds")]
|
||||
public float localizationInterval = 2.0f;
|
||||
[Tooltip("Filter localizer poses for smoother results")]
|
||||
[SerializeField]
|
||||
protected bool m_UseFiltering = true;
|
||||
[Tooltip("Reset localizer filtering when relocalized against a different map than the previous time")]
|
||||
[SerializeField]
|
||||
protected bool m_ResetOnMapChange = false;
|
||||
[Tooltip("Try to localize at maximum speed at app startup / resume")]
|
||||
[SerializeField]
|
||||
protected bool m_BurstMode = true;
|
||||
[SerializeField]
|
||||
protected bool m_EnableLogging;
|
||||
|
||||
public LocalizerStats stats { get; protected set; } = new LocalizerStats();
|
||||
public int lastLocalizedMapId { get; protected set; }
|
||||
public LocalizerPose lastLocalizedPose = default;
|
||||
public bool isTracking { get; protected set; }
|
||||
public bool isLocalizing { get; protected set; }
|
||||
|
||||
public Action<LocalizerPose> OnPoseFound;
|
||||
public Action<int> OnMapChanged;
|
||||
public Action OnReset;
|
||||
|
||||
protected ImmersalSDK m_Sdk = null;
|
||||
protected IntPtr m_PixelBuffer = IntPtr.Zero;
|
||||
protected float m_LastLocalizeTime = 0.0f;
|
||||
protected float m_BurstStartTime = 0.0f;
|
||||
protected bool m_BurstModeActive = false;
|
||||
protected bool m_LocalizeContinuously = false;
|
||||
protected Camera m_Cam = null;
|
||||
protected float m_WarpThresholdDistSq = 5.0f * 5.0f;
|
||||
protected float m_WarpThresholdCosAngle = Mathf.Cos(20.0f * Mathf.PI / 180.0f);
|
||||
|
||||
public bool burstMode
|
||||
{
|
||||
get { return m_BurstMode; }
|
||||
set
|
||||
{
|
||||
SetBurstMode(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool useFiltering
|
||||
{
|
||||
get { return m_UseFiltering; }
|
||||
set { m_UseFiltering = value; }
|
||||
}
|
||||
|
||||
public bool resetOnMapChange
|
||||
{
|
||||
get { return m_ResetOnMapChange; }
|
||||
set { m_ResetOnMapChange = value; }
|
||||
}
|
||||
|
||||
public bool autoStart
|
||||
{
|
||||
get { return m_AutoStart; }
|
||||
set
|
||||
{
|
||||
m_AutoStart = value;
|
||||
SetContinuousLocalization(value);
|
||||
}
|
||||
}
|
||||
|
||||
#region Virtual methods
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
m_Sdk = ImmersalSDK.Instance;
|
||||
lastLocalizedMapId = -1;
|
||||
SetBurstMode(burstMode);
|
||||
SetContinuousLocalization(autoStart);
|
||||
}
|
||||
|
||||
public virtual void OnEnable()
|
||||
{
|
||||
m_Cam = Camera.main;
|
||||
}
|
||||
|
||||
public virtual void OnDisable()
|
||||
{
|
||||
isTracking = false;
|
||||
}
|
||||
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
m_PixelBuffer = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public virtual void OnApplicationPause(bool pauseStatus)
|
||||
{
|
||||
Reset();
|
||||
|
||||
if (!pauseStatus)
|
||||
SetBurstMode(burstMode);
|
||||
}
|
||||
|
||||
public virtual void Localize()
|
||||
{
|
||||
LocalizerDebugLog(string.Format("Successful localizations: {0}/{1}", stats.localizationSuccessCount, stats.localizationAttemptCount));
|
||||
isLocalizing = false;
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
lastLocalizedMapId = -1;
|
||||
|
||||
stats.localizationAttemptCount = stats.localizationSuccessCount = 0;
|
||||
SetBurstMode(burstMode);
|
||||
|
||||
foreach (KeyValuePair<Transform, SpaceContainer> item in ARSpace.transformToSpace)
|
||||
item.Value.filter.ResetFiltering();
|
||||
|
||||
OnReset?.Invoke();
|
||||
}
|
||||
|
||||
public virtual void StartLocalizing()
|
||||
{
|
||||
Reset();
|
||||
SetContinuousLocalization(autoStart);
|
||||
}
|
||||
|
||||
public virtual void StopLocalizing()
|
||||
{
|
||||
SetContinuousLocalization(false);
|
||||
Reset();
|
||||
}
|
||||
|
||||
public virtual void Pause()
|
||||
{
|
||||
SetContinuousLocalization(false);
|
||||
}
|
||||
|
||||
public virtual void Resume()
|
||||
{
|
||||
SetContinuousLocalization(true);
|
||||
}
|
||||
|
||||
protected virtual void LocalizerDebugLog(string message)
|
||||
{
|
||||
if (m_EnableLogging)
|
||||
{
|
||||
Debug.LogFormat("[{0}]: {1}", this.GetType().Name, message);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (!m_LocalizeContinuously)
|
||||
return;
|
||||
|
||||
if (ARSpace.transformToSpace.Count == 0)
|
||||
{
|
||||
m_BurstStartTime = Time.unscaledTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (useFiltering)
|
||||
{
|
||||
foreach (KeyValuePair<Transform, SpaceContainer> item in ARSpace.transformToSpace)
|
||||
{
|
||||
float distSq = (item.Value.filter.position - item.Value.targetPosition).sqrMagnitude;
|
||||
float cosAngle = Quaternion.Dot(item.Value.filter.rotation, item.Value.targetRotation);
|
||||
if (item.Value.filter.SampleCount() == 1 || distSq > m_WarpThresholdDistSq || cosAngle < m_WarpThresholdCosAngle)
|
||||
{
|
||||
item.Value.targetPosition = item.Value.filter.position;
|
||||
item.Value.targetRotation = item.Value.filter.rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
float smoothing = 0.025f;
|
||||
float steps = Time.deltaTime / (1.0f / 60.0f);
|
||||
if (steps < 1.0f)
|
||||
steps = 1.0f;
|
||||
else if (steps > 6.0f)
|
||||
steps = 6.0f;
|
||||
float alpha = 1.0f - Mathf.Pow(1.0f - smoothing, steps);
|
||||
|
||||
item.Value.targetRotation = Quaternion.Slerp(item.Value.targetRotation, item.Value.filter.rotation, alpha);
|
||||
item.Value.targetPosition = Vector3.Lerp(item.Value.targetPosition, item.Value.filter.position, alpha);
|
||||
}
|
||||
ARSpace.UpdateSpace(item.Value, item.Value.targetPosition, item.Value.targetRotation);
|
||||
}
|
||||
}
|
||||
|
||||
float curTime = Time.unscaledTime;
|
||||
if (m_BurstModeActive) // try to localize at max speed during app start/resume
|
||||
{
|
||||
if (!isLocalizing && isTracking)
|
||||
{
|
||||
float elapsedTime = curTime - m_BurstStartTime;
|
||||
isLocalizing = true;
|
||||
|
||||
Localize();
|
||||
|
||||
if (stats.localizationSuccessCount == 10 || elapsedTime >= 15f)
|
||||
{
|
||||
m_BurstModeActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLocalizing && isTracking && (curTime - m_LastLocalizeTime) >= localizationInterval)
|
||||
{
|
||||
m_LastLocalizeTime = curTime;
|
||||
isLocalizing = true;
|
||||
|
||||
Localize();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void SetBurstMode(bool on)
|
||||
{
|
||||
m_BurstStartTime = Time.unscaledTime;
|
||||
m_BurstModeActive = on;
|
||||
}
|
||||
|
||||
private void SetContinuousLocalization(bool on)
|
||||
{
|
||||
m_LocalizeContinuously = on;
|
||||
}
|
||||
|
||||
public static void GetLocalizerPose(out LocalizerPose localizerPose, int mapId, Vector3 pos, Quaternion rot, Matrix4x4 m, double[] mapToEcef = null)
|
||||
{
|
||||
localizerPose = default;
|
||||
|
||||
if (mapToEcef == null)
|
||||
{
|
||||
mapToEcef = ARSpace.mapIdToMap[mapId].MapToEcefGet();
|
||||
}
|
||||
|
||||
double[] wgs84 = new double[3];
|
||||
int r = Immersal.Core.PosMapToWgs84(wgs84, ARHelper.SwitchHandedness(pos), mapToEcef);
|
||||
|
||||
if (r == 0)
|
||||
{
|
||||
localizerPose.valid = true;
|
||||
localizerPose.mapToEcef = mapToEcef;
|
||||
localizerPose.matrix = m;
|
||||
localizerPose.lastUpdatedPose = new Pose(pos, rot);
|
||||
localizerPose.vLatitude = wgs84[0];
|
||||
localizerPose.vLongitude = wgs84[1];
|
||||
localizerPose.vAltitude = wgs84[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06ec00cf50a16443a92eafce8161c1a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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;
|
||||
|
||||
namespace Immersal.AR
|
||||
{
|
||||
public class PoseFilter
|
||||
{
|
||||
public Vector3 position = Vector3.zero;
|
||||
public Quaternion rotation = Quaternion.identity;
|
||||
|
||||
private static uint m_HistorySize = 8;
|
||||
private Vector3[] m_P = new Vector3[m_HistorySize];
|
||||
private Vector3[] m_X = new Vector3[m_HistorySize];
|
||||
private Vector3[] m_Z = new Vector3[m_HistorySize];
|
||||
private uint m_Samples = 0;
|
||||
|
||||
public uint SampleCount()
|
||||
{
|
||||
return m_Samples;
|
||||
}
|
||||
|
||||
public void InvalidateHistory()
|
||||
{
|
||||
m_Samples = 0;
|
||||
}
|
||||
|
||||
public void ResetFiltering()
|
||||
{
|
||||
position = Vector3.zero;
|
||||
rotation = Quaternion.identity;
|
||||
InvalidateHistory();
|
||||
}
|
||||
|
||||
public void RefinePose(Matrix4x4 r)
|
||||
{
|
||||
uint idx = m_Samples% m_HistorySize;
|
||||
m_P[idx] = r.GetColumn(3);
|
||||
m_X[idx] = r.GetColumn(0);
|
||||
m_Z[idx] = r.GetColumn(2);
|
||||
m_Samples++;
|
||||
uint n = m_Samples > m_HistorySize ? m_HistorySize : m_Samples;
|
||||
position = FilterAVT(m_P, n);
|
||||
Vector3 x = Vector3.Normalize(FilterAVT(m_X, n));
|
||||
Vector3 z = Vector3.Normalize(FilterAVT(m_Z, n));
|
||||
Vector3 up = Vector3.Normalize(Vector3.Cross(z, x));
|
||||
rotation = Quaternion.LookRotation(z, up);
|
||||
}
|
||||
|
||||
private Vector3 FilterAVT(Vector3[] buf, uint n)
|
||||
{
|
||||
Vector3 mean = Vector3.zero;
|
||||
for (uint i = 0; i < n; i++)
|
||||
mean += buf[i];
|
||||
mean /= (float)n;
|
||||
if (n <= 2)
|
||||
return mean;
|
||||
float s = 0;
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
s += Vector3.SqrMagnitude(buf[i] - mean);
|
||||
}
|
||||
s /= (float)n;
|
||||
Vector3 avg = Vector3.zero;
|
||||
int ib = 0;
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
float d = Vector3.SqrMagnitude(buf[i] - mean);
|
||||
if (d <= s)
|
||||
{
|
||||
avg += buf[i];
|
||||
ib++;
|
||||
}
|
||||
}
|
||||
if (ib > 0)
|
||||
{
|
||||
avg /= (float)ib;
|
||||
return avg;
|
||||
}
|
||||
return mean;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3081ca6de5f736847b8e96b9683e38d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
323
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/Core.cs
Normal file
323
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/Core.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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.Runtime.InteropServices;
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace Immersal
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct LocalizeInfo
|
||||
{
|
||||
public int handle;
|
||||
public float px, py, pz;
|
||||
public float r00, r01, r02, r10, r11, r12, r20, r21, r22;
|
||||
public int confidence;
|
||||
};
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void LogCallback(IntPtr msg);
|
||||
|
||||
public static class Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a Vector3 point cloud representation of the map data.
|
||||
/// </summary>
|
||||
/// <param name="mapHandle">An integer map handle</param>
|
||||
/// <param name="points">A preallocated Vector3 array for the points</param>
|
||||
/// <returns>Returns the number of points if succeeded, 0 otherwise.</returns>
|
||||
public static int GetPointCloud(int mapHandle, Vector3[] points)
|
||||
{
|
||||
GCHandle vector3ArrayHandle = GCHandle.Alloc(points, GCHandleType.Pinned);
|
||||
int n = Native.icvPointsGet(mapHandle, vector3ArrayHandle.AddrOfPinnedObject(), points.Length);
|
||||
vector3ArrayHandle.Free();
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get point count of the map's point cloud.
|
||||
/// </summary>
|
||||
/// <param name="mapHandle">An integer map handle</param>
|
||||
/// <returns>Returns the number of points.</returns>
|
||||
public static int GetPointCloudSize(int mapHandle) => Native.icvPointsGetCount(mapHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Load map data from a .bytes file.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Map data as a byte array</param>
|
||||
/// <returns>An integer map handle.</returns>
|
||||
public static int LoadMap(byte[] buffer, string license) => Native.icvLoadMap(buffer, license);
|
||||
|
||||
/// <summary>
|
||||
/// Free the map data from memory.
|
||||
/// </summary>
|
||||
/// <param name="mapHandle">An integer map handle</param>
|
||||
/// <returns>Returns 1 if succeeded, 0 otherwise.</returns>
|
||||
public static int FreeMap(int mapHandle) => Native.icvFreeMap(mapHandle);
|
||||
|
||||
public static int UndistortFishEyeImage(byte[] result, int byteSizeMax, byte[] pixels, int width,
|
||||
int height, int channels, ref Vector4 intrinsics, ref Vector4 distCoeffs, float alpha)
|
||||
{
|
||||
GCHandle resultHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
|
||||
GCHandle pixelsHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
|
||||
int r = Native.icvUndistortFishEye(resultHandle.AddrOfPinnedObject(), byteSizeMax,
|
||||
pixelsHandle.AddrOfPinnedObject(), width, height, channels, ref intrinsics, ref distCoeffs, alpha);
|
||||
resultHandle.Free();
|
||||
pixelsHandle.Free();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position and orientation of the image within the map.
|
||||
/// </summary>
|
||||
/// <param name="width">Image width</param>
|
||||
/// <param name="height">Image height</param>
|
||||
/// <param name="intrinsics">Camera intrinsics</param>
|
||||
/// <param name="pixels">Raw pixel buffer data from the camera</param>
|
||||
/// <param name="rot">Camera rotation as float array</param>
|
||||
/// <returns>An integer map ID if succeeded, -1 otherwise</returns>
|
||||
public static LocalizeInfo LocalizeImage(int n, int[] handles, int width,
|
||||
int height, ref Vector4 intrinsics, IntPtr pixels, int solverType, float[] rot)
|
||||
{
|
||||
GCHandle intHandle = GCHandle.Alloc(handles, GCHandleType.Pinned);
|
||||
LocalizeInfo result = Native.icvLocalize(n, intHandle.AddrOfPinnedObject(), width, height,
|
||||
ref intrinsics, pixels, solverType, rot);
|
||||
intHandle.Free();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static LocalizeInfo LocalizeImage(int n, int[] handles, int width,
|
||||
int height, ref Vector4 intrinsics, IntPtr pixels)
|
||||
{
|
||||
int sFlag = 0;
|
||||
float[] rot = new float[1];
|
||||
return LocalizeImage(n, handles, width, height, ref intrinsics, pixels, sFlag, rot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position and orientation of the image within the map.
|
||||
/// </summary>
|
||||
/// <param name="width">Image width</param>
|
||||
/// <param name="height">Image height</param>
|
||||
/// <param name="intrinsics">Camera intrinsics</param>
|
||||
/// <param name="pixels">Raw pixel buffer data from the camera</param>
|
||||
/// <returns>An integer map ID if succeeded, -1 otherwise</returns>
|
||||
public static LocalizeInfo LocalizeImage(int width, int height,
|
||||
ref Vector4 intrinsics, IntPtr pixels)
|
||||
{
|
||||
int n = 0;
|
||||
int[] handles = new int[1];
|
||||
return LocalizeImage(n, handles, width, height, ref intrinsics, pixels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position and orientation of the image within the map.
|
||||
/// </summary>
|
||||
/// <param name="width">Image width</param>
|
||||
/// <param name="height">Image height</param>
|
||||
/// <param name="intrinsics">Camera intrinsics</param>
|
||||
/// <param name="pixels">Raw pixel buffer data from the camera</param>
|
||||
/// <param name="rot">Camera rotation as float array</param>
|
||||
/// <returns>An integer map ID if succeeded, -1 otherwise</returns>
|
||||
public static LocalizeInfo LocalizeImage(int width, int height,
|
||||
ref Vector4 intrinsics, IntPtr pixels, float[] rot)
|
||||
{
|
||||
int n = 0;
|
||||
int[] handles = new int[1];
|
||||
return LocalizeImage(n, handles, width, height, ref intrinsics, pixels, 1, rot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="ecef"></param>
|
||||
/// <param name="map"></param>
|
||||
/// <param name="mapToEcef"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="wgs84"></param>
|
||||
/// <param name="ecef"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="ecef"></param>
|
||||
/// <param name="wgs84"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="map"></param>
|
||||
/// <param name="ecef"></param>
|
||||
/// <param name="mapToEcef"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="wgs84"></param>
|
||||
/// <param name="map"></param>
|
||||
/// <param name="mapToEcef"></param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="ecef"></param>
|
||||
/// <param name="map"></param>
|
||||
/// <param name="mapToEcef"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="map"></param>
|
||||
/// <param name="ecef"></param>
|
||||
/// <param name="mapToEcef"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="parameter">Parameter name</param>
|
||||
/// <param name="value">An integer parameter value</param>
|
||||
/// <returns>Returns 1 if succeeded, -1 otherwise.</returns>
|
||||
public static int SetInteger(string parameter, int value) => Native.icvSetInteger(parameter, value);
|
||||
}
|
||||
|
||||
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, string license);
|
||||
|
||||
[DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int icvFreeMap(int mapHandle);
|
||||
|
||||
[DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int icvUndistortFishEye(IntPtr capture, int captureSizeMax, IntPtr pixels,
|
||||
int width, int height, int channels, ref Vector4 intrinsics, ref Vector4 distCoeffs, float alpha);
|
||||
|
||||
[DllImport(Assembly, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern LocalizeInfo icvLocalize(int n, IntPtr handles, int width,
|
||||
int height, ref Vector4 intrinsics, IntPtr pixels, int sFlag, float[] rot);
|
||||
|
||||
[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);
|
||||
}
|
||||
}
|
||||
12
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/Core.cs.meta
Normal file
12
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/Core.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a0cde8ffdea2d24bb36590dbe44cb53
|
||||
timeCreated: 1464961012
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 068ff142a326946a4921c7907dd3368c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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.
|
||||
===============================================================================*/
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
|
||||
public class ReadOnlyDrawer : PropertyDrawer
|
||||
{
|
||||
public override float GetPropertyHeight(SerializedProperty property,
|
||||
GUIContent label)
|
||||
{
|
||||
return EditorGUI.GetPropertyHeight(property, label, true);
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position,
|
||||
SerializedProperty property,
|
||||
GUIContent label)
|
||||
{
|
||||
GUI.enabled = false;
|
||||
EditorGUI.PropertyField(position, property, label, true);
|
||||
GUI.enabled = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a04c0995666dab4988bf70980c9d391
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
336
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/ImmersalSDK.cs
Normal file
336
Assets/Plugins/ImmersalSDK/Runtime/Core/Scripts/ImmersalSDK.cs
Normal file
@@ -0,0 +1,336 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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 UnityEngine.Events;
|
||||
using Unity.Collections;
|
||||
using UnityEngine.XR.ARFoundation;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
using Immersal.AR;
|
||||
using UnityEngine.XR.ARSubsystems;
|
||||
using AOT;
|
||||
|
||||
namespace Immersal
|
||||
{
|
||||
public class ImmersalSDK : MonoBehaviour
|
||||
{
|
||||
public static string sdkVersion = "1.20.0";
|
||||
public static bool isHWAR = false;
|
||||
// private static readonly string[] ServerList = new[] {"https://api.immersal.com", "https://immersal.hexagon.com.cn"};
|
||||
|
||||
public enum CameraResolution { Default, HD, FullHD, Max }; // With Huawei AR Engine SDK, only Default (640x480) and Max (1440x1080) are supported.
|
||||
|
||||
private static ImmersalSDK instance = null;
|
||||
|
||||
[Tooltip("Application target frame rate")]
|
||||
private int m_TargetFrameRate = 60;
|
||||
[SerializeField]
|
||||
[Tooltip("Android resolution")]
|
||||
private CameraResolution m_AndroidResolution = CameraResolution.FullHD;
|
||||
[Tooltip("Downsample image to HD resolution")]
|
||||
[SerializeField]
|
||||
private bool m_Downsample = true;
|
||||
|
||||
public UnityEvent onPoseLost = null;
|
||||
public UnityEvent onPoseFound = null;
|
||||
|
||||
public int secondsToDecayPose = 10;
|
||||
|
||||
public LocalizerBase Localizer { get; private set; }
|
||||
public int TrackingQuality { get; private set; }
|
||||
|
||||
private ARCameraManager m_CameraManager;
|
||||
private ARSession m_ARSession;
|
||||
private bool m_bCamConfigDone = false;
|
||||
private string m_LocalizationServer;
|
||||
private int m_PreviousResults = 0;
|
||||
private int m_CurrentResults = 0;
|
||||
private int q = 0;
|
||||
private float m_LatestPoseUpdated = 0f;
|
||||
private bool m_HasPose = false;
|
||||
private XRCameraConfiguration? m_InitialConfig;
|
||||
|
||||
public static HttpClient client;
|
||||
|
||||
public int targetFrameRate
|
||||
{
|
||||
get { return m_TargetFrameRate; }
|
||||
set
|
||||
{
|
||||
m_TargetFrameRate = value;
|
||||
SetFrameRate();
|
||||
}
|
||||
}
|
||||
|
||||
public CameraResolution androidResolution
|
||||
{
|
||||
get { return m_AndroidResolution; }
|
||||
set
|
||||
{
|
||||
m_AndroidResolution = value;
|
||||
ConfigureCamera();
|
||||
}
|
||||
}
|
||||
|
||||
public bool downsample
|
||||
{
|
||||
get { return m_Downsample; }
|
||||
set
|
||||
{
|
||||
m_Downsample = value;
|
||||
SetDownsample();
|
||||
}
|
||||
}
|
||||
|
||||
public ARCameraManager cameraManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_CameraManager == null)
|
||||
{
|
||||
m_CameraManager = UnityEngine.Object.FindObjectOfType<ARCameraManager>();
|
||||
}
|
||||
return m_CameraManager;
|
||||
}
|
||||
}
|
||||
|
||||
public ARSession arSession
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ARSession == null)
|
||||
{
|
||||
m_ARSession = UnityEngine.Object.FindObjectOfType<ARSession>();
|
||||
}
|
||||
return m_ARSession;
|
||||
}
|
||||
}
|
||||
|
||||
public static ImmersalSDK Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (instance == null && !Application.isPlaying)
|
||||
{
|
||||
instance = UnityEngine.Object.FindObjectOfType<ImmersalSDK>();
|
||||
}
|
||||
#endif
|
||||
if (instance == null)
|
||||
{
|
||||
Debug.LogError("No ImmersalSDK instance found. Ensure one exists in the scene.");
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
if (instance != this)
|
||||
{
|
||||
Debug.LogError("There must be only one ImmersalSDK object in a scene.");
|
||||
UnityEngine.Object.DestroyImmediate(this);
|
||||
return;
|
||||
}
|
||||
|
||||
LogCallback callback_delegate = new LogCallback(Log);
|
||||
IntPtr intptr_delegate = Marshal.GetFunctionPointerForDelegate(callback_delegate);
|
||||
Native.PP_RegisterLogCallback(intptr_delegate);
|
||||
|
||||
HttpClientHandler handler = new HttpClientHandler();
|
||||
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
|
||||
client = new HttpClient(handler);
|
||||
client.DefaultRequestHeaders.ExpectContinue = false;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
SetFrameRate();
|
||||
#if !UNITY_EDITOR
|
||||
SetDownsample();
|
||||
#endif
|
||||
|
||||
onPoseLost?.Invoke();
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(LogCallback))]
|
||||
public static void Log(IntPtr ansiString)
|
||||
{
|
||||
string msg = Marshal.PtrToStringAnsi(ansiString);
|
||||
Debug.LogFormat("Plugin: {0}", msg);
|
||||
}
|
||||
|
||||
private void SetFrameRate()
|
||||
{
|
||||
Application.targetFrameRate = targetFrameRate;
|
||||
}
|
||||
|
||||
private void SetDownsample()
|
||||
{
|
||||
if (downsample)
|
||||
{
|
||||
Core.SetInteger("LocalizationMaxPixels", 960*720);
|
||||
}
|
||||
else
|
||||
{
|
||||
Core.SetInteger("LocalizationMaxPixels", 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Localizer != null)
|
||||
{
|
||||
LocalizerStats stats = Localizer.stats;
|
||||
if (stats.localizationAttemptCount > 0)
|
||||
{
|
||||
q = CurrentResults(stats.localizationSuccessCount);
|
||||
|
||||
if (!m_HasPose && q > 1)
|
||||
{
|
||||
m_HasPose = true;
|
||||
onPoseFound?.Invoke();
|
||||
}
|
||||
|
||||
if (m_HasPose && (q < 1 || !Localizer.isTracking))
|
||||
{
|
||||
m_HasPose = false;
|
||||
Localizer.Reset();
|
||||
m_PreviousResults = 0;
|
||||
m_CurrentResults = 0;
|
||||
onPoseLost?.Invoke();
|
||||
}
|
||||
|
||||
TrackingQuality = q;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isHWAR)
|
||||
{
|
||||
if (!m_bCamConfigDone && cameraManager != null)
|
||||
ConfigureCamera();
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureCamera()
|
||||
{
|
||||
#if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS)
|
||||
var cameraSubsystem = cameraManager.subsystem;
|
||||
if (cameraSubsystem == null || !cameraSubsystem.running)
|
||||
return;
|
||||
var configurations = cameraSubsystem.GetConfigurations(Allocator.Temp);
|
||||
if (!configurations.IsCreated || (configurations.Length <= 0))
|
||||
return;
|
||||
int bestError = int.MaxValue;
|
||||
var currentConfig = cameraSubsystem.currentConfiguration;
|
||||
int dw = (int)currentConfig?.width;
|
||||
int dh = (int)currentConfig?.height;
|
||||
if (dw == 0 && dh == 0)
|
||||
return;
|
||||
|
||||
CameraResolution reso = androidResolution;
|
||||
|
||||
if (!m_bCamConfigDone)
|
||||
{
|
||||
m_InitialConfig = currentConfig;
|
||||
}
|
||||
|
||||
switch (reso)
|
||||
{
|
||||
case CameraResolution.Default:
|
||||
dw = (int)currentConfig?.width;
|
||||
dh = (int)currentConfig?.height;
|
||||
break;
|
||||
case CameraResolution.HD:
|
||||
dw = 1280;
|
||||
dh = 720;
|
||||
break;
|
||||
case CameraResolution.FullHD:
|
||||
dw = 1920;
|
||||
dh = 1080;
|
||||
break;
|
||||
case CameraResolution.Max:
|
||||
dw = 80000;
|
||||
dh = 80000;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var config in configurations)
|
||||
{
|
||||
int perror = config.width * config.height - dw * dh;
|
||||
if (Math.Abs(perror) < bestError)
|
||||
{
|
||||
bestError = Math.Abs(perror);
|
||||
currentConfig = config;
|
||||
}
|
||||
}
|
||||
|
||||
if (reso != CameraResolution.Default) {
|
||||
Debug.LogFormat("resolution = {0}x{1}", (int)currentConfig?.width, (int)currentConfig?.height);
|
||||
cameraSubsystem.currentConfiguration = currentConfig;
|
||||
}
|
||||
else
|
||||
{
|
||||
cameraSubsystem.currentConfiguration = m_InitialConfig;
|
||||
}
|
||||
#endif
|
||||
m_bCamConfigDone = true;
|
||||
}
|
||||
|
||||
int CurrentResults(int localizationResults) {
|
||||
int diffResults = localizationResults - m_PreviousResults;
|
||||
m_PreviousResults = localizationResults;
|
||||
if (diffResults > 0)
|
||||
{
|
||||
m_LatestPoseUpdated = Time.time;
|
||||
m_CurrentResults += diffResults;
|
||||
if (m_CurrentResults > 3)
|
||||
{
|
||||
m_CurrentResults = 3;
|
||||
}
|
||||
}
|
||||
else if (Time.time - m_LatestPoseUpdated > secondsToDecayPose)
|
||||
{
|
||||
m_LatestPoseUpdated = Time.time;
|
||||
if (m_CurrentResults > 0)
|
||||
{
|
||||
m_CurrentResults--;
|
||||
}
|
||||
}
|
||||
|
||||
return m_CurrentResults;
|
||||
}
|
||||
|
||||
public void RegisterLocalizer(LocalizerBase localizer)
|
||||
{
|
||||
Localizer = localizer;
|
||||
Localizer.OnReset += OnLocalizerReset;
|
||||
}
|
||||
|
||||
public void UnRegisterLocalizer()
|
||||
{
|
||||
Localizer.OnReset -= OnLocalizerReset;
|
||||
Localizer = null;
|
||||
}
|
||||
|
||||
private void OnLocalizerReset()
|
||||
{
|
||||
m_CurrentResults = m_PreviousResults = 0;
|
||||
m_HasPose = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc609bf82f8e346d593bc1a41c2b7cb8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class ReadOnlyAttribute : PropertyAttribute
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2058786f596e5a34bae16afef4229107
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fbaad84308ffb477989e6e185c52e381
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,323 @@
|
||||
/*===============================================================================
|
||||
Copyright (C) 2023 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.Threading.Tasks;
|
||||
using Immersal.AR;
|
||||
using Rokid.UXR.Module;
|
||||
using Rokid.UXR.Native;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Immersal.XR.Rokid
|
||||
{
|
||||
public struct RokidCameraData
|
||||
{
|
||||
public long Timestamp;
|
||||
public int Width;
|
||||
public int Height;
|
||||
public byte[] Bytes;
|
||||
public Pose Pose;
|
||||
public Vector4 Distortion;
|
||||
public float Alpha;
|
||||
public Vector4 Intrinsics;
|
||||
}
|
||||
|
||||
public enum CameraType
|
||||
{
|
||||
NV21 = 2 //ARGB not supported.
|
||||
}
|
||||
|
||||
public class RokidLocalizer : LocalizerBase
|
||||
{
|
||||
private static RokidLocalizer instance = null;
|
||||
|
||||
public static RokidLocalizer Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (instance == null && !Application.isPlaying)
|
||||
{
|
||||
instance = UnityEngine.Object.FindObjectOfType<RokidLocalizer>();
|
||||
}
|
||||
#endif
|
||||
if (instance == null)
|
||||
{
|
||||
Debug.LogError("No RokidLocalizer instance found. Ensure one exists in the scene.");
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public int Channels => m_cameraType == CameraType.NV21 ? 1 : 4;
|
||||
public int MultipleLocalizationsCount = 20;
|
||||
public UnityEvent OnMultipleLocalizations = null;
|
||||
public RokidCameraData m_LatestCameraData;
|
||||
|
||||
private bool m_IsInitialized = false;
|
||||
private bool m_MultipleLocalizationEventInvoked = false;
|
||||
private CameraType m_cameraType = CameraType.NV21;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
if (instance != this)
|
||||
{
|
||||
Debug.LogError("There must be only one RokidLocalizer object in a scene.");
|
||||
UnityEngine.Object.DestroyImmediate(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
NativeInterface.NativeAPI.StartCameraPreview();
|
||||
// 1 = ARGB, 2 = NV21
|
||||
NativeInterface.NativeAPI.SetCameraPreviewDataType((int) m_cameraType);
|
||||
NativeInterface.NativeAPI.OnCameraDataUpdate += OnCameraDataUpdate;
|
||||
m_IsInitialized = true;
|
||||
isTracking = true;
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
m_Sdk.RegisterLocalizer(instance);
|
||||
|
||||
Screen.sleepTimeout = SleepTimeout.NeverSleep;
|
||||
NativeInterface.NativeAPI.Recenter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Listener of Camera data
|
||||
/// </summary>
|
||||
/// <param name="width">preview size width</param>
|
||||
/// <param name="height">preview size height</param>
|
||||
/// <param name="yuvImage">camera image</param>
|
||||
/// <param name="ts">timestamp</param>
|
||||
public void OnCameraDataUpdate(int width, int height, byte[] data, long ts)
|
||||
{
|
||||
Pose pose = NativeInterface.NativeAPI.GetHistoryCameraPhysicsPose(ts);
|
||||
|
||||
// fisheye:alpha,k1,k2,k3,k4;
|
||||
var d = new float[5];
|
||||
NativeInterface.NativeAPI.GetDistortion(d);
|
||||
var distortionCoefficients = new Vector4(d[1], d[2], d[3], d[4]);
|
||||
var alpha = d[0];
|
||||
var intrinsics = GetIntrinsics();
|
||||
|
||||
m_LatestCameraData = new RokidCameraData
|
||||
{
|
||||
Timestamp = ts,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Bytes = data,
|
||||
Pose = pose,
|
||||
Distortion = distortionCoefficients,
|
||||
Intrinsics = intrinsics,
|
||||
Alpha = alpha
|
||||
};
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
if (m_IsInitialized)
|
||||
{
|
||||
NativeInterface.NativeAPI.OnCameraDataUpdate -= OnCameraDataUpdate;
|
||||
NativeInterface.NativeAPI.StopCameraPreview();
|
||||
NativeInterface.NativeAPI.ClearCameraDataUpdate();
|
||||
m_IsInitialized = false;
|
||||
isTracking = false;
|
||||
}
|
||||
}
|
||||
|
||||
override public void OnDestroy()
|
||||
{
|
||||
Release();
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
override public void OnApplicationPause(bool pauseStatus)
|
||||
{
|
||||
if (pauseStatus)
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
base.OnApplicationPause(pauseStatus);
|
||||
}
|
||||
|
||||
override protected void Update()
|
||||
{
|
||||
if (m_IsInitialized == false && NativeInterface.NativeAPI.IsPreviewing())
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
base.Update();
|
||||
}
|
||||
|
||||
public override async void Localize()
|
||||
{
|
||||
var data = m_LatestCameraData;
|
||||
data = await UndistortImage(data);
|
||||
SetPixelBuffer(data.Bytes);
|
||||
|
||||
if (m_PixelBuffer != IntPtr.Zero)
|
||||
{
|
||||
stats.localizationAttemptCount++;
|
||||
|
||||
Vector3 camPos = data.Pose.position;
|
||||
Quaternion camRot = data.Pose.rotation;
|
||||
float startTime = Time.realtimeSinceStartup;
|
||||
|
||||
Task<LocalizeInfo> t = Task.Run(() =>
|
||||
{
|
||||
return Immersal.Core.LocalizeImage(data.Width, data.Height, ref data.Intrinsics, m_PixelBuffer);
|
||||
});
|
||||
|
||||
await t;
|
||||
|
||||
LocalizeInfo locInfo = t.Result;
|
||||
|
||||
Matrix4x4 resultMatrix = Matrix4x4.identity;
|
||||
resultMatrix.m00 = locInfo.r00;
|
||||
resultMatrix.m01 = locInfo.r01;
|
||||
resultMatrix.m02 = locInfo.r02;
|
||||
resultMatrix.m03 = locInfo.px;
|
||||
resultMatrix.m10 = locInfo.r10;
|
||||
resultMatrix.m11 = locInfo.r11;
|
||||
resultMatrix.m12 = locInfo.r12;
|
||||
resultMatrix.m13 = locInfo.py;
|
||||
resultMatrix.m20 = locInfo.r20;
|
||||
resultMatrix.m21 = locInfo.r21;
|
||||
resultMatrix.m22 = locInfo.r22;
|
||||
resultMatrix.m23 = locInfo.pz;
|
||||
|
||||
Vector3 pos = resultMatrix.GetColumn(3);
|
||||
Quaternion rot = resultMatrix.rotation;
|
||||
|
||||
int mapHandle = locInfo.handle;
|
||||
int mapId = ARMap.MapHandleToId(mapHandle);
|
||||
float elapsedTime = Time.realtimeSinceStartup - startTime;
|
||||
|
||||
if (mapId > 0 && ARSpace.mapIdToMap.ContainsKey(mapId))
|
||||
{
|
||||
LocalizerDebugLog(string.Format("Relocalized in {0} seconds", elapsedTime));
|
||||
stats.localizationSuccessCount++;
|
||||
|
||||
if (stats.localizationSuccessCount >= MultipleLocalizationsCount &&
|
||||
!m_MultipleLocalizationEventInvoked)
|
||||
{
|
||||
OnMultipleLocalizations.Invoke();
|
||||
m_MultipleLocalizationEventInvoked = true;
|
||||
}
|
||||
|
||||
ARMap map = ARSpace.mapIdToMap[mapId];
|
||||
|
||||
if (mapId != lastLocalizedMapId)
|
||||
{
|
||||
if (resetOnMapChange)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
lastLocalizedMapId = mapId;
|
||||
OnMapChanged?.Invoke(mapId);
|
||||
}
|
||||
|
||||
rot *= Quaternion.Euler(0f, 0f, 180.0f);
|
||||
pos = ARHelper.SwitchHandedness(pos);
|
||||
rot = ARHelper.SwitchHandedness(rot);
|
||||
|
||||
MapOffset mo = ARSpace.mapIdToOffset[mapId];
|
||||
|
||||
Matrix4x4 offsetNoScale = Matrix4x4.TRS(mo.position, mo.rotation, Vector3.one);
|
||||
Vector3 scaledPos = Vector3.Scale(pos, mo.scale);
|
||||
Matrix4x4 cloudSpace = offsetNoScale * Matrix4x4.TRS(scaledPos, rot, Vector3.one);
|
||||
Matrix4x4 trackerSpace = Matrix4x4.TRS(camPos, camRot, Vector3.one);
|
||||
Matrix4x4 m = trackerSpace * (cloudSpace.inverse);
|
||||
|
||||
if (useFiltering)
|
||||
mo.space.filter.RefinePose(m);
|
||||
else
|
||||
ARSpace.UpdateSpace(mo.space, m.GetColumn(3), m.rotation);
|
||||
|
||||
Vector3 p = m.GetColumn(3);
|
||||
Vector3 euler = m.rotation.eulerAngles;
|
||||
|
||||
GetLocalizerPose(out lastLocalizedPose, mapId, pos, rot, m.inverse);
|
||||
map.NotifySuccessfulLocalization(mapId);
|
||||
OnPoseFound?.Invoke(lastLocalizedPose);
|
||||
}
|
||||
else
|
||||
{
|
||||
LocalizerDebugLog(string.Format("Localization attempt failed after {0} seconds", elapsedTime));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("No camera pixel buffer");
|
||||
}
|
||||
|
||||
base.Localize();
|
||||
}
|
||||
|
||||
|
||||
private void SetPixelBuffer(byte[] data)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* pinnedData = data)
|
||||
{
|
||||
m_PixelBuffer = (IntPtr) pinnedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vector4 GetIntrinsics()
|
||||
{
|
||||
Vector4 intrinsics = Vector4.zero;
|
||||
float[] focalLength = new float[2];
|
||||
float[] principalPoint = new float[2];
|
||||
NativeInterface.NativeAPI.GetFocalLength(focalLength);
|
||||
NativeInterface.NativeAPI.GetPrincipalPoint(principalPoint);
|
||||
intrinsics.x = focalLength[0];
|
||||
intrinsics.y = focalLength[1];
|
||||
intrinsics.z = principalPoint[0];
|
||||
intrinsics.w = principalPoint[1];
|
||||
return intrinsics;
|
||||
}
|
||||
|
||||
private async Task<RokidCameraData> UndistortImage(RokidCameraData data)
|
||||
{
|
||||
Task<(byte[], int)> d = Task.Run(() =>
|
||||
{
|
||||
var result = new byte[Channels * data.Width * data.Height];
|
||||
var r = Immersal.Core.UndistortFishEyeImage(result, result.Length, data.Bytes,
|
||||
data.Width, data.Height, Channels, ref data.Intrinsics,
|
||||
ref data.Distortion, data.Alpha);
|
||||
return (result, r);
|
||||
});
|
||||
|
||||
await d;
|
||||
data.Bytes = d.Result.Item1;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da24111645ab24d549a37ea7da86304b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
Assets/Plugins/ImmersalSDK/Runtime/ImmersalSDK.asmdef
Normal file
22
Assets/Plugins/ImmersalSDK/Runtime/ImmersalSDK.asmdef
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "ImmersalSDK",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"Unity.XR.ARFoundation",
|
||||
"Unity.XR.ARSubsystems",
|
||||
"Unity.EditorCoroutines.Editor",
|
||||
"UnityEngine.SpatialTracking",
|
||||
"Unity.TextMeshPro",
|
||||
"Unity.XR.CoreUtils",
|
||||
"Rokid.Unity.XR"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff70fce64a3314305977bdf5610ed86b
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user