This commit is contained in:
Han
2025-06-23 17:38:24 +08:00
657 changed files with 123720 additions and 73 deletions

View File

@@ -11,6 +11,7 @@ stages:
- 07.RKTools
- 08.UniTask
- 09.CodeChecker
- 99.imdk_unity
# 00.StaryEvo
job_StaryEvo:
only:
@@ -27,6 +28,9 @@ job_StaryEvo:
- cd Assets/00.StaryEvo
# 获取当前版本
- CURRENT_VERSION=$(node -p "require('./package.json').version")
- echo "registry=http://${SERVER_HOST}/npm" > .npmrc
- echo "//${SERVER_HOST}/:_authToken=${AuthToken}" >> .npmrc
- npm publish
@@ -226,5 +230,25 @@ job_CodeChecker:
allow_failure: true
when: 'on_success'
# 99.imdk_unity
job_CodeChecker:
only:
- master
stage: 99.imdk_unity
tags:
- xosmoPlugin
before_script:
- echo '开始发布'
- node -v # 验证 Node.js 版本
script:
- cd Assets/99.imdk_unity
# 获取当前版本
- CURRENT_VERSION=$(node -p "require('./package.json').version")
- echo "registry=http://${SERVER_HOST}/npm" > .npmrc
- echo "//${SERVER_HOST}/:_authToken=${AuthToken}" >> .npmrc
- npm publish
- echo '99.imdk_unity构建完成'
allow_failure: true
when: 'on_success'
after_script:
- echo "====== 发布完成 ========="

View File

@@ -86,7 +86,7 @@ namespace Stary.Evo.Editor
[ShowIf("@ buildAssetType== BuildAssetType.Login")] [BoxGroup("Login", showLabel: false)]
public string ip, userName, password;
[ShowIf("@ buildAssetType== BuildAssetType.Login")]
[BoxGroup("Login", showLabel: false)]
[Button("登录", ButtonSizes.Large)]
@@ -161,7 +161,7 @@ namespace Stary.Evo.Editor
#region HyBridCLRBuild
[BoxGroup("Build", showLabel: false)]
[HideIf("@ packageName== \"Main\" || buildAssetType!= BuildAssetType.Build")]
[HideIf("@ buildAssetType!= BuildAssetType.Build")]
[Title("打包dll(子包仅打一次)", titleAlignment: TitleAlignments.Centered)]
[HorizontalGroup("Build/HyBridClrBuildEntity"), HideLabel]
public BuildAssetEntity hyBridClrBuildEntity =
@@ -274,23 +274,11 @@ namespace Stary.Evo.Editor
[HideLabel]
public BuildAssetEntity onBuildPipelineEntity;
// [BoxGroup("Build", showLabel: false)]
// [TitleGroup("Build/UpdateAssetButton", alignment: TitleAlignments.Centered)]
// // [ShowIf("@ BuildAssetDataSetting.environmentType== EnvironmentType.Release ", true)]
// [ReadOnly]
// [InfoBox("服务器资源盘位未存在", InfoMessageType.Error, "@ _isCheckDriveExist==false")]
// [InfoBox("1、映射网络驱动器\n2、添加http://192.168.31.100:5005/alist/HotRefresh地址\n3、设置盘符为Z盘", InfoMessageType.Info,
// "@ _isCheckDriveExist==false")]
// [HideIf(
// "@ selectedPackageNames==\"Main\" ||BuildAssetDataSetting.environmentType!= EnvironmentType.Release || buildAssetType!= BuildAssetType.Build")]
// public string updateServerPath;
[BoxGroup("Build", showLabel: false)]
[Title("上传资源", titleAlignment: TitleAlignments.Centered)]
[HideLabel]
[ShowIf(
"@ BuildAssetDataSetting.environmentType== EnvironmentType.Release&&selectedPackageNames!=\"Main\" && buildAssetType== BuildAssetType.Build")]
"@ BuildAssetDataSetting.environmentType== EnvironmentType.Release && buildAssetType== BuildAssetType.Build")]
public BuildAssetEntity onUpdateBuildPipelineEntity;
@@ -385,7 +373,14 @@ namespace Stary.Evo.Editor
public static async Task UpdateFileDataResDomain(BuildAssetDataSetting setting, string zipFilePath)
{
string ip = PlayerPrefs.GetString("LoginIp");
//初始化读取资源配置表
HotfixMainResDomain hotfixMainResDomain = Resources.Load<HotfixMainResDomain>("HotfixMainResDomain");
if (hotfixMainResDomain == null)
{
Debug.LogError($"UnityEvo:读取资源配置表失败【HotfixMainResDomain】...表不存在");
return;
}
var ip = hotfixMainResDomain.hotfixMainResDomainEntity.ipconfig;
var messageEntity = await WebRequestSystem.PostFile(ip + "/FileLoad/UpLoadFile", new[] { zipFilePath });
EditorUtility.DisplayProgressBar("提示", $"开始上传{setting.packageName}(上传zip文件)", 0.5f);
if (messageEntity.code == 200)
@@ -444,9 +439,6 @@ namespace Stary.Evo.Editor
/// </summary>
private async void GetHostBuildPackageVersion(BuildAssetDataSetting setting)
{
if (selectedPackageNames == "Main")
return;
var resDmainAddRequst = new ResDmainRequst()
{
ProductName = Application.identifier,

View File

@@ -123,10 +123,10 @@ namespace Stary.Evo.Editor
public virtual void ExecuteBuild()
{
// GetPackageVersion();
if (dataSetting.packageName == "Main")
{
dataSetting.GetBuildPackageVersion(true);
}
// if (dataSetting.packageName == "Main")
// {
// dataSetting.GetBuildPackageVersion(true);
// }
}

View File

@@ -67,7 +67,14 @@ namespace Stary.Evo.Editor
$"Assets/Domain/{oneKeyBUildEntity.DomainName}/Conf/BuildAssetDataSetting.asset");
if (buildAssetDataSetting != null)
{
string ip = PlayerPrefs.GetString("LoginIp");
//初始化读取资源配置表
HotfixMainResDomain hotfixMainResDomain = Resources.Load<HotfixMainResDomain>("HotfixMainResDomain");
if (hotfixMainResDomain == null)
{
Debug.LogError($"UnityEvo:读取资源配置表失败【HotfixMainResDomain】...表不存在");
continue;
}
var ip = hotfixMainResDomain.hotfixMainResDomainEntity.ipconfig;
string url = $"{ip}/ResDomain/GetResDomainByDomain";
var resDmainRequst = new ResDmainRequst()
{

View File

@@ -15,7 +15,6 @@ namespace Stary.Evo.Editor
{
private static AssetBundleCollectorPackage package;
private static string packageName;
public static string DomainRoot
@@ -23,10 +22,6 @@ namespace Stary.Evo.Editor
get { return Application.dataPath + "/Domain"; }
}
public static string MainDomainRoot
{
get { return Application.dataPath + "/Main"; }
}
// public static string SpriteRemotedAtlas
// {
@@ -65,7 +60,8 @@ namespace Stary.Evo.Editor
{
if (oneKeyBUildEntity.isRaw)
{
CopyDllHotUpdateAssembly(oneKeyBUildEntity.DomainName, $"{DomainRoot}/{oneKeyBUildEntity.DomainName}/AddressableRes");
CopyDllHotUpdateAssembly(oneKeyBUildEntity.DomainName,
$"{DomainRoot}/{oneKeyBUildEntity.DomainName}/AddressableRes");
CopyDllStrippedAOTDllOutputRootDir($"{DomainRoot}/{oneKeyBUildEntity.DomainName}/AddressableRes");
}
}
@@ -137,15 +133,10 @@ namespace Stary.Evo.Editor
AssetDatabase.LoadAssetAtPath<DomainConfig>(configPath);
packageName = domainConfig.domain;
Mark();
if (!oneKeyBUildEntity.DomainName.Equals("Main"))
CreateRes(packageName,
$"{DomainRoot}/{oneKeyBUildEntity.DomainName}/AddressableRes",
$"{DomainRoot}/{oneKeyBUildEntity.DomainName}/HotUpdate");
else
CreateRes(packageName,
$"{DomainRoot}/{oneKeyBUildEntity.DomainName}/AddressableRes",
$"{MainDomainRoot}/Script/Runtime/HotUpdate");
CreateRes(packageName,
$"{DomainRoot}/{oneKeyBUildEntity.DomainName}/AddressableRes",
$"{DomainRoot}/{oneKeyBUildEntity.DomainName}/HotUpdate");
}
}
}
@@ -156,8 +147,8 @@ namespace Stary.Evo.Editor
// //清空主包旧数据
AssetBundleCollectorPackage assetBundleCollectorPackage = null;
//copydll
if (!BuildAssetWindow.GetBuildPackageName().Equals("Main"))
AddHotfixAddressableDll();
AddHotfixAddressableDll();
//清空用户旧数据
foreach (var package in AssetBundleCollectorSettingData.Setting.Packages)
@@ -179,14 +170,11 @@ namespace Stary.Evo.Editor
AssetDatabase.LoadAssetAtPath<DomainConfig>(configPath);
packageName = domainConfig.domain;
Mark();
if (!BuildAssetWindow.GetBuildPackageName().Equals("Main"))
CreateRes(packageName,
$"{DomainRoot}/{BuildAssetWindow.GetBuildPackageName()}/AddressableRes",
$"{DomainRoot}/{BuildAssetWindow.GetBuildPackageName()}/HotUpdate");
else
CreateRes(packageName,
$"{DomainRoot}/{BuildAssetWindow.GetBuildPackageName()}/AddressableRes",
$"{MainDomainRoot}/Script/Runtime/HotUpdate");
CreateRes(packageName,
$"{DomainRoot}/{BuildAssetWindow.GetBuildPackageName()}/AddressableRes",
$"{DomainRoot}/{BuildAssetWindow.GetBuildPackageName()}/HotUpdate");
EditorUtility.DisplayDialog("自动标记", "自动标记成功", "确定");
}

View File

@@ -1,6 +1,7 @@
using System;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Stary.Evo
{
@@ -24,6 +25,9 @@ namespace Stary.Evo
[Sirenix.OdinInspector.ReadOnly] public string className;
public LoadResType loadResType;
[Sirenix.OdinInspector.ReadOnly] [ShowIf("loadResType", LoadResType.Prefab)]
/// <summary>
@@ -33,34 +37,13 @@ namespace Stary.Evo
[ShowIf("loadResType", LoadResType.Scene)]
public string mainScene;
// [ShowIfGroup("loadResType", LoadResType.Prefab)]
// public PonitPrefabType ponitPrefabType;
// [ShowIfGroup("loadResType", LoadResType.Prefab)]
// [ShowIf("ponitPrefabType", PonitPrefabType.Plural)]
// [OnCollectionChanged("OnPointConfDichChanged")]
// public Dictionary<string, string> PonitConfDic = new Dictionary<string, string>();
// private void OnPointConfDichChanged(CollectionChangeInfo info, object value)
// {
// Debug.Log("Received callback BEFORE CHANGE with the following info: " + info +
// ", and the following collection instance: " + value);
// }
[ShowIf("loadResType", LoadResType.Scene)]
public LoadSceneMode loadSceneMode;
public enum LoadResType
{
Prefab,
Scene
}
// public enum PonitPrefabType
// {
// Single,
// Plural
// }
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "com.staryevo.main",
"version": "1.2.16",
"version": "1.3.2",
"displayName": "00.StaryEvo",
"description": "This is an Framework package(后台服务器版本端口9527)",
"unity": "2021.3",

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f441e4b77db3b4f9ba98e2ae40142506
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Immersal.XR
{
[CustomEditor(typeof(DeviceLocalization))]
public class DeviceLocalizationEditor : Editor
{
private SerializedProperty configurationMode;
private SerializedProperty solverTypeProperty;
private SerializedProperty priorNNCountProperty;
private SerializedProperty priorScaleProperty;
private SerializedProperty priorRadiusProperty;
private SerializedProperty onProgressEventProperty;
private void OnEnable()
{
configurationMode = serializedObject.FindProperty("m_ConfigurationMode");
solverTypeProperty = serializedObject.FindProperty("m_SolverType");
priorNNCountProperty = serializedObject.FindProperty("m_PriorNNCount");
priorRadiusProperty = serializedObject.FindProperty("m_PriorRadius");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// Configuration mode
EditorGUILayout.HelpBox(configurationMode.enumValueIndex == (int)ConfigurationMode.Always ? "This localization method will be active even if there are no maps configured to use it." : "This localization method will only be active if there are maps configured to use it.", MessageType.Info);
configurationMode.enumValueIndex = EditorGUILayout.Popup("Configuration mode", configurationMode.enumValueIndex, configurationMode.enumNames);
EditorGUILayout.Separator();
if (solverTypeProperty.enumValueIndex is (int)SolverType.Prior or (int)SolverType.Lean)
{
EditorGUILayout.HelpBox("This Solver type is experimental. Extensive testing is advised.", MessageType.Warning);
}
// SolverType
solverTypeProperty.enumValueIndex = EditorGUILayout.Popup("Solver type", solverTypeProperty.enumValueIndex, solverTypeProperty.enumNames);
if (solverTypeProperty.enumValueIndex == (int)SolverType.Prior)
{
EditorGUILayout.PropertyField(priorNNCountProperty, new GUIContent("Nearest neighbour min"));
EditorGUILayout.PropertyField(priorRadiusProperty, new GUIContent("Radius"));
}
// Apply
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 486b4a0e3043f44b7aa3217d7cff1f65
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Immersal.XR
{
[CustomEditor(typeof(GeoLocalization))]
public class GeoLocalizationEditor : Editor
{
private SerializedProperty configurationMode;
private SerializedProperty solverTypeProperty;
private SerializedProperty priorNNCountMinProperty;
private SerializedProperty priorNNCountMaxProperty;
private SerializedProperty priorScaleProperty;
private SerializedProperty priorRadiusProperty;
private SerializedProperty onProgressEventProperty;
private void OnEnable()
{
configurationMode = serializedObject.FindProperty("m_ConfigurationMode");
solverTypeProperty = serializedObject.FindProperty("m_SolverType");
priorNNCountMinProperty = serializedObject.FindProperty("m_PriorNNCountMin");
priorNNCountMaxProperty = serializedObject.FindProperty("m_PriorNNCountMax");
priorScaleProperty = serializedObject.FindProperty("m_PriorScale");
priorRadiusProperty = serializedObject.FindProperty("m_PriorRadius");
onProgressEventProperty = serializedObject.FindProperty("OnProgress");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// Configuration mode
EditorGUILayout.HelpBox(configurationMode.enumValueIndex == (int)ConfigurationMode.Always ? "This localization method will be active even if there are no maps configured to use it." : "This localization method will only be active if there are maps configured to use it.", MessageType.Info);
configurationMode.enumValueIndex = EditorGUILayout.Popup("Configuration mode", configurationMode.enumValueIndex, configurationMode.enumNames);
EditorGUILayout.Separator();
if (solverTypeProperty.enumValueIndex is (int)SolverType.Prior or (int)SolverType.Lean)
{
EditorGUILayout.HelpBox("This Solver type is experimental. Extensive testing is advised.", MessageType.Warning);
}
// SolverType
solverTypeProperty.enumValueIndex = EditorGUILayout.Popup("Solver type", solverTypeProperty.enumValueIndex, solverTypeProperty.enumNames);
if (solverTypeProperty.enumValueIndex == (int)SolverType.Prior)
{
EditorGUILayout.PropertyField(priorNNCountMinProperty, new GUIContent("Nearest neighbour min"));
EditorGUILayout.PropertyField(priorNNCountMaxProperty, new GUIContent("Nearest neighbour max"));
EditorGUILayout.PropertyField(priorScaleProperty, new GUIContent("Scale"));
EditorGUILayout.PropertyField(priorRadiusProperty, new GUIContent("Radius"));
}
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(onProgressEventProperty, new GUIContent("Upload progress"));
// Apply
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3e1bea21e32424fe78db71c1f7435af4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
{
"name": "Immersal.Core.Editor",
"references": [
"Immersal.Core",
"Unity.EditorCoroutines.Editor",
"Unity.XR.Management",
"Unity.XR.Management.Editor",
"Unity.RenderPipelines.Universal.Runtime"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.magicleap.unitysdk",
"expression": "1.7.0",
"define": "IMMERSAL_MAGIC_LEAP_ENABLED"
}
],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6084709da3b1941e0b68429c7e238813
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,772 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.XR.Management;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.XR.Management;
namespace Immersal
{
internal enum ProjectValidationState
{
Uninitialized = 0,
Issues = 1,
NoIssues = 2
}
internal class ImmersalProjectValidationWindow : EditorWindow, IActiveBuildTargetChanged
{
private static ImmersalProjectValidationWindow window = null;
private Vector2 m_ScrollViewPos = Vector2.zero;
private static ProjectValidationState SavedValidationState
{
get => (ProjectValidationState)PlayerPrefs.GetInt(ImmersalProjectValidation.PlayerPrefsStateString, 0);
set => PlayerPrefs.SetInt(ImmersalProjectValidation.PlayerPrefsStateString, (int)value);
}
[InitializeOnLoadMethod]
internal static void InitializeOnLoad()
{
EditorApplication.delayCall += () =>
{
if (SavedValidationState is ProjectValidationState.Uninitialized or ProjectValidationState.Issues)
{
SavedValidationState = ProjectValidationState.Issues;
ShowWindow();
}
};
}
[MenuItem("Immersal SDK/Project Validation")]
private static void MenuItem()
{
ShowWindow();
}
private static void ShowWindow(BuildTargetGroup buildTargetGroup = BuildTargetGroup.Unknown)
{
if (window == null)
{
window = (ImmersalProjectValidationWindow) GetWindow(typeof(ImmersalProjectValidationWindow));
window.titleContent = Content.Title;
window.minSize = new Vector2(500.0f, 300.0f);
}
window.UpdateIssues();
window.Show();
}
private static void InitStyles()
{
if (Styles.s_ListLabel != null)
return;
Styles.s_ListLabel = new GUIStyle(Styles.s_SelectionStyle)
{
border = new RectOffset(0, 0, 0, 0),
padding = new RectOffset(5, 5, 5, 5),
margin = new RectOffset(5, 5, 5, 5)
};
Styles.s_TargetPlatformLabel = new GUIStyle(EditorStyles.label)
{
fontSize = 9,
fontStyle = FontStyle.Normal,
padding = new RectOffset(10, 10, 0, 0),
};
Styles.s_IssuesTitleLabel = new GUIStyle(EditorStyles.label)
{
fontSize = 16,
fontStyle = FontStyle.Bold,
padding = new RectOffset(10, 10, 0, 0),
};
Styles.s_Wrap = new GUIStyle(EditorStyles.label)
{
wordWrap = true,
alignment = TextAnchor.MiddleLeft,
padding = new RectOffset(0, 5, 5, 5)
};
Styles.s_Icon = new GUIStyle(EditorStyles.label)
{
margin = new RectOffset(10, 10, 8, 0),
fixedWidth = Content.IconSize.x * 2
};
Styles.s_InfoBanner = new GUIStyle(EditorStyles.label)
{
padding = new RectOffset(10, 10, 15, 5)
};
Styles.s_Fix = new GUIStyle(EditorStyles.miniButton)
{
stretchWidth = false,
fixedWidth = 80,
margin = new RectOffset(0, 0, 8, 5)
};
Styles.s_FixAll = new GUIStyle(EditorStyles.miniButton)
{
stretchWidth = false,
fixedWidth = 80,
margin = new RectOffset(0, 10, 5, 8)
};
}
private readonly List<ImmersalProjectIssue> projectIssues = new List<ImmersalProjectIssue>();
private List<ImmersalProjectIssue> fixAllIssues = new List<ImmersalProjectIssue>();
private double lastUpdate;
private const double updateInterval = 1.0;
private const double backgroundUpdateInterval = 1.0;
private static class Content
{
public static readonly GUIContent Title = new GUIContent("Immersal Project Validation", "");
public static readonly GUIContent WarningIcon = EditorGUIUtility.IconContent("Warning@2x");
public static readonly GUIContent ErrorIcon = EditorGUIUtility.IconContent("Error@2x");
public static readonly GUIContent FixButton = new GUIContent("Fix", "");
public static readonly GUIContent PlayMode = new GUIContent("Exit play mode", EditorGUIUtility.IconContent("console.infoicon").image);
public static readonly Vector2 IconSize = new Vector2(16.0f, 16.0f);
}
private static class Styles
{
public static GUIStyle s_SelectionStyle = "TV Selection";
public static GUIStyle s_IssuesBackground = "ScrollViewAlt";
public static GUIStyle s_ListLabel;
public static GUIStyle s_TargetPlatformLabel;
public static GUIStyle s_IssuesTitleLabel;
public static GUIStyle s_Wrap;
public static GUIStyle s_Icon;
public static GUIStyle s_InfoBanner;
public static GUIStyle s_Fix;
public static GUIStyle s_FixAll;
}
protected void OnFocus() => UpdateIssues(true);
protected void Update() => UpdateIssues();
private void UpdateIssues(bool force = false)
{
var interval = EditorWindow.focusedWindow == this ? updateInterval : backgroundUpdateInterval;
if (!force && EditorApplication.timeSinceStartup - lastUpdate < interval)
return;
// Fix all
foreach (ImmersalProjectIssue issue in fixAllIssues)
{
issue.Fix?.Invoke();
}
fixAllIssues.Clear();
ImmersalProjectValidation.CheckIssues(projectIssues);
Repaint();
if (projectIssues.Count > 0)
{
if(SavedValidationState != ProjectValidationState.Uninitialized)
SavedValidationState = ProjectValidationState.Issues;
}
else
{
SavedValidationState = ProjectValidationState.NoIssues;
}
lastUpdate = EditorApplication.timeSinceStartup;
}
public void OnGUI()
{
InitStyles();
EditorGUIUtility.SetIconSize(Content.IconSize);
using (new EditorGUI.DisabledScope(fixAllIssues.Count > 0))
{
EditorGUILayout.BeginVertical();
if (EditorApplication.isPlaying && projectIssues.Count > 0)
{
GUILayout.Label(Content.PlayMode, Styles.s_InfoBanner);
}
EditorGUILayout.Space();
bool fixableIssues = projectIssues.Any(f => f.Fix != null);
EditorGUILayout.LabelField($"Target platform: {ImmersalProjectValidation.ActiveBuildTarget.ToString()}", Styles.s_TargetPlatformLabel);
EditorGUILayout.BeginHorizontal();
using (new EditorGUI.DisabledScope(EditorApplication.isPlaying))
{
EditorGUILayout.LabelField($"Project issues ({projectIssues.Count}):", Styles.s_IssuesTitleLabel);
}
if (fixableIssues)
{
using (new EditorGUI.DisabledScope(EditorApplication.isPlaying))
{
if (GUILayout.Button("Fix All", Styles.s_FixAll))
{
fixAllIssues = new List<ImmersalProjectIssue>();
foreach (ImmersalProjectIssue issue in projectIssues)
{
if (!issue.RequiresManualFix)
fixAllIssues.Add(issue);
}
}
}
}
EditorGUILayout.EndHorizontal();
m_ScrollViewPos = EditorGUILayout.BeginScrollView(m_ScrollViewPos, Styles.s_IssuesBackground,
GUILayout.ExpandHeight(true));
using (new EditorGUI.DisabledScope(EditorApplication.isPlaying))
{
foreach (ImmersalProjectIssue issue in projectIssues)
{
EditorGUILayout.BeginHorizontal(Styles.s_ListLabel);
GUILayout.Label(issue.Error ? Content.ErrorIcon : Content.WarningIcon, Styles.s_Icon,
GUILayout.Width(Content.IconSize.x));
GUILayout.Label(issue.Message(), Styles.s_Wrap);
GUILayout.FlexibleSpace();
GUILayout.Label("", GUILayout.Width(Content.IconSize.x * 1.5f));
if (issue.Fix != null)
{
if (GUILayout.Button(Content.FixButton, Styles.s_Fix))
{
issue.Fix();
}
}
else if (fixableIssues)
{
GUILayout.Label("", GUILayout.Width(80.0f));
}
EditorGUILayout.EndHorizontal();
}
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
}
public void OnActiveBuildTargetChanged(BuildTarget previousTarget, BuildTarget newTarget)
{
UpdateIssues(true);
}
public int callbackOrder => 0;
}
#if UNITY_EDITOR
public interface IImmersalProjectIssueProvider
{
public string Name { get; }
public bool Enabled { get; set; }
public bool DisableDefaultIssues { get; }
public IEnumerable<ImmersalProjectIssue> Issues { get; }
}
public class ImmersalProjectIssue
{
public Func<string> Message;
public Func<bool> Check;
public Action Fix;
public bool Error;
public bool RequiresManualFix;
}
public static class ImmersalProjectValidation
{
public static string PlayerPrefsStateString = "ImmersalProjectValidationState";
private static IImmersalProjectIssueProvider m_DefaultIssueProvider = new DefaultImmersalProjectIssueProvider();
private static Dictionary<string, IImmersalProjectIssueProvider> m_IssueProviders = new Dictionary<string, IImmersalProjectIssueProvider>();
public static BuildTargetGroup ActiveBuildTargetGroup = ActiveBuildTarget switch
{
BuildTarget.iOS => BuildTargetGroup.iOS,
BuildTarget.Android => BuildTargetGroup.Android,
BuildTarget.StandaloneOSX => BuildTargetGroup.Standalone,
BuildTarget.StandaloneWindows => BuildTargetGroup.Standalone,
BuildTarget.StandaloneWindows64 => BuildTargetGroup.Standalone,
BuildTarget.StandaloneLinux64 => BuildTargetGroup.Standalone,
BuildTarget.WSAPlayer => BuildTargetGroup.WSA,
_ => BuildTargetGroup.Unknown
};
public static BuildTarget ActiveBuildTarget => EditorUserBuildSettings.activeBuildTarget;
public static void RegisterIssueProvider(IImmersalProjectIssueProvider provider)
{
m_IssueProviders[provider.Name] = provider;
}
public static void CheckIssues(List<ImmersalProjectIssue> issues)
{
issues.Clear();
if (m_IssueProviders.Count > 1)
{
issues.Add(new ImmersalProjectIssue
{
Check = () => false,
Message = () => "Multiple packages affecting project issues.",
Error = false
});
}
foreach (IImmersalProjectIssueProvider provider in m_IssueProviders.Values)
{
if (provider.DisableDefaultIssues)
m_DefaultIssueProvider.Enabled = false;
if (provider.Enabled)
issues.AddRange(provider.Issues);
}
if (m_DefaultIssueProvider.Enabled)
issues.AddRange(m_DefaultIssueProvider.Issues);
issues.RemoveAll(issue => issue.Check?.Invoke() ?? false);
}
}
internal class DefaultImmersalProjectIssueProvider : IImmersalProjectIssueProvider
{
public string Name => "Default";
public bool DisableDefaultIssues => false;
public bool Enabled { get; set; } = true;
public IEnumerable<ImmersalProjectIssue> Issues => DefaultProjectIssues;
// ReSharper disable once HeapView.ObjectAllocation
private static readonly ImmersalProjectIssue[] DefaultProjectIssues =
{
// Graphics API
new ImmersalProjectIssue()
{
Message = () =>
{
return ImmersalProjectValidation.ActiveBuildTarget switch
{
BuildTarget.iOS => "Graphics API must be set to Metal",
BuildTarget.Android => "Graphics API must be set to OpenGLES3",
BuildTarget.StandaloneWindows => "Graphics API must set to OpenGLCore",
_ => "Unsupported GraphicsAPI for current build target."
};
},
Check = () =>
{
GraphicsDeviceType[] graphicAPIs = PlayerSettings.GetGraphicsAPIs(ImmersalProjectValidation.ActiveBuildTarget);
return ImmersalProjectValidation.ActiveBuildTarget switch
{
BuildTarget.iOS => graphicAPIs.Length == 1 && graphicAPIs[0] == GraphicsDeviceType.Metal,
BuildTarget.Android => graphicAPIs.Length == 1 && graphicAPIs[0] == GraphicsDeviceType.OpenGLES3,
BuildTarget.StandaloneWindows => graphicAPIs.Length == 1 && graphicAPIs[0] == GraphicsDeviceType.OpenGLCore,
_ => true
};
},
Fix = () =>
{
GraphicsDeviceType[] cga = PlayerSettings.GetGraphicsAPIs(ImmersalProjectValidation.ActiveBuildTarget);
var autoGraphicAPI = PlayerSettings.GetUseDefaultGraphicsAPIs(ImmersalProjectValidation.ActiveBuildTarget);
if (autoGraphicAPI)
PlayerSettings.SetUseDefaultGraphicsAPIs(ImmersalProjectValidation.ActiveBuildTarget, false);
GraphicsDeviceType[] graphicAPIs = ImmersalProjectValidation.ActiveBuildTarget switch
{
BuildTarget.iOS => new GraphicsDeviceType[] { GraphicsDeviceType.Metal },
BuildTarget.Android => new GraphicsDeviceType[] { GraphicsDeviceType.OpenGLES3 },
BuildTarget.StandaloneWindows => new GraphicsDeviceType[] { GraphicsDeviceType.OpenGLCore },
_ => PlayerSettings.GetGraphicsAPIs(ImmersalProjectValidation.ActiveBuildTarget)
};
PlayerSettings.SetGraphicsAPIs(ImmersalProjectValidation.ActiveBuildTarget, graphicAPIs);
},
Error = true,
},
// IL2CPP
new ImmersalProjectIssue()
{
Message = () => "IL2CPP must be enabled.",
Check = () => PlayerSettings.GetScriptingBackend(ImmersalProjectValidation.ActiveBuildTargetGroup) == ScriptingImplementation.IL2CPP,
Fix = () => { PlayerSettings.SetScriptingBackend(ImmersalProjectValidation.ActiveBuildTargetGroup, ScriptingImplementation.IL2CPP); },
Error = true,
},
// Allow unsafe code
new ImmersalProjectIssue()
{
Message = () => "Allow 'unsafe' code must be enabled.",
Check = () => PlayerSettings.allowUnsafeCode,
Fix = () => { PlayerSettings.allowUnsafeCode = true; },
Error = true,
},
// Camera usage description
new ImmersalProjectIssue()
{
Message = () => "Camera Usage Description must be defined.",
Check = () => ImmersalProjectValidation.ActiveBuildTarget != BuildTarget.iOS || PlayerSettings.iOS.cameraUsageDescription != "",
Fix = () => { PlayerSettings.iOS.cameraUsageDescription = "Required for augmented reality support."; },
Error = true,
},
// Location usage description
new ImmersalProjectIssue()
{
Message = () => "Location Usage Description must be defined.",
Check = () => ImmersalProjectValidation.ActiveBuildTarget != BuildTarget.iOS || PlayerSettings.iOS.locationUsageDescription != "",
Fix = () => { PlayerSettings.iOS.locationUsageDescription = "Required for satellite positioning support."; },
Error = true,
},
// minimum ios version 12.0
new ImmersalProjectIssue()
{
Message = () => "Target minimum iOS Version must be 12.0 or higher.",
Check = () => ImmersalProjectValidation.ActiveBuildTarget != BuildTarget.iOS || (float.TryParse(PlayerSettings.iOS.targetOSVersionString, out float minVersion) && minVersion >= 12.0f),
Fix = () => { PlayerSettings.iOS.targetOSVersionString = "12.0"; },
Error = true,
},
// minimum android api version 26
new ImmersalProjectIssue()
{
Message = () => "Minimum Android API Level must be 26 or higher.",
Check = () => ImmersalProjectValidation.ActiveBuildTarget != BuildTarget.Android ||
PlayerSettings.Android.minSdkVersion >= AndroidSdkVersions.AndroidApiLevel26,
Fix = () => { PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel26; },
Error = true,
},
// ARM64
new ImmersalProjectIssue()
{
Message = () => "ARM64 Target Architecture must be enabled.",
Check = () => ImmersalProjectValidation.ActiveBuildTarget != BuildTarget.Android ||
(PlayerSettings.Android.targetArchitectures & AndroidArchitecture.ARM64) != 0,
Fix = () => { PlayerSettings.Android.targetArchitectures |= AndroidArchitecture.ARM64; },
Error = true,
},
// ARKit loader
new ImmersalProjectIssue()
{
Message = () => "ARKit XR-Plugin Provider must be enabled.",
Check = () => ImmersalProjectValidation.ActiveBuildTarget != BuildTarget.iOS || IsPluginLoaderEnabled("AR Kit Loader") ,
Fix = () =>
{
EditorUserBuildSettings.selectedBuildTargetGroup = ImmersalProjectValidation.ActiveBuildTargetGroup;
SettingsService.OpenProjectSettings("Project/XR Plug-in Management");
},
Error = true,
RequiresManualFix = true
},
// ARCore loader
new ImmersalProjectIssue()
{
Message = () => "ARCore XR-Plugin Provider must be enabled.",
Check = () => ImmersalProjectValidation.ActiveBuildTarget != BuildTarget.Android || IsPluginLoaderEnabled("AR Core Loader"),
Fix = () =>
{
EditorUserBuildSettings.selectedBuildTargetGroup = ImmersalProjectValidation.ActiveBuildTargetGroup;
SettingsService.OpenProjectSettings("Project/XR Plug-in Management");
},
Error = true,
RequiresManualFix = true
},
new ImmersalProjectIssue()
{
Message = () => "Universal Render Pipeline is recommended",
Check = () => GraphicsSettings.defaultRenderPipeline != null,
Fix = SetupRenderPipeline,
Error = false,
RequiresManualFix = false
},
// android manifest exists
new ImmersalProjectIssue()
{
Message = () => "Custom Android Manifest is required.",
Check = () => ImmersalProjectValidation.ActiveBuildTarget != BuildTarget.Android || CheckManifestExists(),
Fix = CreateManifest,
Error = true,
},
// android manifest has necessary attributes
new ImmersalProjectIssue()
{
Message = () => "Android Manifest should include network permissions",
Check = () => ImmersalProjectValidation.ActiveBuildTarget != BuildTarget.Android || CheckManifestContent(),
Fix = ConfigureManifest,
Error = false,
},
};
private static bool IsPluginLoaderEnabled(string loaderName)
{
XRGeneralSettings generalSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(ImmersalProjectValidation.ActiveBuildTargetGroup);
if (generalSettings == null)
return false;
XRManagerSettings managerSettings = generalSettings.AssignedSettings;
string loaderNameNoWhitespace = loaderName.Replace(" ", "");
return managerSettings != null && managerSettings.activeLoaders.Any(loader => (loader.name == loaderName || loader.name == loaderNameNoWhitespace));
}
private static void SetupRenderPipeline()
{
string destinationPath = Application.dataPath;
// Renderer and RenderPipelineAsset
if (CopyFromPackage("Editor/ImmersalURPAsset_Renderer.asset", Path.Combine(destinationPath, "ImmersalURPAsset_Renderer.asset"), true))
{
// Load RendererData
string rendererDataPath = Path.Combine("Assets", "ImmersalURPAsset_Renderer.asset");
UniversalRendererData rendererData =
(UniversalRendererData)AssetDatabase.LoadAssetAtPath(rendererDataPath, typeof(UniversalRendererData));
if (rendererData != null)
{
// Create / load RenderPipelineAsset
UniversalRenderPipelineAsset rpa = UniversalRenderPipelineAsset.Create(rendererData);
// Save to file
string rpaSavePath = Path.Combine("Assets", "ImmersalURPAsset.asset");
AssetDatabase.CreateAsset(rpa, rpaSavePath);
// Configure
ConfigureRenderPipelineAsset(rpa);
// Set in settings
GraphicsSettings.defaultRenderPipeline = rpa;
QualitySettings.renderPipeline = rpa;
}
}
// Global Settings
if (CopyFromPackage("Editor/ImmersalURPGlobalSettings.asset", Path.Combine(destinationPath, "ImmersalURPGlobalSettings.asset"), true))
{
string settingsPath = Path.Combine("Assets", "ImmersalURPGlobalSettings.asset");
RenderPipelineGlobalSettings settings =
(RenderPipelineGlobalSettings)AssetDatabase.LoadAssetAtPath(settingsPath,
typeof(RenderPipelineGlobalSettings));
if (settings != null)
{
GraphicsSettings.RegisterRenderPipelineSettings<UniversalRenderPipeline>(settings);
}
}
}
private static void ConfigureRenderPipelineAsset(UniversalRenderPipelineAsset asset)
{
asset.supportsHDR = false;
asset.cascadeBorder = 0.1f;
}
private static bool CopyFromPackage(string packageRelativePath, string destinationPath, bool refreshDatabase = true)
{
string sourcePath = Path.Combine("Packages/com.immersal.core/", packageRelativePath);
string absolutePath = FixWindowsPath(Path.GetFullPath(sourcePath));
destinationPath = FixWindowsPath(destinationPath);
if (File.Exists(absolutePath))
{
if (File.Exists(destinationPath))
{
ImmersalLogger.LogWarning(
$"Attempting to copy file from Immersal package but destination already exists: {destinationPath}");
}
else
{
// Have to manually create directories or CopyFileOrDirectory will error on Windows
string dirName = Path.GetDirectoryName(destinationPath);
if (dirName == null)
return false;
if (!Directory.Exists(dirName))
Directory.CreateDirectory(dirName);
FileUtil.CopyFileOrDirectory(absolutePath, destinationPath);
}
if (refreshDatabase)
AssetDatabase.Refresh();
return true;
}
ImmersalLogger.LogWarning($"Could not locate {packageRelativePath} in Immersal package.");
return false;
}
// Unity API expects forward-slashes everywhere, but .NET methods produce paths with back-slashes on Windows
private static string FixWindowsPath(string path)
{
return path.Replace("\\", "/");
}
private static bool CheckManifestExists()
{
return File.Exists(GetManifestPath());
}
private static bool CheckManifestContent()
{
if (CheckManifestExists())
{
AndroidManifest manifest = new AndroidManifest(GetManifestPath());
bool internet = manifest.CheckPermission("android.permission.INTERNET");
bool network = manifest.CheckPermission("android.permission.ACCESS_NETWORK_STATE");
return internet && network;
}
return false;
}
private static string GetManifestPath()
{
return Path.Combine(Application.dataPath, "Plugins/Android/AndroidManifest.xml");
}
private static void CreateManifest()
{
CopyFromPackage("Editor/SampleAndroidManifest.xml", GetManifestPath());
}
private static void ConfigureManifest()
{
if (CheckManifestExists())
{
AndroidManifest manifest = new AndroidManifest(GetManifestPath());
manifest.AddPermission("android.permission.INTERNET");
manifest.AddPermission("android.permission.ACCESS_NETWORK_STATE");
manifest.Save();
AssetDatabase.Refresh();
}
}
}
internal class AndroidXmlDocument : XmlDocument
{
private string m_Path;
protected XmlNamespaceManager nsMgr;
public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
public AndroidXmlDocument(string path)
{
m_Path = path;
using (var reader = new XmlTextReader(m_Path))
{
reader.Read();
Load(reader);
}
nsMgr = new XmlNamespaceManager(NameTable);
nsMgr.AddNamespace("android", AndroidXmlNamespace);
}
public string Save()
{
return SaveAs(m_Path);
}
public string SaveAs(string path)
{
using (var writer = new XmlTextWriter(path, new UTF8Encoding(false)))
{
writer.Formatting = Formatting.Indented;
Save(writer);
}
return path;
}
}
internal class AndroidManifest : AndroidXmlDocument
{
private readonly XmlElement ApplicationElement;
public AndroidManifest(string path) : base(path)
{
ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement;
}
private XmlAttribute CreateAndroidAttribute(string key, string value)
{
XmlAttribute attr = CreateAttribute("android", key, AndroidXmlNamespace);
attr.Value = value;
return attr;
}
private XmlElement CreatePermissionElement(string permissionName)
{
XmlElement elem = CreateElement("uses-permission");
XmlAttribute attr = CreateAttribute("android", "name", AndroidXmlNamespace);
attr.Value = permissionName;
elem.Attributes.Append(attr);
return elem;
}
internal XmlNode GetActivityWithLaunchIntent()
{
return SelectSingleNode("/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and " +
"intent-filter/category/@android:name='android.intent.category.LAUNCHER']", nsMgr);
}
internal void SetApplicationTheme(string appTheme)
{
ApplicationElement.Attributes.Append(CreateAndroidAttribute("theme", appTheme));
}
internal void SetStartingActivityName(string activityName)
{
GetActivityWithLaunchIntent().Attributes.Append(CreateAndroidAttribute("name", activityName));
}
internal void AddPermission(string permissionName)
{
XmlElement ManifestElement = SelectSingleNode("/manifest") as XmlElement;
XmlElement permissionElement = CreatePermissionElement(permissionName);
ManifestElement?.AppendChild(permissionElement);
}
internal bool CheckPermission(string permissionName)
{
XmlElement ManifestElement = SelectSingleNode("/manifest") as XmlElement;
XmlNodeList children = ManifestElement?.ChildNodes;
if (children == null) return false;
foreach (XmlNode child in children)
{
if (child.Name != "uses-permission") continue;
if (child.Attributes == null) continue;
XmlAttribute attr = child.Attributes?["android:name"];
if (attr == null) continue;
if (attr.Value == permissionName) return true;
}
return false;
}
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 39d80592149a7480bbaa221e6c227a34
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,164 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#if UNITY_EDITOR
using System;
using System.Collections;
using Immersal.XR;
using Unity.EditorCoroutines.Editor;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.Networking;
using Immersal.REST;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
namespace Immersal
{
[CustomEditor(typeof(ImmersalSDK))]
public class ImmersalSDKEditor : Editor, IPreprocessBuildWithReport
{
private SerializedProperty immersalServer;
private SerializedProperty immersalServerUrl;
private void OnEnable()
{
immersalServer = serializedObject.FindProperty("ImmersalServer");
immersalServerUrl = serializedObject.FindProperty("m_CustomServerUrl");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
ImmersalSDK obj = (ImmersalSDK)target;
EditorGUILayout.HelpBox("Immersal SDK v" + ImmersalSDK.sdkVersion, MessageType.Info);
EditorGUILayout.LabelField("Cloud configuration", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
ImmersalSDK.APIServer serverSelection = (ImmersalSDK.APIServer)EditorGUILayout.EnumPopup("Immersal Server", (ImmersalSDK.APIServer)immersalServer.enumValueIndex);
if (EditorGUI.EndChangeCheck())
{
immersalServer.enumValueIndex = (int)serverSelection;
}
if (serverSelection == ImmersalSDK.APIServer.CustomServer && immersalServerUrl != null)
{
EditorGUILayout.PropertyField(immersalServerUrl);
}
DrawPropertiesExcluding(serializedObject, "m_Script", "ImmersalServer", "m_CustomServerUrl");
serializedObject.ApplyModifiedProperties();
}
// Preprocess builds
public int callbackOrder => 0;
public void OnPreprocessBuild(BuildReport report)
{
// ARMaps
XRMap[] maps = FindObjectsOfType<XRMap>();
foreach (XRMap map in maps)
{
if (!map.PreBuildCheck(out string msg))
{
throw new BuildFailedException(msg);
}
}
}
}
public class LoginWindow: EditorWindow
{
private string myEmail = "";
private string myPassword = "";
private string myToken = "";
private ImmersalSDK sdk = null;
private static UnityWebRequest request;
[MenuItem("Immersal SDK/Login")]
public static void ShowWindow()
{
EditorWindow.GetWindow<LoginWindow>("Immersal Login");
}
void OnGUI()
{
GUILayout.Label("Credentials", EditorStyles.boldLabel);
myEmail = EditorGUILayout.TextField("Email", myEmail);
myPassword = EditorGUILayout.PasswordField("Password", myPassword);
if (GUILayout.Button("Login"))
{
SDKLoginRequest loginRequest = new SDKLoginRequest();
loginRequest.login = myEmail;
loginRequest.password = myPassword;
EditorCoroutineUtility.StartCoroutine(Login(loginRequest), this);
}
EditorGUILayout.Separator();
myToken = EditorGUILayout.TextField("Token", myToken);
EditorGUILayout.Separator();
EditorGUILayout.Separator();
EditorGUILayout.Separator();
EditorGUILayout.LabelField("(C) 2023 Immersal - Part of Hexagon. All Right Reserved.");
}
void OnInspectorUpdate()
{
Repaint();
}
IEnumerator Login(SDKLoginRequest loginRequest)
{
string jsonString = JsonUtility.ToJson(loginRequest);
sdk = ImmersalSDK.Instance;
using (UnityWebRequest request = UnityWebRequest.Put(string.Format(ImmersalHttp.URL_FORMAT, sdk.localizationServer, SDKLoginRequest.endpoint), jsonString))
{
request.method = UnityWebRequest.kHttpVerbPOST;
request.useHttpContinue = false;
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Accept", "application/json");
yield return request.SendWebRequest();
#if UNITY_2020_1_OR_NEWER
if (request.result != UnityWebRequest.Result.Success)
#else
if (request.isNetworkError || request.isHttpError)
#endif
{
Debug.LogError(request.error);
}
else
{
SDKLoginResult loginResult = JsonUtility.FromJson<SDKLoginResult>(request.downloadHandler.text);
if (loginResult.error == "none")
{
myToken = loginResult.token;
sdk.developerToken = myToken;
// Apply override to the prefab instance
PrefabUtility.RecordPrefabInstancePropertyModifications(sdk);
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
}
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0b763741518b4f588fd56817d8bf5ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-6306797109214245143
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0ae4d376c05df421b98c5b39b25b0d29, type: 3}
m_Name: ARBackgroundRendererFeature
m_EditorClassIdentifier:
m_Active: 1
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3}
m_Name: ImmersalURPAsset_Renderer
m_EditorClassIdentifier:
debugShaders:
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3}
hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}
m_RendererFeatures:
- {fileID: -6306797109214245143}
m_RendererFeatureMap: e90ed3aa71c179a8
m_UseNativeRenderPass: 0
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
xrSystemData: {fileID: 11400000, guid: 60e1133243b97e347b653163a8c01b64, type: 2}
shaders:
blitPS: {fileID: 4800000, guid: c17132b1f77d20942aa75f8429c0f8bc, type: 3}
copyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3}
screenSpaceShadowPS: {fileID: 4800000, guid: 0f854b35a0cf61a429bd5dcfea30eddd, type: 3}
samplingPS: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3}
stencilDeferredPS: {fileID: 4800000, guid: e9155b26e1bc55942a41e518703fe304, type: 3}
fallbackErrorPS: {fileID: 4800000, guid: e6e9a19c3678ded42a3bc431ebef7dbd, type: 3}
fallbackLoadingPS: {fileID: 4800000, guid: 7f888aff2ac86494babad1c2c5daeee2, type: 3}
materialErrorPS: {fileID: 4800000, guid: 5fd9a8feb75a4b5894c241777f519d4e, type: 3}
coreBlitPS: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3}
coreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b, type: 3}
blitHDROverlay: {fileID: 4800000, guid: a89bee29cffa951418fc1e2da94d1959, type: 3}
cameraMotionVector: {fileID: 4800000, guid: c56b7e0d4c7cb484e959caeeedae9bbf, type: 3}
objectMotionVector: {fileID: 4800000, guid: 7b3ede40266cd49a395def176e1bc486, type: 3}
dataDrivenLensFlare: {fileID: 4800000, guid: 6cda457ac28612740adb23da5d39ea92, type: 3}
m_AssetVersion: 2
m_OpaqueLayerMask:
serializedVersion: 2
m_Bits: 4294967295
m_TransparentLayerMask:
serializedVersion: 2
m_Bits: 4294967295
m_DefaultStencilState:
overrideStencilState: 0
stencilReference: 0
stencilCompareFunction: 8
passOperation: 0
failOperation: 0
zFailOperation: 0
m_ShadowTransparentReceive: 1
m_RenderingMode: 0
m_DepthPrimingMode: 0
m_CopyDepthMode: 0
m_AccurateGbufferNormals: 0
m_IntermediateTextureMode: 1

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 939f6cfbed10c417bb69bff14fa799bd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2ec995e51a6e251468d2a3fd8a686257, type: 3}
m_Name: ImmersalURPGlobalSettings
m_EditorClassIdentifier:
k_AssetVersion: 3
m_RenderingLayerNames:
- Light Layer default
- Light Layer 1
- Light Layer 2
- Light Layer 3
- Light Layer 4
- Light Layer 5
- Light Layer 6
- Light Layer 7
m_ValidRenderingLayers: 255
lightLayerName0: Light Layer default
lightLayerName1: Light Layer 1
lightLayerName2: Light Layer 2
lightLayerName3: Light Layer 3
lightLayerName4: Light Layer 4
lightLayerName5: Light Layer 5
lightLayerName6: Light Layer 6
lightLayerName7: Light Layer 7
m_StripDebugVariants: 1
m_StripUnusedPostProcessingVariants: 0
m_StripUnusedVariants: 1
m_StripUnusedLODCrossFadeVariants: 1
m_StripScreenCoordOverrideVariants: 1
supportRuntimeDebugDisplay: 0
m_ShaderVariantLogLevel: 0
m_ExportShaderVariants: 1

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 95b35ca7c5dee41ae911d5ac6a1c665a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
namespace Immersal
{
[CustomPropertyDrawer(typeof(InterfaceAttribute))]
public class InterfaceDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
InterfaceAttribute att = attribute as InterfaceAttribute;
if (property.propertyType != SerializedPropertyType.ObjectReference)
{
EditorGUI.LabelField(position, label.text, "InterfaceType Attribute can only be used with MonoBehaviour Components!");
return;
}
// Pick a specific component
MonoBehaviour oldComp = property.objectReferenceValue as MonoBehaviour;
GameObject temp = null;
string oldName = "";
if (Event.current.type == EventType.Repaint)
{
if (oldComp == null)
{
temp = new GameObject("None [" + att.TypeOfInterface.Name + "]");
oldComp = temp.AddComponent<MonoInterface>();
}
else
{
oldName = oldComp.name;
oldComp.name = oldName + " [" + att.TypeOfInterface.Name + "]";
}
}
MonoBehaviour comp = EditorGUI.ObjectField(position, label, oldComp, typeof(MonoBehaviour), true) as MonoBehaviour;
if (Event.current.type == EventType.Repaint)
{
if (temp != null)
GameObject.DestroyImmediate(temp);
else
oldComp.name = oldName;
}
// Make sure something changed.
if (oldComp == comp) return;
// If a component is assigned, make sure it is the interface we are looking for.
if (comp != null)
{
// Make sure component is of the right interface
if (comp.GetType() != att.TypeOfInterface)
// Component failed. Check game object.
comp = comp.gameObject.GetComponent(att.TypeOfInterface) as MonoBehaviour;
// Item failed test. Do not override old component
if (comp == null)
{
var previousColor = GUI.color;
GUI.color = Color.red;
EditorGUI.LabelField(position, label, new GUIContent("Assigned Property does not implement required interface!"));
GUI.color = previousColor;
return;
}
}
property.objectReferenceValue = comp;
property.serializedObject.ApplyModifiedProperties();
}
}
public class MonoInterface : MonoBehaviour
{
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6d3fafdb9a614dfe9e84a3f6e45f652
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,68 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Immersal.XR
{
[CustomEditor(typeof(PoseFilter))]
public class PoseFilterEditor : Editor
{
private SerializedProperty filterMethodProperty;
private SerializedProperty historySizeProperty;
private SerializedProperty useConfidenceProperty;
private SerializedProperty confidenceMaxProperty;
private SerializedProperty confidenceImpactProperty;
private void OnEnable()
{
filterMethodProperty = serializedObject.FindProperty("m_FilterMethod");
historySizeProperty = serializedObject.FindProperty("m_HistorySize");
useConfidenceProperty = serializedObject.FindProperty("m_UseConfidence");
confidenceMaxProperty = serializedObject.FindProperty("m_ConfidenceMax");
confidenceImpactProperty = serializedObject.FindProperty("m_ConfidenceImpact");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
if (filterMethodProperty.enumValueIndex == (int)FilterMethod.Legacy)
{
EditorGUILayout.HelpBox("Legacy filtering method is can cause issues with large maps and offsets. The simple filtering method is recommended instead.", MessageType.Warning);
}
else if (filterMethodProperty.enumValueIndex == (int)FilterMethod.Advanced)
{
EditorGUILayout.HelpBox("Using advanced features and parameters can decrease the quality of filtering in some configurations. Some features are experimental. Extensive testing is advised.", MessageType.Info);
}
// Method
filterMethodProperty.enumValueIndex = EditorGUILayout.Popup("Filter method", filterMethodProperty.enumValueIndex, filterMethodProperty.enumNames);
if (filterMethodProperty.enumValueIndex == (int)FilterMethod.Advanced)
{
EditorGUILayout.PropertyField(historySizeProperty, new GUIContent("History size"));
EditorGUILayout.PropertyField(useConfidenceProperty, new GUIContent("Apply confidence"));
if (useConfidenceProperty.boolValue)
{
EditorGUILayout.PropertyField(confidenceImpactProperty, new GUIContent("Confidence impact"));
EditorGUILayout.PropertyField(confidenceMaxProperty, new GUIContent("Confidence max value"));
}
}
// Apply
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7bd069f80548a47a69f5abff37f19afe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3050b4a46d51a4a1c96ed495132603bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.unity3d.player" xmlns:tools="http://schemas.android.com/tools" android:installLocation="preferExternal">
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
<application android:theme="@style/UnityThemeSelector" android:icon="@mipmap/app_icon" android:label="@string/app_name" android:usesCleartextTraffic="true">
<activity android:name="com.unity3d.player.UnityPlayerActivity"
android:theme="@style/UnityThemeSelector"
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b052e1bcf916a43a5a8e011d0276f7cb
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Immersal.XR
{
[CustomEditor(typeof(ServerLocalization))]
public class ServerLocalizationEditor : Editor
{
private SerializedProperty configurationMode;
private SerializedProperty solverTypeProperty;
private SerializedProperty priorNNCountMinProperty;
private SerializedProperty priorNNCountMaxProperty;
private SerializedProperty priorScaleProperty;
private SerializedProperty priorRadiusProperty;
private SerializedProperty onProgressEventProperty;
private void OnEnable()
{
configurationMode = serializedObject.FindProperty("m_ConfigurationMode");
solverTypeProperty = serializedObject.FindProperty("m_SolverType");
priorNNCountMinProperty = serializedObject.FindProperty("m_PriorNNCountMin");
priorNNCountMaxProperty = serializedObject.FindProperty("m_PriorNNCountMax");
priorScaleProperty = serializedObject.FindProperty("m_PriorScale");
priorRadiusProperty = serializedObject.FindProperty("m_PriorRadius");
onProgressEventProperty = serializedObject.FindProperty("OnProgress");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// Configuration mode
EditorGUILayout.HelpBox(configurationMode.enumValueIndex == (int)ConfigurationMode.Always ? "This localization method will be active even if there are no maps configured to use it." : "This localization method will only be active if there are maps configured to use it.", MessageType.Info);
configurationMode.enumValueIndex = EditorGUILayout.Popup("Configuration mode", configurationMode.enumValueIndex, configurationMode.enumNames);
EditorGUILayout.Separator();
if (solverTypeProperty.enumValueIndex is (int)SolverType.Prior or (int)SolverType.Lean)
{
EditorGUILayout.HelpBox("This Solver type is experimental. Extensive testing is advised.", MessageType.Warning);
}
// SolverType
solverTypeProperty.enumValueIndex = EditorGUILayout.Popup("Solver type", solverTypeProperty.enumValueIndex, solverTypeProperty.enumNames);
if (solverTypeProperty.enumValueIndex == (int)SolverType.Prior)
{
EditorGUILayout.PropertyField(priorNNCountMinProperty, new GUIContent("Nearest neighbour min"));
EditorGUILayout.PropertyField(priorNNCountMaxProperty, new GUIContent("Nearest neighbour max"));
EditorGUILayout.PropertyField(priorScaleProperty, new GUIContent("Scale"));
EditorGUILayout.PropertyField(priorRadiusProperty, new GUIContent("Radius"));
}
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(onProgressEventProperty, new GUIContent("Upload progress"));
// Apply
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2f45c072c48b493a87a8158bee264f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,580 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#if UNITY_EDITOR
using System;
using UnityEngine;
using UnityEditor;
using UnityEngine.Networking;
using System.IO;
using System.Collections;
using System.Linq;
using System.Threading.Tasks;
using Unity.EditorCoroutines.Editor;
using Immersal.REST;
using Object = UnityEngine.Object;
namespace Immersal.XR
{
[CustomEditor(typeof(XRMap))]
public class XRMapEditor : Editor
{
private ImmersalSDK sdk;
private GameObject sceneParentObj;
private ISceneUpdateable sceneParent;
private int userEnteredMapId = -1;
private bool[] downloadSelections = { true, false, false };
// Localization method requires some extra variables
private static ILocalizationMethod[] availableMethods;
private static string[] availableMethodNames;
private int selectedTypeIndex; // Current selection
// Other properties
private SerializedProperty mapFileProperty;
private SerializedProperty mapIdProperty;
private SerializedProperty mapNameProperty;
private SerializedProperty privacyProperty;
private SerializedProperty alignmentProperty;
private SerializedProperty wgs84Property;
private void OnEnable()
{
XRMap map = (XRMap)target;
mapFileProperty = serializedObject.FindProperty("mapFile");
mapIdProperty = serializedObject.FindProperty("m_MapId");
mapNameProperty = serializedObject.FindProperty("m_MapName");
privacyProperty = serializedObject.FindProperty("privacy");
alignmentProperty = serializedObject.FindProperty("mapAlignment");
wgs84Property = serializedObject.FindProperty("wgs84");
// Localization method
if (!CheckAvailableLocalizationMethods())
{
RefreshAvailableLocalizationMethods();
}
// Check for null
if (map.LocalizationMethod.IsNullOrDead())
{
ImmersalLogger.LogWarning($"Map {map.name} has invalid localization method, resetting to DeviceLocalization.");
if (!TrySetLocalizationMethod(map, typeof(DeviceLocalization)))
{
ImmersalLogger.LogError($"Failed. Uninitializing map {map.name}");
map.Uninitialize();
}
}
// Get current index
if (!map.LocalizationMethod.IsNullOrDead() && availableMethodNames != null)
{
selectedTypeIndex = Array.IndexOf(availableMethodNames, map.LocalizationMethod.GetType().Name);
if (selectedTypeIndex == -1) selectedTypeIndex = 0;
}
}
private bool CheckAvailableLocalizationMethods()
{
if (availableMethods == null || availableMethodNames == null)
return false;
foreach (ILocalizationMethod localizationMethod in availableMethods)
{
if (localizationMethod.IsNullOrDead())
return false;
}
return true;
}
private void RefreshAvailableLocalizationMethods()
{
ILocalizationMethod[] methods = MapManager.AvailableLocalizationMethods;
if (methods != null)
{
availableMethods = methods;
availableMethodNames = availableMethods.Select(m => m.GetType().Name).ToArray();
}
}
[InitializeOnLoadMethod]
private static void ClearAvailableLocalizationMethodTypes()
{
availableMethods = null;
availableMethodNames = null;
}
public override void OnInspectorGUI()
{
if (BuildPipeline.isBuildingPlayer)
return;
serializedObject.Update();
XRMap obj = (XRMap)target;
// Configure styles
GUIStyle centeredBoldLabel = new GUIStyle(EditorStyles.boldLabel);
centeredBoldLabel.alignment = TextAnchor.MiddleCenter;
GUIStyle bigLabel = new GUIStyle(EditorStyles.boldLabel);
bigLabel.fontSize = 16;
// The Custom Editor has two states
// Map is configured
if (obj.IsConfigured)
{
Color oldColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(0.5f, 1f, 0.6f);
EditorGUILayout.HelpBox("Map configured!", MessageType.Info);
GUI.backgroundColor = oldColor;
// Reconfigure
if (GUILayout.Button("Reconfigure map"))
{
obj.Uninitialize();
}
EditorGUILayout.Space();
// Localization method
EditorGUI.BeginChangeCheck();
if (availableMethods != null && availableMethodNames.Length > 0)
{
selectedTypeIndex =
EditorGUILayout.Popup("Localization method", selectedTypeIndex, availableMethodNames);
}
if (EditorGUI.EndChangeCheck())
{
TrySetLocalizationMethod(obj, availableMethods[selectedTypeIndex]);
}
// Map options
MapOptionsSection(obj);
// Metadata
EditorGUILayout.Space();
EditorGUILayout.LabelField("Map Metadata", EditorStyles.boldLabel);
GUI.enabled = false;
EditorGUILayout.PropertyField(mapIdProperty);
EditorGUILayout.PropertyField(mapNameProperty);
EditorGUILayout.PropertyField(privacyProperty);
EditorGUILayout.PropertyField(wgs84Property);
EditorGUILayout.PropertyField(alignmentProperty);
GUI.enabled = true;
// Map Alignment controls
EditorGUILayout.HelpBox("Alignment metadata stored in right-handed coordinate system. Captured (default) alignment is in ECEF coordinates", MessageType.Info);
GUILayout.BeginHorizontal();
if (GUILayout.Button(new GUIContent("Load Alignment", "Loads alignment from map metadata. Coordinate system is unknown (ECEF or Unity's)")))
{
EditorCoroutineUtility.StartCoroutine(MapAlignmentLoad(), this);
}
if (GUILayout.Button(new GUIContent("Save Alignment", "Saves current (local transform) alignment to map metadata")))
{
EditorCoroutineUtility.StartCoroutine(MapAlignmentSave(), this);
}
oldColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(1f, 0.5f, 0.6f);
if (GUILayout.Button(new GUIContent("Reset Alignment", "Fetches the original captured alignment metadata in ECEF coordinates")))
{
EditorCoroutineUtility.StartCoroutine(MapAlignmentReset(), this);
}
GUI.backgroundColor = oldColor;
GUILayout.EndHorizontal();
// Visualization
EditorGUILayout.Space();
EditorGUILayout.LabelField("Visualization", EditorStyles.boldLabel);
if (obj.Visualization != null)
{
if (GUILayout.Button("Select visualization"))
{
Selection.SetActiveObjectWithContext(obj.Visualization.gameObject, null);
}
if (GUILayout.Button("Remove visualization"))
{
obj.RemoveVisualization();
}
}
else
{
if (GUILayout.Button("Add visualization"))
{
obj.CreateVisualization();
Selection.SetActiveObjectWithContext(obj.Visualization.gameObject, null);
}
}
}
// Map unconfigured
else
{
// Check that a parent implements ISceneUpdateable
ISceneUpdateable sceneUpdateable = obj.transform.GetComponentInParent<ISceneUpdateable>(true);
if (sceneUpdateable != null)
{
// Configuration instructions and options
Color oldColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(1f, 0.5f, 0.6f);
EditorGUILayout.HelpBox("Map has not been configured! Add or download map data below.", MessageType.Warning);
GUI.backgroundColor = oldColor;
EditorGUILayout.Space();
EditorGUILayout.LabelField("Local map file", bigLabel);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Drag and drop your map file (.bytes) here or use the selector to find one.", EditorStyles.wordWrappedLabel);
// Mapfile option
EditorGUILayout.PropertyField(mapFileProperty);
//currentMapFile = obj.mapFile;
TextAsset mapFile = mapFileProperty.objectReferenceValue as TextAsset;
if (mapFile != null)
{
string bytesPath = AssetDatabase.GetAssetPath(mapFile);
if (bytesPath.EndsWith(".bytes"))
{
if (TrySetLocalizationMethod(obj, typeof(DeviceLocalization)))
{
obj.Configure(mapFile);
}
}
else
{
ImmersalLogger.Log($"{AssetDatabase.GetAssetPath(mapFile)} is not a valid map file");
obj.mapFile = null;
}
}
// Download options
EditorGUILayout.Space(12f);
EditorGUILayout.LabelField("OR", centeredBoldLabel, GUILayout.ExpandWidth(true));
EditorGUILayout.Space(12f);
EditorGUILayout.LabelField("Download from cloud", bigLabel);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Input your map id and select which data to download. You can configure the save path in the ImmersalSDK configuration.", EditorStyles.wordWrappedLabel);
userEnteredMapId = EditorGUILayout.IntField("Map id: ", userEnteredMapId);
EditorGUILayout.Space();
// Section for selecting data types to download
DownloadSelectionSection();
// Download
if (GUILayout.Button("Download"))
{
// metadata is always downloaded
EditorCoroutineUtility.StartCoroutine(
MapManager.DownloadMapMetadata(userEnteredMapId, metadata =>
{
// apply meta
obj.SetMetadata(metadata, true);
// mapfile
if (downloadSelections[1])
{
EditorCoroutineUtility.StartCoroutine(MapManager.DownloadMapFile(
obj.mapId, obj.mapName, (result, mapFileAsset) =>
{
if (TrySetLocalizationMethod(obj, typeof(DeviceLocalization)))
{
// apply map file and process
obj.Configure(mapFileAsset);
}
}), this);
}
else
{
if (TrySetLocalizationMethod(obj, typeof(ServerLocalization)))
{
obj.Configure();
}
}
// vis
if (downloadSelections[2])
{
EditorCoroutineUtility.StartCoroutine(MapManager.DownloadSparseFile(
obj.mapId, obj.mapName, (result, path) =>
{
// apply bytes and process
obj.CreateVisualization();
obj.Visualization.LoadPly(path);
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
}), this);
}
}), this);
}
}
// Invalid parent
else
{
EditorGUILayout.HelpBox("Scene parent does not implement ISceneUpdateable.", MessageType.Error, true);
}
}
serializedObject.ApplyModifiedProperties();
}
private bool TrySetLocalizationMethod(XRMap map, Type localizationMethodType)
{
ILocalizationMethod method = availableMethods.FirstOrDefault(m => m.GetType() == localizationMethodType);
return TrySetLocalizationMethod(map, method);
}
private bool TrySetLocalizationMethod(XRMap map, ILocalizationMethod localizationMethod)
{
if (localizationMethod.IsNullOrDead())
{
ImmersalLogger.LogError("Invalid localization method assignment.");
MapManager.RefreshLocalizationMethods();
RefreshAvailableLocalizationMethods();
return false;
}
// Ensure index is also updated when call is not originating from direct index change
selectedTypeIndex = Array.IndexOf(availableMethodNames, localizationMethod.GetType().Name);
if (selectedTypeIndex == -1) selectedTypeIndex = 0;
//map.LocalizationMethodType = localizationMethodType;
map.LocalizationMethod = localizationMethod;
serializedObject.ApplyModifiedProperties();
map.UpdateMapOptions();
EditorUtility.SetDirty(map);
return true;
}
// Render map configurations
private void MapOptionsSection(XRMap map)
{
if (map.MapOptions == null)
return;
if (map.MapOptions.Count == 0)
return;
EditorGUILayout.Space();
EditorGUILayout.LabelField($"{availableMethodNames[selectedTypeIndex]} options", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
foreach (IMapOption configuration in map.MapOptions)
{
configuration.DrawEditorGUI(map);
}
if (EditorGUI.EndChangeCheck())
{
map.SerializeMapOptions();
EditorUtility.SetDirty(map);
}
}
// Render download options
private void DownloadSelectionSection()
{
string[] labels = { "Metadata", "Mapfile", "Visualization" };
string[] minis = {
"The map metadata contains information about the map alignment, privacy, etc. It is the minimum requirement for configuring maps.",
"The map file is a binary representation of the map. This is required for embedding maps for offline use.",
"This is the sparse point cloud of the map that can be used to visualize the map."
};
bool[] enabled = { false, true, true };
GUILayout.BeginHorizontal();
// Left column
GUILayout.BeginVertical();
for (int i = 0; i < labels.Length; i++)
{
GUILayout.BeginVertical();
GUILayout.Label(labels[i]);
GUILayout.Label(minis[i], EditorStyles.wordWrappedMiniLabel);
GUILayout.EndVertical();
EditorGUILayout.Space();
}
GUILayout.EndVertical();
// Right column
GUILayout.BeginVertical();
for (int i = 0; i < downloadSelections.Length; i++)
{
GUILayout.BeginVertical();
GUILayout.FlexibleSpace();
GUI.enabled = enabled[i];
downloadSelections[i] = EditorGUILayout.Toggle(downloadSelections[i]); //, GUILayout.ExpandWidth(true));
GUILayout.FlexibleSpace();
GUILayout.EndVertical();
}
GUILayout.EndVertical();
GUI.enabled = true;
GUILayout.EndHorizontal();
}
#region Alignment methods
private IEnumerator MapAlignmentLoad()
{
//
// Loads map metadata, updates XR Map metadata info, extracts the alignment, converts it to Unity's coordinate system and sets the map transform
//
XRMap obj = (XRMap)target;
sdk = ImmersalSDK.Instance;
// Check if metadata file exists already
string destinationFolder = MapManager.GetDirectoryPath();
string existingFilePath = Path.Combine(destinationFolder, $"{obj.mapId}-{obj.mapName}-metadata.json");
if (File.Exists(existingFilePath))
{
XRMap.MetadataFile metadataFile = JsonUtility.FromJson<XRMap.MetadataFile>(File.ReadAllText(existingFilePath));
obj.SetMetadata(metadataFile, true);
obj.ApplyAlignment();
yield break;
}
// Download
EditorCoroutineUtility.StartCoroutine(MapManager.DownloadMapMetadata(obj.mapId, result =>
{
obj.SetMetadata(result, false);
obj.ApplyAlignment();
}), this);
}
private IEnumerator MapAlignmentSave()
{
//
// Updates map metadata to the Cloud Service and reloads to keep local files in sync
//
XRMap obj = (XRMap)target;
sdk = ImmersalSDK.Instance;
Vector3 pos = obj.transform.localPosition;
Quaternion rot = obj.transform.localRotation;
float scl = (obj.transform.localScale.x + obj.transform.localScale.y + obj.transform.localScale.z) / 3f; // Only uniform scale metadata is supported
// IMPORTANT
// Switching coordinate system handedness from Unity's left-handed system to Immersal Cloud Service's default right-handed system
Matrix4x4 b = Matrix4x4.TRS(pos, rot, obj.transform.localScale);
Matrix4x4 a = b.SwitchHandedness();
pos = a.GetColumn(3);
rot = a.rotation;
// Update map alignment metadata to Immersal Cloud Service
SDKMapAlignmentSetRequest r = new SDKMapAlignmentSetRequest();
r.token = sdk.developerToken;
r.id = obj.mapId;
r.tx = pos.x;
r.ty = pos.y;
r.tz = pos.z;
r.qx = rot.x;
r.qy = rot.y;
r.qz = rot.z;
r.qw = rot.w;
r.scale = scl;
string jsonString = JsonUtility.ToJson(r);
UnityWebRequest request = UnityWebRequest.Put(string.Format(ImmersalHttp.URL_FORMAT, ImmersalSDK.Instance.localizationServer, SDKMapAlignmentSetRequest.endpoint), jsonString);
request.method = UnityWebRequest.kHttpVerbPOST;
request.useHttpContinue = false;
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Accept", "application/json");
request.SendWebRequest();
while (!request.isDone)
{
yield return null;
}
#if UNITY_2020_1_OR_NEWER
if (request.result != UnityWebRequest.Result.Success)
#else
if (request.isNetworkError || request.isHttpError)
#endif
{
ImmersalLogger.LogError($"Failed to save alignment for map id {obj.mapId}\n{request.error}");
}
else
{
SDKMapAlignmentSetResult result = JsonUtility.FromJson<SDKMapAlignmentSetResult>(request.downloadHandler.text);
if (result.error == "none")
{
// Reload the metadata from Immersal Cloud Service to keep local files in sync
EditorCoroutineUtility.StartCoroutine(MapAlignmentLoad(), this);
}
}
}
private IEnumerator MapAlignmentReset()
{
//
// Reset map alignment to the original captured data and reload metadata from the Immersal Cloud Service to keep local files in sync
//
XRMap obj = (XRMap)target;
sdk = ImmersalSDK.Instance;
// Reset alignment on Immersal Cloud Service
SDKMapAlignmentResetRequest r = new SDKMapAlignmentResetRequest();
r.token = sdk.developerToken;
r.id = obj.mapId;
string jsonString = JsonUtility.ToJson(r);
UnityWebRequest request = UnityWebRequest.Put(string.Format(ImmersalHttp.URL_FORMAT, ImmersalSDK.Instance.localizationServer, SDKMapAlignmentResetRequest.endpoint), jsonString);
request.method = UnityWebRequest.kHttpVerbPOST;
request.useHttpContinue = false;
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Accept", "application/json");
request.SendWebRequest();
while (!request.isDone)
{
yield return null;
}
#if UNITY_2020_1_OR_NEWER
if (request.result != UnityWebRequest.Result.Success)
#else
if (request.isNetworkError || request.isHttpError)
#endif
{
ImmersalLogger.LogError($"Failed to reset alignment for map id {obj.mapId}\n{request.error}");
}
else
{
SDKMapAlignmentResetResult result = JsonUtility.FromJson<SDKMapAlignmentResetResult>(request.downloadHandler.text);
if (result.error == "none")
{
// Reload the metadata from Immersal Cloud Service to keep local files in sync
EditorCoroutineUtility.StartCoroutine(MapAlignmentLoad(), this);
}
}
}
#endregion'
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b38c61e43b9434129bf029ea93c69cea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,116 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sdk@immersal.com for licensing requests.
===============================================================================*/
#if UNITY_EDITOR
using System;
using System.IO;
using Unity.EditorCoroutines.Editor;
using UnityEditor;
using UnityEngine;
namespace Immersal.XR
{
[CustomEditor(typeof(XRMapVisualization))]
public class XRMapVisualizationEditor : Editor
{
// visualization
private SerializedProperty renderModeProperty;
private SerializedProperty pointColorProperty;
private static float pointSizeSliderValue = 0.33f;
private static bool renderAs3dPointsToggle = true;
private void OnEnable()
{
pointSizeSliderValue = EditorPrefs.GetFloat("pointSizeSliderValue", pointSizeSliderValue);
renderAs3dPointsToggle = EditorPrefs.GetBool("pointSizeSliderValue", renderAs3dPointsToggle);
renderModeProperty = serializedObject.FindProperty("renderMode");
pointColorProperty = serializedObject.FindProperty("m_PointColor");
}
public override void OnInspectorGUI()
{
if (BuildPipeline.isBuildingPlayer)
return;
XRMapVisualization obj = (XRMapVisualization)target;
obj.UpdateMaterial();
if (obj.IsVisualized)
{
if (!Application.isPlaying)
{
if (GUILayout.Button("Reset visualization"))
{
obj.ClearVisualization();
}
}
EditorGUILayout.PropertyField(renderModeProperty);
EditorGUILayout.PropertyField(pointColorProperty);
EditorGUI.BeginChangeCheck();
pointSizeSliderValue = EditorGUILayout.Slider("Point Size", pointSizeSliderValue, 0f, 1f);
renderAs3dPointsToggle = EditorGUILayout.Toggle("Render as 3D Points", renderAs3dPointsToggle);
if (EditorGUI.EndChangeCheck())
{
XRMapVisualization.pointSize = pointSizeSliderValue;
XRMapVisualization.renderAs3dPoints = renderAs3dPointsToggle;
EditorPrefs.SetFloat("pointSizeSliderValue", pointSizeSliderValue);
EditorPrefs.SetBool("pointSizeSliderValue", renderAs3dPointsToggle);
obj.UpdateMaterial();
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
}
}
else
{
EditorGUILayout.HelpBox("To visualize the ARMap in the Editor, load either the sparse or dense PLY file.", MessageType.Warning);
// Ply loading
if (!Application.isPlaying)
{
if (GUILayout.Button("Load local sparse ply file"))
{
PickAndLoadPly(obj);
}
if (GUILayout.Button("Download sparse ply file"))
{
EditorCoroutineUtility.StartCoroutine(MapManager.DownloadSparseFile(
obj.Map.mapId, obj.Map.mapName,
(result, path) =>
{
// apply bytes and process
obj.LoadPly(path);
}), this);
}
}
}
serializedObject.ApplyModifiedProperties();
}
private void PickAndLoadPly(XRMapVisualization obj)
{
string path = EditorUtility.OpenFilePanel("Select Ply", "", "ply");
if (path.Length != 0)
{
obj.LoadPly(path);
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6462ada5e0cb43e1a7572ad1fc02ccb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Immersal.XR
{
[CustomEditor(typeof(XRSpace))]
public class XRSpaceEditor : Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("Place XR Maps and all content under this object.\n(you can also use a custom implementation of ISceneParent)", MessageType.Info);
if (Application.isPlaying)
{
EditorGUILayout.HelpBox("DataProcessors added at runtime are not serialized and thus do not show up in the inspector.", MessageType.Warning);
}
DrawDefaultInspector();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b4db60c4a514e4af982813880c0ddae7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 666ab8dba47f544338191598ba6454f2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
{
"name": "Immersal.Core",
"references": [
"Unity.XR.ARFoundation",
"Unity.XR.ARSubsystems",
"Unity.XR.CoreUtils",
"Unity.TextMeshPro"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2e7d06324bb40437f864b52eb770c3c5
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ebb9a2aa61c1c4adfa46fdb884506587
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bbddf664aa7a14ace8ba3d4ed402b232
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: c0cb792fbfeed43ac9c56884627eaa0d
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
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:

View File

@@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: 3ff5eb1b694e9493c81a4e7cefc3fa40
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
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:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1e8a5e52ef86f4c60981bff8a85d6016
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: 75b23102b03aa4e37862216603fe6cca
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 1
isOverridable: 1
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:
AndroidSharedLibraryType: Executable
CPU: X86_64
- 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:

View File

@@ -0,0 +1,129 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
#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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d5ebb2a7d1c34821bfc011129e06e21
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 315d898c8a9b741e2abf8ebf066826e5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a372d43f1ea534be097e7631d0b49a2c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: 304d68efbb95e42698724e8c83b2d707
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 1
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
AndroidSharedLibraryType: Executable
CPU: ARMv7
- 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:

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: d110880cd997a4cd0aca0eabf4284cde
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
AndroidSharedLibraryType: Executable
CPU: ARMv7
- 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:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e61930de07ae54862bde5a3e16380735
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
//
// NativeLocation.h
// Immersal SDK
//
// Created by Mikko on 29/05/2020.
//
//
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIKit.h>
@interface NativeLocation : NSObject <CLLocationManagerDelegate>
- (NativeLocation *)init;
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
@end

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: 3047a557b77d947608c98b823c7d4d5b
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 0
settings:
AndroidSharedLibraryType: Executable
CPU: ARMv7
- 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: 1
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,132 @@
//
// NativeLocation.m
// Immersal SDK
//
// Created by Mikko on 29/05/2020.
//
//
#import "NativeLocation.h"
double latitude;
double longitude;
double altitude;
double haccuracy;
double vaccuracy;
@implementation NativeLocation
CLLocationManager *locationManager;
static bool isEnabled = NO;
- (NativeLocation *)init
{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
[locationManager requestWhenInUseAuthorization];
return self;
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status;
{
/* switch (status) {
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusAuthorizedAlways:
isEnabled = YES; break;
default:
isEnabled = NO; break;
}*/
}
- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error;
{
isEnabled = NO;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
{
CLLocation *location = [locations lastObject];
latitude = location.coordinate.latitude;
longitude = location.coordinate.longitude;
altitude = location.altitude;
haccuracy = location.horizontalAccuracy;
vaccuracy = location.verticalAccuracy;
isEnabled = YES;
//NSLog(@"lat: %f long: %f alt: %f", latitude, longitude, altitude);
}
- (void)start
{
if (locationManager != NULL) {
[locationManager startUpdatingLocation];
}
}
- (void)stop
{
if (locationManager != NULL) {
[locationManager stopUpdatingLocation];
}
isEnabled = NO;
}
@end
static NativeLocation* locationDelegate = NULL;
extern "C"
{
void startLocation()
{
if (locationDelegate == NULL) {
locationDelegate = [[NativeLocation alloc] init];
}
[locationDelegate start];
}
void stopLocation()
{
if (locationDelegate != NULL) {
[locationDelegate stop];
}
}
double getLatitude()
{
return latitude;
}
double getLongitude()
{
return longitude;
}
double getAltitude()
{
return altitude;
}
double getHorizontalAccuracy()
{
return haccuracy;
}
double getVerticalAccuracy()
{
return vaccuracy;
}
bool locationServicesEnabled()
{
return isEnabled;
}
}

View File

@@ -0,0 +1,37 @@
fileFormatVersion: 2
guid: d437760fb73c64fbea6c103e65c7b46a
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: 4e5497290e47344189a08bc45a2d4ec6
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 0
settings:
AndroidSharedLibraryType: Executable
CPU: ARMv7
- 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: 1
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies: Security;
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c47c562ad1e504ce5bbc75ec607a6979
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: 6f2fc1147aedf422eb4fc088c5bb4290
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 1
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux64: 0
Exclude OSXUniversal: 0
Exclude Win: 0
Exclude Win64: 0
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
AndroidSharedLibraryType: Executable
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: OSX
- 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: x86_64
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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>23D56</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>14.2</string>
<key>DTSDKBuild</key>
<string>23C53</string>
<key>DTSDKName</key>
<string>macosx14.2</string>
<key>DTXcode</key>
<string>1520</string>
<key>DTXcodeBuild</key>
<string>15C500b</string>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2022 Immersal - Part of Hexagon. All rights reserved.</string>
</dict>
</plist>

View File

@@ -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>

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: cf35b419ea46d43e291ce52433f69fab
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 1
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux64: 0
Exclude OSXUniversal: 0
Exclude Win: 0
Exclude Win64: 0
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
AndroidSharedLibraryType: Executable
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: x86_64
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: c97dcf0869d9843e3b51c44c38f6c532
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux64: 0
Exclude OSXUniversal: 0
Exclude Win: 0
Exclude Win64: 0
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
AndroidSharedLibraryType: Executable
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: x86_64
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 73c6a3909744c47c1a172adf16bfb53e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2aad495dd66e94cf0b2f8e6aa0e76ad2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,440 @@
%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: 4876485604010972054}
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}
serializedVersion: 2
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:
- {fileID: 4358890709939687853}
- {fileID: 5728853109968807875}
- {fileID: 542854591214680858}
- {fileID: 3561577978855217239}
- {fileID: 8846418124030723115}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4876485604010972054
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: 22271c1298ba444ffacf4138cd9ef2eb, type: 3}
m_Name:
m_EditorClassIdentifier:
defaultServer: 0
developerToken:
m_Session: {fileID: 4858077625584781203}
m_Platform: {fileID: 6469108069241648846}
m_Localizer: {fileID: 4174534150879381888}
m_SceneUpdater: {fileID: 2545414792828475238}
m_TrackingAnalyzer: {fileID: 6509155160601367522}
m_TargetFrameRate: 60
m_Downsample: 1
m_LoggingLevel: 2
m_DownloadDirectory: Map Data
OnInitializationComplete:
m_PersistentCalls:
m_Calls: []
OnReset:
m_PersistentCalls:
m_Calls: []
--- !u!1 &1624132911465332519
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3561577978855217239}
- component: {fileID: 2545414792828475238}
m_Layer: 0
m_Name: SceneUpdater
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3561577978855217239
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1624132911465332519}
serializedVersion: 2
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: 4682760393691964}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2545414792828475238
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1624132911465332519}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 10b6f3f5d6c78474d867465b74b8a539, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &4025295615373336692
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6323821621449692113}
- component: {fileID: 3150119538342652997}
m_Layer: 0
m_Name: DeviceLocalization
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6323821621449692113
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4025295615373336692}
serializedVersion: 2
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: 542854591214680858}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &3150119538342652997
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4025295615373336692}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d8be42d7c34a64923aa637c8abcaeb7d, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ConfigurationMode: 1
m_SolverType: 0
--- !u!1 &4483478919481266077
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5728853109968807875}
- component: {fileID: 6469108069241648846}
m_Layer: 0
m_Name: ARFoundationSupport
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &5728853109968807875
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4483478919481266077}
serializedVersion: 2
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: 4682760393691964}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6469108069241648846
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4483478919481266077}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ff1599bedf7cb4f2bb03770be4930ce8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_MaxConfigurationAttempts: 10
m_MsBetweenConfigurationAttempts: 100
m_AndroidResolution: 2
m_iOSResolution: 0
--- !u!1 &5513217393547005349
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8846418124030723115}
- component: {fileID: 6509155160601367522}
m_Layer: 0
m_Name: TrackingAnalyzer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8846418124030723115
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5513217393547005349}
serializedVersion: 2
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: 4682760393691964}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6509155160601367522
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5513217393547005349}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d3670ede57ef84f68845b3bb411d4a87, type: 3}
m_Name:
m_EditorClassIdentifier:
m_SecondsToDecayPose: 2
m_SeparateLocalizationResults: 0
OnPlatformTrackingLost:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 4876485604010972054}
m_TargetAssemblyTypeName: Immersal.ImmersalSDK, Immersal.Core
m_MethodName: TriggerSoftReset
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
OnTrackingWell:
m_PersistentCalls:
m_Calls: []
OnTrackingLost:
m_PersistentCalls:
m_Calls: []
--- !u!1 &6218135140821756341
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4358890709939687853}
- component: {fileID: 4858077625584781203}
m_Layer: 0
m_Name: ImmersalSession
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4358890709939687853
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6218135140821756341}
serializedVersion: 2
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: 4682760393691964}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4858077625584781203
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6218135140821756341}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1998a90ed503d46ffa5a88d3c7505c28, type: 3}
m_Name:
m_EditorClassIdentifier:
m_AutoStart: 1
m_UpdateContinuously: 1
m_SessionUpdateInterval: 2
m_BurstMode: 1
m_ResetOnPause: 0
m_ProcessData: 0
m_SessionDataProcessors: []
OnPause:
m_PersistentCalls:
m_Calls: []
OnResume:
m_PersistentCalls:
m_Calls: []
OnReset:
m_PersistentCalls:
m_Calls: []
--- !u!1 &8087551170109127229
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1533780097516228215}
- component: {fileID: 4803580791226008317}
m_Layer: 0
m_Name: ServerLocalization
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1533780097516228215
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8087551170109127229}
serializedVersion: 2
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: 542854591214680858}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4803580791226008317
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8087551170109127229}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 82ac8f41066bd43cca7a925e70116602, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ConfigurationMode: 0
m_SolverType: 0
--- !u!1 &9015867826600335194
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 542854591214680858}
- component: {fileID: 4174534150879381888}
m_Layer: 0
m_Name: Localizer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &542854591214680858
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9015867826600335194}
serializedVersion: 2
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:
- {fileID: 6323821621449692113}
- {fileID: 1533780097516228215}
m_Father: {fileID: 4682760393691964}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4174534150879381888
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9015867826600335194}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0113bf92df9fa4e24bd92cedc0f00ec6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_LocalizationMethodObjects:
- {fileID: 3150119538342652997}
- {fileID: 4803580791226008317}
OnFirstSuccessfulLocalization:
m_PersistentCalls:
m_Calls: []
OnSuccessfulLocalizations:
m_PersistentCalls:
m_Calls: []
OnLocalizationResult:
m_PersistentCalls:
m_Calls: []
OnFailedLocalizations:
m_PersistentCalls:
m_Calls: []

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2c6deb7afc7d34561941b1429c48ffaf
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 18ff1b8c4a5924d558858261f7c27693
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: fb91ea361218943c5b6e55189c0af6f7
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 387bc195dff33405dbe9c8cc3b051912
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 665b1ef59d2854b70b4806c15ce9fa52
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,503 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
using UnityEngine;
using UnityEngine.Events;
using System;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Immersal.XR;
using Immersal.REST;
using AOT;
using UnityEngine.Serialization;
using Object = UnityEngine.Object;
namespace Immersal
{
public class ImmersalSDK : MonoBehaviour
{
// SDK properties
public static string sdkVersion = "2.1.1";
private static readonly string[] ServerList = new[] {"https://api.immersal.com", "https://immersal.hexagon.com.cn"};
public enum APIServer { DefaultServer = 0, ChinaServer = 1, CustomServer = 2 };
public APIServer ImmersalServer = APIServer.DefaultServer;
[Tooltip("SDK developer token")]
public string developerToken;
#region Interfaced components
[Header("Components")]
/*
* ImmersalSDK modular framework works with any implementation of the main components:
* IImmersalSession, IPlatformSupport, ILocalizer, ISceneUpdater and ITrackingAnalyzer.
*
* Serialization and inspector support for these implementation references are
* handled with an Object reference Property with a custom Attribute as well as
* a custom PropertyDrawer. An additional public Property is used for casting
* the Object to the relevant interface type.
*/
[SerializeField][InterfaceAttribute(typeof(IImmersalSession))]
private Object m_Session;
public IImmersalSession Session => m_Session as IImmersalSession;
[SerializeField][InterfaceAttribute(typeof(IPlatformSupport))]
private Object m_Platform;
public IPlatformSupport PlatformSupport => m_Platform as IPlatformSupport;
[SerializeField][InterfaceAttribute(typeof(ILocalizer))]
private Object m_Localizer;
public ILocalizer Localizer => m_Localizer as ILocalizer;
[SerializeField][InterfaceAttribute(typeof(ISceneUpdater))]
private Object m_SceneUpdater;
public ISceneUpdater SceneUpdater => m_SceneUpdater as ISceneUpdater;
[SerializeField][InterfaceAttribute(typeof(ITrackingAnalyzer))]
private Object m_TrackingAnalyzer;
public ITrackingAnalyzer TrackingAnalyzer => m_TrackingAnalyzer as ITrackingAnalyzer;
#endregion
// Configuration properties
[Header("Configuration")]
[SerializeField]
[Tooltip("Initialize the SDK on Awake")]
private bool m_InitializeAutomatically = true;
[SerializeField]
[Tooltip("Application target frame rate")]
private int m_TargetFrameRate = 60;
[Tooltip("Downsample image to HD resolution")]
[SerializeField]
private bool m_Downsample = true;
[SerializeField]
private ImmersalLogger.LoggingLevel m_LoggingLevel = ImmersalLogger.LoggingLevel.ErrorsAndWarnings;
[Header("Editor")]
[Tooltip("Map data download location in relation to Assets")]
[SerializeField]
private string m_DownloadDirectory = "Map Data";
// Public
public ITrackingStatus TrackingStatus => TrackingAnalyzer.TrackingStatus;
public bool IsReady => m_IsReady;
public int LicenseLevel => m_LicenseLevel;
public bool HasValidated => m_HasValidated;
public static HttpClient client;
public int targetFrameRate
{
get { return m_TargetFrameRate; }
set
{
m_TargetFrameRate = value;
SetFrameRate();
}
}
public string localizationServer
{
get => ImmersalServer == APIServer.CustomServer ? m_CustomServerUrl : ServerList[(int)ImmersalServer];
set
{
ImmersalServer = APIServer.CustomServer;
m_CustomServerUrl = value;
}
}
public bool downsample
{
get { return m_Downsample; }
set
{
m_Downsample = value;
SetDownsample();
}
}
public string DownloadDirectory
{
get { return m_DownloadDirectory; }
}
private void SetDownsample()
{
if (downsample)
{
Core.SetInteger("LocalizationMaxPixels", 960*720);
}
else
{
Core.SetInteger("LocalizationMaxPixels", 0);
}
}
// Events
[Header("Events")]
public UnityEvent OnInitializationComplete;
public UnityEvent OnUserValidationComplete;
public UnityEvent OnReset;
[SerializeField]
private string m_CustomServerUrl = "";
private int m_LicenseLevel = -1;
private bool m_IsReady = false;
private bool m_HasValidated = false;
private bool m_PluginCallbackRegistered = false;
private bool m_PlatformIsConfigured = false;
private bool m_LocalizerIsConfigured = false;
private bool m_MapsAreRegisteredAndLoaded = false;
#region Singleton pattern
private static ImmersalSDK instance = null;
public static ImmersalSDK Instance
{
get
{
#if UNITY_EDITOR
if (instance == null && !Application.isPlaying)
{
instance = FindObjectOfType<ImmersalSDK>();
}
#endif
if (instance == null)
{
ImmersalLogger.LogError("No ImmersalSDK instance found. Ensure one exists in the scene.");
}
return instance;
}
}
#endregion
private void Awake()
{
if (instance == null)
{
instance = this;
}
if (instance != this)
{
ImmersalLogger.LogError("There must be only one ImmersalSDK object in a scene.");
UnityEngine.Object.DestroyImmediate(this);
return;
}
if (m_InitializeAutomatically)
_ = Initialize();
}
public async Task Initialize()
{
ImmersalLogger.Level = m_LoggingLevel;
ImmersalLogger.Log("Initializing ImmersalSDK", ImmersalLogger.LoggingLevel.Verbose);
if (PlatformSupport == null || Localizer == null || SceneUpdater == null || TrackingAnalyzer == null)
{
throw new Exception("ImmersalSDK has null references, please assign all Properties.");
}
// plugin log callback
if (!m_PluginCallbackRegistered)
{
LogCallback callback_delegate = new LogCallback(Log);
IntPtr intptr_delegate = Marshal.GetFunctionPointerForDelegate(callback_delegate);
Native.PP_RegisterLogCallback(intptr_delegate);
m_PluginCallbackRegistered = true;
}
// http client
HttpClientHandler handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
client = new HttpClient(handler);
client.DefaultRequestHeaders.ExpectContinue = false;
client.Timeout = TimeSpan.FromDays(1);
// validate user
if (developerToken != null && developerToken.Length > 0)
{
PlayerPrefs.SetString("token", developerToken);
ValidateUser();
}
SetFrameRate();
#if !UNITY_EDITOR
SetDownsample();
#endif
MapManager.RefreshLocalizationMethods();
await ConfigureComponents();
if (m_IsReady)
{
ImmersalLogger.Log("Immersal SDK ready!", ImmersalLogger.LoggingLevel.Verbose);
OnInitializationComplete?.Invoke();
}
}
public async void ValidateUser()
{
if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.Android)
{
m_LicenseLevel = Core.ValidateUser(developerToken);
string licenseLevel = m_LicenseLevel >= 1 ? "Enterprise" : "Free";
ImmersalLogger.Log($"{licenseLevel} License");
}
else
{
JobStatusAsync j = new JobStatusAsync();
SDKStatusResult result = await j.RunJobAsync();
m_LicenseLevel = result.level;
string licenseLevel = m_LicenseLevel >= 1 ? "Enterprise" : "Free";
ImmersalLogger.Log($"{licenseLevel} License");
}
m_HasValidated = true;
OnUserValidationComplete?.Invoke();
}
private async Task ConfigureComponents()
{
if (!m_MapsAreRegisteredAndLoaded)
{
m_MapsAreRegisteredAndLoaded = await RegisterAndLoadMaps();
}
if (!m_PlatformIsConfigured)
{
m_PlatformIsConfigured = await ConfigurePlatform();
if (m_PlatformIsConfigured)
ImmersalLogger.Log("Platform configured.", ImmersalLogger.LoggingLevel.Verbose);
}
if (!m_LocalizerIsConfigured)
{
m_LocalizerIsConfigured = await ConfigureLocalizer();
if (m_LocalizerIsConfigured)
ImmersalLogger.Log("Localizer configured.", ImmersalLogger.LoggingLevel.Verbose);
}
m_IsReady = m_PlatformIsConfigured && m_LocalizerIsConfigured && m_MapsAreRegisteredAndLoaded;
}
private async Task<bool> RegisterAndLoadMaps()
{
XRMap[] maps = FindObjectsOfType<XRMap>();
ImmersalLogger.Log($"Found {maps.Length} maps in scene.");
List<Task> mapRegisterTasks = new List<Task>();
foreach (XRMap map in maps)
{
ISceneUpdateable sceneUpdateable = map.gameObject.transform.GetComponentInParent<ISceneUpdateable>(true);
if (sceneUpdateable != null)
{
ImmersalLogger.Log($"Starting RegisterAndLoad for map {map.mapId}");
// map registering might initiate downloads so we should not await here
Task t = MapManager.RegisterAndLoadMap(map, sceneUpdateable);
mapRegisterTasks.Add(t);
}
}
// wait for all register tasks to finish
await Task.WhenAll(mapRegisterTasks.ToArray());
List<XRMap> registeredMaps = MapManager.GetRegisteredMaps();
ImmersalLogger.Log($"Registered maps: {(registeredMaps.Count > 0 ? string.Join(",", registeredMaps) : "none")}", ImmersalLogger.LoggingLevel.Verbose);
return true;
}
private async Task<bool> ConfigurePlatform()
{
try
{
IPlatformConfigureResult result = await PlatformSupport.ConfigurePlatform();
return result.Success;
}
catch (Exception e)
{
AbortWithException(e, "PlatformSupport");
}
return false;
}
private async Task<bool> ConfigureLocalizer()
{
// Get all registered maps
List<XRMap> maps = MapManager.GetRegisteredMaps();
// Create a mapping of maps and the associated localization methods
Dictionary<ILocalizationMethod, XRMap[]> mapping = maps
.GroupBy(map => map.LocalizationMethod)
.ToDictionary(
group => group.Key,
group => group.ToArray());
DefaultLocalizerConfiguration config = new DefaultLocalizerConfiguration
{
ConfigurationsToAdd = mapping,
StopRunningTasks = true
};
try
{
ILocalizerConfigurationResult result = await Localizer.ConfigureLocalizer(config);
return result.Success;
}
catch (Exception e)
{
AbortWithException(e, "Localizer");
}
return false;
}
private void SetFrameRate()
{
Application.targetFrameRate = targetFrameRate;
}
// Convenience method
// Note: no data processing chain here
public async Task LocalizeOnce()
{
if (m_IsReady)
{
ILocalizationResults localizerResults;
IPlatformUpdateResult platformResult = await PlatformSupport.UpdatePlatform();
if (platformResult.Success)
{
localizerResults = await Localizer.Localize(platformResult.CameraData);
foreach (ILocalizationResult result in localizerResults.Results)
{
if (result.Success && MapManager.TryGetMapEntry(result.MapId, out MapEntry entry))
{
await SceneUpdater.UpdateScene(entry, platformResult.CameraData, result);
}
}
}
else
{
// Localization never ran due to failed platform update
// so we need invalidate results.
localizerResults = new LocalizationResults
{
Results = Array.Empty<ILocalizationResult>()
};
}
// Update tracking status
TrackingAnalyzer.Analyze(platformResult.Status, localizerResults);
}
}
private void AbortWithException(Exception e, string logPrefix = "")
{
m_IsReady = false;
ImmersalLogger.LogError($"{logPrefix}: {e.Message}");
Debug.LogException(e);
}
private void OnDestroy()
{
m_IsReady = false;
MapManager.RemoveAllMaps(false);
}
public void TriggerSoftReset()
{
SoftReset();
}
public async Task SoftReset()
{
await Session.ResetSession();
await ResetScenes();
TrackingAnalyzer.Reset();
}
public void TriggerResetScenes()
{
ResetScenes();
}
public async Task ResetScenes()
{
ImmersalLogger.Log("Resetting SceneUpdateables");
List<ISceneUpdateable> sceneUpdateables = MapManager.GetSceneUpdateablesInUse();
foreach (ISceneUpdateable sceneUpdateable in sceneUpdateables)
{
await sceneUpdateable.ResetScene();
}
}
[ContextMenu("RestartSdk")]
public async void RestartSdk()
{
ImmersalLogger.Log("Restarting ImmersalSDK", ImmersalLogger.LoggingLevel.Verbose);
m_IsReady = false;
// Stop and clean up components
await Session.StopSession();
// Clear maps
MapManager.RemoveAllMaps();
m_MapsAreRegisteredAndLoaded = false;
m_LocalizerIsConfigured = false;
await Localizer.StopAndCleanUp();
m_PlatformIsConfigured = false;
await PlatformSupport.StopAndCleanUp();
// Reset scenes
await ResetScenes();
// Invoke event
OnReset?.Invoke();
// Restart
await Initialize();
}
// Logging callback for plugin
[MonoPInvokeCallback(typeof(LogCallback))]
public static void Log(IntPtr ansiString)
{
string msg = Marshal.PtrToStringAnsi(ansiString);
ImmersalLogger.Log($"Plugin: {msg}", ImmersalLogger.LoggingLevel.Verbose);
}
}
public class ComponentTaskCriticalException : Exception
{
public ComponentTaskCriticalException(string message) : base(message) { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22271c1298ba444ffacf4138cd9ef2eb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d3df0ff440d74453be3bc85d3f9369ca
timeCreated: 1707400520

View File

@@ -0,0 +1,599 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
using System;
using UnityEngine;
namespace Immersal.REST
{
public struct SDKJobState
{
public const string Done = "done";
public const string Sparse = "sparse";
public const string Processing = "processing";
public const string Failed = "failed";
public const string Pending = "pending";
}
public enum SDKJobType { Map, Stitch, Alignment, Edit };
public enum SDKJobPrivacy { Private, Public };
public struct DeviceInfo
{
public static string DeviceModel => SystemInfo.deviceModel != SystemInfo.unsupportedIdentifier
? SystemInfo.deviceModel
: "unknown";
}
[Serializable]
public struct SDKJob
{
public int id;
public int type;
public string version;
public int creator;
public int size;
public string status;
public int privacy;
public string name;
public double latitude;
public double longitude;
public double altitude;
public string created;
public string modified;
public string sha256_al;
public string sha256_sparse;
public string sha256_dense;
public string sha256_tex;
}
[Serializable]
public struct SDKMapId
{
public int id;
}
[Serializable]
public struct SDKLoginRequest
{
public static string endpoint = "login";
public string login;
public string password;
}
[Serializable]
public struct SDKLoginResult
{
public string error;
public int userId;
public string token;
public int level;
}
[Serializable]
public struct SDKClearRequest
{
public static string endpoint = "clear";
public string token;
public bool anchor;
}
[Serializable]
public struct SDKClearResult
{
public string error;
}
[Serializable]
public struct SDKConstructRequest
{
public static string endpoint = "construct";
public string token;
public string name;
public int featureCount;
public int featureType;
public bool preservePoses;
public int windowSize;
public bool mapTrim;
public int featureFilter;
public int compressionLevel;
public int dense;
}
[Serializable]
public struct SDKConstructResult
{
public string error;
public int id;
public int size;
}
[Serializable]
public struct SDKStatusRequest
{
public static string endpoint = "status";
public string token;
}
[Serializable]
public struct SDKStatusResult
{
public string error;
public int userId;
public int imageCount;
public int imageMax;
public bool eulaAccepted;
public int level;
}
[Serializable]
public struct SDKJobsRequest
{
public static string endpoint = "list";
public string token;
}
[Serializable]
public struct SDKGeoJobsRequest
{
public static string endpoint = "geolist";
public string token;
public double latitude;
public double longitude;
public double radius;
}
[Serializable]
public struct SDKJobsResult
{
public string error;
public int count;
public SDKJob[] jobs;
}
[Serializable]
public struct SDKImageRequest
{
public static string endpoint = "capture";
public string deviceModel;
public string token;
public int run;
public int index;
public bool anchor;
public double px;
public double py;
public double pz;
public double r00;
public double r01;
public double r02;
public double r10;
public double r11;
public double r12;
public double r20;
public double r21;
public double r22;
public double fx;
public double fy;
public double ox;
public double oy;
public double latitude;
public double longitude;
public double altitude;
}
[Serializable]
public struct SDKImageResult
{
public string error;
public string path;
}
[Serializable]
public struct SDKGeoLocalizeRequest
{
public static string endpoint = "geolocalize";
public string token;
public double fx;
public double fy;
public double ox;
public double oy;
public double latitude;
public double longitude;
public double radius;
public double qx;
public double qy;
public double qz;
public double qw;
public int solverType;
}
[Serializable]
public struct SDKLocalizeRequest
{
public static string endpoint = "localize";
public string deviceModel;
public string token;
public double fx;
public double fy;
public double ox;
public double oy;
public SDKMapId[] mapIds;
public double qx;
public double qy;
public double qz;
public double qw;
public int solverType;
public double priorX;
public double priorY;
public double priorZ;
public double priorScaleX;
public double priorScaleY;
public double priorScaleZ;
public int priorNeighborCountMin;
public int priorNeighborCountMax;
public double priorRadius;
}
[Serializable]
public struct SDKGeoPoseRequest
{
public static string endpoint = "geopose";
public string token;
public double fx;
public double fy;
public double ox;
public double oy;
public SDKMapId[] mapIds;
public double qx;
public double qy;
public double qz;
public double qw;
public int solverType;
public double priorX;
public double priorY;
public double priorZ;
public double priorScaleX;
public double priorScaleY;
public double priorScaleZ;
public int priorNeighborCountMin;
public int priorNeighborCountMax;
public double priorRadius;
}
[Serializable]
public struct SDKLocalizeResult
{
public string error;
public bool success;
public int map;
public float px;
public float py;
public float pz;
public float r00;
public float r01;
public float r02;
public float r10;
public float r11;
public float r12;
public float r20;
public float r21;
public float r22;
public int confidence;
public float time;
}
[Serializable]
public struct SDKGeoPoseResult
{
public string error;
public bool success;
public int map;
public double latitude;
public double longitude;
public double ellipsoidHeight;
public float[] quaternion;
}
[Serializable]
public struct SDKEcefRequest
{
public static string endpoint = "ecef";
public string token;
public int id;
}
[Serializable]
public struct SDKEcefResult
{
public string error;
public double[] ecef;
}
[Serializable]
public struct SDKSetMapAccessTokenRequest
{
public static string endpoint = "setmaptoken";
public string token;
public int id;
}
[Serializable]
public struct SDKClearMapAccessTokenRequest
{
public static string endpoint = "clearmaptoken";
public string token;
public int id;
}
[Serializable]
public struct SDKMapAccessTokenResult
{
public string error;
public int mapId;
public string accessToken;
}
[Serializable]
public struct SDKMapBinaryRequest
{
public static string endpoint = "map";
public string token;
public int id;
}
[Serializable]
public struct SDKMapRequest
{
public static string endpoint = "mapb64";
public string token;
public int id;
}
[Serializable]
public struct SDKMapResult
{
public string error;
public string sha256_al;
public string b64;
public byte[] mapData;
public SDKMapMetadataGetResult metadata;
}
[Serializable]
public struct SDKDeleteMapRequest
{
public static string endpoint = "delete";
public string token;
public int id;
}
[Serializable]
public struct SDKDeleteMapResult
{
public string error;
}
[Serializable]
public struct SDKRestoreMapImagesRequest
{
public static string endpoint = "restore";
public string token;
public int id;
public bool clear;
}
[Serializable]
public struct SDKRestoreMapImagesResult
{
public string error;
}
[Serializable]
public struct SDKMapPrivacyRequest
{
public static string endpoint = "privacy";
public string token;
public int id;
public int privacy;
}
[Serializable]
public struct SDKMapPrivacyResult
{
public string error;
}
[Serializable]
public struct SDKMapDownloadRequest
{
public static string endpoint = "mapb64";
public string token;
public int id;
}
[Serializable]
public struct SDKMapDownloadResult
{
public string error;
public string sha256_al;
public string b64;
}
[Serializable]
public struct SDKSparseDownloadRequest
{
public static string endpoint = "sparse";
public string token;
public int id;
}
[Serializable]
public struct SDKSparseDownloadResult
{
public string error;
public byte[] data;
}
[Serializable]
public struct SDKMapUploadRequest
{
public static string endpoint = "uploadmap";
public string token;
public string name;
public double latitude;
public double longitude;
public double altitude;
}
[Serializable]
public struct SDKMapUploadResult
{
public string error;
public int id;
}
[Serializable]
public struct SDKMapMetadataGetRequest
{
public static string endpoint = "metadataget";
public string token;
public int id;
}
[Serializable]
public struct SDKMapMetadataGetResult
{
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;
}
[Serializable]
public struct SDKMapAlignmentSetRequest
{
public static string endpoint = "metadataset";
public string token;
public int id;
public double tx;
public double ty;
public double tz;
public double qw;
public double qx;
public double qy;
public double qz;
public double scale;
}
[Serializable]
public struct SDKMapAlignmentSetResult
{
public string error;
}
[Serializable]
public struct SDKMapAlignmentResetRequest
{
public static string endpoint = "reset";
public string token;
public int id;
}
[Serializable]
public struct SDKMapAlignmentResetResult
{
public string error;
}
[Serializable]
public struct SDKCopyMapRequest
{
public static string endpoint = "copy";
public string token;
public string login;
public int id;
}
[Serializable]
public struct SDKCopyMapResult
{
public string error;
}
[Serializable]
public struct SDKVersionRequest
{
public static string endpoint = "version";
}
[Serializable]
public struct SDKVersionResult
{
public string error;
public string version;
}
[Serializable]
public struct SDKAlignMapsRequest
{
public static string endpoint = "align";
public string token;
public string name;
public SDKMapId[] mapIds;
}
[Serializable]
public struct SDKAlignMapsResult
{
public string error;
public int id;
public int size;
}
[Serializable]
public struct SDKStitchMapsRequest
{
public static string endpoint = "fuse";
public string token;
public string name;
public SDKMapId[] mapIds;
}
[Serializable]
public struct SDKStitchMapsResult
{
public string error;
public int id;
public int size;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e94452b080313413fbb29e91798f73b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 64b9b9737277b49c4a82005474a8a637
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 76b8047eae9f5472293eacba2a735fa0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,87 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
using Immersal.XR;
using UnityEngine;
namespace Immersal.XR
{
public static class ExtensionMethods
{
public static Matrix4x4 SwitchHandedness(this ref Matrix4x4 b)
{
Matrix4x4 D = Matrix4x4.identity;
D.m00 = -1;
b = D * b * D;
return b;
}
public static Quaternion SwitchHandedness(this ref Quaternion b)
{
Matrix4x4 m = Matrix4x4.Rotate(b);
m.SwitchHandedness();
b = m.rotation;
return b;
}
public static float[] ToFloats(this ref Quaternion q)
{
float[] result = new float[4] { q.x, q.y, q.z, q.w };
return result;
}
public static Vector3 SwitchHandedness(this ref Vector3 b)
{
Matrix4x4 m = Matrix4x4.TRS(b, Quaternion.identity, Vector3.one);
m.SwitchHandedness();
b = m.GetColumn(3);
return b;
}
public static double[] QuaternionsToDoubleMatrix3x3(this XRMap.MapAlignment ma)
{
double[] q = new double[] {ma.qw, ma.qx, ma.qy, ma.qz};
double[] 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;
return m;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 868f0f079d08249bfbb371a0eaa8dbfe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace Immersal
{
public class ImmersalLogger : MonoBehaviour
{
public enum LoggingLevel
{
All = 0,
Verbose = 1,
ErrorsAndWarnings = 2,
ErrorsOnly = 3,
None = 4
}
public static LoggingLevel Level = LoggingLevel.All;
private const bool m_IncludeCallerName = true;
private const string m_AdditionalPrefix = "";
// Include calling class name in logging messages
private static string ProcessMessage(string message, string filePath = "")
{
if (m_IncludeCallerName)
{
string callerName;
// check if filePath is provided (by System.Runtime.CompilerServices)
if (!string.IsNullOrEmpty(filePath))
{
callerName = System.IO.Path.GetFileNameWithoutExtension(filePath);
}
else
{
// fall back to fetching calling class name from stack with reflection
// note: will give unexpected results when call originates in an async Task
callerName = new StackFrame(2, false).GetMethod().DeclaringType?.Name
?? "Unknown";
}
message = $"[{callerName}] {message}";
}
return $"{m_AdditionalPrefix}{message}";
}
// note: filePath parameter is automagically included by System.Runtime.CompilerServices.CallerFilePath
public static void Log(string message, LoggingLevel messageLevel = LoggingLevel.All, [CallerFilePath] string filePath = "")
{
if (Level > messageLevel) return;
UnityEngine.Debug.Log(ProcessMessage(message, filePath));
}
public static void LogWarning(string message, [CallerFilePath] string filePath = "")
{
if (Level > LoggingLevel.ErrorsAndWarnings) return;
UnityEngine.Debug.LogWarning(ProcessMessage(message, filePath));
}
public static void LogError(string message, [CallerFilePath] string filePath = "")
{
if (Level > LoggingLevel.ErrorsOnly) return;
UnityEngine.Debug.LogError(ProcessMessage(message, filePath));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96faf0f52d99e4dd3ba0649f6b0f1e09
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
using System;
using UnityEngine;
public class InterfaceAttribute : PropertyAttribute
{
public Type TypeOfInterface;
public InterfaceAttribute(Type type) { this.TypeOfInterface = type; }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: de499560337e94b6799eef1a2fcbd000
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,171 @@
/*===============================================================================
Copyright (C) 2024 Immersal - Part of Hexagon. All Rights Reserved.
This file is part of the Immersal SDK.
The Immersal SDK cannot be copied, distributed, or made available to
third-parties for commercial purposes without written permission of Immersal Ltd.
Contact sales@immersal.com for licensing requests.
===============================================================================*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
namespace Immersal
{
public static class PlyImporter
{
public static Mesh PlyToMesh(byte[] bytes, string name)
{
try
{
using (MemoryStream stream = new MemoryStream(bytes))
{
Mesh mesh = StreamToMesh(stream);
mesh.name = name;
return mesh;
}
}
catch (Exception e)
{
ImmersalLogger.LogError($"Failed importing {name}: {e.Message}");
return null;
}
}
public static Mesh PlyToMesh(string filePath)
{
try
{
var stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
Mesh mesh = StreamToMesh(stream);
mesh.name = Path.GetFileNameWithoutExtension(filePath);
return mesh;
}
catch (Exception e)
{
ImmersalLogger.LogError($"Failed importing {filePath}: {e.Message}");
return null;
}
}
private static Mesh StreamToMesh(Stream stream)
{
if (!CheckHeader(new StreamReader(stream), out int vertexCount))
throw new ArgumentException("Unexpected header data");
PlyDataBody body = ReadDataBody(new BinaryReader(stream), vertexCount);
var mesh = new Mesh
{
name = "Sparse",
indexFormat = vertexCount > 65535 ? IndexFormat.UInt32 : IndexFormat.UInt16
};
mesh.SetVertices(body.Vertices);
mesh.SetColors(body.Colors);
mesh.SetIndices(
Enumerable.Range(0, vertexCount).ToArray(),
MeshTopology.Points, 0
);
mesh.RecalculateBounds();
mesh.UploadMeshData(true);
return mesh;
}
private static bool CheckHeader(StreamReader reader, out int vertexCount, int maxHeaderLineReads = 15)
{
vertexCount = -1;
int readCount = 0;
int linesRead = 0;
// Magic number line ("ply")
var line = reader.ReadLine();
readCount += line.Length + 1;
if (line != "ply")
throw new ArgumentException("Magic number ('ply') mismatch.");
// check if it's binary/little endian.
line = reader.ReadLine();
readCount += line.Length + 1;
if (line != "format binary_little_endian 1.0")
throw new ArgumentException(
"Invalid data format ('" + line + "'). " +
"Should be binary/little endian.");
while (linesRead < maxHeaderLineReads)
{
line = reader.ReadLine();
linesRead++;
readCount += line.Length + 1;
if (line == "end_header") break;
var col = line.Split();
if (col[0] == "element")
{
if (col[1] == "vertex")
{
vertexCount = Convert.ToInt32(col[2]);
}
}
}
// Rewind the stream back to the exact position of the reader.
reader.BaseStream.Position = readCount;
return vertexCount > -1;
}
private static PlyDataBody ReadDataBody(BinaryReader reader, int vertexCount)
{
PlyDataBody data = new PlyDataBody(vertexCount);
for (var i = 0; i < vertexCount; i++)
{
float x = reader.ReadSingle();
float y = reader.ReadSingle();
float z = reader.ReadSingle();
Byte r = reader.ReadByte();
Byte g = reader.ReadByte();
Byte b = reader.ReadByte();
//a = reader.ReadByte();
Byte a = Byte.MaxValue;
data.AddPoint(-x, y, z, r, g, b, a);
}
return data;
}
}
public class PlyDataBody
{
public List<Vector3> Vertices;
public List<Color32> Colors;
public PlyDataBody(int vertexCount)
{
Vertices = new List<Vector3>(vertexCount);
Colors = new List<Color32>(vertexCount);
}
public void AddPoint(
float x, float y, float z,
byte r, byte g, byte b, byte a
)
{
Vertices.Add(new Vector3(x, y, z));
Colors.Add(new Color32(r, g, b, a));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a6e2d23c28b294a9197bb2445580a9f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More