// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.SpatialAwareness; using Microsoft.MixedReality.Toolkit.Utilities; using Unity.Profiling; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.SpatialObjectMeshObserver { /// /// Spatial awareness mesh observer that provides mesh data from a 3D model imported as a Unity asset. /// [MixedRealityDataProvider( typeof(IMixedRealitySpatialAwarenessSystem), SupportedPlatforms.WindowsEditor | SupportedPlatforms.MacEditor | SupportedPlatforms.LinuxEditor, "Spatial Object Mesh Observer", "Providers/ObjectMeshObserver/Profiles/DefaultObjectMeshObserverProfile.asset", "MixedRealityToolkit.Core")] [HelpURL("https://docs.microsoft.com/windows/mixed-reality/mrtk-unity/features/spatial-awareness/spatial-awareness-getting-started")] public class SpatialObjectMeshObserver : BaseSpatialMeshObserver, IMixedRealityCapabilityCheck { /// /// Constructor. /// /// The instance that loaded the service. /// Friendly name of the service. /// Service priority. Used to determine order of instantiation. /// The service's configuration profile. [System.Obsolete("This constructor is obsolete (registrar parameter is no longer required) and will be removed in a future version of the Microsoft Mixed Reality Toolkit.")] public SpatialObjectMeshObserver( IMixedRealityServiceRegistrar registrar, IMixedRealitySpatialAwarenessSystem spatialAwarenessSystem, string name = null, uint priority = DefaultPriority, BaseMixedRealityProfile profile = null) : this(spatialAwarenessSystem, name, priority, profile) { Registrar = registrar; } /// /// Constructor. /// /// Friendly name of the service. /// Service priority. Used to determine order of instantiation. /// The service's configuration profile. public SpatialObjectMeshObserver( IMixedRealitySpatialAwarenessSystem spatialAwarenessSystem, string name = null, uint priority = DefaultPriority, BaseMixedRealityProfile profile = null) : base(spatialAwarenessSystem, name, priority, profile) { } private bool sendObservations = true; private GameObject spatialMeshObject = null; #region BaseSpatialMeshObserver Implementation /// /// Reads the settings from the configuration profile. /// protected override void ReadProfile() { base.ReadProfile(); SpatialObjectMeshObserverProfile profile = ConfigurationProfile as SpatialObjectMeshObserverProfile; if (profile == null) { return; } // SpatialObjectMeshObserver settings spatialMeshObject = profile.SpatialMeshObject; } #endregion BaseSpatialMeshObserver Implementation #region IMixedRealityCapabilityCheck Implementation /// bool IMixedRealityCapabilityCheck.CheckCapability(MixedRealityCapability capability) { return capability == MixedRealityCapability.SpatialAwarenessMesh; } #endregion IMixedRealityCapabilityCheck Implementation #region IMixedRealityDataProvider Implementation /// public override void Update() { if (!IsRunning) { return; } SendMeshObjects(); } #endregion IMixedRealityDataProvider Implementation #region BaseSpatialObserver Implementation /// protected override void CreateObserver() { if (StartupBehavior == AutoStartBehavior.AutoStart) { Resume(); } } /// protected override void CleanupObserver() { if (IsRunning) { Suspend(); } } #endregion BaseSpatialObserver Implementation #region IMixedRealitySpatialAwarenessObserver Implementation private static readonly ProfilerMarker ClearObservationsPerfMarker = new ProfilerMarker("[MRTK] SpatialObjectMeshObserver.ClearObservations"); /// public override void ClearObservations() { using (ClearObservationsPerfMarker.Auto()) { if (IsRunning) { Debug.Log("Cannot clear observations while the observer is running. Suspending this observer."); Suspend(); } foreach (int id in Meshes.Keys) { RemoveMeshObject(id); } // Resend file observations when resumed. sendObservations = true; } } /// public override void Resume() { if (IsRunning) { return; } IsRunning = true; } /// public override void Suspend() { if (!IsRunning) { return; } IsRunning = false; } #endregion IMixedRealitySpatialAwarenessObserver Implementation #region Helpers private int currentMeshId = 0; private static readonly ProfilerMarker SendMeshObjectsPerfMarker = new ProfilerMarker("[MRTK] SpatialObjectMeshObserver.SendMeshObjects"); /// /// Sends the observations using the mesh data contained within the configured 3D model. /// private void SendMeshObjects() { if (!sendObservations) { return; } using (SendMeshObjectsPerfMarker.Auto()) { if (spatialMeshObject != null) { MeshFilter[] meshFilters = spatialMeshObject.GetComponentsInChildren(); for (int i = 0; i < meshFilters.Length; i++) { SpatialAwarenessMeshObject meshObject = SpatialAwarenessMeshObject.Create( meshFilters[i].sharedMesh, MeshPhysicsLayer, $"Spatial Object Mesh {currentMeshId}", currentMeshId, ObservedObjectParent); meshObject.GameObject.transform.localPosition = meshFilters[i].transform.position; meshObject.GameObject.transform.localRotation = meshFilters[i].transform.rotation; ApplyMeshMaterial(meshObject); meshes.Add(currentMeshId, meshObject); meshEventData.Initialize(this, currentMeshId, meshObject); Service?.HandleEvent(meshEventData, OnMeshAdded); currentMeshId++; } } sendObservations = false; } } private static readonly ProfilerMarker RemoveMeshObjectPerfMarker = new ProfilerMarker("[MRTK] SpatialObjectMeshObserver.RemoveMeshObject"); /// /// Removes an observation. /// private void RemoveMeshObject(int meshId) { using (RemoveMeshObjectPerfMarker.Auto()) { if (meshes.TryGetValue(meshId, out SpatialAwarenessMeshObject meshObject)) { // Remove the mesh object from the collection. meshes.Remove(meshId); if (meshObject != null) { SpatialAwarenessMeshObject.Cleanup(meshObject); } // Send the mesh removed event meshEventData.Initialize(this, meshId, null); Service?.HandleEvent(meshEventData, OnMeshRemoved); } } } /// /// Applies the appropriate material, based on the current of the property. /// /// The for which the material is to be applied. private void ApplyMeshMaterial(SpatialAwarenessMeshObject meshObject) { if (meshObject?.Renderer == null) { return; } bool enable = (DisplayOption != SpatialAwarenessMeshDisplayOptions.None); if (enable) { meshObject.Renderer.sharedMaterial = (DisplayOption == SpatialAwarenessMeshDisplayOptions.Visible) ? VisibleMaterial : OcclusionMaterial; meshObject.Collider.material = PhysicsMaterial; } meshObject.Renderer.enabled = enable; } #endregion Helpers } }