AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
RemoveSurfaceVertices.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 
4 using System;
5 using System.Collections;
6 using System.Collections.Generic;
7 using UnityEngine;
8 
9 namespace HoloToolkit.Unity.SpatialMapping
10 {
15  public class RemoveSurfaceVertices : Singleton<RemoveSurfaceVertices>
16  {
17  [Tooltip("The amount, if any, to expand each bounding volume by.")]
18  public float BoundsExpansion = 0.0f;
19 
25  public delegate void EventHandler(object source, EventArgs args);
26 
30  public event EventHandler RemoveVerticesComplete;
31 
35  private bool removingVerts = false;
36 
41  private Queue<Bounds> boundingObjectsQueue;
42 
43 #if UNITY_EDITOR || UNITY_STANDALONE
44  private static readonly float FrameTime = .016f;
48 #else
49  private static readonly float FrameTime = .008f;
53 #endif
54 
55  // GameObject initialization.
56  private void Start()
57  {
58  boundingObjectsQueue = new Queue<Bounds>();
59  removingVerts = false;
60  }
61 
66  public void RemoveSurfaceVerticesWithinBounds(IEnumerable<GameObject> boundingObjects)
67  {
68  if (boundingObjects == null)
69  {
70  return;
71  }
72 
73  if (!removingVerts)
74  {
75  removingVerts = true;
76  AddBoundingObjectsToQueue(boundingObjects);
77 
78  // We use Coroutine to split the work across multiple frames and avoid impacting the frame rate too much.
79  StartCoroutine(RemoveSurfaceVerticesWithinBoundsRoutine());
80  }
81  else
82  {
83  // Add new boundingObjects to end of queue.
84  AddBoundingObjectsToQueue(boundingObjects);
85  }
86  }
87 
92  private void AddBoundingObjectsToQueue(IEnumerable<GameObject> boundingObjects)
93  {
94  foreach (GameObject item in boundingObjects)
95  {
96  Collider boundingCollider = item.GetComponent<Collider>();
97  if (boundingCollider != null)
98  {
99  Bounds bounds = boundingCollider.bounds;
100 
101  // Expand the bounds, if requested.
102  if (BoundsExpansion > 0.0f)
103  {
104  bounds.Expand(BoundsExpansion);
105  }
106 
107  boundingObjectsQueue.Enqueue(bounds);
108  }
109  }
110  }
111 
116  private IEnumerator RemoveSurfaceVerticesWithinBoundsRoutine()
117  {
118  List<MeshFilter> meshFilters = SpatialMappingManager.Instance.GetMeshFilters();
119  float start = Time.realtimeSinceStartup;
120 
121  while (boundingObjectsQueue.Count > 0)
122  {
123  // Get the current boundingObject.
124  Bounds bounds = boundingObjectsQueue.Dequeue();
125 
126  foreach (MeshFilter filter in meshFilters)
127  {
128  // Since this is amortized across frames, the filter can be destroyed by the time
129  // we get here.
130  if (filter == null)
131  {
132  continue;
133  }
134 
135  Mesh mesh = filter.sharedMesh;
136  MeshRenderer meshRenderer = filter.GetComponent<MeshRenderer>();
137 
138  // The mesh renderer bounds are in world space.
139  // If the mesh is null there is nothing to process
140  // If the renderer is null we can't get the renderer bounds
141  // If the renderer's bounds aren't contained inside of the current
142  // bounds from the bounds queue there is no reason to process
143  // If any of the above conditions are met, then we should go to the next meshfilter.
144  if (mesh == null || meshRenderer == null || !meshRenderer.bounds.Intersects(bounds))
145  {
146  // We don't need to do anything to this mesh, move to the next one.
147  continue;
148  }
149 
150  // Remove vertices from any mesh that intersects with the bounds.
151  Vector3[] verts = mesh.vertices;
152  HashSet<int> vertsToRemove = new HashSet<int>();
153 
154  // Find which mesh vertices are within the bounds.
155  for (int i = 0; i < verts.Length; ++i)
156  {
157  if (bounds.Contains(filter.transform.TransformPoint(verts[i])))
158  {
159  // These vertices are within bounds, so mark them for removal.
160  vertsToRemove.Add(i);
161  }
162 
163  // If too much time has passed, we need to return control to the main game loop.
164  if ((Time.realtimeSinceStartup - start) > FrameTime)
165  {
166  // Pause our work here, and continue finding vertices to remove on the next frame.
167  yield return null;
168  start = Time.realtimeSinceStartup;
169  }
170  }
171 
172  if (vertsToRemove.Count == 0)
173  {
174  // We did not find any vertices to remove, so move to the next mesh.
175  continue;
176  }
177 
178  // We found vertices to remove, so now we need to remove any triangles that reference these vertices.
179  int[] indices = mesh.GetTriangles(0);
180  List<int> updatedIndices = new List<int>();
181 
182  for (int index = 0; index < indices.Length; index += 3)
183  {
184  // Each triangle utilizes three slots in the index buffer, check to see if any of the
185  // triangle indices contain a vertex that should be removed.
186  if (vertsToRemove.Contains(indices[index]) ||
187  vertsToRemove.Contains(indices[index + 1]) ||
188  vertsToRemove.Contains(indices[index + 2]))
189  {
190  // Do nothing, we don't want to save this triangle...
191  }
192  else
193  {
194  // Every vertex in this triangle is good, so let's save it.
195  updatedIndices.Add(indices[index]);
196  updatedIndices.Add(indices[index + 1]);
197  updatedIndices.Add(indices[index + 2]);
198  }
199 
200  // If too much time has passed, we need to return control to the main game loop.
201  if ((Time.realtimeSinceStartup - start) > FrameTime)
202  {
203  // Pause our work, and continue making additional planes on the next frame.
204  yield return null;
205  start = Time.realtimeSinceStartup;
206  }
207  }
208 
209  if (indices.Length == updatedIndices.Count)
210  {
211  // None of the verts to remove were being referenced in the triangle list.
212  continue;
213  }
214 
215  // Update mesh to use the new triangles.
216  mesh.SetTriangles(updatedIndices.ToArray(), 0);
217  mesh.RecalculateBounds();
218  yield return null;
219  start = Time.realtimeSinceStartup;
220 
221  // Reset the mesh collider to fit the new mesh.
222  MeshCollider meshCollider = filter.gameObject.GetComponent<MeshCollider>();
223  if (meshCollider != null)
224  {
225  meshCollider.sharedMesh = null;
226  meshCollider.sharedMesh = mesh;
227  }
228  }
229  }
230 
231  Debug.Log("Finished removing vertices.");
232 
233  // We are done removing vertices, trigger an event.
234  EventHandler handler = RemoveVerticesComplete;
235  if (handler != null)
236  {
237  handler(this, EventArgs.Empty);
238  }
239 
240  removingVerts = false;
241  }
242  }
243 }
void RemoveSurfaceVerticesWithinBounds(IEnumerable< GameObject > boundingObjects)
Removes portions of the surface mesh that exist within the bounds of the boundingObjects.
EventHandler RemoveVerticesComplete
EventHandler which is triggered when the RemoveSurfaceVertices is finished.
RemoveSurfaceVertices will remove any vertices from the Spatial Mapping Mesh that fall within the bou...
static T Instance
Returns the Singleton instance of the classes type. If no instance is found, then we search for an in...
Definition: Singleton.cs:26
The SpatialMappingManager class allows applications to use a SurfaceObserver or a stored Spatial Mapp...
Singleton behaviour class, used for components that should only have one instance.
Definition: Singleton.cs:14