unsafe public bool Refreeze(out FragmentId mergedId, out FragmentId[] absorbedFragments) { checkError(); mergedId = currentFragmentId; absorbedFragments = new FragmentId[0]; return(false); }
/// <summary> /// Teleport (as opposed to Move) means that the object is meant to have disappeared at its old position /// and instantaneously reappeared at its new position in frozen space without traversing the space in between. /// </summary> /// <remarks> /// This is equivalent to releasing the existing attachment point and creating a new one, /// except in that the attachment point reference remains valid. /// See <see cref="WorldLockingManager.TeleportAttachmentPoint"/>. /// </remarks> /// <param name="attachPointIface">The attachment point to teleport</param> /// <param name="newFrozenPosition">The position to teleport to.</param> /// <param name="context">The optional context.</param> public void TeleportAttachmentPoint(IAttachmentPoint attachPointIface, Vector3 newFrozenPosition, IAttachmentPoint context) { AttachmentPoint attachPoint = attachPointIface as AttachmentPoint; if (attachPoint != null) { attachPoint.ObjectPosition = newFrozenPosition; // Save the fragment it's currently in, in case it changes here. FragmentId oldFragmentId = attachPoint.FragmentId; // If it's not in a valid fragment, it is still pending and will get processed when the system is ready. if (oldFragmentId.IsKnown()) { FragmentId newFragmentId = GetTargetFragmentId(context); // If there is a valid current fragment, if (newFragmentId.IsKnown()) { // Fill it in with a new one. SetupAttachmentPoint(plugin, attachPoint, context); if (attachPoint.FragmentId != oldFragmentId) { ChangeAttachmentPointFragment(oldFragmentId, attachPoint); } } else { AddPendingAttachmentPoint(attachPoint, context); } } } }
/// <summary> /// Create a frozen anchor visual in the indicated fragment. /// </summary> /// <param name="source">Source data to create from.</param> /// <param name="resource">The created resource.</param> /// <returns></returns> public bool CreateFrozenVisual(AnchorFragmentPose source, out IdPair <AnchorId, FrozenAnchorVisual> resource) { // Already ensured this fragment exists. FragmentId fragmentId = source.fragmentPose.fragmentId; AnchorId anchorId = source.anchorId; FrameVisual frozenFragmentViz; if (!frozenFragmentVisuals.TryGetValue(fragmentId, out frozenFragmentViz)) { resource = new IdPair <AnchorId, FrozenAnchorVisual>() { id = AnchorId.Invalid, target = null }; return(false); } // If there isn't a visualization for this anchor, add one. FrozenAnchorVisual frozenAnchorVisual; frozenAnchorVisual = Prefab_FrozenAnchorViz.Instantiate(anchorId.FormatStr(), frozenFragmentViz); //frozenAnchorVisual.gameObject.AddComponent<AdjusterMoving>(); // Put the frozen anchor vis at the world locked transform of the anchor SetPose(source, frozenAnchorVisual); resource = new IdPair <AnchorId, FrozenAnchorVisual>() { id = source.anchorId, target = frozenAnchorVisual }; return(true); }
/// <summary> /// If conditions have changed to allow finalizing creation of any pending attachment points, /// do it now. /// </summary> private void ProcessPendingAttachmentPoints() { if (CurrentFragmentId.IsKnown() && pendingAttachments.Count > 0) { // We have a valid destination fragment. Note that since this queue is in order of submission, // if an attachment point depends on a second attachment point for context, // that second will be either earlier in the list (because there was no valid current fragment when it was // created) or it will have a valid fragment. So by the time we get to the one with a dependency (pending.context != null), // its dependency will have a valid fragment id. int pendingCount = pendingAttachments.Count; for (int i = 0; i < pendingCount; ++i) { AttachmentPoint target = pendingAttachments[i].target; Vector3 frozenPosition = pendingAttachments[i].target.ObjectPosition; IAttachmentPoint context = pendingAttachments[i].context; SetupAttachmentPoint(plugin, target, context); FragmentId fragmentId = CurrentFragmentId; if (context != null) { fragmentId = context.FragmentId; } Debug.Assert(fragmentId.IsKnown(), $"FragmentId {fragmentId.FormatStr()} invalid from {(context != null ? "context" : "head")} in processing pending"); Fragment fragment = EnsureFragment(fragmentId); Debug.Assert(fragment != null, "Valid fragmentId but no fragment found"); fragment.AddAttachmentPoint(target); } // All pending must now be in a good home fragment, clear the to-do list. pendingAttachments.Clear(); } }
/// <summary> /// Set internals of attachment point to new values. /// </summary> /// <param name="fragmentId">New fragment</param> /// <param name="cachedPosition">Cache last position moved to.</param> /// <param name="anchorId">New anchor id</param> /// <param name="locationFromAnchor">New displacement from anchor</param> public void Set(FragmentId fragmentId, Vector3 cachedPosition, AnchorId anchorId, Vector3 locationFromAnchor) { this.AnchorId = anchorId; this.FragmentId = fragmentId; this.CachedPosition = cachedPosition; this.LocationFromAnchor = locationFromAnchor; }
/// <summary> /// Adjust to refit operations. /// </summary> /// <param name="mainId">The new combined fragment.</param> /// <param name="absorbedIds">Id's of other fragments being merged into mainId.</param> /// <remarks> /// This callback occurs *after* the refit operation. As part of the refit, /// positions of the managed SpacePinOrientables may have changed, and therefore /// their implied orientations must be re-calculated. /// Note that there is an apparent race condition, as there is no order guarantee on /// the order of refit notifications, and the AlignmentManager also relies on the /// refit notification to adjust after refit operations. /// However, both the Orienter and the AlignmentManager rely only on the positions /// having been set, which was accomplished during the refit and before the refit /// notification. So it really doesn't matter whether the Orienter.OnRefit or the /// AlignmentManager.OnRefit is called first. /// </remarks> private void OnRefit(FragmentId mainId, FragmentId[] absorbedIds) { IAlignmentManager alignMgr = WorldLockingManager.GetInstance().AlignmentManager; Reorient(mainId, alignMgr); alignMgr.SendAlignmentAnchors(); }
/// <inheritdocs /> public AnchorId AddAlignmentAnchor(string uniqueName, Pose virtualPose, Pose lockedPose) { FragmentId fragmentId = CurrentFragmentId; AnchorId anchorId = ClaimAnchorId(); if (IsGlobal) { /// Bake in current snapshot of any application imposed transform (teleport). virtualPose = manager.PinnedFromFrozen.Multiply(virtualPose); } else { /// For subtree, applied adjustment transform is LockedFromPinned. Remove existing /// adjustment here by premultiplying PinnedFromLocked. virtualPose = PinnedFromLocked.Multiply(virtualPose); } #if WLT_EXTRA_LOGGING string label = "AddAlign1"; Debug.Log($"F{Time.frameCount} {label} {uniqueName} vp={virtualPose.ToString("F3")} lp={lockedPose.ToString("F3")} sp={manager.SpongyFromLocked.Multiply(lockedPose).ToString("F3")}"); #endif // WLT_EXTRA_LOGGING ReferencePose refPose = new ReferencePose() { name = uniqueName, fragmentId = fragmentId, anchorId = anchorId, virtualPose = virtualPose }; refPose.LockedPose = lockedPose; referencePoses.Add(refPose); QueueForSave(refPose); return(anchorId); }
private FragmentId[] ExtractFragmentIds(FragmentPose[] source) { FragmentId[] ids = new FragmentId[source.Length]; for (int i = 0; i < ids.Length; ++i) { ids[i] = source[i].fragmentId; } return(ids); }
/// <summary> /// Get the current state of a given fragment. /// </summary> /// <param name="id">Identifier of the fragment to query.</param> /// <returns>The state</returns> public AttachmentPointStateType GetFragmentState(FragmentId id) { Fragment fragment; if (fragments.TryGetValue(id, out fragment)) { return(fragment.State); } return(AttachmentPointStateType.Invalid); }
/// <summary> /// Establish which fragment a new attachment point should join. /// </summary> /// <param name="context">Optional spawning attachment point. May be null to "spawn from head".</param> /// <returns>Id of fragment to join. May be FragmentId.Invalid if not currently tracking.</returns> private FragmentId GetTargetFragmentId(IAttachmentPoint context) { FragmentId fragmentId = CurrentFragmentId; if (context != null) { fragmentId = context.FragmentId; } return(fragmentId); }
/// <summary> /// Adjust to refit operations. /// </summary> /// <param name="mainId">The new combined fragment.</param> /// <param name="absorbedIds">Id's of other fragments being merged into mainId.</param> /// <remarks> /// This callback occurs *after* the refit operation. As part of the refit, /// positions of the managed SpacePinOrientables may have changed, and therefore /// their implied orientations must be re-calculated. /// Note that there is an apparent race condition, as there is no order guarantee on /// the order of refit notifications, and the AlignmentManager also relies on the /// refit notification to adjust after refit operations. /// However, both the Orienter and the AlignmentManager rely only on the positions /// having been set, which was accomplished during the refit and before the refit /// notification. So it really doesn't matter whether the Orienter.OnRefit or the /// AlignmentManager.OnRefit is called first. /// </remarks> private void OnRefit(FragmentId mainId, FragmentId[] absorbedIds) { /// Use the last alignment manager used. If none, use the wlt manager's current alignment manager. IAlignmentManager alignMgr = cachedAlignmentManager; if (alignMgr == null) { alignMgr = WorldLockingManager.GetInstance().AlignmentManager; } Reorient(mainId, alignMgr); alignMgr.SendAlignmentAnchors(); }
/// <summary> /// Check existence of fragment with indicated id, /// and create it if it doesn't already exist. /// </summary> /// <param name="id">The fragment id</param> private Fragment EnsureFragment(FragmentId id) { if (!id.IsKnown()) { return(null); } if (!fragments.ContainsKey(id)) { fragments[id] = new Fragment(id); } return(fragments[id]); }
/// <summary> /// Format FragmentId as string (used for visualization) /// </summary> /// <param name="id">Fragment Id to be formatted</param> /// <returns>Formatted string</returns> public static string FormatStr(this FragmentId id) { switch (id) { case FragmentId.Invalid: return("F#INV"); case FragmentId.Unknown: return("F#UNK"); default: return(String.Format("F{0}", (int)id)); } }
/// <inheritdocs /> public void Reorient(FragmentId fragmentId, IAlignmentManager mgr) { if (!InitRotations(fragmentId)) { return; } if (!ComputeRotations()) { return; } if (!SetRotations(mgr)) { return; } }
/// <summary> /// Helper to move an attachment point from one fragment to another. /// </summary> /// <remarks> /// Assumes that the attachment point's FragmentId property has already been set to the new fragment. /// </remarks> /// <param name="oldFragmentId">Source fragment</param> /// <param name="attachPoint">The attachment point</param> private void ChangeAttachmentPointFragment(FragmentId oldFragmentId, AttachmentPoint attachPoint) { Debug.Assert(oldFragmentId != attachPoint.FragmentId, "Moving attachment point from and to same fragment"); Fragment oldFragment = EnsureFragment(oldFragmentId); Fragment newFragment = EnsureFragment(attachPoint.FragmentId); Debug.Assert(oldFragment != null, "Valid fragmentId's but null source fragment"); Debug.Assert(newFragment != null, "Valid fragmentId's but null destination fragment"); // Add to the new fragment newFragment.AddAttachmentPoint(attachPoint); // Remove from the old fragment oldFragment.ReleaseAttachmentPoint(attachPoint); }
unsafe public bool Merge(out FragmentId targetFragment, out FragmentPose[] mergedFragments) { targetFragment = FragmentId.Invalid; if (!FrozenWorld_RefitMerge_Init()) { checkError(); targetFragment = GetMostSignificantFragmentId(); mergedFragments = new FragmentPose[0]; return(false); } checkError(); FrozenWorld_RefitMerge_Prepare(); checkError(); int bufSize = FrozenWorld_RefitMerge_GetNumAdjustedFragments(); checkError(); FrozenWorld_RefitMerge_AdjustedFragment *buf = stackalloc FrozenWorld_RefitMerge_AdjustedFragment[bufSize]; int numAdjustedFragments = FrozenWorld_RefitMerge_GetAdjustedFragments(bufSize, buf); checkError(); mergedFragments = new FragmentPose[numAdjustedFragments]; for (int i = 0; i < numAdjustedFragments; i++) { var fragmentAdjust = new FragmentPose() { fragmentId = (FragmentId)buf[i].fragmentId, pose = FtoU(buf[i].adjustment) }; mergedFragments[i] = fragmentAdjust; } FrozenWorld_FragmentId mergedFragmentId; FrozenWorld_RefitMerge_GetMergedFragmentId(&mergedFragmentId); checkError(); targetFragment = (FragmentId)mergedFragmentId; FrozenWorld_RefitMerge_Apply(); checkError(); return(true); }
/// <summary> /// Invoke a refreeze operation on the plugin, and make all necessary adjustments /// in bookeeping after. /// </summary> /// <returns>True for successful refreeze.</returns> public bool Refreeze() { FragmentId targetFragmentId; FragmentId[] absorbedIds; if (!plugin.Refreeze(out targetFragmentId, out absorbedIds)) { return(false); } Debug.Assert(targetFragmentId.IsKnown(), "Received invalid merged fragment id from successful refreeze"); Fragment targetFragment = EnsureFragment(targetFragmentId); Debug.Assert(targetFragment != null, "Valid fragmentId but no fragment found"); for (int i = 0; i < absorbedIds.Length; ++i) { FragmentId sourceId = absorbedIds[i]; if (sourceId != targetFragmentId) { Fragment sourceFragment; if (fragments.TryGetValue(sourceId, out sourceFragment)) { targetFragment.AbsorbOtherFragment(sourceFragment); fragments.Remove(sourceId); } else { Debug.LogError($"Try to merge in a non-existent fragment {sourceId.ToString()}"); } } } CurrentFragmentId = targetFragmentId; // now apply individual adjustments to each attachment point. targetFragment.AdjustAll(plugin); // now that all adjustments have been made, notify the plugin to finish up the operation. plugin.RefreezeFinish(); refitNotifications?.Invoke(targetFragment.FragmentId, absorbedIds); return(true); }
/// <inheritdocs /> public AnchorId AddAlignmentAnchor(string uniqueName, Pose virtualPose, Pose lockedPose) { FragmentId fragmentId = CurrentFragmentId; AnchorId anchorId = ClaimAnchorId(); ReferencePose refPose = new ReferencePose() { name = uniqueName, fragmentId = fragmentId, anchorId = anchorId, virtualPose = virtualPose }; refPose.LockedPose = lockedPose; referencePoses.Add(refPose); QueueForSave(refPose); return(anchorId); }
/// <summary> /// Collect all orientables in the current fragment for processing. /// </summary> /// <param name="fragmentId"></param> /// <returns></returns> private bool InitRotations(FragmentId fragmentId) { actives.Clear(); for (int i = 0; i < orientables.Count; ++i) { if (orientables[i].FragmentId == fragmentId) { actives.Add( new WeightedRotation() { orientable = orientables[i], weight = 0.0f, rotation = Quaternion.identity } ); } } return(actives.Count > 0); }
/// <summary> /// Create and register a new attachment point. /// </summary> /// <remarks> /// The attachment point itself is a fairly opaque handle. Its effects are propagated to the client via the /// two handlers associated with it. /// The optional context attachment point provides an optional contextual hint to where in the anchor /// graph to bind the new attachment point. /// See <see cref="IAttachmentPointManager.CreateAttachmentPoint"/>. /// </remarks> /// <param name="frozenPosition">The position in the frozen space at which to start the attachment point</param> /// <param name="context">The optional context into which to create the attachment point (may be null)</param> /// <param name="locationHandler">Delegate to handle WorldLocking system adjustments to position</param> /// <param name="stateHandler">Delegate to handle WorldLocking connectivity changes</param> /// <returns>The new attachment point interface.</returns> public IAttachmentPoint CreateAttachmentPoint(Vector3 frozenPosition, IAttachmentPoint context, AdjustLocationDelegate locationHandler, AdjustStateDelegate stateHandler) { FragmentId fragmentId = GetTargetFragmentId(context); AttachmentPoint attachPoint = new AttachmentPoint(locationHandler, stateHandler); attachPoint.ObjectPosition = frozenPosition; if (fragmentId.IsKnown()) { SetupAttachmentPoint(plugin, attachPoint, context); Fragment fragment = EnsureFragment(fragmentId); Debug.Assert(fragment != null, "Valid fragmentId but no fragment found"); fragment.AddAttachmentPoint(attachPoint); } else { AddPendingAttachmentPoint(attachPoint, context); } return(attachPoint); }
/// <summary> /// Call on the plugin to compute the merge, then apply by /// setting transforms and adjusting scene graph. /// </summary> /// <returns>True for successful merge.</returns> public bool Merge() { FragmentId targetFragmentId; FragmentPose[] mergeAdjustments; if (!plugin.Merge(out targetFragmentId, out mergeAdjustments)) { return(false); } Debug.Assert(targetFragmentId.IsKnown(), "Received invalid merged fragment id from successful merge"); Fragment targetFragment = EnsureFragment(targetFragmentId); Debug.Assert(targetFragment != null, "Valid fragmentId but null target fragment from Merge"); int numAbsorbed = mergeAdjustments.Length; for (int i = 0; i < numAbsorbed; ++i) { FragmentId sourceId = mergeAdjustments[i].fragmentId; Pose adjustment = mergeAdjustments[i].pose; Fragment sourceFragment; if (fragments.TryGetValue(sourceId, out sourceFragment)) { targetFragment.AbsorbOtherFragment(sourceFragment, adjustment); fragments.Remove(sourceId); } else { Debug.LogError($"Try to merge in a non-existent fragment {sourceId.ToString()}"); } } CurrentFragmentId = targetFragmentId; ApplyActiveCurrentFragment(); refitNotifications?.Invoke(targetFragment.FragmentId, ExtractFragmentIds(mergeAdjustments)); return(true); }
/// <summary> /// Collect all orientables in the current fragment for processing. /// </summary> /// <param name="fragmentId"></param> /// <returns></returns> private bool InitRotations(FragmentId fragmentId) { actives.Clear(); for (int i = 0; i < orientables.Count; ++i) { if (orientables[i].FragmentId == fragmentId) { actives.Add( new WeightedRotation() { orientable = orientables[i], weight = 0.0f, /// Default rotation is current rotation. The inverse of the model rotation will /// cancel out the model rotation when the coordinate system rotation is computed, /// using the unmodified locked rotation. rotation = orientables[i].LockedRotation * Quaternion.Inverse(orientables[i].ModelRotation) } ); } } return(actives.Count > 0); }
/// <summary> /// Helper function for setting up the internals of an AttachmentPoint /// </summary> /// <param name="plugin">The global plugin</param> /// <param name="target">The attachment point to setup</param> /// <param name="context">The optional context <see cref="CreateAttachmentPoint"/></param> public static void SetupAttachmentPoint(IPlugin plugin, AttachmentPoint target, IAttachmentPoint context) { if (context != null) { AnchorId anchorId; Vector3 locationFromAnchor; plugin.CreateAttachmentPointFromSpawner(context.AnchorId, context.LocationFromAnchor, target.ObjectPosition, out anchorId, out locationFromAnchor); FragmentId fragmentId = context.FragmentId; target.Set(fragmentId, target.ObjectPosition, anchorId, locationFromAnchor); } else { FragmentId currentFragmentId = plugin.GetMostSignificantFragmentId(); AnchorId anchorId; Vector3 locationFromAnchor; plugin.CreateAttachmentPointFromHead(target.ObjectPosition, out anchorId, out locationFromAnchor); FragmentId fragmentId = currentFragmentId; target.Set(fragmentId, target.ObjectPosition, anchorId, locationFromAnchor); } }
/// <summary> /// If still waiting for a valid current fragment since last load, /// and there is a current valid fragment, set it to reference poses. /// </summary> private void CheckFragment() { bool changed = ActiveFragmentId != CurrentFragmentId; if (needFragment && CurrentFragmentId.IsKnown()) { FragmentId fragmentId = CurrentFragmentId; for (int i = 0; i < referencePoses.Count; ++i) { if (!referencePoses[i].fragmentId.IsKnown()) { referencePoses[i].fragmentId = fragmentId; changed = true; } } needFragment = false; } if (changed) { ActivateCurrentFragment(); } }
unsafe public bool Refreeze(out FragmentId mergedId, out FragmentId[] absorbedFragments) { if (!FrozenWorld_RefitRefreeze_Init()) { checkError(); mergedId = GetMostSignificantFragmentId(); absorbedFragments = new FragmentId[0]; return(false); } checkError(); FrozenWorld_RefitRefreeze_Prepare(); checkError(); int bufSize = FrozenWorld_RefitRefreeze_GetNumAdjustedFragments(); checkError(); FrozenWorld_FragmentId *buf = stackalloc FrozenWorld_FragmentId[bufSize]; int numAffected = FrozenWorld_RefitRefreeze_GetAdjustedFragmentIds(bufSize, buf); checkError(); absorbedFragments = new FragmentId[numAffected]; for (int i = 0; i < numAffected; ++i) { absorbedFragments[i] = (FragmentId)buf[i]; } FrozenWorld_FragmentId mergedFragmentId; FrozenWorld_RefitRefreeze_GetMergedFragmentId(&mergedFragmentId); checkError(); mergedId = (FragmentId)mergedFragmentId; return(true); }
/// <summary> /// If still waiting for a valid current fragment since last load, /// and there is a current valid fragment, set it to reference poses. /// </summary> private void CheckFragment() { bool changed = ActiveFragmentId != CurrentFragmentId; if (needFragment && CurrentFragmentId.IsKnown()) { FragmentId fragmentId = CurrentFragmentId; for (int i = 0; i < referencePoses.Count; ++i) { if (!referencePoses[i].fragmentId.IsKnown()) { DebugLogSaveLoad($"Transfer {referencePoses[i].anchorId.FormatStr()} from frag={referencePoses[i].fragmentId.FormatStr()} to {fragmentId.FormatStr()}"); referencePoses[i].fragmentId = fragmentId; changed = true; } } needFragment = false; } if (changed) { ActivateCurrentFragment(); } }
private void UpdateAndCheck(IPlugin plugin, int prime, List <AnchorPose> anchorPoses, List <AnchorEdge> anchorEdges, List <AnchorId> frozenIds) { AnchorPose headPose = anchorPoses[prime]; plugin.ClearSpongyAnchors(); plugin.Step_Init(headPose.pose); plugin.AddSpongyAnchors(anchorPoses); plugin.SetMostSignificantSpongyAnchorId(headPose.anchorId); plugin.AddSpongyEdges(anchorEdges); plugin.Step_Finish(); FragmentId fragmentId = plugin.GetMostSignificantFragmentId(); Debug.Log($"fragmentId={fragmentId}"); var frozenAnchors = plugin.GetFrozenAnchors(); Assert.AreEqual(frozenAnchors.Length, frozenIds.Count, "Unexpected difference between plugin frozen anchors and client frozen anchors counts"); for (int i = 0; i < frozenAnchors.Length; ++i) { Assert.IsTrue(frozenIds.FindIndex(x => x == frozenAnchors[i].anchorId) >= 0, "Plugin has unexpected frozen id"); } }
/// <summary> /// Update the pose for refit operations. /// </summary> /// <param name="adjustement">The adjustment to apply.</param> private void OnLocationUpdate(Pose adjustement) { fragmentId = CurrentFragmentId; lockedPose = adjustement.Multiply(lockedPose); AfterAdjustmentPoseChanged(); }
private void OnRefit(FragmentId mainId, FragmentId[] absorbedIds) { ActiveFragmentId = FragmentId.Unknown; }
private void RefitHandler(FragmentId mergedId, FragmentId[] combined) { Reset(); }