/// <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); }
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); }