diff --git a/Assets~/Profiles/SpatialAwarenessSystemProfile.asset b/Assets~/Profiles/SpatialAwarenessSystemProfile.asset
new file mode 100644
index 0000000..07003d3
--- /dev/null
+++ b/Assets~/Profiles/SpatialAwarenessSystemProfile.asset
@@ -0,0 +1,19 @@
+%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: 80a1ad34855542cbbd15a6a6dde2e935, type: 3}
+ m_Name: MixedRealitySpatialAwarenessSystemProfile
+ m_EditorClassIdentifier:
+ configurations: []
+ meshDisplayOption: 0
+ globalMeshObserverProfile: {fileID: 11400000, guid: 6cbcb6266beaa724780d064525cf2896,
+ type: 2}
+ globalSurfaceObserverProfile: {fileID: 0}
diff --git a/Assets~/Profiles/SpatialAwarenessSystemProfile.asset.meta b/Assets~/Profiles/SpatialAwarenessSystemProfile.asset.meta
new file mode 100644
index 0000000..04a5a97
--- /dev/null
+++ b/Assets~/Profiles/SpatialAwarenessSystemProfile.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 41ce31a4954544c58d16f6b3c4c88f4b
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets~/Profiles/SpatialMeshObserverProfile.asset b/Assets~/Profiles/SpatialMeshObserverProfile.asset
new file mode 100644
index 0000000..7f3aeb4
--- /dev/null
+++ b/Assets~/Profiles/SpatialMeshObserverProfile.asset
@@ -0,0 +1,27 @@
+%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: 34e54930903b41a9bdcbe94df832101b, type: 3}
+ m_Name: MixedRealitySpatialMeshObserverProfile
+ m_EditorClassIdentifier:
+ startupBehavior: 0
+ observationExtents: {x: 3, y: 3, z: 3}
+ isStationaryObserver: 0
+ updateInterval: 0.25
+ physicsLayer: 31
+ meshLevelOfDetail: 1
+ meshTrianglesPerCubicMeter: 0
+ meshRecalculateNormals: 1
+ meshVisibleMaterial: {fileID: 2100000, guid: 58998db165b741089929938ea4d1b249, type: 2}
+ meshOcclusionMaterial: {fileID: 2100000, guid: 0062b17981f34cf18ad1f9060db0cb2b,
+ type: 2}
+ additionalComponents: []
+ meshObjectPrefab: {fileID: 0}
diff --git a/Assets~/Profiles/SpatialMeshObserverProfile.asset.meta b/Assets~/Profiles/SpatialMeshObserverProfile.asset.meta
new file mode 100644
index 0000000..368834b
--- /dev/null
+++ b/Assets~/Profiles/SpatialMeshObserverProfile.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6cbcb6266beaa724780d064525cf2896
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets~/Profiles/spatial_awarenessPlatformServiceConfigurationsProfile.txt b/Assets~/Profiles/spatial_awarenessPlatformServiceConfigurationsProfile.txt
deleted file mode 100644
index 10b2f57..0000000
--- a/Assets~/Profiles/spatial_awarenessPlatformServiceConfigurationsProfile.txt
+++ /dev/null
@@ -1 +0,0 @@
-PlatformServiceConfigurationsProfile goes here
\ No newline at end of file
diff --git a/Editor/BaseSpatialMeshObserverProfileInspector.cs b/Editor/BaseSpatialMeshObserverProfileInspector.cs
new file mode 100644
index 0000000..c711979
--- /dev/null
+++ b/Editor/BaseSpatialMeshObserverProfileInspector.cs
@@ -0,0 +1,107 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Editor.PropertyDrawers;
+using RealityToolkit.Definitions.SpatialObservers;
+using RealityToolkit.Editor;
+using UnityEditor;
+using UnityEditorInternal;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Editor
+{
+ [CustomEditor(typeof(BaseSpatialMeshObserverProfile), true, isFallback = true)]
+ public class BaseSpatialMeshObserverProfileInspector : BaseSpatialObserverProfileInspector
+ {
+ private SerializedProperty meshLevelOfDetail;
+ private SerializedProperty meshRecalculateNormals;
+ private SerializedProperty meshVisibleMaterial;
+ private SerializedProperty meshOcclusionMaterial;
+ private SerializedProperty additionalComponents;
+ private SerializedProperty meshObjectPrefab;
+
+ private ReorderableList additionalComponentsList;
+ private int currentlySelectedConfigurationOption;
+
+ private readonly GUIContent spatialMeshSettingsFoldoutHeader = new GUIContent("Spatial Mesh Settings");
+
+ ///
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+
+ meshLevelOfDetail = serializedObject.FindProperty(nameof(meshLevelOfDetail));
+ meshRecalculateNormals = serializedObject.FindProperty(nameof(meshRecalculateNormals));
+ meshVisibleMaterial = serializedObject.FindProperty(nameof(meshVisibleMaterial));
+ meshOcclusionMaterial = serializedObject.FindProperty(nameof(meshOcclusionMaterial));
+ additionalComponents = serializedObject.FindProperty(nameof(additionalComponents));
+ meshObjectPrefab = serializedObject.FindProperty(nameof(meshObjectPrefab));
+
+ additionalComponentsList = new ReorderableList(serializedObject, additionalComponents, true, false, true, true)
+ {
+ elementHeight = EditorGUIUtility.singleLineHeight * 1.2f
+ };
+
+ additionalComponentsList.drawHeaderCallback += rect =>
+ {
+ EditorGUI.LabelField(rect, new GUIContent(additionalComponents.displayName, additionalComponents.tooltip));
+ };
+ additionalComponentsList.drawElementCallback += DrawConfigurationOptionElement;
+ additionalComponentsList.onAddCallback += OnConfigurationOptionAdded;
+ additionalComponentsList.onRemoveCallback += OnConfigurationOptionRemoved;
+ }
+
+ private void DrawConfigurationOptionElement(Rect rect, int index, bool isActive, bool isFocused)
+ {
+ if (isFocused)
+ {
+ currentlySelectedConfigurationOption = index;
+ }
+
+ var componentItem = additionalComponents.GetArrayElementAtIndex(index);
+
+ TypeReferencePropertyDrawer.FilterConstraintOverride = type => !type.IsAbstract && typeof(Component).IsAssignableFrom(type);
+ EditorGUI.PropertyField(rect, componentItem, GUIContent.none);
+ }
+
+ private void OnConfigurationOptionAdded(ReorderableList list)
+ {
+ additionalComponents.arraySize += 1;
+ var index = additionalComponents.arraySize - 1;
+ additionalComponents.GetArrayElementAtIndex(index).FindPropertyRelative("reference").stringValue = string.Empty;
+ }
+
+ private void OnConfigurationOptionRemoved(ReorderableList list)
+ {
+ if (currentlySelectedConfigurationOption >= 0)
+ {
+ additionalComponents.DeleteArrayElementAtIndex(currentlySelectedConfigurationOption);
+ }
+ }
+
+ ///
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+
+ serializedObject.Update();
+
+ if (meshLevelOfDetail.FoldoutWithBoldLabelPropertyField(spatialMeshSettingsFoldoutHeader))
+ {
+ EditorGUI.indentLevel++;
+ EditorGUILayout.PropertyField(meshRecalculateNormals);
+ EditorGUILayout.PropertyField(meshVisibleMaterial);
+ EditorGUILayout.PropertyField(meshOcclusionMaterial);
+ EditorGUILayout.HelpBox("Additional Components to add to the Spatial Mesh Observer\n\nNote: MeshFilter, MeshRenderer, and MeshCollider are already added automatically as they're required components for a mesh object.", MessageType.Info);
+ additionalComponentsList.DoLayoutList();
+ EditorGUILayout.PropertyField(meshObjectPrefab);
+ EditorGUILayout.HelpBox("The mesh object is procedurally generated, but you can also use an empty prefab object as well with predefined components and data.", MessageType.Info);
+ EditorGUI.indentLevel--;
+ }
+
+ EditorGUILayout.Space();
+
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Editor/BaseSpatialMeshObserverProfileInspector.cs.meta b/Editor/BaseSpatialMeshObserverProfileInspector.cs.meta
new file mode 100644
index 0000000..454944e
--- /dev/null
+++ b/Editor/BaseSpatialMeshObserverProfileInspector.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b60d1a1c362145c4be48c5bb9f8459f7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 6e2e9d716bbb4d8382bd53f11996b90e, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/BaseSpatialObserverProfileInspector.cs b/Editor/BaseSpatialObserverProfileInspector.cs
new file mode 100644
index 0000000..e5803d7
--- /dev/null
+++ b/Editor/BaseSpatialObserverProfileInspector.cs
@@ -0,0 +1,60 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Editor.Profiles;
+using RealityToolkit.Definitions.SpatialObservers;
+using RealityToolkit.Editor;
+using UnityEditor;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Editor
+{
+ [CustomEditor(typeof(BaseSpatialObserverProfile), true, isFallback = true)]
+ public abstract class BaseSpatialObserverProfileInspector : ServiceProfileInspector
+ {
+ private SerializedProperty startupBehavior;
+ private SerializedProperty observationExtents;
+ private SerializedProperty isStationaryObserver;
+ private SerializedProperty updateInterval;
+ private SerializedProperty physicsLayer;
+
+ private readonly GUIContent generalSettingsFoldoutHeader = new GUIContent("General Settings");
+
+ ///
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+
+ startupBehavior = serializedObject.FindProperty(nameof(startupBehavior));
+ startupBehavior.isExpanded = true;
+ observationExtents = serializedObject.FindProperty(nameof(observationExtents));
+ isStationaryObserver = serializedObject.FindProperty(nameof(isStationaryObserver));
+ updateInterval = serializedObject.FindProperty(nameof(updateInterval));
+ physicsLayer = serializedObject.FindProperty(nameof(physicsLayer));
+ }
+
+ ///
+ protected override void RenderConfigurationOptions(bool forceExpanded = false)
+ {
+ RenderHeader("The Spatial Awareness Observer service module supplies the Spatial Awareness System with all the data it needs to understand the world around you.");
+
+ serializedObject.Update();
+
+ if (startupBehavior.FoldoutWithBoldLabelPropertyField(generalSettingsFoldoutHeader))
+ {
+ EditorGUI.indentLevel++;
+ EditorGUILayout.PropertyField(observationExtents);
+ EditorGUILayout.PropertyField(isStationaryObserver);
+ EditorGUILayout.PropertyField(updateInterval);
+ EditorGUILayout.PropertyField(physicsLayer);
+ EditorGUI.indentLevel--;
+ }
+
+ EditorGUILayout.Space();
+
+ DrawServiceModulePropertyDrawer();
+
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Editor/BaseSpatialObserverProfileInspector.cs.meta b/Editor/BaseSpatialObserverProfileInspector.cs.meta
new file mode 100644
index 0000000..8582ced
--- /dev/null
+++ b/Editor/BaseSpatialObserverProfileInspector.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d1bdd90a3c2a4f40a1643acae7338010
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 6e2e9d716bbb4d8382bd53f11996b90e, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/BaseSurfaceObserverProfileInspector.cs b/Editor/BaseSurfaceObserverProfileInspector.cs
new file mode 100644
index 0000000..4e1c914
--- /dev/null
+++ b/Editor/BaseSurfaceObserverProfileInspector.cs
@@ -0,0 +1,72 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityToolkit.Definitions.SpatialObservers;
+using RealityToolkit.Editor;
+using UnityEditor;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Editor
+{
+ [CustomEditor(typeof(BaseSurfaceObserverProfile), true, isFallback = true)]
+ public class BaseSurfaceObserverProfileInspector : BaseSpatialObserverProfileInspector
+ {
+ private readonly GUIContent surfaceFoldoutContent = new GUIContent("Surface Finding Settings");
+
+ private SerializedProperty surfaceFindingMinimumArea;
+ private SerializedProperty displayFloorSurfaces;
+ private SerializedProperty floorSurfaceMaterial;
+ private SerializedProperty displayCeilingSurfaces;
+ private SerializedProperty ceilingSurfaceMaterial;
+ private SerializedProperty displayWallSurfaces;
+ private SerializedProperty wallSurfaceMaterial;
+ private SerializedProperty displayPlatformSurfaces;
+ private SerializedProperty platformSurfaceMaterial;
+
+ private readonly GUIContent ceilingMaterialContent = new GUIContent("Ceiling Material");
+ private readonly GUIContent floorMaterialContent = new GUIContent("Floor Material");
+ private readonly GUIContent platformMaterialContent = new GUIContent("Platform Material");
+ private readonly GUIContent wallMaterialContent = new GUIContent("Wall Material");
+ private readonly GUIContent minimumAreaContent = new GUIContent("Minimum Area");
+
+ ///
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+
+ surfaceFindingMinimumArea = serializedObject.FindProperty(nameof(surfaceFindingMinimumArea));
+ displayFloorSurfaces = serializedObject.FindProperty(nameof(displayFloorSurfaces));
+ floorSurfaceMaterial = serializedObject.FindProperty(nameof(floorSurfaceMaterial));
+ displayCeilingSurfaces = serializedObject.FindProperty(nameof(displayCeilingSurfaces));
+ ceilingSurfaceMaterial = serializedObject.FindProperty(nameof(ceilingSurfaceMaterial));
+ displayWallSurfaces = serializedObject.FindProperty(nameof(displayWallSurfaces));
+ wallSurfaceMaterial = serializedObject.FindProperty(nameof(wallSurfaceMaterial));
+ displayPlatformSurfaces = serializedObject.FindProperty(nameof(displayPlatformSurfaces));
+ platformSurfaceMaterial = serializedObject.FindProperty(nameof(platformSurfaceMaterial));
+ }
+
+ ///
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+
+ serializedObject.Update();
+
+ if (surfaceFindingMinimumArea.FoldoutWithBoldLabelPropertyField(surfaceFoldoutContent, minimumAreaContent))
+ {
+ EditorGUI.indentLevel++;
+ EditorGUILayout.PropertyField(displayFloorSurfaces);
+ EditorGUILayout.PropertyField(floorSurfaceMaterial, floorMaterialContent);
+ EditorGUILayout.PropertyField(displayCeilingSurfaces);
+ EditorGUILayout.PropertyField(ceilingSurfaceMaterial, ceilingMaterialContent);
+ EditorGUILayout.PropertyField(displayWallSurfaces);
+ EditorGUILayout.PropertyField(wallSurfaceMaterial, wallMaterialContent);
+ EditorGUILayout.PropertyField(displayPlatformSurfaces);
+ EditorGUILayout.PropertyField(platformSurfaceMaterial, platformMaterialContent);
+ EditorGUI.indentLevel--;
+ }
+
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Editor/BaseSurfaceObserverProfileInspector.cs.meta b/Editor/BaseSurfaceObserverProfileInspector.cs.meta
new file mode 100644
index 0000000..39619b4
--- /dev/null
+++ b/Editor/BaseSurfaceObserverProfileInspector.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7052291bd6684f0b88835c08ed3937ce
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 6e2e9d716bbb4d8382bd53f11996b90e, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/RealityToolkit.SpatialAwareness.Editor.asmdef b/Editor/RealityToolkit.SpatialAwareness.Editor.asmdef
index 594f524..895d066 100644
--- a/Editor/RealityToolkit.SpatialAwareness.Editor.asmdef
+++ b/Editor/RealityToolkit.SpatialAwareness.Editor.asmdef
@@ -8,7 +8,8 @@
"GUID:9753fcbb5b1feaf459f435ac95e51baa",
"GUID:b2d046948d6452a4b8485efc9ce0f88c",
"GUID:2a3f0ca4e21332c44bfdce311ea8943e",
- "GUID:4ddd23ea56a3a40f0aa0036d1624a53e"
+ "GUID:4ddd23ea56a3a40f0aa0036d1624a53e",
+ "GUID:35678c06c8d288749857954cda15bcab"
],
"includePlatforms": [
"Editor"
diff --git a/Editor/SpatialAwarenessEditorActiveProfileChangeHandler.cs b/Editor/SpatialAwarenessEditorActiveProfileChangeHandler.cs
new file mode 100644
index 0000000..d5c91d2
--- /dev/null
+++ b/Editor/SpatialAwarenessEditorActiveProfileChangeHandler.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Services;
+using RealityCollective.Utilities.Editor;
+using RealityToolkit.SpatialAwareness.Definitions;
+using UnityEditor;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Editor
+{
+ [InitializeOnLoad]
+ public static class EditorActiveProfileChangeHandler
+ {
+ static EditorActiveProfileChangeHandler()
+ {
+ EditorApplication.hierarchyChanged += EditorApplication_hierarchyChanged;
+ }
+
+ private static void EditorApplication_hierarchyChanged()
+ {
+ if (ServiceManager.IsActiveAndInitialized)
+ {
+ if (ServiceManager.Instance.TryGetService(out _) &&
+ LayerUtilities.CheckLayers(SpatialAwarenessSystemProfile.SpatialAwarenessLayers))
+ {
+ Debug.Log($"{nameof(ISpatialAwarenessService)} was enabled, spatial mapping layers added to project.");
+ }
+ else if (!ServiceManager.Instance.TryGetService(out _) &&
+ LayerUtilities.RemoveLayers(SpatialAwarenessSystemProfile.SpatialAwarenessLayers))
+ {
+ Debug.Log($"{nameof(ISpatialAwarenessService)} was disabled, spatial mapping layers removed to project.");
+ }
+ }
+ }
+ }
+}
diff --git a/Editor/SpatialAwarenessEditorActiveProfileChangeHandler.cs.meta b/Editor/SpatialAwarenessEditorActiveProfileChangeHandler.cs.meta
new file mode 100644
index 0000000..4cf4ac0
--- /dev/null
+++ b/Editor/SpatialAwarenessEditorActiveProfileChangeHandler.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9daeda13d83550e4aba46e9202489951
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/SpatialAwarenessPackageModulesInstaller.cs b/Editor/SpatialAwarenessPackageModulesInstaller.cs
index 674f916..fc3e7dc 100644
--- a/Editor/SpatialAwarenessPackageModulesInstaller.cs
+++ b/Editor/SpatialAwarenessPackageModulesInstaller.cs
@@ -4,19 +4,21 @@
using RealityCollective.ServiceFramework.Definitions;
using RealityCollective.ServiceFramework.Editor.Packages;
using RealityCollective.ServiceFramework.Services;
+using RealityToolkit.SpatialAwareness.Definitions;
+using RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers;
+using System.Linq;
using UnityEditor;
namespace RealityToolkit.SpatialAwareness.Editor
{
///
- /// Installs s coming from a third party package
- /// into the in the .
+ /// Installs the spatial awareness package modules in the .
///
[InitializeOnLoad]
public sealed class SpatialAwarenessPackageModulesInstaller : IPackageModulesInstaller
{
///
- /// Static initializer for the installer instance.
+ /// Statis initalizer for the installer instance.
///
static SpatialAwarenessPackageModulesInstaller()
{
@@ -36,57 +38,44 @@ static SpatialAwarenessPackageModulesInstaller()
///
public bool Install(ServiceConfiguration serviceConfiguration)
{
- /*
- -------------------------------------------------------
- TO install modules for the service, uncomment the code below.
-
- Note, in order to correctly assign modules for the service, you need to replace the following (Where spatial_awareness is the service name used to generate this repository):
-
- - spatial_awareness with the correct service type.
- - spatial_awarenessModule with the correct module type.
- - Ispatial_awarenessModule with the correct module interface.
- - spatial_awarenessProfile with the correct profile type.
+ if (!typeof(ISpatialAwarenessServiceModule).IsAssignableFrom(serviceConfiguration.InstancedType.Type))
+ {
+ // This module installer does not accept the configuration type.
+ return false;
+ }
- These are collated from the service and module definitions generated using the Service Template Generator
- -------------------------------------------------------
- if (!typeof(Ispatial_awarenessModule).IsAssignableFrom(serviceConfiguration.InstancedType.Type))
- {
- // This module installer does not accept the configuration type.
- return false;
- }
+ if (!ServiceManager.IsActiveAndInitialized)
+ {
+ UnityEngine.Debug.LogWarning($"Could not install {serviceConfiguration.InstancedType.Type.Name}.{nameof(ServiceManager)} is not initialized.");
+ return false;
+ }
- if (!ServiceManager.IsActiveAndInitialized)
- {
- UnityEngine.Debug.LogWarning($"Could not install {serviceConfiguration.InstancedType.Type.Name}.{nameof(ServiceManager)} is not initialized.");
- return false;
- }
+ if (!ServiceManager.Instance.HasActiveProfile)
+ {
+ UnityEngine.Debug.LogWarning($"Could not install {serviceConfiguration.InstancedType.Type.Name}.{nameof(ServiceManager)} has no active profile.");
+ return false;
+ }
- if (!ServiceManager.Instance.HasActiveProfile)
- {
- UnityEngine.Debug.LogWarning($"Could not install {serviceConfiguration.InstancedType.Type.Name}.{nameof(ServiceManager)} has no active profile.");
- return false;
- }
+ if (!ServiceManager.Instance.TryGetServiceProfile(out var spatialAwarenessServiceProfile))
+ {
+ UnityEngine.Debug.LogWarning($"Could not install {serviceConfiguration.InstancedType.Type.Name}.{nameof(SpatialAwarenessSystemProfile)} not found.");
+ return false;
+ }
- if (!ServiceManager.Instance.TryGetServiceProfile(out var spatial_awarenessProfile))
- {
- UnityEngine.Debug.LogWarning($"Could not install {serviceConfiguration.InstancedType.Type.Name}.{nameof(spatial_awarenessProfile)} not found.");
- return false;
- }
+ // Setup the configuration.
+ var typedServiceConfiguration = new ServiceConfiguration(serviceConfiguration.InstancedType.Type, serviceConfiguration.Name, serviceConfiguration.Priority, serviceConfiguration.RuntimePlatforms, serviceConfiguration.Profile);
- // Setup the configuration.
- var typedServiceConfiguration = new ServiceConfiguration(serviceConfiguration.InstancedType.Type, serviceConfiguration.Name, serviceConfiguration.Priority, serviceConfiguration.RuntimePlatforms, serviceConfiguration.Profile);
+ // Make sure it's not already in the target profile.
+ if (spatialAwarenessServiceProfile.ServiceConfigurations.All(sc => sc.InstancedType.Type != serviceConfiguration.InstancedType.Type))
+ {
+ spatialAwarenessServiceProfile.AddConfiguration(typedServiceConfiguration);
+ UnityEngine.Debug.Log($"Successfully installed the {serviceConfiguration.InstancedType.Type.Name} to {spatialAwarenessServiceProfile.name}.");
+ }
+ else
+ {
+ UnityEngine.Debug.Log($"Skipped installing the {serviceConfiguration.InstancedType.Type.Name} to {spatialAwarenessServiceProfile.name}. Already installed.");
+ }
- // Make sure it is not already in the target profile.
- if (spatial_awarenessProfile.ServiceConfigurations.All(sc => sc.InstancedType.Type != serviceConfiguration.InstancedType.Type))
- {
- spatial_awarenessProfile.AddConfiguration(typedServiceConfiguration);
- UnityEngine.Debug.Log($"Successfully installed the {serviceConfiguration.InstancedType.Type.Name} to {spatial_awarenessProfile.name}.");
- }
- else
- {
- UnityEngine.Debug.Log($"Skipped installing the {serviceConfiguration.InstancedType.Type.Name} to {spatial_awarenessProfile.name}. Already installed.");
- }
- */
return true;
}
}
diff --git a/Editor/SpatialAwarenessSserviceProfileInspector.cs b/Editor/SpatialAwarenessSserviceProfileInspector.cs
new file mode 100644
index 0000000..9305a7b
--- /dev/null
+++ b/Editor/SpatialAwarenessSserviceProfileInspector.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Editor.Profiles;
+using RealityToolkit.Editor;
+using RealityToolkit.SpatialAwareness.Definitions;
+using UnityEditor;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Editor
+{
+ [CustomEditor(typeof(SpatialAwarenessSystemProfile))]
+ public class SpatialAwarenessSystemProfileInspector : ServiceProfileInspector
+ {
+ private SerializedProperty meshDisplayOption;
+ private SerializedProperty globalMeshObserverProfile;
+ private SerializedProperty globalSurfaceObserverProfile;
+
+ private readonly GUIContent generalSettingsFoldoutHeader = new GUIContent("General Settings");
+
+ ///
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+
+ meshDisplayOption = serializedObject.FindProperty(nameof(meshDisplayOption));
+ meshDisplayOption.isExpanded = true;
+ globalMeshObserverProfile = serializedObject.FindProperty(nameof(globalMeshObserverProfile));
+ globalSurfaceObserverProfile = serializedObject.FindProperty(nameof(globalSurfaceObserverProfile));
+ }
+
+ ///
+ protected override void RenderConfigurationOptions(bool forceExpanded = false)
+ {
+ RenderHeader("Spatial Awareness can enhance your experience by enabling objects to interact with the real world.\n\nBelow is a list of registered Spatial Observers that can gather data about your environment.");
+
+ serializedObject.Update();
+
+ if (meshDisplayOption.FoldoutWithBoldLabelPropertyField(generalSettingsFoldoutHeader))
+ {
+ EditorGUILayout.Space();
+ EditorGUI.indentLevel++;
+ EditorGUILayout.PropertyField(globalMeshObserverProfile);
+ EditorGUILayout.PropertyField(globalSurfaceObserverProfile);
+ EditorGUI.indentLevel--;
+ }
+
+ DrawServiceModulePropertyDrawer();
+
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Editor/SpatialAwarenessSserviceProfileInspector.cs.meta b/Editor/SpatialAwarenessSserviceProfileInspector.cs.meta
new file mode 100644
index 0000000..651d7cf
--- /dev/null
+++ b/Editor/SpatialAwarenessSserviceProfileInspector.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 04221c08803841039e2565acde6fee74
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 6e2e9d716bbb4d8382bd53f11996b90e, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions.meta b/Runtime/Definitions.meta
new file mode 100644
index 0000000..f83834f
--- /dev/null
+++ b/Runtime/Definitions.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a29a368d50cd4cf41bede519d94e4f63
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialAwarenessMeshLevelOfDetail.cs b/Runtime/Definitions/SpatialAwarenessMeshLevelOfDetail.cs
new file mode 100644
index 0000000..738894f
--- /dev/null
+++ b/Runtime/Definitions/SpatialAwarenessMeshLevelOfDetail.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+namespace RealityToolkit.SpatialAwareness.Definitions
+{
+ ///
+ /// Enumeration defining levels of detail for the spatial awareness service.
+ ///
+ public enum SpatialAwarenessMeshLevelOfDetail
+ {
+ ///
+ /// The low level of detail is well suited for identifying large
+ /// environmental features, such as floors and walls.
+ ///
+ Low = 0,
+
+ ///
+ /// The medium level of detail is suited for fast environmental mesh occlusion.
+ ///
+ Medium = 1,
+
+ ///
+ /// The high level of detail is well suited for mesh occlusion for object like hands.
+ ///
+ High = 2,
+
+ ///
+ /// A custom level of detail which is overridden by the platform specific profile.
+ ///
+ Custom = 3
+ }
+}
diff --git a/Runtime/Definitions/SpatialAwarenessMeshLevelOfDetail.cs.meta b/Runtime/Definitions/SpatialAwarenessMeshLevelOfDetail.cs.meta
new file mode 100644
index 0000000..18c7a5c
--- /dev/null
+++ b/Runtime/Definitions/SpatialAwarenessMeshLevelOfDetail.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 070fd5bf1b0f45769f1c7b5d6adddafd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialAwarenessServiceProfile.cs b/Runtime/Definitions/SpatialAwarenessServiceProfile.cs
new file mode 100644
index 0000000..2269ed4
--- /dev/null
+++ b/Runtime/Definitions/SpatialAwarenessServiceProfile.cs
@@ -0,0 +1,60 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Definitions;
+using RealityToolkit.Definitions.SpatialObservers;
+using RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers;
+using System;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Definitions
+{
+ ///
+ /// Configuration profile settings for setting up the spatial awareness system.
+ ///
+ public class SpatialAwarenessSystemProfile : BaseServiceProfile
+ {
+ public static readonly Tuple[] SpatialAwarenessLayers =
+ {
+ new Tuple(30, SpatialAwarenessMeshesLayerName),
+ new Tuple(31, SpatialAwarenessSurfacesLayerName)
+ };
+
+ ///
+ /// The name of the Spatial Awareness Mesh Physics Layer.
+ ///
+ public const string SpatialAwarenessMeshesLayerName = "Spatial Awareness Meshes";
+
+ ///
+ /// The name of the Spatial Awareness Surfaces Physics Layer.
+ ///
+ public const string SpatialAwarenessSurfacesLayerName = "Spatial Awareness Surfaces";
+
+ [SerializeField]
+ [Tooltip("Indicates how the BaseSpatialMeshObserver is to display surface meshes within the application.")]
+ private SpatialMeshDisplayOptions meshDisplayOption = SpatialMeshDisplayOptions.None;
+
+ ///
+ /// Indicates how the is to display surface meshes within the application.
+ ///
+ public SpatialMeshDisplayOptions MeshDisplayOption => meshDisplayOption;
+
+ [SerializeField]
+ [Tooltip("The global mesh observer profile settings to use for the mesh observer service module if no profile is provided.")]
+ private BaseSpatialMeshObserverProfile globalMeshObserverProfile = null;
+
+ ///
+ /// The global mesh observer profile settings to use for the s if no profile is provided.
+ ///
+ public BaseSpatialMeshObserverProfile GlobalMeshObserverProfile => globalMeshObserverProfile;
+
+ [SerializeField]
+ [Tooltip("The global mesh observer profile settings to use for the mesh observer service module if no profile is provided.")]
+ private BaseSurfaceObserverProfile globalSurfaceObserverProfile = null;
+
+ ///
+ /// The global mesh observer profile settings to use for the s if no profile is provided.
+ ///
+ public BaseSurfaceObserverProfile GlobalSurfaceObserverProfile => globalSurfaceObserverProfile;
+ }
+}
diff --git a/Runtime/Definitions/SpatialAwarenessServiceProfile.cs.meta b/Runtime/Definitions/SpatialAwarenessServiceProfile.cs.meta
new file mode 100644
index 0000000..f75e83c
--- /dev/null
+++ b/Runtime/Definitions/SpatialAwarenessServiceProfile.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 80a1ad34855542cbbd15a6a6dde2e935
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 9275054f8248b2f408eb427eaa609a58, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialAwarenessSurfaceTypes.cs b/Runtime/Definitions/SpatialAwarenessSurfaceTypes.cs
new file mode 100644
index 0000000..eaa170c
--- /dev/null
+++ b/Runtime/Definitions/SpatialAwarenessSurfaceTypes.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+namespace RealityToolkit.SpatialAwareness.Definitions
+{
+ ///
+ /// Enumeration defining the types of planar surfaces that are supported by the spatial awareness surface finding subsystem.
+ ///
+ [System.Flags]
+ public enum SpatialAwarenessSurfaceTypes
+ {
+ ///
+ /// An unknown / unsupported type of surface.
+ ///
+ Unknown = 1 << 0,
+
+ ///
+ /// The environment’s floor.
+ ///
+ Floor = 1 << 1,
+
+ ///
+ /// The environment’s ceiling.
+ ///
+ Ceiling = 1 << 2,
+
+ ///
+ /// A wall within the user’s space.
+ ///
+ Wall = 1 << 3,
+
+ ///
+ /// A raised, horizontal surface such as a shelf.
+ ///
+ ///
+ /// Platforms, like floors, that can be used for placing objects
+ /// requiring a horizontal surface.
+ ///
+ Platform = 1 << 4
+ }
+}
diff --git a/Runtime/Definitions/SpatialAwarenessSurfaceTypes.cs.meta b/Runtime/Definitions/SpatialAwarenessSurfaceTypes.cs.meta
new file mode 100644
index 0000000..43d9bf5
--- /dev/null
+++ b/Runtime/Definitions/SpatialAwarenessSurfaceTypes.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d88eef87bbe24202858d14941aa66c33
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialMeshDisplayOptions.cs b/Runtime/Definitions/SpatialMeshDisplayOptions.cs
new file mode 100644
index 0000000..f3d3bba
--- /dev/null
+++ b/Runtime/Definitions/SpatialMeshDisplayOptions.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+namespace RealityToolkit.SpatialAwareness.Definitions
+{
+ ///
+ /// Options for how the spatial mesh is to be displayed by the spatial awareness system.
+ ///
+ public enum SpatialMeshDisplayOptions
+ {
+ ///
+ /// Disable the spatial mesh renderer and collider.
+ ///
+ None = 0,
+
+ ///
+ /// Display the spatial mesh using the configured material.
+ ///
+ Visible,
+
+ ///
+ /// Display the spatial mesh using the configured occlusion material.
+ ///
+ Occlusion,
+
+ ///
+ /// Only enable the collider for the spatial mesh object.
+ ///
+ Collision,
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Definitions/SpatialMeshDisplayOptions.cs.meta b/Runtime/Definitions/SpatialMeshDisplayOptions.cs.meta
new file mode 100644
index 0000000..0f5dac2
--- /dev/null
+++ b/Runtime/Definitions/SpatialMeshDisplayOptions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f836f941d69342bdac7acaba39587ecd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialMeshObject.cs b/Runtime/Definitions/SpatialMeshObject.cs
new file mode 100644
index 0000000..6f38ac6
--- /dev/null
+++ b/Runtime/Definitions/SpatialMeshObject.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Definitions
+{
+ ///
+ /// A Spatial Mesh Object is the Spatial Awareness System's representation of a spatial object with mesh information.
+ ///
+ public struct SpatialMeshObject
+ {
+ ///
+ /// Constructor.
+ ///
+ ///
+ ///
+ public SpatialMeshObject(Guid id, GameObject gameObject) : this()
+ {
+ Id = id;
+ GameObject = gameObject;
+ LastUpdated = DateTimeOffset.MinValue;
+ }
+
+ ///
+ /// The id of the spatial mesh object.
+ ///
+ public Guid Id { get; internal set; }
+
+ private GameObject gameObject;
+
+ ///
+ /// The reference of the Spatial Mesh Object.
+ ///
+ public GameObject GameObject
+ {
+ get => gameObject;
+ internal set
+ {
+ gameObject = value;
+
+ Renderer = gameObject.GetComponent();
+ Filter = gameObject.GetComponent();
+ Collider = gameObject.GetComponent();
+ }
+ }
+
+ public Mesh Mesh
+ {
+ get => Filter.sharedMesh;
+ internal set
+ {
+ // Reset the surface mesh collider to fit the updated mesh.
+ // Unity tribal knowledge indicates that to change the mesh assigned to a
+ // mesh collider and mesh filter, the mesh must first be set to null. Presumably there
+ // is a side effect in the setter when setting the shared mesh to null.
+ Filter.sharedMesh = null;
+ Filter.sharedMesh = value;
+ Collider.sharedMesh = null;
+ Collider.sharedMesh = Filter.sharedMesh;
+ }
+ }
+
+ ///
+ /// The reference for the Spatial Mesh Object.
+ ///
+ public MeshRenderer Renderer { get; private set; }
+
+ ///
+ /// The reference for the Spatial Mesh Object.
+ ///
+ public MeshFilter Filter { get; private set; }
+
+ ///
+ /// The reference for the Spatial Mesh Object.
+ ///
+ public MeshCollider Collider { get; private set; }
+
+ ///
+ /// The last time this object was updated.
+ ///
+ public DateTimeOffset LastUpdated { get; set; }
+ }
+}
diff --git a/Runtime/Definitions/SpatialMeshObject.cs.meta b/Runtime/Definitions/SpatialMeshObject.cs.meta
new file mode 100644
index 0000000..19b6cef
--- /dev/null
+++ b/Runtime/Definitions/SpatialMeshObject.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1d5b5d326f2f4248844d855ef4d8beda
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialObserverStatus.cs b/Runtime/Definitions/SpatialObserverStatus.cs
new file mode 100644
index 0000000..bb94e74
--- /dev/null
+++ b/Runtime/Definitions/SpatialObserverStatus.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+namespace RealityToolkit.SpatialAwareness.Definitions
+{
+ ///
+ /// Enumerates possible changes that may happen to a spatial observers
+ /// by the .
+ ///
+ public enum SpatialObserverStatus
+ {
+ ///
+ /// The spatial object was removed. This may happen with objects move outside of the spatial observer bounds,
+ /// when an environment changes in between spatial meshing updates, or when when the spatial observer was refined.
+ ///
+ Removed = 0,
+ ///
+ /// The spatial object was initially recognized and added to the list of tracked spatial objects.
+ ///
+ Added,
+ ///
+ /// The spatial object was already being observed but got updated.
+ ///
+ Updated
+ }
+}
diff --git a/Runtime/Definitions/SpatialObserverStatus.cs.meta b/Runtime/Definitions/SpatialObserverStatus.cs.meta
new file mode 100644
index 0000000..1e8de2a
--- /dev/null
+++ b/Runtime/Definitions/SpatialObserverStatus.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cee64cf971907e840b5d9a13d9c21c3a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialObservers.meta b/Runtime/Definitions/SpatialObservers.meta
new file mode 100644
index 0000000..cea4f1c
--- /dev/null
+++ b/Runtime/Definitions/SpatialObservers.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4481d2e2371442c8a2d9be891960b531
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialObservers/BaseSpatialMeshObserverProfile.cs b/Runtime/Definitions/SpatialObservers/BaseSpatialMeshObserverProfile.cs
new file mode 100644
index 0000000..b43efc4
--- /dev/null
+++ b/Runtime/Definitions/SpatialObservers/BaseSpatialMeshObserverProfile.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework;
+using RealityCollective.ServiceFramework.Attributes;
+using RealityCollective.ServiceFramework.Definitions.Utilities;
+using RealityToolkit.SpatialAwareness.Definitions;
+using UnityEngine;
+
+namespace RealityToolkit.Definitions.SpatialObservers
+{
+ public class BaseSpatialMeshObserverProfile : BaseSpatialObserverProfile
+ {
+ [SerializeField]
+ [Tooltip("Level of detail for the mesh")]
+ private SpatialAwarenessMeshLevelOfDetail meshLevelOfDetail = SpatialAwarenessMeshLevelOfDetail.Low;
+
+ ///
+ /// The desired Unity Physics Layer on which to set the spatial mesh.
+ ///
+ public SpatialAwarenessMeshLevelOfDetail MeshLevelOfDetail => meshLevelOfDetail;
+
+ [SerializeField]
+ [Tooltip("Should normals be recalculated when a mesh is added or updated?")]
+ private bool meshRecalculateNormals = true;
+
+ ///
+ /// Indicates if the spatial awareness system to generate normal for the returned meshes
+ /// as some platforms may not support returning normal along with the spatial mesh.
+ ///
+ public bool MeshRecalculateNormals => meshRecalculateNormals;
+
+ [SerializeField]
+ [Tooltip("Material to use when displaying meshes")]
+ private Material meshVisibleMaterial = null;
+
+ ///
+ /// The material to be used when automatically displaying spatial meshes.
+ ///
+ public Material MeshVisibleMaterial => meshVisibleMaterial;
+
+ [SerializeField]
+ [Tooltip("Material to use when spatial meshes should occlude other objects")]
+ private Material meshOcclusionMaterial = null;
+
+ ///
+ /// The material to be used when spatial meshes should occlude other objects.
+ ///
+ public Material MeshOcclusionMaterial => meshOcclusionMaterial;
+
+ [SerializeField]
+ [Tooltip("Additional components to add to the generated spatial mesh GameObject")]
+ [SystemType(typeof(Component), TypeGrouping.ByAddComponentMenu)]
+ private SystemType[] additionalComponents = new SystemType[0];
+
+ ///
+ /// Additional s to add to the generated spatial mesh s
+ ///
+ public SystemType[] AdditionalComponents => additionalComponents;
+
+ [SerializeField]
+ [Tooltip("Prefab to use for Object Pool instead of generated object.")]
+ private GameObject meshObjectPrefab = null;
+
+ ///
+ /// Prefab to use for Object Pool instead of generated mesh object.
+ ///
+ public GameObject MeshObjectPrefab => meshObjectPrefab;
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Definitions/SpatialObservers/BaseSpatialMeshObserverProfile.cs.meta b/Runtime/Definitions/SpatialObservers/BaseSpatialMeshObserverProfile.cs.meta
new file mode 100644
index 0000000..2bef718
--- /dev/null
+++ b/Runtime/Definitions/SpatialObservers/BaseSpatialMeshObserverProfile.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 34e54930903b41a9bdcbe94df832101b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 9275054f8248b2f408eb427eaa609a58, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialObservers/BaseSpatialObserverProfile.cs b/Runtime/Definitions/SpatialObservers/BaseSpatialObserverProfile.cs
new file mode 100644
index 0000000..c5670a3
--- /dev/null
+++ b/Runtime/Definitions/SpatialObservers/BaseSpatialObserverProfile.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Definitions;
+using RealityCollective.ServiceFramework.Definitions.Utilities;
+using RealityCollective.Utilities.Attributes;
+using UnityEngine;
+
+namespace RealityToolkit.Definitions.SpatialObservers
+{
+ // No Scriptable Object Menu constructor attributes here, as this class is meant to be inherited.
+
+ ///
+ /// Base Mixed Reality Observer Profile.
+ ///
+ public abstract class BaseSpatialObserverProfile : BaseProfile
+ {
+ [SerializeField]
+ [Tooltip("How should the spatial awareness observer behave at startup?")]
+ private AutoStartBehavior startupBehavior = AutoStartBehavior.AutoStart;
+
+ ///
+ /// Indicates if the developer intends for the spatial awareness observer start immediately or wait for manual startup.
+ ///
+ public AutoStartBehavior StartupBehavior => startupBehavior;
+
+ [SerializeField]
+ [Tooltip("The dimensions of the spatial observer volume, in meters.")]
+ private Vector3 observationExtents = Vector3.one * 3;
+
+ ///
+ /// The size of the volume, in meters per axis, from which individual observations will be made.
+ ///
+ public Vector3 ObservationExtents => observationExtents;
+
+ [SerializeField]
+ [Tooltip("Should the spatial observer remain in a fixed location?")]
+ private bool isStationaryObserver = false;
+
+ ///
+ /// The size of the volume, in meters per axis, from which individual observations will be made.
+ ///
+ public bool IsStationaryObserver => isStationaryObserver;
+
+ [SerializeField]
+ [Tooltip("How often, in seconds, should the spatial observer update?")]
+ private float updateInterval = 3.5f;
+
+ ///
+ /// The frequency, in seconds, at which the spatial observer updates.
+ ///
+ public float UpdateInterval => updateInterval;
+
+ [PhysicsLayer]
+ [SerializeField]
+ [Tooltip("Which physics layer should this spatial observer use to create it's objects?")]
+ private int physicsLayer = -1;
+
+ ///
+ /// The physics layer that the spatial observer will use for it's spatial objects.
+ ///
+ public int PhysicsLayer => physicsLayer;
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Definitions/SpatialObservers/BaseSpatialObserverProfile.cs.meta b/Runtime/Definitions/SpatialObservers/BaseSpatialObserverProfile.cs.meta
new file mode 100644
index 0000000..699e643
--- /dev/null
+++ b/Runtime/Definitions/SpatialObservers/BaseSpatialObserverProfile.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 048c415ed81a45218d1dbbd5a034fff7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 9275054f8248b2f408eb427eaa609a58, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Definitions/SpatialObservers/BaseSurfaceObserverProfile.cs b/Runtime/Definitions/SpatialObservers/BaseSurfaceObserverProfile.cs
new file mode 100644
index 0000000..0db0c24
--- /dev/null
+++ b/Runtime/Definitions/SpatialObservers/BaseSurfaceObserverProfile.cs
@@ -0,0 +1,96 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using UnityEngine;
+
+namespace RealityToolkit.Definitions.SpatialObservers
+{
+ // No Scriptable Object Menu constructor attributes here, as this class is meant to be inherited.
+
+ ///
+ /// The base surface observer profile.
+ ///
+ public class BaseSurfaceObserverProfile : BaseSpatialObserverProfile
+ {
+ [SerializeField]
+ [Tooltip("The minimum area, in square meters, of the planar surfaces")]
+ private float surfaceFindingMinimumArea = 0.025f;
+
+ ///
+ /// The minimum area, in square meters, of the planar surfaces.
+ ///
+ public float SurfaceFindingMinimumArea => surfaceFindingMinimumArea;
+
+ [SerializeField]
+ [Tooltip("Automatically display floor surfaces?")]
+ private bool displayFloorSurfaces = false;
+
+ ///
+ /// Indicates if the surface finding subsystem is to automatically display floor surfaces within the application.
+ ///
+ public bool DisplayFloorSurfaces => displayFloorSurfaces;
+
+ [SerializeField]
+ [Tooltip("Material to use when displaying floor surfaces")]
+ private Material floorSurfaceMaterial = null;
+
+ ///
+ /// The material to be used when automatically displaying floor surfaces.
+ ///
+ public Material FloorSurfaceMaterial => floorSurfaceMaterial;
+
+ [SerializeField]
+ [Tooltip("Automatically display ceiling surfaces?")]
+ private bool displayCeilingSurfaces = false;
+
+ ///
+ /// Indicates if the surface finding subsystem is to automatically display ceiling surfaces within the application.
+ ///
+ public bool DisplayCeilingSurface => displayCeilingSurfaces;
+
+ [SerializeField]
+ [Tooltip("Material to use when displaying ceiling surfaces")]
+ private Material ceilingSurfaceMaterial = null;
+
+ ///
+ /// The material to be used when automatically displaying ceiling surfaces.
+ ///
+ public Material CeilingSurfaceMaterial => ceilingSurfaceMaterial;
+
+ [SerializeField]
+ [Tooltip("Automatically display wall surfaces?")]
+ private bool displayWallSurfaces = false;
+
+ ///
+ /// Indicates if the surface finding subsystem is to automatically display wall surfaces within the application.
+ ///
+ public bool DisplayWallSurface => displayWallSurfaces;
+
+ [SerializeField]
+ [Tooltip("Material to use when displaying wall surfaces")]
+ private Material wallSurfaceMaterial = null;
+
+ ///
+ /// The material to be used when automatically displaying wall surfaces.
+ ///
+ public Material WallSurfaceMaterial => wallSurfaceMaterial;
+
+ [SerializeField]
+ [Tooltip("Automatically display platform surfaces?")]
+ private bool displayPlatformSurfaces = false;
+
+ ///
+ /// Indicates if the surface finding subsystem is to automatically display platform surfaces within the application.
+ ///
+ public bool DisplayPlatformSurfaces => displayPlatformSurfaces;
+
+ [SerializeField]
+ [Tooltip("Material to use when displaying platform surfaces")]
+ private Material platformSurfaceMaterial = null;
+
+ ///
+ /// The material to be used when automatically displaying platform surfaces.
+ ///
+ public Material PlatformSurfaceMaterial => platformSurfaceMaterial;
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Definitions/SpatialObservers/BaseSurfaceObserverProfile.cs.meta b/Runtime/Definitions/SpatialObservers/BaseSurfaceObserverProfile.cs.meta
new file mode 100644
index 0000000..bd888f9
--- /dev/null
+++ b/Runtime/Definitions/SpatialObservers/BaseSurfaceObserverProfile.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 81654571a71446ab9301d87206d6c0c5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 9275054f8248b2f408eb427eaa609a58, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/ISpatialAwarenessService.cs b/Runtime/ISpatialAwarenessService.cs
new file mode 100644
index 0000000..8462187
--- /dev/null
+++ b/Runtime/ISpatialAwarenessService.cs
@@ -0,0 +1,158 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Interfaces;
+using RealityToolkit.SpatialAwareness.Definitions;
+using RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers;
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness
+{
+ ///
+ /// The interface definition for Spatial Awareness features in the Reality Toolkit.
+ ///
+ public interface ISpatialAwarenessService : IEventService, IRealityToolkitService
+ {
+ ///
+ /// Parent which will encapsulate all of the spatial awareness system created scene objects.
+ ///
+ GameObject SpatialAwarenessRootParent { get; }
+
+ ///
+ /// Parent which will encapsulate all of the system created s.
+ ///
+ GameObject SpatialMeshesParent { get; }
+
+ ///
+ /// Parent which will encapsulate all of the system created mesh objects.
+ ///
+ GameObject SurfacesParent { get; }
+
+ ///
+ /// Gets or sets the provided mesh on all the
+ ///
+ ///
+ /// Is is possible to override any previously set display options on any specific observers.
+ ///
+ SpatialMeshDisplayOptions SpatialMeshVisibility { get; set; }
+
+ #region Observers Utilities
+
+ ///
+ /// Indicates the current running state of the spatial awareness observer.
+ ///
+ bool IsObserverRunning(ISpatialAwarenessServiceModule observer);
+
+ ///
+ /// Generates a new unique observer id.
+ ///
+ /// All s are required to call this method in their initialization.
+ /// a new unique Id for the observer.
+ uint GenerateNewObserverId();
+
+ ///
+ /// Starts / restarts the spatial observer.
+ ///
+ /// This will cause spatial awareness events to resume.
+ void StartObserver(ISpatialAwarenessServiceModule observer);
+
+ ///
+ /// Stops / pauses the spatial observer.
+ ///
+ /// This will cause spatial awareness events to be suspended until ResumeObserver is called.
+ void SuspendObserver(ISpatialAwarenessServiceModule observer);
+
+ ///
+ /// List of the spatial observers as detected by the spatial awareness system.
+ ///
+ HashSet DetectedSpatialObservers { get; }
+
+ #endregion Observer Utilities
+
+ #region Observer Events
+
+ ///
+ /// Raise the event that a has been detected.
+ ///
+ void RaiseSpatialAwarenessObserverDetected(ISpatialAwarenessServiceModule observer);
+
+ ///
+ /// Raise the event that a has been lost.
+ ///
+ void RaiseSpatialAwarenessObserverLost(ISpatialAwarenessServiceModule observer);
+
+ #endregion Observer Events
+
+ #region Mesh Events
+
+ ///
+ /// The spatial awareness system will call the method to indicate a mesh has been added.
+ ///
+ ///
+ /// The mesh .
+ ///
+ /// This method is to be called by implementations of the interface, and not by application code.
+ ///
+ void RaiseMeshAdded(ISpatialMeshObserver observer, SpatialMeshObject meshObject);
+
+ ///
+ /// The spatial awareness system will call the method to indicate an existing mesh has been updated.
+ ///
+ ///
+ /// The mesh .
+ ///
+ /// This method is to be called by implementations of the interface, and not by application code.
+ ///
+ void RaiseMeshUpdated(ISpatialMeshObserver observer, SpatialMeshObject meshObject);
+
+ ///
+ /// The spatial awareness system will call the method to indicate an existing mesh has been removed.
+ ///
+ ///
+ /// The mesh .
+ ///
+ /// This method is to be called by implementations of the interface, and not by application code.
+ ///
+ void RaiseMeshRemoved(ISpatialMeshObserver observer, SpatialMeshObject meshObject);
+
+ #endregion Mesh Events
+
+ #region Surface Finding Events
+
+ ///
+ /// The spatial awareness system will call the method to indicate a planar surface has been added.
+ ///
+ ///
+ /// Value identifying the surface.
+ /// The surface .
+ ///
+ /// This method is to be called by implementations of the interface, and not by application code.
+ ///
+ void RaiseSurfaceAdded(ISpatialSurfaceObserver observer, Guid surfaceId, GameObject surfaceObject);
+
+ ///
+ /// The spatial awareness system will call the method to indicate an existing planar surface has been updated.
+ ///
+ ///
+ /// Value identifying the surface.
+ /// The surface .
+ ///
+ /// This method is to be called by implementations of the interface, and not by application code.
+ ///
+ void RaiseSurfaceUpdated(ISpatialSurfaceObserver observer, Guid surfaceId, GameObject surfaceObject);
+
+ ///
+ /// The spatial awareness system will call the method to indicate an existing planar surface has been removed.
+ ///
+ ///
+ /// Value identifying the surface.
+ ///
+ /// This method is to be called by implementations of the interface, and not by application code.
+ ///
+ void RaiseSurfaceRemoved(ISpatialSurfaceObserver observer, Guid surfaceId);
+
+ #endregion Surface Finding Events
+ }
+}
diff --git a/Runtime/ISpatialAwarenessService.cs.meta b/Runtime/ISpatialAwarenessService.cs.meta
new file mode 100644
index 0000000..bb3967a
--- /dev/null
+++ b/Runtime/ISpatialAwarenessService.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 681b083370694bf1a10499ad8f70ea0c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Interfaces.meta b/Runtime/Interfaces.meta
new file mode 100644
index 0000000..f804a91
--- /dev/null
+++ b/Runtime/Interfaces.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4a9c8b770d6ac2c44acc9750d2dc2528
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Interfaces/Handlers.meta b/Runtime/Interfaces/Handlers.meta
new file mode 100644
index 0000000..c448681
--- /dev/null
+++ b/Runtime/Interfaces/Handlers.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d33afc1bc61941a79b6f92d487ead097
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Interfaces/Handlers/ISpatialAwarenessMeshHandler.cs b/Runtime/Interfaces/Handlers/ISpatialAwarenessMeshHandler.cs
new file mode 100644
index 0000000..ee6b152
--- /dev/null
+++ b/Runtime/Interfaces/Handlers/ISpatialAwarenessMeshHandler.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using UnityEngine.EventSystems;
+
+namespace RealityToolkit.SpatialAwareness.Interfaces.Handlers
+{
+ ///
+ /// The event handler for all Spatial Awareness Mesh Events.
+ ///
+ public interface ISpatialAwarenessMeshHandler : IEventSystemHandler
+ {
+ ///
+ /// Called when the spatial awareness mesh subsystem adds a mesh.
+ ///
+ /// Data describing the event.
+ void OnMeshAdded(SpatialAwarenessEventData eventData);
+
+ ///
+ /// Called when the spatial awareness mesh subsystem updates an existing mesh.
+ ///
+ /// Data describing the event.
+ void OnMeshUpdated(SpatialAwarenessEventData eventData);
+
+ ///
+ /// Called when the spatial awareness mesh subsystem removes an existing mesh.
+ ///
+ /// Data describing the event.
+ void OnMeshRemoved(SpatialAwarenessEventData eventData);
+ }
+}
diff --git a/Runtime/Interfaces/Handlers/ISpatialAwarenessMeshHandler.cs.meta b/Runtime/Interfaces/Handlers/ISpatialAwarenessMeshHandler.cs.meta
new file mode 100644
index 0000000..a309949
--- /dev/null
+++ b/Runtime/Interfaces/Handlers/ISpatialAwarenessMeshHandler.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 691d0f6b1ffb4dd8945295ce02e9cb9f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Interfaces/Handlers/ISpatialAwarenessSurfaceFindingHandler.cs b/Runtime/Interfaces/Handlers/ISpatialAwarenessSurfaceFindingHandler.cs
new file mode 100644
index 0000000..a6879c7
--- /dev/null
+++ b/Runtime/Interfaces/Handlers/ISpatialAwarenessSurfaceFindingHandler.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using UnityEngine.EventSystems;
+
+namespace RealityToolkit.SpatialAwareness.Interfaces.Handlers
+{
+ ///
+ /// The event handler for all Spatial Awareness Surface Finding Events.
+ ///
+ ///
+ public interface ISpatialAwarenessSurfaceFindingHandler : IEventSystemHandler
+ {
+ ///
+ /// Called when the spatial awareness surface finding subsystem adds a new planar surface.
+ ///
+ /// Data describing the event.
+ void OnSurfaceAdded(SpatialAwarenessEventData eventData);
+
+ ///
+ /// Called when the spatial awareness surface finding subsystem updates an existing planar surface.
+ ///
+ /// Data describing the event.
+ void OnSurfaceUpdated(SpatialAwarenessEventData eventData);
+
+ ///
+ /// Called when the spatial awareness surface finding subsystem removes an existing planar surface.
+ ///
+ /// Data describing the event.
+ void OnSurfaceRemoved(SpatialAwarenessEventData eventData);
+ }
+}
diff --git a/Runtime/Interfaces/Handlers/ISpatialAwarenessSurfaceFindingHandler.cs.meta b/Runtime/Interfaces/Handlers/ISpatialAwarenessSurfaceFindingHandler.cs.meta
new file mode 100644
index 0000000..d43f3f4
--- /dev/null
+++ b/Runtime/Interfaces/Handlers/ISpatialAwarenessSurfaceFindingHandler.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 91d89d7fb06241279c8aff4a03f7b099
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Interfaces/SpatialObservers.meta b/Runtime/Interfaces/SpatialObservers.meta
new file mode 100644
index 0000000..fa5e00c
--- /dev/null
+++ b/Runtime/Interfaces/SpatialObservers.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7593af4ee4bd4171a4f1d28963fd23bc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Interfaces/SpatialObservers/ISpatialAwarenessServiceModule.cs b/Runtime/Interfaces/SpatialObservers/ISpatialAwarenessServiceModule.cs
new file mode 100644
index 0000000..d60d484
--- /dev/null
+++ b/Runtime/Interfaces/SpatialObservers/ISpatialAwarenessServiceModule.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Definitions.Utilities;
+using RealityToolkit.Interfaces.Events;
+
+namespace RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers
+{
+ ///
+ /// The Mixed Reality Spatial Observer service module contract.
+ ///
+ public interface ISpatialAwarenessServiceModule : IRealityToolkitServiceModule, IEventSource
+ {
+ ///
+ /// The startup behavior of the hardware resource.
+ ///
+ AutoStartBehavior StartupBehavior { get; }
+
+ ///
+ /// Gets or sets the frequency, in seconds, at which the spatial observer updates.
+ ///
+ float UpdateInterval { get; set; }
+
+ ///
+ /// The global physics layer to use when creating spatial objects for this observer.
+ ///
+ ///
+ /// Some implementations may override this.
+ ///
+ int PhysicsLayer { get; set; }
+
+ ///
+ /// Is the observer running (actively accumulating spatial data)?
+ ///
+ bool IsRunning { get; }
+
+ ///
+ /// Start the observer.
+ ///
+ void StartObserving();
+
+ ///
+ /// Stop the observer.
+ ///
+ void StopObserving();
+ }
+}
diff --git a/Runtime/Interfaces/SpatialObservers/ISpatialAwarenessServiceModule.cs.meta b/Runtime/Interfaces/SpatialObservers/ISpatialAwarenessServiceModule.cs.meta
new file mode 100644
index 0000000..2540e79
--- /dev/null
+++ b/Runtime/Interfaces/SpatialObservers/ISpatialAwarenessServiceModule.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: be5b660fb0bc4c6a9384d953c769d2ec
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Interfaces/SpatialObservers/ISpatialMeshObserver.cs b/Runtime/Interfaces/SpatialObservers/ISpatialMeshObserver.cs
new file mode 100644
index 0000000..eba8d74
--- /dev/null
+++ b/Runtime/Interfaces/SpatialObservers/ISpatialMeshObserver.cs
@@ -0,0 +1,96 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityToolkit.SpatialAwareness.Definitions;
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers
+{
+ ///
+ /// The interface contract for Mixed Reality spatial observers.
+ ///
+ public interface ISpatialMeshObserver : ISpatialAwarenessServiceModule
+ {
+ ///
+ /// Gets or sets the level of detail, as a value, for the returned spatial mesh.
+ ///
+ SpatialAwarenessMeshLevelOfDetail MeshLevelOfDetail { get; }
+
+ ///
+ /// Gets or sets a value indicating if the spatial awareness system to generate normal for the returned meshes
+ /// as some platforms may not support returning normal along with the spatial mesh.
+ ///
+ bool MeshRecalculateNormals { get; }
+
+ ///
+ /// Gets or sets a value indicating how the mesh subsystem is to display surface meshes within the application.
+ ///
+ ///
+ /// Applications that wish to process the es should set this value to None.
+ ///
+ SpatialMeshDisplayOptions MeshDisplayOption { get; set; }
+
+ ///
+ /// Gets or sets the to be used when displaying s.
+ ///
+ Material MeshVisibleMaterial { get; }
+
+ ///
+ /// Gets or sets the to be used when s should occlude other objects.
+ ///
+ Material MeshOcclusionMaterial { get; }
+
+ ///
+ /// Gets or sets the size of the volume, in meters per axis, from which individual observations will be made.
+ ///
+ Vector3 ObservationExtents { get; }
+
+ ///
+ /// Should the observer remain stationary in the scene?
+ ///
+ ///
+ /// Set IsStationaryObserver set to false, to move the volume with the user.
+ /// If set to true, the origin will be 0,0,0 or the last known location.
+ ///
+ bool IsStationaryObserver { get; }
+
+ ///
+ /// Gets or sets the origin of the observer.
+ ///
+ ///
+ /// Moving the observer origin allows the spatial awareness system to locate and discard meshes as the user
+ /// navigates the environment.
+ ///
+ Vector3 ObserverOrigin { get; }
+
+ ///
+ /// Gets for sets the rotation of the observer
+ ///
+ Quaternion ObserverOrientation { get; }
+
+ ///
+ /// The collection of mesh s that have been observed.
+ ///
+ IReadOnlyDictionary SpatialMeshObjects { get; }
+
+ ///
+ /// Forwards mesh added event to the .
+ ///
+ /// The data.
+ void RaiseMeshAdded(SpatialMeshObject spatialMeshObject);
+
+ ///
+ /// Forwards mesh updated event to the .
+ ///
+ /// The data.
+ void RaiseMeshUpdated(SpatialMeshObject spatialMeshObject);
+
+ ///
+ /// Forwards mesh removed event to the .
+ ///
+ /// The data.
+ void RaiseMeshRemoved(SpatialMeshObject spatialMeshObject);
+ }
+}
diff --git a/Runtime/Interfaces/SpatialObservers/ISpatialMeshObserver.cs.meta b/Runtime/Interfaces/SpatialObservers/ISpatialMeshObserver.cs.meta
new file mode 100644
index 0000000..cf861de
--- /dev/null
+++ b/Runtime/Interfaces/SpatialObservers/ISpatialMeshObserver.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5f5b7bbcea4c4722a76640bd43948763
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Interfaces/SpatialObservers/ISpatialSurfaceObserver.cs b/Runtime/Interfaces/SpatialObservers/ISpatialSurfaceObserver.cs
new file mode 100644
index 0000000..b8d86ff
--- /dev/null
+++ b/Runtime/Interfaces/SpatialObservers/ISpatialSurfaceObserver.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers
+{
+ public interface ISpatialSurfaceObserver : ISpatialAwarenessServiceModule
+ {
+ ///
+ /// Gets or sets the minimum surface area, in square meters, that must be satisfied before a surface is identified.
+ ///
+ float SurfaceFindingMinimumArea { get; }
+
+ ///
+ /// Gets or sets a value indicating if the surface finding subsystem is to automatically display
+ /// floor surfaces within the application. When enabled, the surfaces will be added to the scene
+ /// and rendered using the configured .
+ ///
+ bool DisplayFloorSurfaces { get; set; }
+
+ ///
+ /// Gets or sets the to be used when rendering floor surfaces.
+ ///
+ Material FloorSurfaceMaterial { get; }
+
+ ///
+ /// Gets or sets a value indicating if the surface finding subsystem is to automatically display
+ /// ceiling surfaces within the application. When enabled, the surfaces will be added to the scene
+ /// and rendered using the configured .
+ ///
+ bool DisplayCeilingSurfaces { get; set; }
+
+ ///
+ /// Gets or sets the to be used when rendering ceiling surfaces.
+ ///
+ Material CeilingSurfaceMaterial { get; }
+
+ ///
+ /// Gets or sets a value indicating if the surface finding subsystem is to automatically display
+ /// wall surfaces within the application. When enabled, the surfaces will be added to the scene
+ /// and rendered using the configured .
+ ///
+ bool DisplayWallSurfaces { get; set; }
+
+ ///
+ /// Gets or sets the to be used when rendering wall surfaces.
+ ///
+ Material WallSurfaceMaterial { get; }
+
+ ///
+ /// Gets or sets a value indicating if the surface finding subsystem is to automatically display
+ /// horizontal platform surfaces within the application. When enabled, the surfaces will be added to the scene
+ /// and rendered using the configured .
+ ///
+ bool DisplayPlatformSurfaces { get; set; }
+
+ ///
+ /// Gets or sets the to be used when rendering horizontal platform surfaces.
+ ///
+ Material PlatformSurfaceMaterial { get; }
+
+ ///
+ /// Gets the collection of s being managed by the spatial awareness surface finding subsystem.
+ ///
+ IReadOnlyDictionary PlanarSurfaces { get; }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Interfaces/SpatialObservers/ISpatialSurfaceObserver.cs.meta b/Runtime/Interfaces/SpatialObservers/ISpatialSurfaceObserver.cs.meta
new file mode 100644
index 0000000..699b715
--- /dev/null
+++ b/Runtime/Interfaces/SpatialObservers/ISpatialSurfaceObserver.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7a2aa939576e4e889bb5105c592d3eec
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Modules.meta b/Runtime/Modules.meta
new file mode 100644
index 0000000..2d957ea
--- /dev/null
+++ b/Runtime/Modules.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f5fa96f46161496a8bdf4527ae7b1320
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Modules/BaseSpatialMeshObserver.cs b/Runtime/Modules/BaseSpatialMeshObserver.cs
new file mode 100644
index 0000000..30cbbb0
--- /dev/null
+++ b/Runtime/Modules/BaseSpatialMeshObserver.cs
@@ -0,0 +1,324 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Services;
+using RealityCollective.Utilities.Async;
+using RealityCollective.Utilities.Extensions;
+using RealityToolkit.Definitions.SpatialObservers;
+using RealityToolkit.SpatialAwareness.Definitions;
+using RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Modules
+{
+ ///
+ /// Base class for spatial awareness observers.
+ ///
+ public abstract class BaseSpatialMeshObserver : BaseSpatialObserverServiceModule, ISpatialMeshObserver
+ {
+ ///
+ protected BaseSpatialMeshObserver(string name, uint priority, BaseSpatialMeshObserverProfile profile, ISpatialAwarenessService parentService)
+ : base(name, priority, profile, parentService)
+ {
+ if (profile.IsNull())
+ {
+ profile = ServiceManager.Instance.TryGetServiceProfile(out var spatialAwarenessSystemProfile)
+ ? spatialAwarenessSystemProfile.GlobalMeshObserverProfile
+ : throw new ArgumentException($"Unable to get a valid {nameof(SpatialAwarenessSystemProfile)}!");
+ }
+
+ if (profile.IsNull())
+ {
+ throw new ArgumentNullException($"Missing a {profile.GetType().Name} profile for {name}");
+ }
+
+ MeshLevelOfDetail = profile.MeshLevelOfDetail;
+ MeshRecalculateNormals = profile.MeshRecalculateNormals;
+ meshDisplayOption = parentService.SpatialMeshVisibility;
+ MeshVisibleMaterial = profile.MeshVisibleMaterial;
+ MeshOcclusionMaterial = profile.MeshOcclusionMaterial;
+ ObservationExtents = profile.ObservationExtents;
+ IsStationaryObserver = profile.IsStationaryObserver;
+ var additionalComponents = profile.AdditionalComponents;
+ meshObjectPrefab = profile.MeshObjectPrefab;
+ spatialMeshObjectPool = new Stack();
+
+ if (additionalComponents != null)
+ {
+ requiredMeshComponents = new Type[additionalComponents.Length + 3];
+ requiredMeshComponents[0] = typeof(MeshFilter);
+ requiredMeshComponents[1] = typeof(MeshRenderer);
+ requiredMeshComponents[2] = typeof(MeshCollider);
+
+ for (int i = 3; i < additionalComponents.Length; i++)
+ {
+ var component = additionalComponents[i - 3].Type;
+ Debug.Assert(component != null);
+ requiredMeshComponents[i] = component;
+ }
+ }
+ else
+ {
+ requiredMeshComponents = new[]
+ {
+ typeof(MeshFilter),
+ typeof(MeshRenderer),
+ typeof(MeshCollider)
+ };
+ }
+ }
+
+ private readonly GameObject meshObjectPrefab;
+
+ ///
+ /// When a mesh is created we will need to create a game object with a minimum
+ /// set of components to contain the mesh. These are the required component types.
+ ///
+ private readonly Type[] requiredMeshComponents;
+
+ #region IService Implementation
+
+ ///
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ if (!Application.isPlaying) { return; }
+
+ for (int i = 0; i < 10; i++)
+ {
+ spatialMeshObjectPool.Push(new SpatialMeshObject(Guid.Empty, CreateBlankSpatialMeshGameObject()));
+ }
+ }
+
+ ///
+ public override void Start()
+ {
+ base.Start();
+
+ if (!Application.isPlaying) { return; }
+
+ // If we've got some spatial meshes and were disabled previously, turn them back on.
+ foreach (var meshObject in spatialMeshObjects.Values)
+ {
+ meshObject.GameObject.SetActive(true);
+ }
+ }
+
+ ///
+ public override void Update()
+ {
+ base.Update();
+
+ if (!Application.isPlaying || !IsRunning) { return; }
+
+ lock (spatialMeshObjectPool)
+ {
+ // if we get low in our object pool, then create a few more.
+ if (spatialMeshObjectPool.Count < 5)
+ {
+ spatialMeshObjectPool.Push(new SpatialMeshObject(Guid.Empty, CreateBlankSpatialMeshGameObject()));
+ }
+ }
+ }
+
+ ///
+ public override void Destroy()
+ {
+ base.Destroy();
+
+ if (!Application.isPlaying) { return; }
+
+ spatialMeshObjects.Clear();
+
+ lock (spatialMeshObjectPool)
+ {
+ while (spatialMeshObjectPool.Count > 0)
+ {
+ var meshObject = spatialMeshObjectPool.Pop();
+ meshObject.GameObject.Destroy();
+ }
+
+ Debug.Assert(spatialMeshObjectPool.Count == 0);
+ }
+ }
+
+ #endregion IService Implementation
+
+ #region ISpatialMeshObserver Implementation
+
+ ///
+ public SpatialAwarenessMeshLevelOfDetail MeshLevelOfDetail { get; set; }
+
+ ///
+ public bool MeshRecalculateNormals { get; }
+
+ private SpatialMeshDisplayOptions meshDisplayOption;
+
+ ///
+ public SpatialMeshDisplayOptions MeshDisplayOption
+ {
+ get => meshDisplayOption;
+ set
+ {
+ meshDisplayOption = value;
+
+ if (meshDisplayOption != SpatialMeshDisplayOptions.None)
+ {
+ foreach (var spatialMeshObject in SpatialMeshObjects)
+ {
+ spatialMeshObject.Value.Collider.enabled = true;
+ spatialMeshObject.Value.Renderer.enabled = meshDisplayOption == SpatialMeshDisplayOptions.Visible ||
+ meshDisplayOption == SpatialMeshDisplayOptions.Occlusion;
+ spatialMeshObject.Value.Renderer.sharedMaterial = meshDisplayOption == SpatialMeshDisplayOptions.Visible
+ ? MeshVisibleMaterial
+ : MeshOcclusionMaterial;
+ }
+ }
+ else
+ {
+ foreach (var spatialMeshObject in SpatialMeshObjects)
+ {
+ spatialMeshObject.Value.Renderer.enabled = false;
+ spatialMeshObject.Value.Collider.enabled = false;
+ }
+ }
+ }
+ }
+
+ ///
+ public Material MeshVisibleMaterial { get; }
+
+ ///
+ public Material MeshOcclusionMaterial { get; }
+
+ ///
+ public Vector3 ObservationExtents { get; }
+
+ ///
+ public bool IsStationaryObserver { get; }
+
+ ///
+ public Vector3 ObserverOrigin { get; protected set; } = Vector3.zero;
+
+ ///
+ public Quaternion ObserverOrientation { get; protected set; } = Quaternion.identity;
+
+ private readonly Dictionary spatialMeshObjects = new Dictionary();
+
+ ///
+ ///
+ /// This method returns a copy of the collection maintained by the observer so that application
+ /// code can iterate through the collection without concern for changes to the backing data.
+ ///
+ public IReadOnlyDictionary SpatialMeshObjects => new Dictionary(spatialMeshObjects);
+
+ private readonly Stack spatialMeshObjectPool;
+
+ ///
+ public virtual void RaiseMeshAdded(SpatialMeshObject spatialMeshObject)
+ {
+ SpatialAwarenessSystem.RaiseMeshAdded(this, spatialMeshObject);
+ }
+
+ ///
+ public virtual void RaiseMeshUpdated(SpatialMeshObject spatialMeshObject)
+ {
+ SpatialAwarenessSystem.RaiseMeshUpdated(this, spatialMeshObject);
+ }
+
+ ///
+ public virtual void RaiseMeshRemoved(SpatialMeshObject spatialMeshObject)
+ {
+ if (spatialMeshObjects.TryGetValue(spatialMeshObject.Id, out var spatialMesh))
+ {
+ spatialMeshObjects.Remove(spatialMesh.Id);
+
+ // Raise mesh removed if it was prev enabled.
+ // spatialMesh.GameObject's only get enabled when successfully cooked.
+ // If it's disabled then likely the mesh was removed before cooking completed.
+ if (spatialMesh.GameObject.activeInHierarchy)
+ {
+ SpatialAwarenessSystem.RaiseMeshRemoved(this, spatialMeshObject);
+ }
+
+ spatialMesh.GameObject.SetActive(false);
+ // Recycle this spatial mesh object and add it back to the pool.
+ spatialMesh.GameObject.name = "Reclaimed Spatial Mesh Object";
+ spatialMesh.Mesh = null;
+ spatialMesh.Id = Guid.Empty;
+ spatialMesh.LastUpdated = DateTimeOffset.MinValue;
+
+ lock (spatialMeshObjectPool)
+ {
+ spatialMeshObjectPool.Push(spatialMesh);
+ }
+ }
+ else
+ {
+ Debug.LogError($"{spatialMeshObject.Id} is missing from known spatial objects!");
+ }
+ }
+
+ ///
+ /// Request a from the collection of known spatial objects. If that object doesn't exist take one from our pool.
+ ///
+ /// The id of the .
+ /// A
+ protected async Task RequestSpatialMeshObject(Guid meshId)
+ {
+ if (spatialMeshObjects.TryGetValue(meshId, out var spatialMesh))
+ {
+ return spatialMesh;
+ }
+
+ lock (spatialMeshObjectPool)
+ {
+ if (spatialMeshObjectPool.Count > 0)
+ {
+ spatialMesh = spatialMeshObjectPool.Pop();
+ spatialMesh.Id = meshId;
+ spatialMesh.GameObject.name = $"SpatialMesh_{meshId}";
+ spatialMeshObjects.Add(spatialMesh.Id, spatialMesh);
+ return spatialMesh;
+ }
+ }
+
+ await Awaiters.UnityMainThread;
+ return await RequestSpatialMeshObject(meshId);
+ }
+
+ private GameObject CreateBlankSpatialMeshGameObject()
+ {
+ GameObject newGameObject;
+
+ if (meshObjectPrefab.IsNull())
+ {
+ newGameObject = new GameObject("Blank Spatial Mesh GameObject", requiredMeshComponents)
+ {
+ layer = PhysicsLayer
+ };
+ }
+ else
+ {
+ newGameObject = UnityEngine.Object.Instantiate(meshObjectPrefab);
+
+ for (var i = 0; i < requiredMeshComponents.Length; i++)
+ {
+ newGameObject.EnsureComponent(requiredMeshComponents[i]);
+ }
+
+ newGameObject.layer = PhysicsLayer;
+ }
+
+ newGameObject.transform.SetParent(SpatialAwarenessSystem.SpatialMeshesParent.transform, false);
+ newGameObject.SetActive(false);
+ return newGameObject;
+ }
+
+ #endregion ISpatialMeshObserver Implementation
+ }
+}
diff --git a/Runtime/Modules/BaseSpatialMeshObserver.cs.meta b/Runtime/Modules/BaseSpatialMeshObserver.cs.meta
new file mode 100644
index 0000000..6b6d927
--- /dev/null
+++ b/Runtime/Modules/BaseSpatialMeshObserver.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: faac94d34d98437e94e008e988c0516d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 661c780d86784e0b895a880d2624c8fe, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Modules/BaseSpatialObserverServiceModule.cs b/Runtime/Modules/BaseSpatialObserverServiceModule.cs
new file mode 100644
index 0000000..5c3c4ab
--- /dev/null
+++ b/Runtime/Modules/BaseSpatialObserverServiceModule.cs
@@ -0,0 +1,168 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Definitions.Utilities;
+using RealityCollective.ServiceFramework.Modules;
+using RealityCollective.ServiceFramework.Services;
+using RealityCollective.Utilities.Extensions;
+using RealityToolkit.Definitions.SpatialObservers;
+using RealityToolkit.SpatialAwareness.Definitions;
+using RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers;
+using System;
+using System.Collections;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Modules
+{
+ ///
+ /// Base implementation
+ ///
+ public abstract class BaseSpatialObserverServiceModule : BaseServiceModule, ISpatialAwarenessServiceModule
+ {
+ ///
+ protected BaseSpatialObserverServiceModule(string name, uint priority, BaseSpatialObserverProfile profile, ISpatialAwarenessService parentService)
+ : base(name, priority, profile, parentService)
+ {
+ if (profile.IsNull())
+ {
+ profile = ServiceManager.Instance.TryGetServiceProfile(out var spatialAwarenessSystemProfile)
+ ? spatialAwarenessSystemProfile.GlobalMeshObserverProfile
+ : throw new ArgumentException($"Unable to get a valid {nameof(SpatialAwarenessSystemProfile)}!");
+ }
+
+ if (profile.IsNull())
+ {
+ throw new ArgumentNullException($"Missing a {profile.GetType().Name} profile for {name}");
+ }
+
+ SpatialAwarenessSystem = parentService;
+ SourceId = parentService.GenerateNewObserverId();
+ StartupBehavior = profile.StartupBehavior;
+ UpdateInterval = profile.UpdateInterval;
+ PhysicsLayer = profile.PhysicsLayer;
+ }
+
+ protected readonly ISpatialAwarenessService SpatialAwarenessSystem;
+
+ #region IService Implementation
+
+ ///
+ public override void Start()
+ {
+ base.Start();
+
+ SpatialAwarenessSystem.RaiseSpatialAwarenessObserverDetected(this);
+
+ if (StartupBehavior == AutoStartBehavior.AutoStart)
+ {
+ StartObserving();
+ }
+ }
+
+ ///
+ public override void Destroy()
+ {
+ base.Destroy();
+
+ StopObserving();
+
+ SpatialAwarenessSystem.RaiseSpatialAwarenessObserverLost(this);
+ }
+
+ #endregion IService Implementation
+
+ #region ISpatialObserverDataProvider Implementation
+
+ ///
+ public AutoStartBehavior StartupBehavior { get; }
+
+ ///
+ public float UpdateInterval { get; set; }
+
+ ///
+ public virtual int PhysicsLayer { get; set; }
+
+ ///
+ public bool IsRunning { get; protected set; }
+
+ ///
+ public virtual void StartObserving()
+ {
+ if (!Application.isPlaying) { return; }
+ IsRunning = true;
+ }
+
+ ///
+ public virtual void StopObserving()
+ {
+ if (!Application.isPlaying) { return; }
+ IsRunning = false;
+ }
+
+ #endregion ISpatialObserverDataProvider Implementation
+
+ #region IEventSource Implementation
+
+ ///
+ public string SourceName => Name;
+
+ ///
+ public uint SourceId { get; }
+
+ #endregion IEventSource Implementation
+
+ #region IEquality Implementation
+
+ ///
+ /// Determines if the specified objects are equal.
+ ///
+ ///
+ ///
+ ///
+ public static bool Equals(ISpatialAwarenessServiceModule left, ISpatialAwarenessServiceModule right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ bool IEqualityComparer.Equals(object left, object right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is null) { return false; }
+ if (ReferenceEquals(this, obj)) { return true; }
+ if (obj.GetType() != GetType()) { return false; }
+
+ return Equals((ISpatialAwarenessServiceModule)obj);
+ }
+
+ private bool Equals(ISpatialAwarenessServiceModule other)
+ {
+ return other != null && SourceId == other.SourceId && string.Equals(SourceName, other.SourceName);
+ }
+
+ ///
+ int IEqualityComparer.GetHashCode(object obj)
+ {
+ return obj.GetHashCode();
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = 0;
+ hashCode = (hashCode * 397) ^ (int)SourceId;
+ hashCode = (hashCode * 397) ^ (SourceName != null ? SourceName.GetHashCode() : 0);
+ return hashCode;
+ }
+ }
+
+ #endregion IEquality Implementation
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Modules/BaseSpatialObserverServiceModule.cs.meta b/Runtime/Modules/BaseSpatialObserverServiceModule.cs.meta
new file mode 100644
index 0000000..53b1d63
--- /dev/null
+++ b/Runtime/Modules/BaseSpatialObserverServiceModule.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 762f0edb121b49d0bf13c0831145ab5b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 661c780d86784e0b895a880d2624c8fe, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Modules/BaseSpatialSurfaceObserver.cs b/Runtime/Modules/BaseSpatialSurfaceObserver.cs
new file mode 100644
index 0000000..fcf6d78
--- /dev/null
+++ b/Runtime/Modules/BaseSpatialSurfaceObserver.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Services;
+using RealityCollective.Utilities.Extensions;
+using RealityToolkit.Definitions.SpatialObservers;
+using RealityToolkit.SpatialAwareness.Definitions;
+using RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers;
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace RealityToolkit.SpatialAwareness.Modules
+{
+ ///
+ /// Base implementation.
+ ///
+ public abstract class BaseSpatialSurfaceObserver : BaseSpatialObserverServiceModule, ISpatialSurfaceObserver
+ {
+ ///
+ protected BaseSpatialSurfaceObserver(string name, uint priority, BaseSurfaceObserverProfile profile, ISpatialAwarenessService parentService)
+ : base(name, priority, profile, parentService)
+ {
+ if (profile.IsNull())
+ {
+ profile = ServiceManager.Instance.TryGetServiceProfile(out var spatialAwarenessSystemProfile)
+ ? spatialAwarenessSystemProfile.GlobalSurfaceObserverProfile
+ : throw new ArgumentException($"Unable to get a valid {nameof(SpatialAwarenessSystemProfile)}!");
+ }
+
+ if (profile.IsNull())
+ {
+ throw new ArgumentNullException($"Missing a {profile.GetType().Name} profile for {name}");
+ }
+
+ SurfaceFindingMinimumArea = profile.SurfaceFindingMinimumArea;
+ DisplayFloorSurfaces = profile.DisplayFloorSurfaces;
+ FloorSurfaceMaterial = profile.FloorSurfaceMaterial;
+ DisplayCeilingSurfaces = profile.DisplayCeilingSurface;
+ CeilingSurfaceMaterial = profile.CeilingSurfaceMaterial;
+ DisplayWallSurfaces = profile.DisplayWallSurface;
+ WallSurfaceMaterial = profile.WallSurfaceMaterial;
+ DisplayPlatformSurfaces = profile.DisplayPlatformSurfaces;
+ PlatformSurfaceMaterial = profile.PlatformSurfaceMaterial;
+ }
+
+ ///
+ public float SurfaceFindingMinimumArea { get; }
+
+ ///
+ public bool DisplayFloorSurfaces { get; set; }
+
+ ///
+ public Material FloorSurfaceMaterial { get; }
+
+ ///
+ public bool DisplayCeilingSurfaces { get; set; }
+
+ ///
+ public Material CeilingSurfaceMaterial { get; }
+
+ ///
+ public bool DisplayWallSurfaces { get; set; }
+
+ ///
+ public Material WallSurfaceMaterial { get; }
+
+ ///
+ public bool DisplayPlatformSurfaces { get; set; }
+
+ ///
+ public Material PlatformSurfaceMaterial { get; }
+
+ private readonly Dictionary planarSurfaces = new Dictionary();
+
+ ///
+ public IReadOnlyDictionary PlanarSurfaces => new Dictionary(planarSurfaces);
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Modules/BaseSpatialSurfaceObserver.cs.meta b/Runtime/Modules/BaseSpatialSurfaceObserver.cs.meta
new file mode 100644
index 0000000..36f6e0f
--- /dev/null
+++ b/Runtime/Modules/BaseSpatialSurfaceObserver.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0f2ea5cdb7434c21a6a0c1bdf50df9b8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 661c780d86784e0b895a880d2624c8fe, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/SpatialAwarenessEventData.cs b/Runtime/SpatialAwarenessEventData.cs
new file mode 100644
index 0000000..3bb830d
--- /dev/null
+++ b/Runtime/SpatialAwarenessEventData.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityToolkit.EventDatum;
+using RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers;
+using System;
+using UnityEngine.EventSystems;
+
+namespace RealityToolkit.SpatialAwareness
+{
+ ///
+ /// Data for spatial awareness events.
+ ///
+ public class SpatialAwarenessEventData : GenericBaseEventData
+ {
+ ///
+ /// Identifier of the object associated with this event.
+ ///
+ public Guid Id { get; private set; }
+
+ ///
+ /// The spatial object managed by the spatial awareness system, representing the data in this event.
+ ///
+ public T SpatialObject { get; private set; }
+
+ ///
+ /// Constructor.
+ ///
+ ///
+ public SpatialAwarenessEventData(EventSystem eventSystem) : base(eventSystem) { }
+
+ ///
+ /// Used to initialize/reset the event and populate the data.
+ ///
+ ///
+ ///
+ ///
+ public void Initialize(ISpatialAwarenessServiceModule spatialAwarenessObserver, Guid id, T spatialObject)
+ {
+ BaseInitialize(spatialAwarenessObserver);
+ Id = id;
+ SpatialObject = spatialObject;
+ }
+ }
+}
diff --git a/Runtime/SpatialAwarenessEventData.cs.meta b/Runtime/SpatialAwarenessEventData.cs.meta
new file mode 100644
index 0000000..58d6a16
--- /dev/null
+++ b/Runtime/SpatialAwarenessEventData.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 57f0192ad8324cc2b4a6a7d8569b48da
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/SpatialAwarenessService.cs b/Runtime/SpatialAwarenessService.cs
new file mode 100644
index 0000000..d05c22e
--- /dev/null
+++ b/Runtime/SpatialAwarenessService.cs
@@ -0,0 +1,344 @@
+// Copyright (c) Reality Collective. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using RealityCollective.ServiceFramework.Attributes;
+using RealityCollective.ServiceFramework.Definitions.Platforms;
+using RealityCollective.ServiceFramework.Services;
+using RealityCollective.Utilities.Extensions;
+using RealityToolkit.SpatialAwareness.Definitions;
+using RealityToolkit.SpatialAwareness.Interfaces.Handlers;
+using RealityToolkit.SpatialAwareness.Interfaces.SpatialObservers;
+using RealityToolkit.SpatialAwareness.Modules;
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.EventSystems;
+
+namespace RealityToolkit.SpatialAwareness
+{
+ ///
+ /// Class providing the default implementation of the interface.
+ ///
+ [RuntimePlatform(typeof(AllPlatforms))]
+ [System.Runtime.InteropServices.Guid("05EF9DDC-13C2-47D4-84C5-1C9CB6CC5C1C")]
+ public class SpatialAwarenessService : BaseEventService, ISpatialAwarenessService
+ {
+ ///
+ /// Constructor.
+ ///
+ /// The service display name.
+ /// The service initialization priority.
+ /// The service configuration profile.
+ public SpatialAwarenessService(string name, uint priority, SpatialAwarenessSystemProfile profile)
+ : base(name, priority, profile)
+ {
+ spatialMeshVisibility = profile.MeshDisplayOption;
+ }
+
+ private GameObject spatialAwarenessParent = null;
+
+ ///
+ /// Parent which will encapsulate all of the spatial awareness system created scene objects.
+ ///
+ public GameObject SpatialAwarenessRootParent => spatialAwarenessParent != null ? spatialAwarenessParent : (spatialAwarenessParent = CreateSpatialAwarenessParent);
+
+ ///
+ /// Creates the parent for spatial awareness objects so that the scene hierarchy does not get overly cluttered.
+ ///
+ ///
+ /// The to which spatial awareness created objects will be parented.
+ ///
+ private GameObject CreateSpatialAwarenessParent
+ {
+ get
+ {
+ var spatialAwarenessSystemObject = new GameObject("Spatial Awareness System");
+ var rigTransform = Camera.main.transform.parent;
+ spatialAwarenessSystemObject.transform.SetParent(rigTransform, false);
+ return spatialAwarenessSystemObject;
+ }
+ }
+
+ private GameObject meshParent = null;
+
+ ///
+ public GameObject SpatialMeshesParent => meshParent != null ? meshParent : (meshParent = CreateSecondGenerationParent("Meshes"));
+
+ private GameObject surfaceParent = null;
+
+ ///
+ public GameObject SurfacesParent => surfaceParent != null ? surfaceParent : (surfaceParent = CreateSecondGenerationParent("Surfaces"));
+
+ ///
+ public SpatialMeshDisplayOptions SpatialMeshVisibility
+ {
+ get => spatialMeshVisibility;
+ set
+ {
+ spatialMeshVisibility = value;
+
+ foreach (var observer in DetectedSpatialObservers)
+ {
+ if (observer is BaseSpatialMeshObserver meshObserver)
+ {
+ meshObserver.MeshDisplayOption = spatialMeshVisibility;
+ }
+ }
+ }
+ }
+
+ private SpatialMeshDisplayOptions spatialMeshVisibility;
+
+ ///
+ /// Creates a parent that is a child of the Spatial Awareness System parent so that the scene hierarchy does not get overly cluttered.
+ ///
+ ///
+ /// The to which spatial awareness objects will be parented.
+ ///
+ private GameObject CreateSecondGenerationParent(string name)
+ {
+ var secondGeneration = new GameObject(name);
+ secondGeneration.transform.SetParent(SpatialAwarenessRootParent.transform, false);
+ return secondGeneration;
+ }
+
+ #region ISpatialAwarenessSystem Implementation
+
+ ///
+ public HashSet DetectedSpatialObservers { get; } = new HashSet();
+
+ ///
+ public bool IsObserverRunning(ISpatialAwarenessServiceModule observer)
+ {
+ foreach (var detectedObserver in DetectedSpatialObservers)
+ {
+ if (detectedObserver.SourceId == observer.SourceId)
+ {
+ return observer.IsRunning;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ public uint GenerateNewObserverId()
+ {
+ var newId = (uint)UnityEngine.Random.Range(1, int.MaxValue);
+
+ foreach (var observer in DetectedSpatialObservers)
+ {
+ if (observer.SourceId == newId)
+ {
+ return GenerateNewObserverId();
+ }
+ }
+
+ return newId;
+ }
+
+ ///
+ public void StartObserver(ISpatialAwarenessServiceModule observer)
+ {
+ foreach (var spatialObserver in DetectedSpatialObservers)
+ {
+ if (spatialObserver.SourceId == observer.SourceId)
+ {
+ spatialObserver.StartObserving();
+ break;
+ }
+ }
+ }
+
+ ///
+ public void SuspendObserver(ISpatialAwarenessServiceModule observer)
+ {
+ foreach (var spatialObserver in DetectedSpatialObservers)
+ {
+ if (spatialObserver.SourceId == observer.SourceId)
+ {
+ spatialObserver.StopObserving();
+ break;
+ }
+ }
+ }
+
+ ///
+ public void RaiseSpatialAwarenessObserverDetected(ISpatialAwarenessServiceModule observer)
+ {
+ DetectedSpatialObservers.Add(observer);
+ }
+
+ ///
+ public void RaiseSpatialAwarenessObserverLost(ISpatialAwarenessServiceModule observer)
+ {
+ DetectedSpatialObservers.Remove(observer);
+ }
+
+ #endregion ISpatialAwarenessSystem Implementation
+
+ #region IService Implementation
+
+ private SpatialAwarenessEventData meshEventData = null;
+ private SpatialAwarenessEventData surfaceFindingEventData = null;
+
+ ///
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ if (Application.isPlaying)
+ {
+ var eventSystem = EventSystem.current;
+ meshEventData = new SpatialAwarenessEventData(eventSystem);
+ surfaceFindingEventData = new SpatialAwarenessEventData(eventSystem);
+ }
+ }
+
+ ///
+ public override void Destroy()
+ {
+ base.Destroy();
+
+ if (!Application.isPlaying) { return; }
+
+ if (spatialAwarenessParent != null)
+ {
+ spatialAwarenessParent.transform.DetachChildren();
+ spatialAwarenessParent.Destroy();
+ }
+
+ // Detach the mesh objects (they are to be cleaned up by the observer) and cleanup the parent
+ if (meshParent != null)
+ {
+ meshParent.transform.DetachChildren();
+ meshParent.Destroy();
+ }
+
+ // Detach the surface objects (they are to be cleaned up by the observer) and cleanup the parent
+ if (surfaceParent != null)
+ {
+ surfaceParent.transform.DetachChildren();
+ surfaceParent.Destroy();
+ }
+ }
+
+ #region Mesh Events
+
+ ///
+ public void RaiseMeshAdded(ISpatialMeshObserver observer, SpatialMeshObject spatialMeshObject)
+ {
+ // Parent the mesh object
+ spatialMeshObject.GameObject.transform.parent = SpatialMeshesParent.transform;
+
+ meshEventData.Initialize(observer, spatialMeshObject.Id, spatialMeshObject);
+ HandleEvent(meshEventData, OnMeshAdded);
+ }
+
+ ///
+ /// Event sent whenever a mesh is added.
+ ///
+ private static readonly ExecuteEvents.EventFunction> OnMeshAdded =
+ delegate (ISpatialAwarenessMeshHandler handler, BaseEventData eventData)
+ {
+ var spatialEventData = ExecuteEvents.ValidateEventData>(eventData);
+ handler.OnMeshAdded(spatialEventData);
+ };
+
+ ///
+ public void RaiseMeshUpdated(ISpatialMeshObserver observer, SpatialMeshObject spatialMeshObject)
+ {
+ // Parent the mesh object
+ spatialMeshObject.GameObject.transform.parent = SpatialMeshesParent.transform;
+
+ meshEventData.Initialize(observer, spatialMeshObject.Id, spatialMeshObject);
+ HandleEvent(meshEventData, OnMeshUpdated);
+ }
+
+ ///
+ /// Event sent whenever a mesh is updated.
+ ///
+ private static readonly ExecuteEvents.EventFunction> OnMeshUpdated =
+ delegate (ISpatialAwarenessMeshHandler handler, BaseEventData eventData)
+ {
+ var spatialEventData = ExecuteEvents.ValidateEventData>(eventData);
+ handler.OnMeshUpdated(spatialEventData);
+ };
+
+ ///
+ public void RaiseMeshRemoved(ISpatialMeshObserver observer, SpatialMeshObject spatialMeshObject)
+ {
+ meshEventData.Initialize(observer, spatialMeshObject.Id, spatialMeshObject);
+ HandleEvent(meshEventData, OnMeshRemoved);
+ }
+
+ ///
+ /// Event sent whenever a mesh is discarded.
+ ///
+ private static readonly ExecuteEvents.EventFunction> OnMeshRemoved =
+ delegate (ISpatialAwarenessMeshHandler handler, BaseEventData eventData)
+ {
+ var spatialEventData = ExecuteEvents.ValidateEventData>(eventData);
+ handler.OnMeshRemoved(spatialEventData);
+ };
+
+ #endregion Mesh Events
+
+ #region Surface Finding Events
+
+ ///
+ public void RaiseSurfaceAdded(ISpatialSurfaceObserver observer, Guid surfaceId, GameObject surfaceObject)
+ {
+ surfaceFindingEventData.Initialize(observer, surfaceId, surfaceObject);
+ HandleEvent(surfaceFindingEventData, OnSurfaceAdded);
+ }
+
+ ///
+ /// Event sent whenever a planar surface is added.
+ ///
+ private static readonly ExecuteEvents.EventFunction> OnSurfaceAdded =
+ delegate (ISpatialAwarenessSurfaceFindingHandler handler, BaseEventData eventData)
+ {
+ var spatialEventData = ExecuteEvents.ValidateEventData>(eventData);
+ handler.OnSurfaceAdded(spatialEventData);
+ };
+
+ ///
+ public void RaiseSurfaceUpdated(ISpatialSurfaceObserver observer, Guid surfaceId, GameObject surfaceObject)
+ {
+ surfaceFindingEventData.Initialize(observer, surfaceId, surfaceObject);
+ HandleEvent(surfaceFindingEventData, OnSurfaceUpdated);
+ }
+
+ ///
+ /// Event sent whenever a planar surface is updated.
+ ///
+ private static readonly ExecuteEvents.EventFunction> OnSurfaceUpdated =
+ delegate (ISpatialAwarenessSurfaceFindingHandler handler, BaseEventData eventData)
+ {
+ var spatialEventData = ExecuteEvents.ValidateEventData>(eventData);
+ handler.OnSurfaceUpdated(spatialEventData);
+ };
+
+ ///
+ public void RaiseSurfaceRemoved(ISpatialSurfaceObserver observer, Guid surfaceId)
+ {
+ surfaceFindingEventData.Initialize(observer, surfaceId, null);
+ HandleEvent(surfaceFindingEventData, OnSurfaceRemoved);
+ }
+
+ ///
+ /// Event sent whenever a planar surface is discarded.
+ ///
+ private static readonly ExecuteEvents.EventFunction> OnSurfaceRemoved =
+ delegate (ISpatialAwarenessSurfaceFindingHandler handler, BaseEventData eventData)
+ {
+ var spatialEventData = ExecuteEvents.ValidateEventData>(eventData);
+ handler.OnSurfaceRemoved(spatialEventData);
+ };
+
+ #endregion Surface Finding Events
+
+ #endregion IService Implementation
+ }
+}
diff --git a/Runtime/SpatialAwarenessService.cs.meta b/Runtime/SpatialAwarenessService.cs.meta
new file mode 100644
index 0000000..4b0e524
--- /dev/null
+++ b/Runtime/SpatialAwarenessService.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 95be363c06624d7cb40cb296aa02a5b2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8ac5213854cf4dbabd140decf8df1946, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant: