/// <summary>
        /// If a potential new anchor was prepared (in a previous time step) and is now found to be
        /// located, this routine finalizes it and prepares its edges to be added
        /// </summary>
        /// <param name="newEdges">List that will have new edges appended by this routine</param>
        /// <returns>new anchor id (or Invalid if none was finalized)</returns>
        private AnchorId FinalizeNewAnchor(out List <AnchorEdge> newEdges)
        {
            newEdges = new List <AnchorEdge>();

            if ((newSpongyAnchor == null) || !newSpongyAnchor.IsLocated)
            {
#if WLT_EXTRA_LOGGING
                if (newSpongyAnchor != null)
                {
                    DebugLogExtra($"Can't finalize {newSpongyAnchor.name} because it's still not located.");
                }
#endif // WLT_EXTRA_LOGGING
                return(AnchorId.Invalid);
            }

            AnchorId newId = ClaimAnchorId();
            foreach (var id in newAnchorNeighbors)
            {
                newEdges.Add(new AnchorEdge()
                {
                    anchorId1 = id, anchorId2 = newId
                });
            }
            spongyAnchors.Add(new SpongyAnchorWithId()
            {
                anchorId     = newId,
                spongyAnchor = newSpongyAnchor
            });
            newSpongyAnchor = null;

            return(newId);
        }
Exemplo n.º 2
0
        /// <summary>
        /// If a potential new anchor was prepared (in a previous time step) and is now found to be
        /// located, this routine finalizes it and prepares its edges to be added
        /// </summary>
        /// <param name="newEdges">List that will have new edges appended by this routine</param>
        /// <returns>new anchor id (or Invalid if none was finalized)</returns>
        private AnchorId FinalizeNewAnchor(out List <AnchorEdge> newEdges)
        {
            newEdges = new List <AnchorEdge>();

            if (!newSpongyAnchor || !newSpongyAnchor.isLocated)
            {
                return(AnchorId.Invalid);
            }

            AnchorId newId = ClaimAnchorId();

            foreach (var id in newAnchorNeighbors)
            {
                newEdges.Add(new AnchorEdge()
                {
                    anchorId1 = id, anchorId2 = newId
                });
            }
            spongyAnchors.Add(new SpongyAnchorWithId()
            {
                anchorId     = newId,
                spongyAnchor = newSpongyAnchor
            });
            newSpongyAnchor = null;

            return(newId);
        }
        protected override SpongyAnchor DestroyAnchor(AnchorId id, SpongyAnchor spongyAnchor)
        {
            if (spongyAnchor != null)
            {
                GameObject.Destroy(spongyAnchor.gameObject);
            }
            RemoveSpongyAnchorById(id);

            return(null);
        }
        protected override SpongyAnchor DestroyAnchor(AnchorId id, SpongyAnchor spongyAnchor)
        {
            if (spongyAnchor is SpongyAnchorARF spongyARF)
            {
                spongyARF.Cleanup(arAnchorManager);
            }
            RemoveSpongyAnchorById(id);

            return(null);
        }
Exemplo n.º 5
0
        /// <summary>
        /// prepare potential new anchor, which will only be finalized in a later time step
        /// when isLocalized is actually found to be true (see code before)
        /// </summary>
        /// <param name="pose"></param>
        /// <param name="neighbors"></param>
        private void PrepareNewAnchor(Pose pose, List <AnchorId> neighbors)
        {
            if (newSpongyAnchor)
            {
                UnityEngine.Object.Destroy(newSpongyAnchor.gameObject);
            }

            newSpongyAnchor = CreateAnchor(NextAnchorId());
            newSpongyAnchor.transform.SetGlobalPose(pose);
            newAnchorNeighbors = neighbors;
        }
        /// <summary>
        /// prepare potential new anchor, which will only be finalized in a later time step
        /// when isLocated is actually found to be true (see code before)
        /// </summary>
        /// <param name="pose"></param>
        /// <param name="neighbors"></param>
        private void PrepareNewAnchor(Pose pose, List <AnchorId> neighbors)
        {
            if (newSpongyAnchor != null)
            {
                DebugLogExtra($"Discarding {newSpongyAnchor.name} (located={newSpongyAnchor.IsLocated}) because still not located");
                newSpongyAnchor = DestroyAnchor(AnchorId.Invalid, newSpongyAnchor);
            }

            newSpongyAnchor    = CreateAnchor(NextAnchorId(), worldAnchorParent, pose);
            newAnchorNeighbors = neighbors;
        }
Exemplo n.º 7
0
        /// <summary>
        /// Delete all spongy anchor objects and reset internal state
        /// </summary>
        public void Reset()
        {
            foreach (var anchor in spongyAnchors)
            {
                UnityEngine.Object.Destroy(anchor.spongyAnchor.gameObject);
            }
            spongyAnchors.Clear();

            ResetAnchorIds();
            UnityEngine.Object.Destroy(newSpongyAnchor);
            newSpongyAnchor = null;
        }
        private bool LostTrackingCleanup(string message)
        {
            DebugLogExtra($"{message} Frame {Time.frameCount}");
            lastTrackingInactiveTime = Time.unscaledTime;

            if (newSpongyAnchor)
            {
                newSpongyAnchor = DestroyAnchor(AnchorId.Invalid, newSpongyAnchor);
            }

            ErrorStatus = message;
            return(false);
        }
 /// <summary>
 /// If we have more local anchors than parameterized limit, destroy the furthest.
 /// </summary>
 /// <param name="maxDistAnchorId">Id of the furthest anchor.</param>
 /// <param name="maxDistSpongyAnchor">Reference to the furthest anchor.</param>
 private void CheckForCull(AnchorId maxDistAnchorId, SpongyAnchor maxDistSpongyAnchor)
 {
     /// Anchor limiting is only enabled with a positive limit value.
     if (MaxLocalAnchors > 0)
     {
         if (SpongyAnchors.Count > MaxLocalAnchors)
         {
             if (maxDistSpongyAnchor != null)
             {
                 DestroyAnchor(maxDistAnchorId, maxDistSpongyAnchor);
             }
         }
     }
 }
        /// <summary>
        /// Delete all spongy anchor objects and reset internal state
        /// </summary>
        public void Reset()
        {
            foreach (var anchor in spongyAnchors)
            {
                /// Pass in AnchorId.Invalid, not because the anchors aren't in a list, but
                /// because it's faster to clear the entire lists rather than pulling out each element.
                DestroyAnchor(AnchorId.Invalid, anchor.spongyAnchor);
            }
            // mafinc - should this clear frozen anchors as well?
            spongyAnchors.Clear();
            plugin.ClearFrozenAnchors();

            newSpongyAnchor = DestroyAnchor(AnchorId.Invalid, newSpongyAnchor);
            headTracker.Reset();
        }
        protected override SpongyAnchor DestroyAnchor(AnchorId id, SpongyAnchor spongyAnchor)
        {
            SpongyAnchorXR spongyAnchorXR = spongyAnchor as SpongyAnchorXR;

            if (spongyAnchorXR != null)
            {
                Debug.Assert(anchorsByTrackableId[spongyAnchorXR.TrackableId] == spongyAnchorXR);
                anchorsByTrackableId.Remove(spongyAnchorXR.TrackableId);
                xrReferencePointManager.TryRemoveReferencePoint(spongyAnchorXR.TrackableId);
                GameObject.Destroy(spongyAnchorXR.gameObject);
            }
            RemoveSpongyAnchorById(id);

            return(null);
        }
Exemplo n.º 12
0
        // Start is called before the first frame update
        private void Start()
        {
#if WLT_ENABLE_LEGACY_WSA
            if (worldAnchor == null)
            {
                worldAnchor = gameObject.AddComponent <UnityEngine.XR.WSA.WorldAnchor>();
            }
#else
            dummy = this;
            if (isSaved && lastNotLocatedTime > 0)
            {
                isSaved            = false;
                lastNotLocatedTime = float.NegativeInfinity;
            }
#endif // WLT_ENABLE_LEGACY_WSA
        }
Exemplo n.º 13
0
        /// <summary>
        /// Create missing spongy anchors/edges and feed plugin with up-to-date input
        /// </summary>
        /// <returns>Boolean: Has the plugin received input to provide an adjustment?</returns>
        public bool Update()
        {
            ErrorStatus = "";

#if UNITY_WSA
            if (UnityEngine.XR.WSA.WorldManager.state != UnityEngine.XR.WSA.PositionalLocatorState.Active)
            {
                lastTrackingInactiveTime = Time.unscaledTime;

                if (newSpongyAnchor)
                {
                    UnityEngine.Object.Destroy(newSpongyAnchor.gameObject);
                    newSpongyAnchor = null;
                }

                ErrorStatus = "Lost Tracking";
                return(false);
            }
#endif // UNITY_WSA

            // To communicate spongyHead and spongyAnchor poses to the FrozenWorld engine, they must all be expressed
            // in the same coordinate system. Here, we do not care where this coordinate
            // system is defined and how it fluctuates over time, as long as it can be used to express the
            // relative poses of all the spongy objects within each time step.
            //
            // Note:
            // The low-level input obtained via InputTracking.GetLocal???(XRNode.Head) is automatically kept in sync with
            // Camera.main.transform.local??? (unless XRDevice.DisableAutoXRCameraTracking(Camera.main, true) is used to deactivate
            // this mechanism). In theory, both could be used interchangeably, potentially allowing to avoid the dependency
            // on low-level code at this point. It is not clear though, whether both values follow exactly the same timing or which
            // one is more correct to be used at this point. More research might be necessary.
            //
            // The decision between low-level access via InputTracking and high-level access via Camera.main.transform should
            // be coordinated with the decision between high-level access to WorldAnchor and low-level access to
            // Windows.Perception.Spatial.SpatialAnchor -- see comment at top of SpongyAnchor.cs
            Pose spongyHead = GetHeadPose();

            // place new anchors 1m below head
            Pose newSpongyAnchorPose = spongyHead;
            newSpongyAnchorPose.position.y -= 1;
            newSpongyAnchorPose.rotation    = Quaternion.identity;

            var activeAnchors        = new List <AnchorPose>();
            var innerSphereAnchorIds = new List <AnchorId>();
            var outerSphereAnchorIds = new List <AnchorId>();

            float    minDistSqr      = float.PositiveInfinity;
            AnchorId minDistAnchorId = 0;

            List <AnchorEdge> newEdges;
            AnchorId          newId = FinalizeNewAnchor(out newEdges);

            float innerSphereRadSqr = MinNewAnchorDistance * MinNewAnchorDistance;
            float outerSphereRadSqr = MaxAnchorEdgeLength * MaxAnchorEdgeLength;

            foreach (var keyval in spongyAnchors)
            {
                var id = keyval.anchorId;
                var a  = keyval.spongyAnchor;
                if (a.isLocated)
                {
                    float distSqr    = (a.transform.position - newSpongyAnchorPose.position).sqrMagnitude;
                    var   anchorPose = new AnchorPose()
                    {
                        anchorId = id, pose = a.transform.GetGlobalPose()
                    };
                    activeAnchors.Add(anchorPose);
                    if (distSqr < minDistSqr)
                    {
                        minDistSqr      = distSqr;
                        minDistAnchorId = id;
                    }
                    if (distSqr <= outerSphereRadSqr && id != newId)
                    {
                        outerSphereAnchorIds.Add(id);
                        if (distSqr <= innerSphereRadSqr)
                        {
                            innerSphereAnchorIds.Add(id);
                        }
                    }
                }
            }

            if (newId == 0 && innerSphereAnchorIds.Count == 0)
            {
                if (Time.unscaledTime <= lastTrackingInactiveTime + SpongyAnchor.TrackingStartDelayTime)
                {
                    // Tracking has become active only recently. We suppress creation of new anchors while
                    // new anchors may still be in transition due to SpatialAnchor easing.
                }
                else if (Time.unscaledTime < lastAnchorAddTime + AnchorAddOutTime)
                {
                    // short timeout after creating one anchor to prevent bursts of new, unlocatable anchors
                    // in case of problems in the anchor generation
                }
                else
                {
                    PrepareNewAnchor(newSpongyAnchorPose, outerSphereAnchorIds);
                    lastAnchorAddTime = Time.unscaledTime;
                }
            }

            if (activeAnchors.Count == 0)
            {
                ErrorStatus = "No active anchors";
                return(false);
            }

            // create edges between nearby existing anchors
            if (innerSphereAnchorIds.Count >= 2)
            {
                foreach (var i in innerSphereAnchorIds)
                {
                    if (i != minDistAnchorId)
                    {
                        newEdges.Add(new AnchorEdge()
                        {
                            anchorId1 = i, anchorId2 = minDistAnchorId
                        });
                    }
                }
            }

            plugin.ClearSpongyAnchors();
            plugin.Step_Init(spongyHead);
            plugin.AddSpongyAnchors(activeAnchors);
            plugin.SetMostSignificantSpongyAnchorId(minDistAnchorId);
            plugin.AddSpongyEdges(newEdges);
            plugin.Step_Finish();

            return(true);
        }
 /// <summary>
 /// Platform dependent disposal of local anchors.
 /// </summary>
 /// <param name="id">The id of the anchor to destroy.</param>
 /// <param name="spongyAnchor">Reference to the anchor to destroy.</param>
 /// <returns>Null</returns>
 /// <remarks>
 /// The id is used to delete from any stored lists. If the SpongyAnchor hasn't been
 /// added to any lists (is still initializing), id can be AnchorId.Invalid.
 /// </remarks>
 protected abstract SpongyAnchor DestroyAnchor(AnchorId id, SpongyAnchor spongyAnchor);
        /// <summary>
        /// Create missing spongy anchors/edges and feed plugin with up-to-date input
        /// </summary>
        /// <returns>Boolean: Has the plugin received input to provide an adjustment?</returns>
        public virtual bool Update()
        {
            ErrorStatus = "";

            if (!IsTracking())
            {
                return(LostTrackingCleanup("Lost Tracking"));
            }

            // To communicate spongyHead and spongyAnchor poses to the FrozenWorld engine, they must all be expressed
            // in the same coordinate system. Here, we do not care where this coordinate
            // system is defined and how it fluctuates over time, as long as it can be used to express the
            // relative poses of all the spongy objects within each time step.
            //
            Pose spongyHead = headTracker.GetHeadPose();

            // place new anchors 1m below head
            Pose newSpongyAnchorPose = spongyHead;

            newSpongyAnchorPose.position.y -= 1;
            newSpongyAnchorPose.rotation    = Quaternion.identity;

            var activeAnchors        = new List <AnchorPose>();
            var innerSphereAnchorIds = new List <AnchorId>();
            var outerSphereAnchorIds = new List <AnchorId>();

            float    minDistSqr      = float.PositiveInfinity;
            AnchorId minDistAnchorId = 0;

            float        maxDistSq           = 0;
            AnchorId     maxDistAnchorId     = AnchorId.Invalid;
            SpongyAnchor maxDistSpongyAnchor = null;

            List <AnchorEdge> newEdges;
            AnchorId          newId = FinalizeNewAnchor(out newEdges);

            float innerSphereRadSqr = MinNewAnchorDistance * MinNewAnchorDistance;
            float outerSphereRadSqr = MaxAnchorEdgeLength * MaxAnchorEdgeLength;

            foreach (var keyval in spongyAnchors)
            {
                var id = keyval.anchorId;
                var a  = keyval.spongyAnchor;
                if (a.IsLocated)
                {
                    Pose  aSpongyPose = a.SpongyPose;
                    float distSqr     = (aSpongyPose.position - newSpongyAnchorPose.position).sqrMagnitude;
                    var   anchorPose  = new AnchorPose()
                    {
                        anchorId = id, pose = aSpongyPose
                    };
                    activeAnchors.Add(anchorPose);
                    if (distSqr < minDistSqr)
                    {
                        minDistSqr      = distSqr;
                        minDistAnchorId = id;
                    }
                    if (distSqr <= outerSphereRadSqr && id != newId)
                    {
                        outerSphereAnchorIds.Add(id);
                        if (distSqr <= innerSphereRadSqr)
                        {
                            innerSphereAnchorIds.Add(id);
                        }
                    }
                    if (distSqr > maxDistSq)
                    {
                        maxDistSq           = distSqr;
                        maxDistAnchorId     = id;
                        maxDistSpongyAnchor = a;
                    }
                }
            }

            if (newId == 0 && innerSphereAnchorIds.Count == 0)
            {
                if (Time.unscaledTime <= lastTrackingInactiveTime + TrackingStartDelayTime)
                {
                    // Tracking has become active only recently. We suppress creation of new anchors while
                    // new anchors may still be in transition due to SpatialAnchor easing.
                    DebugLogExtra($"Skip new anchor creation because only recently gained tracking {Time.unscaledTime - lastTrackingInactiveTime}");
                }
                else if (Time.unscaledTime < lastAnchorAddTime + AnchorAddOutTime)
                {
                    // short timeout after creating one anchor to prevent bursts of new, unlocatable anchors
                    // in case of problems in the anchor generation
                    DebugLogExtra($"Skip new anchor creation because waiting on recently made anchor "
                                  + $"{Time.unscaledTime - lastAnchorAddTime} "
                                  + $"- {(newSpongyAnchor != null ? newSpongyAnchor.name : "null")}");
                }
                else
                {
                    PrepareNewAnchor(newSpongyAnchorPose, outerSphereAnchorIds);
                    lastAnchorAddTime = Time.unscaledTime;
                }
            }

            if (activeAnchors.Count == 0)
            {
                ErrorStatus = "No active anchors";
                return(false);
            }

            // create edges between nearby existing anchors
            if (innerSphereAnchorIds.Count >= 2)
            {
                foreach (var i in innerSphereAnchorIds)
                {
                    if (i != minDistAnchorId)
                    {
                        newEdges.Add(new AnchorEdge()
                        {
                            anchorId1 = i, anchorId2 = minDistAnchorId
                        });
                    }
                }
            }

            CheckForCull(maxDistAnchorId, maxDistSpongyAnchor);

#if WLT_DUMP_SPONGY
            DumpSpongy(spongyHead);
#endif // WLT_DUMP_SPONGY

            plugin.ClearSpongyAnchors();
            plugin.Step_Init(spongyHead);
            plugin.AddSpongyAnchors(activeAnchors);
            plugin.SetMostSignificantSpongyAnchorId(minDistAnchorId);
            plugin.AddSpongyEdges(newEdges);
            plugin.Step_Finish();

            return(true);
        }