5 using System.Collections.Generic;
9 #if UNITY_2017_2_OR_NEWER 61 [Tooltip(
"The number of triangles to calculate per cubic meter.")]
62 public float TrianglesPerCubicMeter = 500f;
64 [Tooltip(
"How long to wait (in sec) between Spatial Mapping updates.")]
65 public float TimeBetweenUpdates = 3.5f;
76 [Tooltip(
"The shape of the observation volume.")]
82 return observerVolumeType;
86 if (observerVolumeType != value)
88 observerVolumeType = value;
89 SwitchObservedVolume();
95 private SurfaceObserver observer;
103 private readonly Queue<SurfaceId> surfaceWorkQueue =
new Queue<SurfaceId>();
123 private float updateTime;
126 [Tooltip(
"The extents of the observation volume.")]
127 private Vector3 extents = Vector3.one * 10.0f;
128 public Vector3 Extents
136 if (extents != value)
139 SwitchObservedVolume();
148 [Tooltip(
"The origin of the observation volume.")]
149 private Vector3 origin = Vector3.zero;
161 SwitchObservedVolume();
170 [Tooltip(
"The direction of the observation volume.")]
171 private Quaternion orientation = Quaternion.identity;
172 public Quaternion Orientation
180 if (orientation != value)
186 SwitchObservedVolume();
200 private void Update()
205 if ((ObserverState ==
ObserverStates.Running) && (outstandingMeshRequest == null))
207 if (surfaceWorkQueue.Count > 0)
211 SurfaceId surfaceID = surfaceWorkQueue.Dequeue();
213 string surfaceName = (
"Surface-" + surfaceID.handle);
216 WorldAnchor worldAnchor;
218 if (spareSurfaceObject == null)
220 newSurface = CreateSurfaceObject(
222 objectName: surfaceName,
223 parentObject: transform,
224 meshID: surfaceID.handle,
225 drawVisualMeshesOverride:
false 228 worldAnchor = newSurface.
Object.AddComponent<WorldAnchor>();
232 newSurface = spareSurfaceObject.Value;
233 spareSurfaceObject = null;
236 newSurface.
Object.SetActive(
true);
238 Debug.Assert(newSurface.
Filter.sharedMesh == null);
240 newSurface.
Object.name = surfaceName;
241 Debug.Assert(newSurface.
Object.transform.parent == transform);
242 newSurface.
ID = surfaceID.handle;
243 newSurface.
Renderer.enabled =
false;
245 worldAnchor = newSurface.
Object.GetComponent<WorldAnchor>();
246 Debug.Assert(worldAnchor != null);
249 var surfaceData =
new SurfaceData(
254 TrianglesPerCubicMeter,
258 if (observer.RequestMeshAsync(surfaceData, SurfaceObserver_OnDataReady))
260 outstandingMeshRequest = newSurface;
264 Debug.LogErrorFormat(
"Mesh request for failed. Is {0} a valid Surface ID?", surfaceID.handle);
266 Debug.Assert(outstandingMeshRequest == null);
267 ReclaimSurface(newSurface);
270 else if ((Time.unscaledTime - updateTime) >= TimeBetweenUpdates)
272 observer.Update(SurfaceObserver_OnSurfaceChanged);
273 updateTime = Time.unscaledTime;
285 if (observer == null)
287 observer =
new SurfaceObserver();
288 SwitchObservedVolume();
293 Debug.Log(
"Starting the observer.");
311 Debug.Log(
"Stopping the observer.");
314 surfaceWorkQueue.Clear();
328 if (observer != null)
334 if (outstandingMeshRequest != null)
336 CleanUpSurface(outstandingMeshRequest.Value);
337 outstandingMeshRequest = null;
340 if (spareSurfaceObject != null)
342 CleanUpSurface(spareSurfaceObject.Value);
343 spareSurfaceObject = null;
356 bool originUpdated =
false;
359 if (observer != null)
362 originUpdated =
true;
366 return originUpdated;
372 private void SwitchObservedVolume()
375 if (observer == null)
380 switch (observerVolumeType)
383 observer.SetVolumeAsAxisAlignedBox(origin, extents);
386 observer.SetVolumeAsOrientedBox(origin, extents, orientation);
389 observer.SetVolumeAsSphere(origin, extents.magnitude);
392 observer.SetVolumeAsAxisAlignedBox(origin, extents);
399 private void SurfaceObserver_OnDataReady(SurfaceData cookedData,
bool outputWritten,
float elapsedCookTimeSeconds)
407 if (outstandingMeshRequest == null)
409 Debug.LogErrorFormat(
"Got OnDataReady for surface {0} while no request was outstanding.",
416 if (!IsMatchingSurface(outstandingMeshRequest.Value, cookedData))
418 Debug.LogErrorFormat(
"Got mismatched OnDataReady for surface {0} while request for surface {1} was outstanding.",
419 cookedData.id.handle,
420 outstandingMeshRequest.Value.ID
423 ReclaimSurface(outstandingMeshRequest.Value);
424 outstandingMeshRequest = null;
431 Debug.LogFormat(
"Got OnDataReady for surface {0}, but observer was no longer running.",
435 ReclaimSurface(outstandingMeshRequest.Value);
436 outstandingMeshRequest = null;
443 ReclaimSurface(outstandingMeshRequest.Value);
444 outstandingMeshRequest = null;
449 Debug.Assert(outstandingMeshRequest.Value.Object.activeSelf);
452 SurfaceObject? replacedSurface = UpdateOrAddSurfaceObject(outstandingMeshRequest.Value, destroyGameObjectIfReplaced:
false);
453 outstandingMeshRequest = null;
455 if (replacedSurface != null)
457 ReclaimSurface(replacedSurface.Value);
468 private void SurfaceObserver_OnSurfaceChanged(SurfaceId
id, SurfaceChange changeType, Bounds bounds, DateTime updateTime)
478 case SurfaceChange.Added:
479 case SurfaceChange.Updated:
480 surfaceWorkQueue.Enqueue(
id);
483 case SurfaceChange.Removed:
484 SurfaceObject? removedSurface = RemoveSurfaceIfFound(
id.handle, destroyGameObject:
false);
485 if (removedSurface != null)
487 ReclaimSurface(removedSurface.Value);
492 Debug.LogErrorFormat(
"Unexpected {0} value: {1}.", changeType.GetType(), changeType);
496 private bool IsMatchingSurface(
SurfaceObject surfaceObject, SurfaceData surfaceData)
498 return (surfaceObject.
ID == surfaceData.id.handle)
499 && (surfaceObject.
Filter == surfaceData.outputMesh)
500 && (surfaceObject.
Collider == surfaceData.outputCollider)
508 private void OnDestroy()
515 if (spareSurfaceObject == null)
517 CleanUpSurface(availableSurface, destroyGameObject:
false);
519 availableSurface.
Object.name =
"Unused Surface";
520 availableSurface.
Object.SetActive(
false);
522 spareSurfaceObject = availableSurface;
526 CleanUpSurface(availableSurface);