Hello everyone.

The Mixed Reality Forums here are no longer being used or maintained.

There are a few other places we would like to direct you to for support, both from Microsoft and from the community.

The first way we want to connect with you is our mixed reality developer program, which you can sign up for at https://aka.ms/IWantMR.

For technical questions, please use Stack Overflow, and tag your questions using either hololens or windows-mixed-reality.

If you want to join in discussions, please do so in the HoloDevelopers Slack, which you can join by going to https://aka.ms/holodevelopers, or in our Microsoft Tech Communities forums at https://techcommunity.microsoft.com/t5/mixed-reality/ct-p/MicrosoftMixedReality.

And always feel free to hit us up on Twitter @MxdRealityDev.
Options

Spatial Mapping - Updating only small volumes

Hey,

I have a large room which remains at is is during the use of my app. But inside this room is a small volume which will change.

In order to safe resources and increase performance I only want to update this small area in my room. I know that the SpatialMappingObserver has the public property of "Extent" limiting the volume where the mesh will be saved.

Is it possible to have two spatial observers in one scene? One observing the whole room which will be stopped after the first scan and a second one only observing a fixed volume in the room?

Or might it be better to change to position of my SpatialMapping Prefab after the inital scan?

Best Answer

  • Options
    HoloFanHoloFan
    Answer ✓

    After some Trial-and-Error I have a working solution.
    It is not really possible to extend the SpatialMappingManager by creating a new class inheriting from it as the SpatialMappingSource is bound to the SpatialMappingManager when creating new surfaces.
    Therefore I created a new class only managing the SpatialMappingObservers (named them SubSurfaceObservers). From the testing I did it works quite well. Only the mesh inside the SubSurfaceObservers is updated but the outside Mesh can still be used for raycasting i.e. the Cursor follows it. I tried to follow the SpatialMappingManager by using similar naming conventions.

    Please let me know if there is anything missing or can be done to increase performance.

    using System.Collections.Generic;
    using UnityEngine;
    using HoloToolkit.Unity;
    using HoloToolkit.Unity.SpatialMapping;
    
    namespace OwnDevelopment.HoloLens.Unity
    {
        [RequireComponent(typeof(SpatialMappingManager))]
        public class MultiSpatialMappingManagerExtended : Singleton<MultiSpatialMappingManagerExtended>
        {
            private List<SpatialMappingObserver> subSurfaceObserverList;
    
            [HideInInspector]
            public List<float> subStartTimeList;
    
            protected override void Awake()
            {
                base.Awake();
    
                subSurfaceObserverList = new List<SpatialMappingObserver>();
                subStartTimeList = new List<float>();
            }
    
            // Checks to see if the SubSurfaceObserver is currently running
            public bool IsSubObserverRunning(int observerID)
            {
                bool IsRunning = false;
                if (subSurfaceObserverList.Count > observerID)
                {
                    if (subSurfaceObserverList[observerID].ObserverState == HoloToolkit.Unity.SpatialMapping.ObserverStates.Running)
                    {
                        IsRunning = true;
                    }
                }
                return IsRunning;
            }
    
            // Adds a new SubSurfaceObserver.
            // Paramters are set to deault if not specified.
            public int AddNewSubObserver(Vector3? extents = null, float? TrianglesPerCubMeter = null, float? TimeBetweenUpdates = null)
            {
                if (extents == null)
                {
                    extents = new Vector3(1.0f, 1.0f, 1.0f);
                }
                if (TrianglesPerCubMeter == null)
                {
                    TrianglesPerCubMeter = 500.0f;
                }
                if (TimeBetweenUpdates == null)
                {
                    TimeBetweenUpdates = 3.5f;
                }
                int observerID = subSurfaceObserverList.Count;
                SpatialMappingObserver observer = gameObject.AddComponent<SpatialMappingObserver>() as SpatialMappingObserver;
    
                observer.TrianglesPerCubicMeter = (float)TrianglesPerCubMeter;
                observer.TimeBetweenUpdates = (float)TimeBetweenUpdates;
                observer.Extents = (Vector3)extents;
    
                subSurfaceObserverList.Add(observer);
                subStartTimeList.Add(float.MaxValue);
    
                return observerID;
            }
    
            // Swithes to the names SubSurfaceObserver
            public bool SwitchToSubObserver(int observerID)
            {
                bool SwitchedToSubObserver = false;
                if (subSurfaceObserverList.Count > observerID)
                {
                    SpatialMappingManager.Instance.StopObserver();
                    for (int ID = 0; ID < subSurfaceObserverList.Count; ID++)
                    {
                        StopSubObserver(ID);
                    }
    
                    SpatialMappingManager.Instance.SetSpatialMappingSource(subSurfaceObserverList[observerID]);
                    SwitchedToSubObserver = true;
                }
                return SwitchedToSubObserver;
            }
    
            // Switches back to MainSurfaceObserver
            public void SwitchToMainObserver()
            {
                for (int ID = 0; ID < subSurfaceObserverList.Count; ID++)
                {
                    StopSubObserver(ID);
                }
                SpatialMappingManager.Instance.StopObserver();
                SpatialMappingManager.Instance.SetSpatialMappingSource(null);
            }
    
            // Instructs the SubSurfaceObserver to start updating the SpatialMapping mesh.
            public void StartSubObserver(int observerID)
            {
    #if UNITY_EDITOR || UNITY_UWP
                if (!UnityEngine.VR.VRDevice.isPresent) return;
    #endif
                if (!IsSubObserverRunning(observerID))
                {
                    if (SpatialMappingManager.Instance.Source == subSurfaceObserverList[observerID])
                    {
                        subSurfaceObserverList[observerID].StartObserving();
                        subStartTimeList[observerID] = Time.time;
                    }
                }
            }
    
            // Instructs the SubSurfaceObserver to stop updating the SpatialMapping mesh.
            public void StopSubObserver(int observerID)
            {
                if (IsSubObserverRunning(observerID))
                {
                    subSurfaceObserverList[observerID].StopObserving();
                }
            }
    
            // Instructs the SurfaceObserver to stop and cleanup all meshes.
            public void CleanupSubObserver(int observerID)
            {
                if (subSurfaceObserverList.Count > observerID)
                {
                    subSurfaceObserverList[observerID].CleanupObserver();
                }
            }
    
            // Sets the origin of the SubSurfaceObserver. The SubSurfaceObserver needs to be running.
            public void SetSubObserverOrigin(int observerID, Vector3 origin)
            {
                if (IsSubObserverRunning(observerID))
                {
                    subSurfaceObserverList[observerID].SetObserverOrigin(origin);
                }
            }
    
            // Sets the extents of the SubSurfaceObserver.
            public void SetSubObserverExtents(int observerID, Vector3 extents)
            {
                if (subSurfaceObserverList.Count > observerID)
                {
                    subSurfaceObserverList[observerID].Extents = extents;
                }
            }
    
            // Sets the extents of the SubSurfaceObserver.
            public void SetSubObserverExtents(int observerID, float uniformExtents)
            {
                Vector3 extents = Vector3.one * uniformExtents;
                SetSubObserverExtents(observerID, extents);
            }
    
            // Dummy Method to show how to work with this class
            // Creates a new SubSurfaceObserver at the origin of the app
            // and starts it.
            public void CreateAndStartDummySubObserver()
            {
                int ID = AddNewSubObserver();
                SetSubObserverExtents(ID, 0.5f);
                SwitchToSubObserver(ID);
                StartSubObserver(ID);
                SetSubObserverOrigin(ID, new Vector3(0.0f, 0.0f, 0.0f));
                Debug.Log("Created DummySubObserver and started it.");
            }
        }
    }
    

Answers

  • Options

    Hey Holofan, sounds like a good use of multiple spatial observers. The documentation describes a similar scenario where you have one fixed spatial observer around the play-space and another observer body-locked to the user.

    If you changed the size and position of the first observer instead you would probably have to worry about the surface meshes being out of the observed volume and recycled.

  • Options

    Thank you for your answer.
    I started to write some code. Basically extending the SpatialMappingManager to handle not only the implemented SpatialMappingObserver but to manage a List of SpatialMappingObserver-Objects.
    So far I am getting a lot of errors when accessing these SubObservers. I will work on the code a bit more and let you know when something new comes up.
    But maybe there is a better way to do this beside using a list?

  • Options
    HoloFanHoloFan
    Answer ✓

    After some Trial-and-Error I have a working solution.
    It is not really possible to extend the SpatialMappingManager by creating a new class inheriting from it as the SpatialMappingSource is bound to the SpatialMappingManager when creating new surfaces.
    Therefore I created a new class only managing the SpatialMappingObservers (named them SubSurfaceObservers). From the testing I did it works quite well. Only the mesh inside the SubSurfaceObservers is updated but the outside Mesh can still be used for raycasting i.e. the Cursor follows it. I tried to follow the SpatialMappingManager by using similar naming conventions.

    Please let me know if there is anything missing or can be done to increase performance.

    using System.Collections.Generic;
    using UnityEngine;
    using HoloToolkit.Unity;
    using HoloToolkit.Unity.SpatialMapping;
    
    namespace OwnDevelopment.HoloLens.Unity
    {
        [RequireComponent(typeof(SpatialMappingManager))]
        public class MultiSpatialMappingManagerExtended : Singleton<MultiSpatialMappingManagerExtended>
        {
            private List<SpatialMappingObserver> subSurfaceObserverList;
    
            [HideInInspector]
            public List<float> subStartTimeList;
    
            protected override void Awake()
            {
                base.Awake();
    
                subSurfaceObserverList = new List<SpatialMappingObserver>();
                subStartTimeList = new List<float>();
            }
    
            // Checks to see if the SubSurfaceObserver is currently running
            public bool IsSubObserverRunning(int observerID)
            {
                bool IsRunning = false;
                if (subSurfaceObserverList.Count > observerID)
                {
                    if (subSurfaceObserverList[observerID].ObserverState == HoloToolkit.Unity.SpatialMapping.ObserverStates.Running)
                    {
                        IsRunning = true;
                    }
                }
                return IsRunning;
            }
    
            // Adds a new SubSurfaceObserver.
            // Paramters are set to deault if not specified.
            public int AddNewSubObserver(Vector3? extents = null, float? TrianglesPerCubMeter = null, float? TimeBetweenUpdates = null)
            {
                if (extents == null)
                {
                    extents = new Vector3(1.0f, 1.0f, 1.0f);
                }
                if (TrianglesPerCubMeter == null)
                {
                    TrianglesPerCubMeter = 500.0f;
                }
                if (TimeBetweenUpdates == null)
                {
                    TimeBetweenUpdates = 3.5f;
                }
                int observerID = subSurfaceObserverList.Count;
                SpatialMappingObserver observer = gameObject.AddComponent<SpatialMappingObserver>() as SpatialMappingObserver;
    
                observer.TrianglesPerCubicMeter = (float)TrianglesPerCubMeter;
                observer.TimeBetweenUpdates = (float)TimeBetweenUpdates;
                observer.Extents = (Vector3)extents;
    
                subSurfaceObserverList.Add(observer);
                subStartTimeList.Add(float.MaxValue);
    
                return observerID;
            }
    
            // Swithes to the names SubSurfaceObserver
            public bool SwitchToSubObserver(int observerID)
            {
                bool SwitchedToSubObserver = false;
                if (subSurfaceObserverList.Count > observerID)
                {
                    SpatialMappingManager.Instance.StopObserver();
                    for (int ID = 0; ID < subSurfaceObserverList.Count; ID++)
                    {
                        StopSubObserver(ID);
                    }
    
                    SpatialMappingManager.Instance.SetSpatialMappingSource(subSurfaceObserverList[observerID]);
                    SwitchedToSubObserver = true;
                }
                return SwitchedToSubObserver;
            }
    
            // Switches back to MainSurfaceObserver
            public void SwitchToMainObserver()
            {
                for (int ID = 0; ID < subSurfaceObserverList.Count; ID++)
                {
                    StopSubObserver(ID);
                }
                SpatialMappingManager.Instance.StopObserver();
                SpatialMappingManager.Instance.SetSpatialMappingSource(null);
            }
    
            // Instructs the SubSurfaceObserver to start updating the SpatialMapping mesh.
            public void StartSubObserver(int observerID)
            {
    #if UNITY_EDITOR || UNITY_UWP
                if (!UnityEngine.VR.VRDevice.isPresent) return;
    #endif
                if (!IsSubObserverRunning(observerID))
                {
                    if (SpatialMappingManager.Instance.Source == subSurfaceObserverList[observerID])
                    {
                        subSurfaceObserverList[observerID].StartObserving();
                        subStartTimeList[observerID] = Time.time;
                    }
                }
            }
    
            // Instructs the SubSurfaceObserver to stop updating the SpatialMapping mesh.
            public void StopSubObserver(int observerID)
            {
                if (IsSubObserverRunning(observerID))
                {
                    subSurfaceObserverList[observerID].StopObserving();
                }
            }
    
            // Instructs the SurfaceObserver to stop and cleanup all meshes.
            public void CleanupSubObserver(int observerID)
            {
                if (subSurfaceObserverList.Count > observerID)
                {
                    subSurfaceObserverList[observerID].CleanupObserver();
                }
            }
    
            // Sets the origin of the SubSurfaceObserver. The SubSurfaceObserver needs to be running.
            public void SetSubObserverOrigin(int observerID, Vector3 origin)
            {
                if (IsSubObserverRunning(observerID))
                {
                    subSurfaceObserverList[observerID].SetObserverOrigin(origin);
                }
            }
    
            // Sets the extents of the SubSurfaceObserver.
            public void SetSubObserverExtents(int observerID, Vector3 extents)
            {
                if (subSurfaceObserverList.Count > observerID)
                {
                    subSurfaceObserverList[observerID].Extents = extents;
                }
            }
    
            // Sets the extents of the SubSurfaceObserver.
            public void SetSubObserverExtents(int observerID, float uniformExtents)
            {
                Vector3 extents = Vector3.one * uniformExtents;
                SetSubObserverExtents(observerID, extents);
            }
    
            // Dummy Method to show how to work with this class
            // Creates a new SubSurfaceObserver at the origin of the app
            // and starts it.
            public void CreateAndStartDummySubObserver()
            {
                int ID = AddNewSubObserver();
                SetSubObserverExtents(ID, 0.5f);
                SwitchToSubObserver(ID);
                StartSubObserver(ID);
                SetSubObserverOrigin(ID, new Vector3(0.0f, 0.0f, 0.0f));
                Debug.Log("Created DummySubObserver and started it.");
            }
        }
    }
    
Sign In or Register to comment.