forked from microsoft/MixedRealityToolkit-Unity
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSpatialMappingSource.cs
More file actions
339 lines (292 loc) · 13.8 KB
/
SpatialMappingSource.cs
File metadata and controls
339 lines (292 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine;
using UnityEngine.Rendering;
namespace HoloToolkit.Unity.SpatialMapping
{
public class SpatialMappingSource : MonoBehaviour
{
/// <summary>
/// Surface object
/// </summary>
public struct SurfaceObject
{
public int ID;
public GameObject Object;
public MeshRenderer Renderer;
public MeshFilter Filter;
public MeshCollider Collider;
}
public struct SurfaceUpdate
{
public SurfaceObject Old;
public SurfaceObject New;
}
/// <summary>
/// Collection of surface objects that have been created for this spatial mapping source.
/// </summary>
public ReadOnlyCollection<SurfaceObject> SurfaceObjects
{
get { return surfaceObjects; }
}
public event EventHandler<DataEventArgs<SurfaceObject>> SurfaceAdded;
public event EventHandler<DataEventArgs<SurfaceUpdate>> SurfaceUpdated;
public event EventHandler<DataEventArgs<SurfaceObject>> SurfaceRemoved;
public event EventHandler<EventArgs> RemovingAllSurfaces;
/// <summary>
/// When a mesh is created we will need to create a game object with a minimum
/// set of components to contain the mesh. These are the required component types.
/// </summary>
protected readonly Type[] componentsRequiredForSurfaceMesh =
{
typeof(MeshFilter),
typeof(MeshRenderer),
typeof(MeshCollider)
};
/// <summary>
/// Material to use for rendering the mesh
/// </summary>
protected virtual Material RenderMaterial { get { return SpatialMappingManager.Instance.SurfaceMaterial; } }
private readonly List<SurfaceObject> surfaceObjectsWriteable;
private readonly ReadOnlyCollection<SurfaceObject> surfaceObjects;
public SpatialMappingSource()
{
surfaceObjectsWriteable = new List<SurfaceObject>();
surfaceObjects = new ReadOnlyCollection<SurfaceObject>(surfaceObjectsWriteable);
}
protected virtual void Awake()
{
// Nothing.
}
/// <summary>
/// Create a new surface object.
/// </summary>
/// <param name="mesh">The mesh to attach. Can be null.</param>
/// <param name="objectName">What to name this object.</param>
/// <param name="parentObject">What to parent this object to.</param>
/// <param name="meshID">Optional user specified ID for the mesh.</param>
/// <param name="drawVisualMeshesOverride">If specified, overrides the default value for enabling/disabling the mesh renderer.</param>
/// <param name="castShadowsOverride">If specified, overrides the default value for casting shadows.</param>
/// <returns>The newly created surface object.</returns>
protected SurfaceObject CreateSurfaceObject(
Mesh mesh,
string objectName,
Transform parentObject,
int meshID = 0,
bool? drawVisualMeshesOverride = null,
bool? castShadowsOverride = null
)
{
SurfaceObject surfaceObject = new SurfaceObject();
surfaceObject.ID = meshID;
surfaceObject.Object = new GameObject(objectName, componentsRequiredForSurfaceMesh);
surfaceObject.Object.transform.SetParent(parentObject);
surfaceObject.Object.layer = SpatialMappingManager.Instance.PhysicsLayer;
surfaceObject.Filter = surfaceObject.Object.GetComponent<MeshFilter>();
surfaceObject.Filter.sharedMesh = mesh;
surfaceObject.Renderer = surfaceObject.Object.GetComponent<MeshRenderer>();
surfaceObject.Renderer.sharedMaterial = RenderMaterial;
surfaceObject.Renderer.enabled = (drawVisualMeshesOverride ?? SpatialMappingManager.Instance.DrawVisualMeshes);
surfaceObject.Renderer.shadowCastingMode = ((castShadowsOverride ?? SpatialMappingManager.Instance.CastShadows) ? ShadowCastingMode.On : ShadowCastingMode.Off);
surfaceObject.Collider = surfaceObject.Object.GetComponent<MeshCollider>();
// Reset the surface mesh collider to fit the updated mesh.
// Unity tribal knowledge indicates that to change the mesh assigned to a
// mesh collider, the mesh must first be set to null. Presumably there
// is a side effect in the setter when setting the shared mesh to null.
surfaceObject.Collider.sharedMesh = null;
surfaceObject.Collider.sharedMesh = surfaceObject.Filter.sharedMesh;
return surfaceObject;
}
/// <summary>
/// Add the surface to <see cref="SurfaceObjects"/>.
/// </summary>
/// <param name="toAdd">The surface to add.</param>
protected void AddSurfaceObject(SurfaceObject toAdd)
{
surfaceObjectsWriteable.Add(toAdd);
var handlers = SurfaceAdded;
if (handlers != null)
{
handlers(this, DataEventArgs.Create(toAdd));
}
}
/// <summary>
/// Update the first surface with a matching ID if one exists in <see cref="SurfaceObjects"/>, otherwise add the surface as new.
/// </summary>
/// <param name="toUpdateOrAdd">The surface to be updated or added.</param>
/// <param name="destroyGameObjectIfReplaced">If a surface is updated, and a game object is being replaced, pass true to destroy the outgoing game object or false otherwise.</param>
/// <param name="destroyMeshesIfReplaced">If a surface is updated, and new meshes are replacing old meshes, pass true to destroy the outgoing meshes or false otherwise.</param>
/// <returns>The surface object that was updated or null if one was not found meaning a new surface was added.</returns>
protected SurfaceObject? UpdateOrAddSurfaceObject(SurfaceObject toUpdateOrAdd, bool destroyGameObjectIfReplaced = true, bool destroyMeshesIfReplaced = true)
{
SurfaceObject? replaced = null;
for (int iSurface = 0; iSurface < surfaceObjectsWriteable.Count; iSurface++)
{
SurfaceObject existing = surfaceObjectsWriteable[iSurface];
if (existing.ID == toUpdateOrAdd.ID)
{
surfaceObjectsWriteable[iSurface] = toUpdateOrAdd;
var handlers = SurfaceUpdated;
if (handlers != null)
{
handlers(this, DataEventArgs.Create(new SurfaceUpdate { Old = existing, New = toUpdateOrAdd }));
}
CleanUpSurface(
existing,
destroyGameObjectIfReplaced,
destroyMeshesIfReplaced,
objectToPreserve: toUpdateOrAdd.Object,
meshToPreserveA: toUpdateOrAdd.Filter.sharedMesh,
meshToPreserveB: toUpdateOrAdd.Collider.sharedMesh
);
replaced = existing;
break;
}
}
if (replaced == null)
{
AddSurfaceObject(toUpdateOrAdd);
}
return replaced;
}
/// <summary>
/// Remove the first surface with the specified ID if one exists in <see cref="SurfaceObjects"/>.
/// </summary>
/// <param name="surfaceID">The ID of the surface to remove.</param>
/// <param name="destroyGameObject">True to destroy the <see cref="SurfaceObject.Object"/> associated with the surface, false otherwise.</param>
/// <param name="destroyMeshes">True to destroy the meshes associated with the surface, false otherwise.</param>
/// <returns>The surface object if one was found and removed or null if one was not found.</returns>
protected SurfaceObject? RemoveSurfaceIfFound(int surfaceID, bool destroyGameObject = true, bool destroyMeshes = true)
{
SurfaceObject? removed = null;
for (int iSurface = 0; iSurface < surfaceObjectsWriteable.Count; iSurface++)
{
SurfaceObject surface = surfaceObjectsWriteable[iSurface];
if (surface.ID == surfaceID)
{
surfaceObjectsWriteable.RemoveAt(iSurface);
var handlers = SurfaceRemoved;
if (handlers != null)
{
handlers(this, DataEventArgs.Create(surface));
}
CleanUpSurface(surface, destroyGameObject, destroyMeshes);
removed = surface;
break;
}
}
return removed;
}
/// <summary>
/// Clean up the resources associated with the surface.
/// </summary>
/// <param name="surface">The surface whose resources will be cleaned up.</param>
/// <param name="destroyGameObject"></param>
/// <param name="destroyMeshes"></param>
/// <param name="objectToPreserve">If the surface's game object matches this parameter, it will not be destroyed.</param>
/// <param name="meshToPreserveA">If either of the surface's meshes matches this parameter, it will not be destroyed.</param>
/// <param name="meshToPreserveB">If either of the surface's meshes matches this parameter, it will not be destroyed.</param>
protected void CleanUpSurface(
SurfaceObject surface,
bool destroyGameObject = true,
bool destroyMeshes = true,
GameObject objectToPreserve = null,
Mesh meshToPreserveA = null,
Mesh meshToPreserveB = null
)
{
if (destroyGameObject
&& (surface.Object != null)
&& (surface.Object != objectToPreserve)
)
{
Destroy(surface.Object);
Debug.Assert(surface.GetType().IsValueType(), "If surface is no longer a value type, you should probably set surface.Object to null.");
}
Mesh filterMesh = surface.Filter.sharedMesh;
Mesh colliderMesh = surface.Collider.sharedMesh;
if (destroyMeshes
&& (filterMesh != null)
&& (filterMesh != meshToPreserveA)
&& (filterMesh != meshToPreserveB)
)
{
Destroy(filterMesh);
surface.Filter.sharedMesh = null;
}
if (destroyMeshes
&& (colliderMesh != null)
&& (colliderMesh != filterMesh)
&& (colliderMesh != meshToPreserveA)
&& (colliderMesh != meshToPreserveB)
)
{
Destroy(colliderMesh);
surface.Collider.sharedMesh = null;
}
}
/// <summary>
/// Cleans up references to objects that we have created.
/// </summary>
/// <param name="destroyGameObjects">True to destroy the game objects of each surface, false otherwise.</param>
/// <param name="destroyMeshes">True to destroy the meshes of each surface, false otherwise.</param>
protected void Cleanup(bool destroyGameObjects = true, bool destroyMeshes = true)
{
var handlers = RemovingAllSurfaces;
if (handlers != null)
{
handlers(this, EventArgs.Empty);
}
for (int index = 0; index < surfaceObjectsWriteable.Count; index++)
{
CleanUpSurface(surfaceObjectsWriteable[index], destroyGameObjects, destroyMeshes);
}
surfaceObjectsWriteable.Clear();
}
/// <summary>
/// Gets all mesh filters that have a valid mesh.
/// </summary>
/// <returns>A list of filters, each with a mesh containing at least one triangle.</returns>
public virtual List<MeshFilter> GetMeshFilters()
{
List<MeshFilter> meshFilters = new List<MeshFilter>();
for (int index = 0; index < surfaceObjectsWriteable.Count; index++)
{
if (surfaceObjectsWriteable[index].Filter != null &&
surfaceObjectsWriteable[index].Filter.sharedMesh != null &&
surfaceObjectsWriteable[index].Filter.sharedMesh.vertexCount > 2)
{
meshFilters.Add(surfaceObjectsWriteable[index].Filter);
}
}
return meshFilters;
}
/// <summary>
/// Gets all mesh renderers that have been created.
/// </summary>
/// <returns></returns>
public virtual List<MeshRenderer> GetMeshRenderers()
{
List<MeshRenderer> meshRenderers = new List<MeshRenderer>();
for (int index = 0; index < surfaceObjectsWriteable.Count; index++)
{
if (surfaceObjectsWriteable[index].Renderer != null)
{
meshRenderers.Add(surfaceObjectsWriteable[index].Renderer);
}
}
return meshRenderers;
}
/// <summary>
/// Saves all the currently created spatial source meshes in world space.
/// </summary>
/// <param name="fileName">Name to give the mesh file. Exclude path and extension.</param>
public void SaveSpatialMeshes(string fileName)
{
MeshSaver.Save(fileName, GetMeshFilters());
}
}
}