using System.Collections.Generic; using UnityEngine; using UnityEngine.Windows.Speech; using Academy.HoloToolkit.Unity; /// /// The SurfaceManager class allows applications to scan the environment for a specified amount of time /// and then process the Spatial Mapping Mesh (find planes, remove vertices) after that time has expired. /// public class PlaySpaceManager : Singleton { [Tooltip("When checked, the SurfaceObserver will stop running after a specified amount of time.")] public bool limitScanningByTime = true; [Tooltip("How much time (in seconds) that the SurfaceObserver will run after being started; used when 'Limit Scanning By Time' is checked.")] public float scanTime = 30.0f; [Tooltip("Material to use when rendering Spatial Mapping meshes while the observer is running.")] public Material defaultMaterial; [Tooltip("Optional Material to use when rendering Spatial Mapping meshes after the observer has been stopped.")] public Material secondaryMaterial; [Tooltip("Minimum number of floor planes required in order to exit scanning/processing mode.")] public uint minimumFloors = 1; [Tooltip("Minimum number of wall planes required in order to exit scanning/processing mode.")] public uint minimumWalls = 1; /// /// Indicates if processing of the surface meshes is complete. /// private bool meshesProcessed = false; /// /// GameObject initialization. /// private void Start() { // Update surfaceObserver and storedMeshes to use the same material during scanning. SpatialMappingManager.Instance.SetSurfaceMaterial(defaultMaterial); // Register for the MakePlanesComplete event. SurfaceMeshesToPlanes.Instance.MakePlanesComplete += SurfaceMeshesToPlanes_MakePlanesComplete; } /// /// Called once per frame. /// private void Update() { // Check to see if the spatial mapping data has been processed // and if we are limiting how much time the user can spend scanning. if (!meshesProcessed && limitScanningByTime) { // If we have not processed the spatial mapping data // and scanning time is limited... // Check to see if enough scanning time has passed // since starting the observer. if (limitScanningByTime && ((Time.time - SpatialMappingManager.Instance.StartTime) < scanTime)) { // If we have a limited scanning time, then we should wait until // enough time has passed before processing the mesh. } else { // The user should be done scanning their environment, // so start processing the spatial mapping data... /* TODO: 3.a DEVELOPER CODING EXERCISE 3.a */ // 3.a: Check if IsObserverRunning() is true on the // SpatialMappingManager.Instance. if(SpatialMappingManager.Instance.IsObserverRunning()) { // 3.a: If running, Stop the observer by calling // StopObserver() on the SpatialMappingManager.Instance. SpatialMappingManager.Instance.StopObserver(); } // 3.a: Call CreatePlanes() to generate planes. CreatePlanes(); // 3.a: Set meshesProcessed to true. meshesProcessed = true; } } } /// /// Handler for the SurfaceMeshesToPlanes MakePlanesComplete event. /// /// Source of the event. /// Args for the event. private void SurfaceMeshesToPlanes_MakePlanesComplete(object source, System.EventArgs args) { /* TODO: 3.a DEVELOPER CODING EXERCISE 3.a */ // Collection of floor and table planes that we can use to set horizontal items on. List horizontal = new List(); // Collection of wall planes that we can use to set vertical items on. List vertical = new List(); // 3.a: Get all floor and table planes by calling // SurfaceMeshesToPlanes.Instance.GetActivePlanes(). // Assign the result to the 'horizontal' list. horizontal = SurfaceMeshesToPlanes.Instance.GetActivePlanes(PlaneTypes.Table | PlaneTypes.Floor); // 3.a: Get all wall planes by calling // SurfaceMeshesToPlanes.Instance.GetActivePlanes(). // Assign the result to the 'vertical' list. vertical = SurfaceMeshesToPlanes.Instance.GetActivePlanes(PlaneTypes.Wall); // Check to see if we have enough horizontal planes (minimumFloors) // and vertical planes (minimumWalls), to set holograms on in the world. if (horizontal.Count >= minimumFloors && vertical.Count >= minimumWalls) { // We have enough floors and walls to place our holograms on... // 3.a: Let's reduce our triangle count by removing triangles // from SpatialMapping meshes that intersect with our active planes. // Call RemoveVertices(). // Pass in all activePlanes found by SurfaceMeshesToPlanes.Instance. RemoveVertices(SurfaceMeshesToPlanes.Instance.ActivePlanes); // 3.a: We can indicate to the user that scanning is over by // changing the material applied to the Spatial Mapping meshes. // Call SpatialMappingManager.Instance.SetSurfaceMaterial(). // Pass in the secondaryMaterial. SpatialMappingManager.Instance.SetSurfaceMaterial(secondaryMaterial); // 3.a: We are all done processing the mesh, so we can now // initialize a collection of Placeable holograms in the world // and use horizontal/vertical planes to set their starting positions. // Call SpaceCollectionManager.Instance.GenerateItemsInWorld(). // Pass in the lists of horizontal and vertical planes that we found earlier. SpaceCollectionManager.Instance.GenerateItemsInWorld(horizontal, vertical); } else { // We do not have enough floors/walls to place our holograms on... // 3.a: Re-enter scanning mode so the user can find more surfaces by // calling StartObserver() on the SpatialMappingManager.Instance. SpatialMappingManager.Instance.StartObserver(); // 3.a: Re-process spatial data after scanning completes by // re-setting meshesProcessed to false. meshesProcessed = false; } } /// /// Creates planes from the spatial mapping surfaces. /// private void CreatePlanes() { // Generate planes based on the spatial map. SurfaceMeshesToPlanes surfaceToPlanes = SurfaceMeshesToPlanes.Instance; if (surfaceToPlanes != null && surfaceToPlanes.enabled) { surfaceToPlanes.MakePlanes(); } } /// /// Removes triangles from the spatial mapping surfaces. /// /// private void RemoveVertices(IEnumerable boundingObjects) { RemoveSurfaceVertices removeVerts = RemoveSurfaceVertices.Instance; if (removeVerts != null && removeVerts.enabled) { removeVerts.RemoveSurfaceVerticesWithinBounds(boundingObjects); } } /// /// Called when the GameObject is unloaded. /// private void OnDestroy() { if (SurfaceMeshesToPlanes.Instance != null) { SurfaceMeshesToPlanes.Instance.MakePlanesComplete -= SurfaceMeshesToPlanes_MakePlanesComplete; } } }