Example #1
0
        /// <summary>
        /// Handles the SurfaceObserver's OnSurfaceChanged event.
        /// </summary>
        /// <param name="surfaceId">The identifier assigned to the surface which has changed.</param>
        /// <param name="changeType">The type of change that occurred on the surface.</param>
        /// <param name="bounds">The bounds of the surface.</param>
        /// <param name="updateTime">The date and time at which the change occurred.</param>
        private async void SurfaceObserver_OnSurfaceChanged(SurfaceId surfaceId, SurfaceChange changeType, Bounds bounds, DateTime updateTime)
        {
            // If we're adding or updating a mesh
            if (changeType != SurfaceChange.Removed)
            {
                var spatialMeshObject = await RequestSpatialMeshObject(surfaceId.handle);

                spatialMeshObject.GameObject.name = $"SpatialMesh_{surfaceId.handle.ToString()}";
                var worldAnchor = spatialMeshObject.GameObject.EnsureComponent <WorldAnchor>();
                var surfaceData = new SurfaceData(surfaceId, spatialMeshObject.Filter, worldAnchor, spatialMeshObject.Collider, MeshTrianglesPerCubicMeter, true);

                if (!observer.RequestMeshAsync(surfaceData, OnDataReady))
                {
                    Debug.LogError($"Mesh request failed for spatial observer with Id {surfaceId.handle.ToString()}");
                    RaiseMeshRemoved(spatialMeshObject);
                }

                void OnDataReady(SurfaceData cookedData, bool outputWritten, float elapsedCookTimeSeconds)
                {
                    if (!outputWritten)
                    {
                        Debug.LogWarning($"No output for {cookedData.id.handle}");
                        return;
                    }

                    if (!SpatialMeshObjects.TryGetValue(cookedData.id.handle, out SpatialMeshObject meshObject))
                    {
                        // Likely it was removed before data could be cooked.
                        return;
                    }

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

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

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

                    meshObject.GameObject.SetActive(true);

                    switch (changeType)
                    {
                    case SurfaceChange.Added:
                        RaiseMeshAdded(meshObject);
                        break;

                    case SurfaceChange.Updated:
                        RaiseMeshUpdated(meshObject);
                        break;
                    }
                }
            }
            else if (SpatialMeshObjects.TryGetValue(surfaceId.handle, out SpatialMeshObject meshObject))
            {
                RaiseMeshRemoved(meshObject);
            }
        }
Example #2
0
        /// <summary>
        /// Called once per frame.
        /// </summary>
        private void Update()
        {
            if ((ObserverState == ObserverStates.Running) && (outstandingMeshRequest == null))
            {
                if (surfaceWorkQueue.Count > 0)
                {
                    // We're using a simple first-in-first-out rule for requesting meshes, but a more sophisticated algorithm could prioritize
                    // the queue based on distance to the user or some other metric.
                    SurfaceId surfaceID = surfaceWorkQueue.Dequeue();

                    string surfaceName = ("Surface-" + surfaceID.handle);

                    SurfaceObject newSurface;
                    WorldAnchor   worldAnchor;

                    if (spareSurfaceObject == null)
                    {
                        newSurface = CreateSurfaceObject(
                            mesh: null,
                            objectName: surfaceName,
                            parentObject: transform,
                            meshID: surfaceID.handle,
                            drawVisualMeshesOverride: false
                            );

                        worldAnchor = newSurface.Object.AddComponent <WorldAnchor>();
                    }
                    else
                    {
                        newSurface         = spareSurfaceObject.Value;
                        spareSurfaceObject = null;

                        Debug.Assert(!newSurface.Object.activeSelf);
                        newSurface.Object.SetActive(true);

                        Debug.Assert(newSurface.Filter.sharedMesh == null);
                        Debug.Assert(newSurface.Collider.sharedMesh == null);
                        newSurface.Object.name = surfaceName;
                        Debug.Assert(newSurface.Object.transform.parent == transform);
                        newSurface.ID = surfaceID.handle;
                        newSurface.Renderer.enabled = false;

                        worldAnchor = newSurface.Object.GetComponent <WorldAnchor>();
                        Debug.Assert(worldAnchor != null);
                    }

                    var surfaceData = new SurfaceData(
                        surfaceID,
                        newSurface.Filter,
                        worldAnchor,
                        newSurface.Collider,
                        TrianglesPerCubicMeter,
                        _bakeCollider: true
                        );

                    if (observer.RequestMeshAsync(surfaceData, SurfaceObserver_OnDataReady))
                    {
                        outstandingMeshRequest = newSurface;
                    }
                    else
                    {
                        Debug.LogErrorFormat("Mesh request for failed. Is {0} a valid Surface ID?", surfaceID.handle);

                        Debug.Assert(outstandingMeshRequest == null);
                        ReclaimSurface(newSurface);
                    }
                }
                else if ((Time.unscaledTime - updateTime) >= TimeBetweenUpdates)
                {
                    observer.Update(SurfaceObserver_OnSurfaceChanged);
                    updateTime = Time.unscaledTime;
                }
            }
        }
        /// <summary>
        /// Issue a request to the Surface Observer to begin baking the mesh.
        /// </summary>
        /// <param name="surfaceId">ID of the mesh to bake.</param>
        private void RequestMesh(SurfaceId surfaceId)
        {
            using (RequestMeshPerfMarker.Auto())
            {
                string meshName = ("SpatialMesh - " + surfaceId.handle);

                SpatialAwarenessMeshObject newMesh;
                WorldAnchor worldAnchor;

                if (spareMeshObject == null)
                {
                    newMesh = SpatialAwarenessMeshObject.Create(
                        null,
                        MeshPhysicsLayer,
                        meshName,
                        surfaceId.handle,
                        ObservedObjectParent);

                    // The WorldAnchor component places its object where the anchor is in the same space as the camera.
                    // But since the camera is repositioned by the MixedRealityPlayspace's transform, the meshes' transforms
                    // should also the WorldAnchor position repositioned by the MixedRealityPlayspace's transform.
                    // So rather than put the WorldAnchor on the mesh's GameObject, the WorldAnchor is placed out of the way in the scene,
                    // and its transform is concatenated with the Playspace transform to compute the transform on the mesh's object.
                    // That adapting the WorldAnchor's transform into playspace is done by the internal PlayspaceAdapter component.
                    // The GameObject the WorldAnchor is placed on is unimportant, but it is convenient for cleanup to make it a child
                    // of the GameObject whose transform will track it.
                    GameObject anchorHolder = new GameObject(meshName + "_anchor");
                    anchorHolder.AddComponent <PlayspaceAdapter>();          // replace with required component?
                    worldAnchor = anchorHolder.AddComponent <WorldAnchor>(); // replace with required component and GetComponent()?
                    anchorHolder.transform.SetParent(newMesh.GameObject.transform, false);
                }
                else
                {
                    newMesh         = spareMeshObject;
                    spareMeshObject = null;

                    newMesh.GameObject.name = meshName;
                    newMesh.Id = surfaceId.handle;
                    newMesh.GameObject.SetActive(true);

                    // There should be exactly one child on the newMesh.GameObject, and that is the GameObject added above
                    // to hold the WorldAnchor component and adapter.
                    Debug.Assert(newMesh.GameObject.transform.childCount == 1, "Expecting a single child holding the WorldAnchor");
                    worldAnchor = newMesh.GameObject.transform.GetChild(0).gameObject.GetComponent <WorldAnchor>();
                }

                Debug.Assert(worldAnchor != null);

                SurfaceData surfaceData = new SurfaceData(
                    surfaceId,
                    newMesh.Filter,
                    worldAnchor,
                    newMesh.Collider,
                    TrianglesPerCubicMeter,
                    true);

                if (observer.RequestMeshAsync(surfaceData, SurfaceObserver_OnDataReady))
                {
                    outstandingMeshObject = newMesh;
                }
                else
                {
                    Debug.LogError($"Mesh request failed for Id == surfaceId.handle");
                    outstandingMeshObject = null;
                    ReclaimMeshObject(newMesh);
                }
            }
        }
Example #4
0
        /// <summary>
        /// Handles the SurfaceObserver's OnSurfaceChanged event.
        /// </summary>
        /// <param name="id">The identifier assigned to the surface which has changed.</param>
        /// <param name="changeType">The type of change that occurred on the surface.</param>
        /// <param name="bounds">The bounds of the surface.</param>
        /// <param name="updateTime">The date and time at which the change occurred.</param>
        private void SurfaceObserver_OnSurfaceChanged(SurfaceId id, SurfaceChange changeType, Bounds bounds, System.DateTime updateTime)
        {
            // Verify that the client of the Surface Observer is expecting updates.
            if (ObserverState != ObserverStates.Running)
            {
                return;
            }

            GameObject surface;

            switch (changeType)
            {
            // Adding and updating are nearly identical.  The only difference is if a new gameobject to contain
            // the surface needs to be created.
            case SurfaceChange.Added:
            case SurfaceChange.Updated:
                // Check to see if the surface is known to the observer.
                // If so, we want to add it for cleanup after we get new meshes
                // We do this because Unity doesn't properly cleanup baked collision data
                if (surfaces.TryGetValue(id.handle, out surface))
                {
                    pendingCleanup.Add(id.handle, surface);
                    surfaces.Remove(id.handle);
                }

                // Get an available surface object ready to be used
                surface = GetSurfaceObject(id.handle, transform);

                // Add the surface to our dictionary of known surfaces so
                // we can interact with it later.
                surfaces.Add(id.handle, surface);

                // Add the request to create the mesh for this surface to our work queue.
                QueueSurfaceDataRequest(id, surface);
                break;

            case SurfaceChange.Removed:
                // Always process surface removal events.
                // This code can be made more thread safe
                if (surfaces.TryGetValue(id.handle, out surface))
                {
                    surfaces.Remove(id.handle);
                    CleanupSurface(surface);
                    RemoveSurfaceObject(surface, false);
                }
                break;
            }

            //List<MeshFilter> filters = SpatialMappingManager.Instance.GetMeshFilters();

            //CombineInstance[] combine = new CombineInstance[filters.Count];

            //int i = 0;
            //while (i < filters.Count)
            //{
            //    combine[i].mesh = filters[i].mesh;
            //    combine[i].transform = filters[i].transform.localToWorldMatrix;
            //    i++;
            //}

            //Mesh mesh = new Mesh();

            //mesh.CombineMeshes(combine);

            //byte[] serialized = MeshSerializer.WriteMesh(mesh, true);

            //GameObject obj = PhotonView.Find("MyPrefabName");

            //PhotonView photonView = PhotonView.Get(obj);

            //if (photonView)
            //{
            //    photonView.RPC("GetStreamData", RpcTarget.All, serialized);
            //}


            // Event
            if (SurfaceChanged != null)
            {
                SurfaceChanged(id, changeType, bounds, updateTime);
            }
        }
    // This handler receives surface changed events and is propagated by the
    // Update method on SurfaceObserver.
    void SurfaceChangedHandler(SurfaceId id, SurfaceChange changeType, Bounds bounds, DateTime updateTime)
    {
        SurfaceEntry entry;

        switch (changeType)
        {
        case SurfaceChange.Added:
        case SurfaceChange.Updated:
            if (m_Surfaces.TryGetValue(id.handle, out entry))
            {
                // If this surface has already been baked, mark it as needing bake
                // in addition to the update time so the "next surface to bake"
                // logic will order it correctly.
                if (entry.m_BakedState == BakedState.Baked)
                {
                    entry.m_BakedState = BakedState.UpdatePostBake;
                    entry.m_UpdateTime = updateTime;

                    //send mesh to the ground staton. //old way

                    /*
                     * NetworkMeshSource.getSingleton().sendMesh(entry.m_Surface.GetComponent<MeshFilter>().mesh,
                     *  entry.m_Surface.transform.position,
                     *  entry.m_Surface.transform.rotation);
                     */
                }
            }
            else
            {
                // This is a brand new surface so create an entry for it.
                entry = new SurfaceEntry();
                entry.m_BakedState = BakedState.NeverBaked;
                entry.m_UpdateTime = updateTime;
                entry.m_Id         = id.handle;
                entry.m_Surface    = new GameObject(System.String.Format("Surface-{0}", id.handle));
                entry.m_Surface.AddComponent <MeshFilter>();
                entry.m_Surface.AddComponent <MeshCollider>();
                MeshRenderer mr = entry.m_Surface.AddComponent <MeshRenderer>();
                mr.shadowCastingMode = ShadowCastingMode.Off;
                mr.receiveShadows    = false;
                entry.m_Surface.AddComponent <WorldAnchor>();
                entry.m_Surface.GetComponent <MeshRenderer>().sharedMaterial = m_drawMat;
                m_Surfaces[id.handle] = entry;
                if (!SurfacesList.Contains(entry))
                {
                    SurfacesList.Add(entry);
                }
            }
            break;

        case SurfaceChange.Removed:
            if (m_Surfaces.TryGetValue(id.handle, out entry))
            {
                m_Surfaces.Remove(id.handle);
                Mesh mesh = entry.m_Surface.GetComponent <MeshFilter>().mesh;
                if (mesh)
                {
                    Destroy(mesh);
                }
                Destroy(entry.m_Surface);
            }
            break;
        }
    }
Example #6
0
        /// <summary>
        /// This handler receives events when surfaces change, and propagates those events
        /// using the SurfaceObserver’s Update method
        /// </summary>
        /// <param name="id">Handle identifying the surface</param>
        /// <param name="changeType">Reason for update</param>
        /// <param name="bounds">New bounds of th esurface</param>
        /// <param name="updateTime">Time stamp of modification.</param>
        private void SurfaceChangedHandler(SurfaceId id, SurfaceChange changeType, Bounds bounds, DateTime updateTime)
        {
            SurfaceEntry entry;

            switch (changeType)
            {
            case SurfaceChange.Added:
            case SurfaceChange.Updated:
                if (surfaces.TryGetValue(id.handle, out entry))
                {
                    // If the system has already baked this Surface, lower its priority.
                    if (entry.currentState == BakedState.Baked)
                    {
                        entry.currentState = BakedState.UpdatePostBake;
                        entry.updateTime   = updateTime;
                    }
                }
                else
                {
                    // This is a brand new Surface so create an entry for it.
                    entry = new SurfaceEntry();
                    entry.currentState        = BakedState.NeverBaked;
                    entry.updateTime          = updateTime;
                    entry.handle              = id.handle;
                    entry.surfaceObject       = new GameObject(System.String.Format("Surface-{0}", id.handle));
                    entry.surfaceObject.layer = spatialMappingLayer;
                    if (HangerObject != null)
                    {
                        entry.surfaceObject.transform.SetParent(HangerObject, false);
                    }
                    entry.surfaceObject.AddComponent <MeshFilter>();
                    if (Collide)
                    {
                        entry.surfaceObject.AddComponent <MeshCollider>();
                    }
                    MeshRenderer mr = entry.surfaceObject.AddComponent <MeshRenderer>();
                    mr.shadowCastingMode   = ShadowCastingMode.Off;
                    mr.receiveShadows      = false;
                    mr.sharedMaterial      = DrawMaterial;
                    mr.enabled             = this.Display;
                    entry.worldAnchorChild = new GameObject(entry.surfaceObject.name + "WorldAnchor");
                    entry.worldAnchorChild.transform.SetParent(entry.surfaceObject.transform, false);
                    entry.worldAnchorChild.AddComponent <WorldAnchor>();
                    // Add an adapter component to keep the surface object where the WorldAnchor means it to be.
                    var adapter = entry.surfaceObject.AddComponent <WorldAnchorAdapter>();
                    adapter.TargetObject      = entry.surfaceObject.transform;
                    adapter.WorldAnchorObject = entry.worldAnchorChild;
                    surfaces[id.handle]       = entry;
                }
                break;

            case SurfaceChange.Removed:
                if (surfaces.TryGetValue(id.handle, out entry))
                {
                    surfaces.Remove(id.handle);
                    Destroy(entry.surfaceObject);
                    // Note entry.worldAnchorChild is child of surfaceObject, so will get destroyed
                    // along with components.
                }
                break;
            }
        }