Example #1
0
        /// <summary>
        /// Creates a <see cref="SpatialMeshObject"/>.
        /// </summary>
        /// <param name="mesh"></param> todo: add comments
        /// <param name="name"></param>
        /// <param name="meshId"></param>
        /// <returns>
        /// SpatialMeshObject containing the fields that describe the mesh.
        /// </returns>
        protected SpatialMeshObject CreateSpatialMeshObject(
            Mesh mesh,
            string name,
            int meshId)
        {
            SpatialMeshObject newMesh = new SpatialMeshObject();

            newMesh.Id               = meshId;
            newMesh.GameObject       = new GameObject(name, requiredMeshComponents);
            newMesh.GameObject.layer = MixedRealityToolkit.SpatialAwarenessSystem.MeshPhysicsLayer;

            newMesh.Filter            = newMesh.GameObject.GetComponent <MeshFilter>();
            newMesh.Filter.sharedMesh = mesh;

            newMesh.Renderer = newMesh.GameObject.GetComponent <MeshRenderer>();

            // 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.
            newMesh.Collider            = newMesh.GameObject.GetComponent <MeshCollider>();
            newMesh.Collider.sharedMesh = null;
            newMesh.Collider.sharedMesh = newMesh.Filter.sharedMesh;

            return(newMesh);
        }
Example #2
0
        /// <summary>
        /// Clean up the resources associated with the surface.
        /// </summary>
        /// <param name="meshObject">The <see cref="SpatialMeshObject"/> whose resources will be cleaned up.</param>
        /// <param name="destroyGameObject"></param>
        /// <param name="destroyMeshes"></param>
        protected void CleanupMeshObject(
            SpatialMeshObject meshObject,
            bool destroyGameObject = true,
            bool destroyMeshes     = true)
        {
            if (destroyGameObject && (meshObject.GameObject != null))
            {
                Object.Destroy(meshObject.GameObject);
                meshObject.GameObject = null;
            }

            Mesh filterMesh   = meshObject.Filter.sharedMesh;
            Mesh colliderMesh = meshObject.Collider.sharedMesh;

            if (destroyMeshes)
            {
                if (filterMesh != null)
                {
                    Object.Destroy(filterMesh);
                    meshObject.Filter.sharedMesh = null;
                }

                if ((colliderMesh != null) && (colliderMesh != filterMesh))
                {
                    Object.Destroy(colliderMesh);
                    meshObject.Collider.sharedMesh = null;
                }
            }
        }
        /// <inheritdoc />
        public void RaiseMeshUpdated(IMixedRealitySpatialMeshObserver observer, SpatialMeshObject spatialMeshObject)
        {
            // Parent the mesh object
            spatialMeshObject.GameObject.transform.parent = SpatialMeshesParent.transform;

            meshEventData.Initialize(observer, spatialMeshObject.Id, spatialMeshObject);
            HandleEvent(meshEventData, OnMeshUpdated);
        }
Example #4
0
        /// <summary>
        /// Reclaims the <see cref="SpatialMeshObject"/> to allow for later reuse.
        /// </summary>
        /// <param name="availableMeshObject"></param>
        protected void ReclaimMeshObject(SpatialMeshObject availableMeshObject)
        {
            if (!spareMeshObject.HasValue)
            {
                // Cleanup the mesh object.
                // Do not destroy the game object, destroy the meshes.
                CleanupMeshObject(availableMeshObject, false);

                availableMeshObject.GameObject.name = "Unused Spatial Mesh";
                availableMeshObject.GameObject.SetActive(false);

                spareMeshObject = availableMeshObject;
            }
            else
            {
                // Cleanup the mesh object.
                // Destroy the game object, destroy the meshes.
                CleanupMeshObject(availableMeshObject);
            }
        }
 /// <inheritdoc />
 public void RaiseMeshRemoved(IMixedRealitySpatialMeshObserver observer, SpatialMeshObject spatialMeshObject)
 {
     meshEventData.Initialize(observer, spatialMeshObject.Id, spatialMeshObject);
     HandleEvent(meshEventData, OnMeshRemoved);
 }
Example #6
0
        private async Task GenerateMeshAsync(SpatialSurfaceInfo meshInfo, SpatialMeshObject spatialMeshObject)
        {
            spatialMeshObject.LastUpdated = meshInfo.UpdateTime;

            // TODO Check if the spatialSurfaceMeshOptions are correct for what we need.
            var spatialSurfaceMesh = await meshInfo.TryComputeLatestMeshAsync(TrianglesPerCubicMeter, spatialSurfaceMeshOptions);

            await Awaiters.UnityMainThread;

            var mesh = spatialMeshObject.Mesh == null ? new Mesh() : spatialMeshObject.Mesh;

            mesh.name = $"Mesh_{meshInfo.Id}";

            await Awaiters.BackgroundThread;

            if (MeshRecalculateNormals)
            {
                var normalCount  = (int)spatialSurfaceMesh.VertexNormals.ElementCount;
                var normals      = new NativeArray <VertexData>(normalCount, Allocator.None);
                var vertexBuffer = DataReader.FromBuffer(spatialSurfaceMesh.VertexPositions.Data);
                var normalBuffer = DataReader.FromBuffer(spatialSurfaceMesh.VertexNormals.Data);

                for (int i = 0; i < normalCount; i++)
                {
                    normals[i] = new VertexData
                    {
                        // TODO Check if spatialSurfaceMesh.VertexPositionScale needs to be accounted for.
                        Position = new Vector3(vertexBuffer.ReadSingle(), vertexBuffer.ReadSingle(), -vertexBuffer.ReadSingle()),
                        Normal   = new Vector3(normalBuffer.ReadSingle(), normalBuffer.ReadSingle(), -normalBuffer.ReadSingle())
                    };
                }

                mesh.SetVertexBufferParams(normalCount, NormalsLayout);
                mesh.SetVertexBufferData(normals, 0, 0, normalCount);

                vertexBuffer.Dispose();
                normalBuffer.Dispose();
                normals.Dispose();
            }
            else
            {
                var vertexCount  = (int)spatialSurfaceMesh.VertexPositions.ElementCount;
                var vertices     = new NativeArray <Vector3>(vertexCount, Allocator.None);
                var vertexBuffer = DataReader.FromBuffer(spatialSurfaceMesh.VertexPositions.Data);

                for (int i = 0; i < vertexCount; i++)
                {
                    // TODO Check if spatialSurfaceMesh.VertexPositionScale needs to be accounted for.
                    vertices[i] = new Vector3(vertexBuffer.ReadSingle(), vertexBuffer.ReadSingle(), -vertexBuffer.ReadSingle());
                }

                mesh.SetVertexBufferParams(vertexCount, VertexLayout);
                mesh.SetVertexBufferData(vertices, 0, 0, vertexCount);

                vertexBuffer.Dispose();
                vertices.Dispose();
            }

            var indicesCount  = (int)spatialSurfaceMesh.TriangleIndices.ElementCount;
            var indices       = new NativeArray <short>(indicesCount, Allocator.None);
            var indicesBuffer = DataReader.FromBuffer(spatialSurfaceMesh.TriangleIndices.Data);

            for (int i = 0; i < indicesCount; i++)
            {
                indices[i] = indicesBuffer.ReadInt16();
            }

            mesh.SetIndexBufferParams(indicesCount, IndexFormat.UInt16);
            mesh.SetIndexBufferData(indices, 0, 0, indicesCount);

            indicesBuffer.Dispose();
            indices.Dispose();

            mesh.SetSubMesh(0, new SubMeshDescriptor(0, indicesCount));
            mesh.Optimize();
            mesh.RecalculateBounds();

            if (MeshRecalculateNormals)
            {
                mesh.RecalculateNormals();
            }

            spatialMeshObject.Mesh = mesh;

            await Awaiters.UnityMainThread;
        }
Example #7
0
        /// <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)
        {
            if (!IsRunning)
            {
                return;
            }

            if (!outstandingMeshObject.HasValue)
            {
                Debug.LogWarning($"OnDataReady called for mesh id {cookedData.id.handle} while no request was outstanding.");
                return;
            }

            if (!outputWritten)
            {
                Debug.LogWarning($"OnDataReady reported no data written for mesh id {cookedData.id.handle}");
                ReclaimMeshObject(outstandingMeshObject.Value);
                outstandingMeshObject = null;
                return;
            }

            // Since there is only one outstanding mesh object, update the id to match
            // the one received after baking.
            SpatialMeshObject meshObject = outstandingMeshObject.Value;

            meshObject.Id         = cookedData.id.handle;
            outstandingMeshObject = null;

            // Apply the appropriate material to the mesh.
            SpatialMeshDisplayOptions displayOption = SpatialAwarenessSystem.MeshDisplayOption;

            if (displayOption != SpatialMeshDisplayOptions.None)
            {
                meshObject.Renderer.enabled        = true;
                meshObject.Renderer.sharedMaterial = (displayOption == SpatialMeshDisplayOptions.Visible) ?
                                                     SpatialAwarenessSystem.MeshVisibleMaterial :
                                                     SpatialAwarenessSystem.MeshOcclusionMaterial;
            }
            else
            {
                meshObject.Renderer.enabled = false;
            }

            // Recalculate the mesh normals if requested.
            if (SpatialAwarenessSystem.MeshRecalculateNormals)
            {
                meshObject.Filter.sharedMesh.RecalculateNormals();
            }

            // Add / update the mesh to our collection
            bool sendUpdatedEvent = false;

            if (meshObjects.ContainsKey(cookedData.id.handle))
            {
                // Reclaim the old mesh object for future use.
                ReclaimMeshObject(meshObjects[cookedData.id.handle]);
                meshObjects.Remove(cookedData.id.handle);

                sendUpdatedEvent = true;
            }
            meshObjects.Add(cookedData.id.handle, meshObject);

            if (sendUpdatedEvent)
            {
                SpatialAwarenessSystem.RaiseMeshUpdated(cookedData.id.handle, meshObject.GameObject);
            }
            else
            {
                SpatialAwarenessSystem.RaiseMeshAdded(cookedData.id.handle, meshObject.GameObject);
            }
        }
Example #8
0
        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));
        }