/// <summary> /// Called by the surface observer when a mesh has had its data changed. /// </summary> /// <param name="bakedData">The data describing the surface.</param> /// <param name="outputWritten">If the data was successfully updated.</param> /// <param name="elapsedBakeTimeSeconds">How long it took to update.</param> private void MappingObserver_DataReady(SurfaceData bakedData, bool outputWritten, float elapsedBakeTimeSeconds) { if (!outputWritten) return; AddOrUpdateMeshInList(bakedData); }
protected override void OnSurfaceDataReady(SpatialMappingBase requester, SurfaceData bakedData, bool outputWritten, float elapsedBakeTimeSeconds) { SpatialMappingBase.Surface surface; if (base.surfaceObjects.TryGetValue(bakedData.id.handle, out surface)) { surface.awaitingBake = false; if (outputWritten) { if (surface.gameObject == null) { Debug.LogError(string.Format("A SpatialMappingCollider component can not apply baked data to the surface with id \"{0}\" because its GameObject is null.", bakedData.id.handle)); } else if (bakedData.outputCollider != null) { if (requester != this) { base.CloneBakedComponents(bakedData, surface); } bakedData.outputCollider.gameObject.layer = this.layer; if (this.material != null) { bakedData.outputCollider.material = this.material; } } } } }
protected override void AddRequiredComponentsForBaking(SpatialMappingBase.Surface surface) { base.AddRequiredComponentsForBaking(surface); if (surface.meshCollider == null) { surface.meshCollider = surface.gameObject.AddComponent <MeshCollider>(); } SurfaceData surfaceData = surface.surfaceData; surfaceData.outputCollider = surface.meshCollider; surface.surfaceData = surfaceData; }
/// <summary> /// Handler for RequestMeshAsync which will be used to set the layer, material, and collision options on the resulting mesh /// </summary> /// <param name="bakedData">The resulting data from the RequestMeshAsync call</param> /// <param name="outputWritten">Whether or not the output was written</param> /// <param name="elapsedBakeTimeSeconds">How long the baking took in seconds</param> protected override void SurfaceObserver_OnDataReady(SurfaceData bakedData, bool outputWritten, float elapsedBakeTimeSeconds) { if (bakedData.outputMesh != null) { base.SurfaceObserver_OnDataReady(bakedData, outputWritten, elapsedBakeTimeSeconds); bakedData.outputCollider.gameObject.layer = MeshLayer; if (PhysicMaterial != null) { bakedData.outputCollider.material = PhysicMaterial; } bakedData.outputCollider.enabled = _enableCollisions; } }
/// <summary> /// Handler for RequestMeshAsync which will be used to set the material on the resulting mesh /// </summary> /// <param name="bakedData">The resulting data from the RequestMeshAsync call</param> /// <param name="outputWritten">Whether or not the output was written</param> /// <param name="elapsedBakeTimeSeconds">How long the baking took in seconds</param> protected override void SurfaceObserver_OnDataReady(SurfaceData bakedData, bool outputWritten, float elapsedBakeTimeSeconds) { if (bakedData.outputMesh != null) { GameObject go; if (SpatialMeshObjects.TryGetValue(bakedData.id, out go) && go != null) { if (go.GetComponent<MeshRenderer>() == null) { MeshRenderer meshRenderer = go.AddComponent<MeshRenderer>(); meshRenderer.receiveShadows = false; meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; } ApplyRenderingSetting(bakedData.outputMesh.GetComponent<MeshRenderer>()); } } }
/// <summary> /// Handles when a surface is added or updated by either creating the needed components or finding them in either the RemovedMeshObjects collection or the SpatialMeshObjects collection /// /// If a surface is contained in the RemovedMeshObjects collection, the enabled state for its MeshCollider or MeshRenderer is restored if appropriate. The GameObject will be moved into SpatialMeshObjects, and the RemovedMeshHolder will be removed from RemovedMeshObject /// If a surface is not found in either collection, a new GameObject will be created for it and it will be added to SpatialMeshObjects indexed by its id. /// After the GameObject is handled as appropriately, SurfaceObserver.RequestMeshAsync will be called for the appropriate settings. /// </summary> /// <param name="surfaceId">The id of the surface that was added or updated</param> /// <param name="updateTime">The time at which the surface was modified</param> /// <param name="bake">Whether or not this component should request to back a collider for the surface</param> protected virtual void HandleAdd(SurfaceId surfaceId, System.DateTime updateTime, bool bake) { if (RemovedMeshObjects.ContainsKey(surfaceId)) { SpatialMeshObjects[surfaceId] = RemovedMeshObjects[surfaceId].gameObject; if (RemovedMeshObjects[surfaceId].wasMeshColliderEnabled) { MeshCollider collider = SpatialMeshObjects[surfaceId].GetComponent<MeshCollider>(); collider.enabled = true; } if (RemovedMeshObjects[surfaceId].wasMeshRendererEnabled) { MeshRenderer mr = SpatialMeshObjects[surfaceId].GetComponent<MeshRenderer>(); mr.enabled = true; } RemovedMeshObjects.Remove(surfaceId); } else if (!SpatialMeshObjects.ContainsKey(surfaceId)) { SpatialMeshObjects[surfaceId] = new GameObject("spatial-mapping-" + surfaceId.handle); SpatialMeshObjects[surfaceId].transform.parent = this.transform; } GameObject target = SpatialMeshObjects[surfaceId]; SurfaceData sd = new SurfaceData( //the surface id returned from the system surfaceId, //the mesh filter that is populated with the spatial mapping data for this mesh (target.GetComponent<MeshFilter>() == null) ? target.AddComponent<MeshFilter>() : target.GetComponent<MeshFilter>(), //the world anchor used to position the spatial mapping mesh in the world (target.GetComponent<WorldAnchor>() == null) ? target.AddComponent<WorldAnchor>() : target.GetComponent<WorldAnchor>(), //the mesh collider that is populated with collider data for this mesh, if true is passed to bakeMeshes below (target.GetComponent<MeshCollider>() == null) ? target.AddComponent<MeshCollider>() : target.GetComponent<MeshCollider>(), //triangles per cubic meter requested for this mesh TrianglesPerCubicMeter, //bakeMeshes - if true, the mesh collider is populated, if false, the mesh collider is empty. bake ); surfaceObserver.RequestMeshAsync(sd, SurfaceObserver_OnDataReady); }
/// <summary> /// Handler for when the SurfaceObserver completes RequestMeshAsync /// /// The base class defines this function to make it easier for subclasses to modify meshes upon completion without needing to handle the actual processing /// </summary> /// <param name="bakedData">The processed SurfaceData</param> /// <param name="outputWritten">Whether or not output was written</param> /// <param name="elapsedBakeTimeSeconds">The time in seconds it took to request and populate the mesh</param> protected virtual void SurfaceObserver_OnDataReady(SurfaceData bakedData, bool outputWritten, float elapsedBakeTimeSeconds) { // Passthrough }
public bool RequestMeshAsync(SurfaceData dataRequest, SurfaceDataReadyDelegate onDataReady) { if (onDataReady == null) { throw new ArgumentNullException("onDataReady"); } if (dataRequest.outputMesh == null) { throw new ArgumentNullException("dataRequest.outputMesh"); } if (dataRequest.outputAnchor == null) { throw new ArgumentNullException("dataRequest.outputAnchor"); } if ((dataRequest.outputCollider == null) && dataRequest.bakeCollider) { throw new ArgumentException("dataRequest.outputCollider must be non-NULL if dataRequest.bakeCollider is true", "dataRequest.outputCollider"); } if (dataRequest.trianglesPerCubicMeter < 0.0) { throw new ArgumentException("dataRequest.trianglesPerCubicMeter must be greater than zero", "dataRequest.trianglesPerCubicMeter"); } bool flag = Internal_AddToWorkQueue(this.m_Observer, onDataReady, dataRequest.id.handle, dataRequest.outputMesh, dataRequest.outputAnchor, dataRequest.outputCollider, dataRequest.trianglesPerCubicMeter, dataRequest.bakeCollider); if (!flag) { Debug.LogError("RequestMeshAsync has failed. Is your surface ID valid?"); } return flag; }
/// <summary> /// Handles the SurfaceObserver's OnDataReady event. /// </summary> /// <param name="cookedData">Struct containing output data.</param> /// <param name="outputWritten">Set to true if output has been written.</param> /// <param name="elapsedCookTimeSeconds">Seconds between mesh cook request and propagation of this event.</param> private void SurfaceObserver_OnDataReady(SurfaceData cookedData, bool outputWritten, float elapsedCookTimeSeconds) { GameObject surface; if (surfaces.TryGetValue(cookedData.id.handle, out surface)) { // Set the draw material for the renderer. MeshRenderer renderer = surface.GetComponent<MeshRenderer>(); renderer.sharedMaterial = SpatialMappingManager.Instance.SurfaceMaterial; renderer.enabled = SpatialMappingManager.Instance.DrawVisualMeshes; if (SpatialMappingManager.Instance.CastShadows == false) { renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; } } surfaceWorkOutstanding = false; }
/// <summary> /// Calls GetMeshAsync to update the SurfaceData and re-activate the surface object when ready. /// </summary> /// <param name="id">Identifier of the SurfaceData object to update.</param> /// <param name="surface">The SurfaceData object to update.</param> private void QueueSurfaceDataRequest(SurfaceId id, GameObject surface) { SurfaceData surfaceData = new SurfaceData(id, surface.GetComponent<MeshFilter>(), surface.GetComponent<WorldAnchor>(), surface.GetComponent<MeshCollider>(), TrianglesPerCubicMeter, true); surfaceWorkQueue.Enqueue(surfaceData); }
/// <summary> /// Handles the SurfaceObserver's OnDataReady event. /// </summary> /// <param name="cookedData">Struct containing output data.</param> /// <param name="outputWritten">Set to true if output has been written.</param> /// <param name="elapsedCookTimeSeconds">Seconds between mesh cook request and propagation of this event.</param> private void SurfaceObserver_OnDataReady(SurfaceData cookedData, bool outputWritten, float elapsedCookTimeSeconds) { //We have new visuals, so we can disable and cleanup the older surface GameObject surfaceToCleanup; if (pendingCleanup.TryGetValue(cookedData.id.handle, out surfaceToCleanup)) { CleanupSurface(surfaceToCleanup); pendingCleanup.Remove(cookedData.id.handle); } GameObject surface; if (surfaces.TryGetValue(cookedData.id.handle, out surface)) { // Set the draw material for the renderer. MeshRenderer renderer = surface.GetComponent<MeshRenderer>(); renderer.sharedMaterial = SpatialMappingManager.Instance.SurfaceMaterial; renderer.enabled = SpatialMappingManager.Instance.DrawVisualMeshes; if (SpatialMappingManager.Instance.CastShadows == false) { renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; } } surfaceWorkOutstanding = false; SurfaceObserver.SurfaceDataReadyDelegate dataReady = DataReady; if (dataReady != null) { dataReady(cookedData, outputWritten, elapsedCookTimeSeconds); } }
/// <summary> /// Updates an element of the behavior's mesh list from an element of the the spatial mapping's surface object list. /// Element will be either added or updated to match up to the surfaceObject list. /// </summary> /// <param name="surfaceId">The unique ID for the mesh (matches the id provided by spatial mapping)</param> /// <param name="surfaceObjectIndex">Index in the surfaceObjects list</param> /// <param name="surfaceObjects">The list of surfaceObjects</param> /// <param name="meshDataIndex">Index into the locally stored mesh data list</param> private void AddOrUpdateMeshInList( SurfaceData bakedData) { SurfaceId surfaceId = bakedData.id; MeshFilter meshFilter = bakedData.outputMesh; int meshDataIndex = FindMeshIndexInInputMeshList(surfaceId.handle); SpatialUnderstandingDll.MeshData meshData = new SpatialUnderstandingDll.MeshData(); int meshUpdateID = (meshDataIndex >= 0) ? (inputMeshList[meshDataIndex].LastUpdateID + 1) : 1; if ((meshFilter != null) && (meshFilter.mesh != null) && (meshFilter.mesh.triangles.Length > 0)) { // Fix surface mesh normals so we can get correct plane orientation. meshFilter.mesh.RecalculateNormals(); // Convert meshData.CopyFrom(meshFilter, surfaceId.handle, meshUpdateID); } else { // No filter yet, add as an empty mesh (will be updated later in the update loop) meshData.CopyFrom(null, surfaceId.handle, meshUpdateID); } // And add it (unless an index of an update item is specified) if (meshDataIndex < 0) { inputMeshList.Add(meshData); } else { inputMeshList[meshDataIndex] = meshData; } }
protected override void OnSurfaceDataReady(SpatialMappingBase requester, SurfaceData bakedData, bool outputWritten, float elapsedBakeTimeSeconds) { SpatialMappingBase.Surface surface; if (base.surfaceObjects.TryGetValue(bakedData.id.handle, out surface)) { surface.awaitingBake = false; if (outputWritten) { if (surface.gameObject == null) { Debug.LogError(string.Format("A SpatialMappingRenderer component can not apply baked data to a surface with id \"{0}\" because its GameObject is null.", bakedData.id.handle)); } else { if (requester != this) { base.CloneBakedComponents(bakedData, surface); } if (surface.meshRenderer == null) { surface.meshRenderer = surface.gameObject.GetComponent<MeshRenderer>(); if (surface.meshRenderer == null) { surface.meshRenderer = surface.gameObject.AddComponent<MeshRenderer>(); } surface.meshRenderer.receiveShadows = false; surface.meshRenderer.shadowCastingMode = ShadowCastingMode.Off; } this.ApplyRenderSettings(surface.meshRenderer); } } } }