/// <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(UnityEngine.XR.WSA.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 meshRenderer = surface.GetComponent <MeshRenderer>();
                meshRenderer.sharedMaterial = SpatialMappingManager.Instance.SurfaceMaterial;
                meshRenderer.enabled        = SpatialMappingManager.Instance.DrawVisualMeshes;

                if (SpatialMappingManager.Instance.CastShadows == false)
                {
                    meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
                }
            }

            surfaceWorkOutstanding = false;
            UnityEngine.XR.WSA.SurfaceObserver.SurfaceDataReadyDelegate dataReady = DataReady;
            if (dataReady != null)
            {
                dataReady(cookedData, outputWritten, elapsedCookTimeSeconds);
            }
        }
 private bool IsMatchingSurface(SurfaceObject surfaceObject, UnityEngine.XR.WSA.SurfaceData surfaceData)
 {
     return((surfaceObject.ID == surfaceData.id.handle) &&
            (surfaceObject.Filter == surfaceData.outputMesh) &&
            (surfaceObject.Collider == surfaceData.outputCollider)
            );
 }
Esempio n. 3
0
        /// <summary>
        /// Attempt to attach an <see cref="InteractableFacade"/> found in the given <see cref="SurfaceData"/> to this <see cref="InteractorFacade"/>.
        /// </summary>
        /// <param name="data">The collision data containing a valid Interactable.</param>
        public virtual void Grab(UnityEngine.XR.WSA.SurfaceData data)
        {
            if (data == null || data.CollisionData.transform == null)
            {
                return;
            }

            Grab(data.CollisionData.transform.gameObject.TryGetComponent <InteractableFacade>(true, true), null, null);
        }
Esempio n. 4
0
        /// <summary>
        /// Gets a <see cref="DestinationLocation"/> if one exists in the given <see cref="SurfaceData"/> colliding transform or parent.
        /// </summary>
        /// <param name="data">The data to check.</param>
        /// <returns>The found <see cref="DestinationLocation"/>.</returns>
        protected virtual DestinationLocation GetLocation(UnityEngine.XR.WSA.SurfaceData data)
        {
            if (data == null || data.CollisionData.transform == null)
            {
                return(null);
            }

            return(data.CollisionData.transform.gameObject.TryGetComponent <DestinationLocation>(false, true));
        }
        public virtual void SetSource(UnityEngine.XR.WSA.SurfaceData source)
        {
            if (source == null || source.Transform == null)
            {
                return;
            }

            Source = source.Transform.GetComponentInParent <PointerFacade>();
        }
        /// <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(UnityEngine.XR.WSA.SurfaceData bakedData, bool outputWritten, float elapsedBakeTimeSeconds)
        {
            if (!outputWritten)
            {
                return;
            }

            AddOrUpdateMeshInList(bakedData);
        }
        /// <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(UnityEngine.XR.WSA.SurfaceData cookedData, bool outputWritten, float elapsedCookTimeSeconds)
        {
            if (outstandingMeshRequest == null)
            {
                Debug.LogErrorFormat("Got OnDataReady for surface {0} while no request was outstanding.",
                                     cookedData.id.handle
                                     );

                return;
            }

            if (!IsMatchingSurface(outstandingMeshRequest.Value, cookedData))
            {
                Debug.LogErrorFormat("Got mismatched OnDataReady for surface {0} while request for surface {1} was outstanding.",
                                     cookedData.id.handle,
                                     outstandingMeshRequest.Value.ID
                                     );

                ReclaimSurface(outstandingMeshRequest.Value);
                outstandingMeshRequest = null;

                return;
            }

            if (ObserverState != ObserverStates.Running)
            {
                Debug.LogFormat("Got OnDataReady for surface {0}, but observer was no longer running.",
                                cookedData.id.handle
                                );

                ReclaimSurface(outstandingMeshRequest.Value);
                outstandingMeshRequest = null;

                return;
            }

            if (!outputWritten)
            {
                ReclaimSurface(outstandingMeshRequest.Value);
                outstandingMeshRequest = null;

                return;
            }

            Debug.Assert(outstandingMeshRequest.Value.Object.activeSelf);
            outstandingMeshRequest.Value.Renderer.enabled = SpatialMappingManager.Instance.DrawVisualMeshes;

            SurfaceObject?replacedSurface = UpdateOrAddSurfaceObject(outstandingMeshRequest.Value, destroyGameObjectIfReplaced: false);

            outstandingMeshRequest = null;

            if (replacedSurface != null)
            {
                ReclaimSurface(replacedSurface.Value);
            }
        }
Esempio n. 8
0
        public void Exit(UnityEngine.XR.WSA.SurfaceData data)
        {
            DestinationLocation location = GetLocation(data);

            if (location == null)
            {
                return;
            }

            location.Exit(data);
        }
        /// <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(UnityEngine.XR.WSA.SurfaceId id, GameObject surface)
        {
            UnityEngine.XR.WSA.SurfaceData surfaceData = new UnityEngine.XR.WSA.SurfaceData(id,
                                                                                            surface.GetComponent <MeshFilter>(),
                                                                                            surface.GetComponent <UnityEngine.XR.WSA.WorldAnchor>(),
                                                                                            surface.GetComponent <MeshCollider>(),
                                                                                            TrianglesPerCubicMeter,
                                                                                            true);

            surfaceWorkQueue.Enqueue(surfaceData);
        }
Esempio n. 10
0
        /// <summary>
        /// Creates the payload to emit on the hovering events of <see cref="Entered"/> and <see cref="Exited"/>.
        /// </summary>
        /// <param name="data">The data that is mutating the hover state.</param>
        /// <returns>The data to emit.</returns>
        protected virtual UnityEngine.XR.WSA.SurfaceData CreateHoverPayload(UnityEngine.XR.WSA.SurfaceData data)
        {
            if (Origin == null || !Origin.activeInHierarchy)
            {
                return(data);
            }

            hoverHit           = data.CollisionData;
            hoverHit.point     = Origin.transform.position;
            data.CollisionData = hoverHit;
            return(data);
        }
Esempio n. 11
0
        public virtual void Enter(UnityEngine.XR.WSA.SurfaceData data)
        {
            if (data.Transform == null || !SourceValidity.Accepts(data.Transform))
            {
                return;
            }

            IsHovered = true;
            HoveringElements.Add(data);
            if (HoveringElements.Count == 1)
            {
                HoverActivated?.Invoke();
            }
            Entered?.Invoke(CreateHoverPayload(data));
        }
Esempio n. 12
0
        public virtual void Exit(UnityEngine.XR.WSA.SurfaceData data)
        {
            if (data.Transform == null || !SourceValidity.Accepts(data.Transform) || !HoveringElements.Contains(data))
            {
                return;
            }

            IsHovered = false;
            HoveringElements.Remove(data);
            Exited?.Invoke(CreateHoverPayload(data));
            if (HoveringElements.Count == 0)
            {
                HoverDeactivated?.Invoke();
            }
        }
Esempio n. 13
0
        /// <summary>
        /// Creates the payload to emit on the <see cref="Activated"/> event.
        /// </summary>
        /// <param name="data">The default data to potentially mutate.</param>
        /// <returns>The data to emit.</returns>
        protected virtual TransformData CreateSelectedPayload(UnityEngine.XR.WSA.SurfaceData data)
        {
            if (Destination == null || !Destination.activeInHierarchy)
            {
                return(data);
            }

            selectedPayload.Clear();
            selectedPayload.Transform = Destination.transform;
            if (!ApplyDestinationRotation)
            {
                selectedPayload.RotationOverride = data.Rotation;
            }
            return(selectedPayload);
        }
Esempio n. 14
0
        public void Select(UnityEngine.XR.WSA.SurfaceData data)
        {
            if (SelectedLocation != null && data != null)
            {
                SelectedLocation.Deselect();
            }

            DestinationLocation location = GetLocation(data);

            if (location == null)
            {
                return;
            }

            location.Select(data);
            SelectedLocation = location;
        }
Esempio n. 15
0
        public virtual void Select(UnityEngine.XR.WSA.SurfaceData data)
        {
            if (data.Transform == null || !SourceValidity.Accepts(data.Transform) || !HoveringElements.Contains(data))
            {
                return;
            }

            IsActivated = true;
            Activated?.Invoke(CreateSelectedPayload(data));

            if (!EmitExitOnSelect)
            {
                return;
            }

            foreach (UnityEngine.XR.WSA.SurfaceData element in HoveringElements.ToArray())
            {
                Exit(element);
            }
            HoveringElements.Clear();
        }
        /// <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(
            UnityEngine.XR.WSA.SurfaceData bakedData)
        {
            UnityEngine.XR.WSA.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;
            }
        }
        /// <summary>
        /// Called once per frame.
        /// </summary>
        private void Update()
        {
            // Only do processing if the observer is running.
            if (ObserverState == ObserverStates.Running)
            {
                // If we don't have mesh creation in flight, but we could schedule mesh creation, do so.
                if (surfaceWorkOutstanding == false && surfaceWorkQueue.Count > 0)
                {
                    // Pop the SurfaceData off the queue.  A more sophisticated algorithm could prioritize
                    // the queue based on distance to the user or some other metric.
                    UnityEngine.XR.WSA.SurfaceData surfaceData = surfaceWorkQueue.Dequeue();

                    // If RequestMeshAsync succeeds, then we have successfully scheduled mesh creation.
                    surfaceWorkOutstanding = observer.RequestMeshAsync(surfaceData, SurfaceObserver_OnDataReady);
                }
                // If we don't have any other work to do, and enough time has passed since the previous
                // update request, request updates for the spatial mapping data.
                else if (surfaceWorkOutstanding == false && (Time.time - updateTime) >= TimeBetweenUpdates)
                {
                    observer.Update(SurfaceObserver_OnSurfaceChanged);
                    updateTime = Time.time;
                }
            }
        }
 /// <summary>
 /// Extracts the <see cref="Source"/> <see cref="GameObject"/> from the given <see cref="SurfaceData"/> payload data.
 /// </summary>
 /// <param name="data">The <see cref="SurfaceData"/> payload data to extract from.</param>
 public virtual void DoExtract(UnityEngine.XR.WSA.SurfaceData data)
 {
     Extract(data);
 }
 /// <summary>
 /// Emits the Exited event.
 /// </summary>
 public virtual void EmitExited(UnityEngine.XR.WSA.SurfaceData data)
 {
     Facade.Exited?.Invoke(data);
 }
        /// <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.
                    UnityEngine.XR.WSA.SurfaceId surfaceID = surfaceWorkQueue.Dequeue();

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

                    SurfaceObject newSurface;
                    UnityEngine.XR.WSA.WorldAnchor worldAnchor;

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

                        worldAnchor = newSurface.Object.AddComponent <UnityEngine.XR.WSA.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 <UnityEngine.XR.WSA.WorldAnchor>();
                        Debug.Assert(worldAnchor != null);
                    }

                    var surfaceData = new UnityEngine.XR.WSA.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>
 /// Extracts the <see cref="Source"/> <see cref="GameObject"/> from the given <see cref="SurfaceData"/> payload data.
 /// </summary>
 /// <param name="data">The <see cref="SurfaceData"/> payload data to extract from.</param>
 /// <returns>The <see cref="Source"/> <see cref="GameObject"/> within the <see cref="SurfaceData"/>.</returns>
 public virtual GameObject Extract(UnityEngine.XR.WSA.SurfaceData data)
 {
     SetSource(data);
     return(Extract());
 }