/// <summary>
        /// Save WorldLocking state in a background task
        /// </summary>
        private async Task saveAsync()
        {
            if (hasPendingLoadTask || hasPendingSaveTask)
            {
                return;
            }

            hasPendingSaveTask = true;

            try
            {
                // mafinc - future work might include making an incremental save,
                // appending deltas, rather than new file every time.
                if (File.Exists(stateFileNameBase + ".new"))
                {
                    File.Delete(stateFileNameBase + ".new");
                }

                await AnchorManager.SaveAnchors();

                if (AnchorManager.SupportsPersistence)
                {
                    alignmentManager.Save();

                    using (var file = File.Create(stateFileNameBase + ".new"))
                    {
                        using (var ps = Plugin.CreateSerializer())
                        {
                            ps.IncludePersistent = true;
                            ps.IncludeTransient  = false;
                            ps.GatherRecord();
                            await ps.WriteRecordToAsync(file);
                        }
                    }

                    if (File.Exists(stateFileNameBase + ".old"))
                    {
                        File.Delete(stateFileNameBase + ".old");
                    }
                    if (File.Exists(stateFileNameBase))
                    {
                        File.Move(stateFileNameBase, stateFileNameBase + ".old");
                    }
                    File.Move(stateFileNameBase + ".new", stateFileNameBase);

                    lastSavingTime = Time.unscaledTime;
                }
            }
            finally
            {
                hasPendingSaveTask = false;
            }
        }
        /// <summary>
        /// Load the WorldLocking state in a background task
        /// </summary>
        private async Task loadAsync()
        {
            if (hasPendingLoadTask || hasPendingSaveTask)
            {
                return;
            }

            hasPendingLoadTask = true;

            try
            {
                // reset in any case to guarantee clean state even if no files have been read successfully
                Reset();

                string[] tryFileNames = { stateFileNameBase, stateFileNameBase + ".old" };

                foreach (var fileName in tryFileNames)
                {
                    if (File.Exists(fileName))
                    {
                        using (var file = File.OpenRead(fileName))
                        {
                            using (var pds = Plugin.CreateDeserializer())
                            {
                                pds.IncludePersistent = true;
                                pds.IncludeTransient  = false;
                                await pds.ReadRecordFromAsync(file);

                                pds.ApplyRecord();
                            }
                        }
                        await AnchorManager.LoadAnchors();

                        if (AnchorManager.SupportsPersistence)
                        {
                            AlignmentManager.Load();
                        }

                        // finish when reading was successful
                        return;
                    }
                }
            }
            finally
            {
                hasPendingLoadTask = false;
            }
        }
        private WorldLockingManager()
        {
            CreateUpdaterNode();

            /// It might look nicer to pull these off into internal setup functions,
            /// but by leaving them in the constructor, these fields can be marked "readonly",
            /// which they conceptually are.
            Plugin = new Plugin();
            DiagnosticRecordings.Start(Plugin);
            var am = new AnchorManager(Plugin);

            anchorManager = am;
            var fm = new FragmentManager(Plugin);

            fragmentManager        = fm;
            attachmentPointManager = fm;
            /// Note the alignmentManager accesses the FragmentManager in its constructor
            /// to register for refit notifications. Either FragmentManager needs to be fully
            /// setup before constructing AlignmentManager, or that registration needs to be deferred.
            alignmentManager = new AlignmentManager(this);
        }
        /// <summary>
        /// Update is called by the update proxy.
        /// </summary>
        private void Update()
        {
            ErrorStatus = "";

            if (hasPendingLoadTask)
            {
                ErrorStatus = "pending background load task";
                return;
            }

            if (AdjustmentFrame == null)
            {
                Debug.Log("No WLM update because no adjustment frame set");
                ErrorStatus = "no adjustment frame";
                return;
            }

            // AnchorManager.Update takes care of creating anchors&edges and feeding the up-to-date state
            // into the FrozenWorld engine
            bool hasSpongyAnchors = AnchorManager.Update();

//#if UNITY_WSA
            if (!hasSpongyAnchors)
            {
                // IFragmentManager.Pause() will set all fragments to disconnected.
                ErrorStatus = AnchorManager.ErrorStatus;
                FragmentManager.Pause();
                return;
            }
//#endif // UNITY_WSA

            try
            {
                DiagnosticRecordings.Update();
            }
            catch (Exception exception)
            {
                Debug.LogErrorFormat("Error writing WorldLocking diagnostics record: {0}", exception);
            }

            // The basic output from the FrozenWorld engine (current fragment and its alignment)
            // are applied to the unity scene
            FragmentManager.Update(AutoRefreeze, AutoMerge);

            /// The following assumes a camera hierarchy like this:
            /// Nodes_A => AdjustmentFrame => Nodes_B => camera
            /// The cumulative effect of Nodes_B is to transform from Spongy space to playspace.
            /// Spongy space is the space that the camera moves about in, and is the space that
            /// coordinates coming from scene agnostic APIs like XR are in.
            /// (Note the MRTK APIs are in Unity's global space, not Spongy space.
            /// The internal structure of that graph is inconsequential here, the only dependency
            /// is on the cumulative transform, PlayspaceFromSpongy.
            /// Likewise, the cumulative effect of Nodes_A is to transform from alignment space (described below)
            /// to Unity's global space, referred to here as FrozenSpace.
            /// The AdjustmentFrame's transform is composed of two transforms.
            /// The first comes from the FrozenWorld engine DLL as the inverse of Plugin.GetAlignment(),
            /// and transforms from Playspace to the base stable world locked space, labeled as
            /// LockedFromPlayspace.
            /// The second transforms from this stable but arbitrary space to a space locked
            /// to a finite set of real world markers. This transform is labeled PinnedFromLocked.
            /// The transform chain equivalent of the above camera hierarchy is:
            /// FrozenFromPinned * [PinnedFromLocked * LockedFromPlayspace] * PlayspaceFromSpongy * SpongyFromCamera
            ///
            /// FrozenFromSpongy and its inverse are useful for converting between the coordinates of scene agnostic APIs (e.g. XR)
            /// and Frozen coordinates, i.e. Unity's global space.
            /// FrozenFromLocked is convenient for converting between the "frozen" coordinates of the FrozenWorld engine DLL
            /// and Unity's global space, i.e. Frozen coordinate.
            if (Enabled)
            {
                Pose playspaceFromLocked = Plugin.GetAlignment();
                LockedFromPlayspace = playspaceFromLocked.Inverse();

                SpongyFromCamera = Plugin.GetSpongyHead();

                Pose lockedHeadPose = LockedFromPlayspace.Multiply(PlayspaceFromSpongy.Multiply(SpongyFromCamera));
                alignmentManager.ComputePinnedPose(lockedHeadPose);
                PinnedFromLocked = alignmentManager.PinnedFromLocked;
            }
            else
            {
                SpongyFromCamera = Camera.main.transform.GetLocalPose();
                /// Note leave adjustment and pinning transforms alone, to facilitate
                /// comparison of behavior when toggling FW enabled.
            }

            AdjustmentFrame.SetLocalPose(PinnedFromLocked.Multiply(LockedFromPlayspace));

#if false && WLT_ARSUBSYSTEMS_PRESENT
            if ((AdjustmentFrame.GetGlobalPose().position != Vector3.zero) || (AdjustmentFrame.GetGlobalPose().rotation != Quaternion.identity))
            {
                Debug.Log($"WLT: Adj{AnchorManagerXR.DebugVector3("O=", AdjustmentFrame.GetGlobalPose().position)}, {AnchorManagerXR.DebugEuler("R=", AdjustmentFrame.GetGlobalPose().rotation.eulerAngles)}");
            }
#endif // WLT_ARSUBSYSTEMS_PRESENT

            AutoSaveTriggerHook();
        }
        private IAnchorManager SelectAnchorManager(IPlugin plugin, IHeadPoseTracker headTracker)
        {
            Debug.Log($"Select {shared.anchorSettings.anchorSubsystem} anchor manager.");
            if (AnchorManager != null)
            {
                Debug.Log("Creating new anchormanager, but have old one. Reseting it before replacing.");
                AnchorManager.Reset();
            }
            var anchorSettings = shared.anchorSettings;

#if WLT_ARFOUNDATION_PRESENT
            if (anchorSettings.anchorSubsystem == AnchorSettings.AnchorSubsystem.ARFoundation)
            {
                Debug.Log($"Trying to create ARF anchor manager on {anchorSettings.ARSessionSource.name} and {anchorSettings.ARSessionOriginSource.name}");
                AnchorManagerARF arfAnchorManager = AnchorManagerARF.TryCreate(plugin, headTracker,
                                                                               anchorSettings.ARSessionSource, anchorSettings.ARSessionOriginSource);
                if (arfAnchorManager != null)
                {
                    Debug.Log("Success creating ARF anchor manager");
                    return(arfAnchorManager);
                }
                Debug.Log("Failed to create requested AR Foundation anchor manager!");
            }
#endif // WLT_ARFOUNDATION_PRESENT
#if WLT_ARSUBSYSTEMS_PRESENT
            if (anchorSettings.anchorSubsystem == AnchorSettings.AnchorSubsystem.XRSDK)
            {
                Debug.Log($"Trying to create XR anchor manager");
                AnchorManagerXR xrAnchorManager = AnchorManagerXR.TryCreate(plugin, headTracker);
                if (xrAnchorManager != null)
                {
                    Debug.Log("Success creating XR anchor manager");
                    return(xrAnchorManager);
                }
                Debug.Log("Failed to create requested XR SDK anchor manager!");
            }
#endif // WLT_ARSUBSYSTEMS_PRESENT
#if UNITY_WSA && !UNITY_2020_1_OR_NEWER
            if (anchorSettings.anchorSubsystem == AnchorSettings.AnchorSubsystem.WSA)
            {
                AnchorManagerWSA wsaAnchorManager = AnchorManagerWSA.TryCreate(plugin, headTracker);
                if (wsaAnchorManager != null)
                {
                    Debug.Log("Success creating WSA anchor manager");
                    return(wsaAnchorManager);
                }
                Debug.Log("Failed to create requested WSA anchor manager!");
            }
#endif // UNITY_WSA
#if WLT_ARCORE_SDK_INCLUDED
            if (anchorSettings.anchorSubsystem == AnchorSettings.AnchorSubsystem.ARCore)
            {
                AnchorManagerARCore arCoreAnchorManager = AnchorManagerARCore.TryCreate(plugin, headTracker);
                if (arCoreAnchorManager != null)
                {
                    Debug.Log("Success creating ARCore anchor manager");
                    return(arCoreAnchorManager);
                }
                Debug.Log("Failed to create requested ARCore anchor manager!");
            }
#endif // WLT_ARCORE_SDK_INCLUDED
            if (anchorSettings.anchorSubsystem != AnchorSettings.AnchorSubsystem.Null)
            {
                Debug.Log("Failure creating useful anchor manager of any type. Creating null manager");
                anchorSettings.anchorSubsystem = AnchorSettings.AnchorSubsystem.Null;
                shared.anchorSettings          = anchorSettings;
            }
            AnchorManagerNull nullAnchorManager = AnchorManagerNull.TryCreate(plugin, headTracker);
            Debug.Assert(nullAnchorManager != null, "Creation of Null anchor manager should never fail.");
            return(nullAnchorManager);
        }
Example #6
0
        private async Task <IAnchorManager> SelectAnchorManager(IPlugin plugin, IHeadPoseTracker headTracker)
        {
#if false
            DebugLogSetup($"Select {shared.anchorSettings.anchorSubsystem} anchor manager.");
            if (AnchorManager != null)
            {
                DebugLogSetup("Creating new anchor manager, but have old one. Reseting it before replacing.");
                AnchorManager.Reset();
            }
            var anchorSettings = shared.anchorSettings;
#else
            if (AnchorManager != null)
            {
                DebugLogSetup("Creating new anchor manager, but have old one. Reseting it before replacing.");
                AnchorManager.Reset();
            }
            var anchorSettings = shared.anchorSettings;
#if UNITY_EDITOR
            if (anchorSettings.NullSubsystemInEditor)
            {
                DebugLogSetup($"Switching from {anchorSettings.anchorSubsystem} to AnchorSubsystem.Null because running in editor.");
                anchorSettings.anchorSubsystem = AnchorSettings.AnchorSubsystem.Null;
            }
#endif // UNITY_EDITOR
            DebugLogSetup($"Select {anchorSettings.anchorSubsystem} anchor manager.");
#endif
#if WLT_ARFOUNDATION_PRESENT
            if (anchorSettings.anchorSubsystem == AnchorSettings.AnchorSubsystem.ARFoundation)
            {
                DebugLogSetup($"Trying to create ARF anchor manager on {anchorSettings.ARSessionSource.name} and {anchorSettings.ARSessionOriginSource.name}");
                AnchorManagerARF arfAnchorManager = await AnchorManagerARF.TryCreate(plugin, headTracker,
                                                                                     anchorSettings.ARSessionSource, anchorSettings.ARSessionOriginSource);

                if (arfAnchorManager != null)
                {
                    DebugLogSetup("Success creating ARF anchor manager");
                    return(arfAnchorManager);
                }
                Debug.LogError("Failed to create requested AR Foundation anchor manager!");
            }
#endif // WLT_ARFOUNDATION_PRESENT
#if WLT_ARSUBSYSTEMS_PRESENT
            if (anchorSettings.anchorSubsystem == AnchorSettings.AnchorSubsystem.XRSDK)
            {
                DebugLogSetup($"Trying to create XR anchor manager");
                AnchorManagerXR xrAnchorManager = await AnchorManagerXR.TryCreate(plugin, headTracker);

                if (xrAnchorManager != null)
                {
                    DebugLogSetup("Success creating XR anchor manager");
                    return(xrAnchorManager);
                }
                Debug.LogError("Failed to create requested XR SDK anchor manager!");
            }
#endif // WLT_ARSUBSYSTEMS_PRESENT
#if UNITY_WSA && !UNITY_2020_1_OR_NEWER
            if (anchorSettings.anchorSubsystem == AnchorSettings.AnchorSubsystem.WSA)
            {
                AnchorManagerWSA wsaAnchorManager = AnchorManagerWSA.TryCreate(plugin, headTracker);
                if (wsaAnchorManager != null)
                {
                    DebugLogSetup("Success creating WSA anchor manager");
                    return(wsaAnchorManager);
                }
                Debug.LogError("Failed to create requested WSA anchor manager!");
            }
#endif // UNITY_WSA
#if WLT_ARCORE_SDK_INCLUDED
            if (anchorSettings.anchorSubsystem == AnchorSettings.AnchorSubsystem.ARCore)
            {
                AnchorManagerARCore arCoreAnchorManager = AnchorManagerARCore.TryCreate(plugin, headTracker);
                if (arCoreAnchorManager != null)
                {
                    DebugLogSetup("Success creating ARCore anchor manager");
                    return(arCoreAnchorManager);
                }
                Debug.LogError("Failed to create requested ARCore anchor manager!");
            }
#endif // WLT_ARCORE_SDK_INCLUDED
            if (anchorSettings.anchorSubsystem != AnchorSettings.AnchorSubsystem.Null)
            {
                DebugLogSetup("Failure creating useful anchor manager of any type. Creating null manager");
                anchorSettings.anchorSubsystem = AnchorSettings.AnchorSubsystem.Null;
                shared.anchorSettings          = anchorSettings;
            }
            AnchorManagerNull nullAnchorManager = AnchorManagerNull.TryCreate(plugin, headTracker);
            Debug.Assert(nullAnchorManager != null, "Creation of Null anchor manager should never fail.");
            /// No-op await here to suppress warnings if no anchor manager system which requires asynchronous startup is compiled in.
            await Task.CompletedTask;
            return(nullAnchorManager);
        }