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