private async Task <MeshGenerationResult> GenerateMeshAsync(MlMeshing2.MLMeshingBlockInfo meshInfo, SpatialMeshObject spatialMeshObject) { int levelOfDetail = (int)MeshLevelOfDetail; if (levelOfDetail < 0) { Debug.LogWarning($"{MeshLevelOfDetail} is unsupported! Falling back to low level of detail."); levelOfDetail = 0; } var blockRequest = new MlMeshing2.MLMeshingBlockRequest { id = meshInfo.id, level = (MlMeshing2.MLMeshingLOD)levelOfDetail }; var meshRequest = new MlMeshing2.MLMeshingMeshRequest { request_count = 1, data = blockRequest }; if (!MlMeshing2.MLMeshingRequestMesh(meshingClientHandle, in meshRequest, out var outRequestHandle).IsOk) { Debug.LogError("Failed to request a new mesh!"); return(new MeshGenerationResult(meshInfo.id, MlMeshing2.MLMeshingResult.Failed)); } var meshRequestResult = new MlApi.MLResult(MlApi.MLResult.Code.Pending); var outMeshResult = new MlMeshing2.MLMeshingMesh { result = MlMeshing2.MLMeshingResult.Pending }; await Awaiters.BackgroundThread; while (meshRequestResult.Value == MlApi.MLResult.Code.Pending) { meshRequestResult = MlMeshing2.MLMeshingGetMeshResult(meshingClientHandle, outRequestHandle, out outMeshResult); await Task.Delay(25); // TODO make this delay configurable? } await Awaiters.UnityMainThread; if (!meshRequestResult.IsOk || outMeshResult.result == MlMeshing2.MLMeshingResult.Failed || !MlMeshing2.MLMeshingFreeResource(meshingClientHandle, outRequestHandle).IsOk) { return(new MeshGenerationResult(meshInfo.id, MlMeshing2.MLMeshingResult.Failed)); } if (outMeshResult.data_count != meshRequest.request_count) { Debug.LogError($"Mesh Block count mismatch! Expected {meshRequest.request_count} but got {outMeshResult.data_count} blocks."); return(new MeshGenerationResult(meshInfo.id, MlMeshing2.MLMeshingResult.Failed)); } if (meshInfo.id != outMeshResult.data.id) { Debug.LogError($"Mesh info id mismatch!\n->{meshInfo.id}\n<-{outMeshResult.data.id}"); return(new MeshGenerationResult(meshInfo.id, MlMeshing2.MLMeshingResult.Failed)); } var mesh = spatialMeshObject.Mesh == null ? new Mesh() : spatialMeshObject.Mesh; mesh.name = $"Mesh_{meshInfo.id}"; if (outMeshResult.data.vertex_count == 0 || outMeshResult.data.vertex == null || outMeshResult.data.index_count == 0 || outMeshResult.data.index == null) { return(new MeshGenerationResult(meshInfo.id, outMeshResult.result)); } await Awaiters.BackgroundThread; if (MeshRecalculateNormals) { var normals = new NativeArray <VertexData>((int)outMeshResult.data.vertex_count, Allocator.None); for (int i = 0; i < normals.Length; i++) { normals[i] = new VertexData { Position = outMeshResult.data.vertex[i], Normal = outMeshResult.data.normal[i] }; } mesh.SetVertexBufferParams((int)outMeshResult.data.vertex_count, NormalsLayout); mesh.SetVertexBufferData(normals, 0, 0, (int)outMeshResult.data.vertex_count); normals.Dispose(); } else { var vertices = new NativeArray <Vector3>((int)outMeshResult.data.vertex_count, Allocator.None); for (int i = 0; i < vertices.Length; i++) { vertices[i] = outMeshResult.data.vertex[i]; } mesh.SetVertexBufferParams((int)outMeshResult.data.vertex_count, VertexLayout); mesh.SetVertexBufferData(vertices, 0, 0, (int)outMeshResult.data.vertex_count); vertices.Dispose(); } var indices = new NativeArray <short>(outMeshResult.data.index_count, Allocator.None); for (int i = 0; i < outMeshResult.data.index_count; i++) { indices[i] = (short)outMeshResult.data.index[i]; } mesh.SetIndexBufferParams(outMeshResult.data.index_count, IndexFormat.UInt16); mesh.SetIndexBufferData(indices, 0, 0, outMeshResult.data.index_count); indices.Dispose(); mesh.SetSubMesh(0, new SubMeshDescriptor(0, outMeshResult.data.index_count)); mesh.Optimize(); mesh.RecalculateBounds(); if (MeshRecalculateNormals) { mesh.RecalculateNormals(); } spatialMeshObject.Mesh = mesh; await Awaiters.UnityMainThread; return(new MeshGenerationResult(meshInfo.id, outMeshResult.result)); }
private async void MeshInfo_Update(MlMeshing2.MLMeshingBlockInfo meshInfo) { if (meshInfo.state == MlMeshing2.MLMeshingMeshState.Unchanged) { return; } // If we're adding or updating a mesh if (meshInfo.state != MlMeshing2.MLMeshingMeshState.Deleted) { var spatialMeshObject = await RequestSpatialMeshObject(meshInfo.id.ToGuid()); MeshGenerationResult meshResult; try { meshResult = await GenerateMeshAsync(meshInfo, spatialMeshObject); } catch (Exception e) { Debug.LogError(e); RaiseMeshRemoved(spatialMeshObject); return; } if (meshResult.Status == MlMeshing2.MLMeshingResult.Pending) { return; } if (meshResult.Status != MlMeshing2.MLMeshingResult.Success) { Debug.LogWarning($"No output for {meshResult.Id} | {meshResult.Status}"); RaiseMeshRemoved(spatialMeshObject); return; } if (!SpatialMeshObjects.TryGetValue(meshResult.Id.ToGuid(), out var meshObject)) { Debug.LogWarning($"Failed to find a spatial mesh object for {meshResult.Id}!"); // Likely it was removed before data could be cooked. return; } // Apply the appropriate material to the mesh. var displayOption = MeshDisplayOption; if (displayOption != SpatialMeshDisplayOptions.None) { meshObject.Collider.enabled = true; meshObject.Renderer.enabled = displayOption == SpatialMeshDisplayOptions.Visible || displayOption == SpatialMeshDisplayOptions.Occlusion; meshObject.Renderer.sharedMaterial = displayOption == SpatialMeshDisplayOptions.Visible ? MeshVisibleMaterial : MeshOcclusionMaterial; } else { meshObject.Renderer.enabled = false; meshObject.Collider.enabled = false; } if (!meshObject.GameObject.activeInHierarchy) { meshObject.GameObject.SetActive(true); } switch (meshInfo.state) { case MlMeshing2.MLMeshingMeshState.New: RaiseMeshAdded(meshObject); break; case MlMeshing2.MLMeshingMeshState.Updated: RaiseMeshUpdated(meshObject); break; } } else if (SpatialMeshObjects.TryGetValue(meshInfo.id.ToGuid(), out var meshObject)) { RaiseMeshRemoved(meshObject); } }