From 55434e969585d4adf06c5c8bad1bfb7c9d2f758b Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Sat, 20 Jul 2024 19:32:17 +0200 Subject: [PATCH 01/35] XRSEEActions added (Main Control) --- Assets/SEE/XR/XRSEEActions.cs | 185 +++++++++++++++++++++++++++++ Assets/SEE/XR/XRSEEActions.cs.meta | 11 ++ 2 files changed, 196 insertions(+) create mode 100644 Assets/SEE/XR/XRSEEActions.cs create mode 100644 Assets/SEE/XR/XRSEEActions.cs.meta diff --git a/Assets/SEE/XR/XRSEEActions.cs b/Assets/SEE/XR/XRSEEActions.cs new file mode 100644 index 0000000000..35ad611242 --- /dev/null +++ b/Assets/SEE/XR/XRSEEActions.cs @@ -0,0 +1,185 @@ +using SEE.Controls; +using SEE.Controls.Actions; +using SEE.DataModel.DG; +using SEE.GO; +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Assertions; +using UnityEngine.InputSystem; +using UnityEngine.XR.Interaction.Toolkit; +using UnityEngine.XR.Interaction.Toolkit.Inputs; +using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers; +using UnityEngine.XR.Interaction.Toolkit.Interactables; +using UnityEngine.XR.Interaction.Toolkit.Interactors; +using UnityEngine.XR.Interaction.Toolkit.Locomotion; + +public class XRSEEActions : MonoBehaviour +{ + [SerializeField] + [Tooltip("Reads input data from the right hand controller. Input Action must be a Value action type (Vector 2).")] + XRInputValueReader m_RightHandTurnInput = new XRInputValueReader("Right Hand Snap Turn"); + + /// + /// Reads input data from the right hand controller. Input Action must be a Value action type (Vector 2). + /// + public XRInputValueReader rightHandTurnInput + { + get => m_RightHandTurnInput; + set => XRInputReaderUtility.SetInputProperty(ref m_RightHandTurnInput, value, this); + } + + public InputHelpers.Button rightTurnButton = InputHelpers.Button.PrimaryAxis2DRight; + public InputHelpers.Button leftTurnButton = InputHelpers.Button.SecondaryAxis2DLeft; + + [SerializeField] + XRRayInteractor m_RayInteractor; + public static XRRayInteractor RayInteractor { get; set; } + + private bool hovering = false; + public static GameObject hoveredGameObject { get; set; } + + public static Transform oldParent { get; set; } + + public InputActionReference inputAction; + + public InputActionReference openContextMenu; + // Start is called before the first frame update + private void Awake() + { + rightHandTurnInput.inputAction.performed += Rotate; + inputAction.action.Enable(); + inputAction.action.performed += Action; + openContextMenu.action.Enable(); + openContextMenu.action.performed += OpenContexMenu; + RayInteractor = m_RayInteractor; + } + + public void OnHoverEnter(HoverEnterEventArgs args) + { + Debug.Log($"{args.interactorObject} hovered over {args.interactableObject}", this); + hovering = true; + hoveredGameObject = args.interactableObject.transform.gameObject; + oldParent = args.interactableObject.transform.parent; + Debug.Log(oldParent.name + "cool"); + if (hoveredGameObject.transform.TryGetComponent(out InteractableObject io)) + { + io.SetHoverFlag(HoverFlag.World, true, true); + } + } + + public void OnHoverExited(HoverExitEventArgs args) + { + Debug.Log($"{args.interactorObject} stopped hovered over {args.interactableObject}", this); + hovering = false; + hoveredGameObject = args.interactableObject.transform.gameObject; + oldParent = null; + if (hoveredGameObject.transform.TryGetComponent(out InteractableObject io)) + { + io.SetHoverFlag(HoverFlag.World, false, true); + } + } + + public void OnSelectEnter(SelectEnterEventArgs args) + { + if(GlobalActionHistory.Current() == ActionStateTypes.Move || GlobalActionHistory.Current() == ActionStateTypes.Draw || GlobalActionHistory.Current() == ActionStateTypes.ShowCode + || GlobalActionHistory.Current() == ActionStateTypes.Rotate) + { + if (GlobalActionHistory.Current() == ActionStateTypes.Move && Selected == true) + { + Selected = false; + } + else + { + Selected = true; + } + } + } + + public void OnSelectExited(SelectExitEventArgs args) + { + if (GlobalActionHistory.Current() == ActionStateTypes.Draw || GlobalActionHistory.Current() == ActionStateTypes.ShowCode + || GlobalActionHistory.Current() == ActionStateTypes.Rotate) + { + Selected = false; + } + } + public static bool Selected { get; set; } + public static bool Delete { get; set; } + private void Action(InputAction.CallbackContext context) + { + if (hovering) + { + if(GlobalActionHistory.Current() == ActionStateTypes.NewEdge || GlobalActionHistory.Current() == ActionStateTypes.Delete || + GlobalActionHistory.Current() == ActionStateTypes.NewNode) + { + Delete = true; + } + } + } + + public static bool ContextMenu { get; set; } + + private void OpenContexMenu(InputAction.CallbackContext context) + { + if (hovering) + { + if (hoveredGameObject.gameObject.TryGetNode(out Node node)) + { + ContextMenu = true; + } + } + } + + private void Rotate(InputAction.CallbackContext context) + { + if (hovering && GlobalActionHistory.Current() == ActionStateTypes.Rotate) + { + if (hoveredGameObject.gameObject.TryGetNode(out Node node)) + { + var amount = GetTurnAmount(m_RightHandTurnInput.ReadValue()); + if (Math.Abs(amount) > 0f) + { + hoveredGameObject.transform.Rotate(0, amount * Time.deltaTime, 0); + } + ContextMenu = true; + } + } + } + + /// + /// Determines the turn amount in degrees for the given vector. + /// + /// Input vector, such as from a thumbstick. + /// Returns the turn amount in degrees for the given vector. + protected virtual float GetTurnAmount(Vector2 input) + { + if (input == Vector2.zero) + return 0f; + + var cardinal = CardinalUtility.GetNearestCardinal(input); + switch (cardinal) + { + case Cardinal.North: + break; + case Cardinal.South: + break; + case Cardinal.East: + return 45f; + case Cardinal.West: + return -45f; + default: + Assert.IsTrue(false, $"Unhandled {nameof(Cardinal)}={cardinal}"); + break; + } + + return 0f; + } + + // Update is called once per frame + void Update() + { + GlobalActionHistory.Update(); + } +} diff --git a/Assets/SEE/XR/XRSEEActions.cs.meta b/Assets/SEE/XR/XRSEEActions.cs.meta new file mode 100644 index 0000000000..6e4ea00c06 --- /dev/null +++ b/Assets/SEE/XR/XRSEEActions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: df5e8c7d8b96d6944aaafef73a15f5b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 1b4872466e07849b305bd531b153f014a52dc4b7 Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Sun, 21 Jul 2024 00:13:07 +0200 Subject: [PATCH 02/35] Added MoveAction/ShowCode/ShowTree --- Assets/SEE/Controls/Actions/MoveAction.cs | 113 +++++++++++++----- Assets/SEE/Controls/Actions/ShowCodeAction.cs | 67 +++++++---- Assets/SEE/Controls/Actions/ShowTree.cs | 4 +- Assets/SEE/UI/Notification/Notification.cs | 10 ++ Assets/SEE/UI/PopupMenu/PopupMenu.cs | 5 + .../SEE/UI/StateIndicator/StateIndicator.cs | 5 + Assets/SEE/UI/Window/BaseWindow.cs | 7 +- .../UI/Window/CodeWindow/DesktopCodeWindow.cs | 10 ++ Assets/SEE/UI/Window/DesktopWindowSpace.cs | 23 +++- Assets/SEE/Utils/Raycasting.cs | 12 +- 10 files changed, 199 insertions(+), 57 deletions(-) diff --git a/Assets/SEE/Controls/Actions/MoveAction.cs b/Assets/SEE/Controls/Actions/MoveAction.cs index 919b1cf2db..77b12d1004 100644 --- a/Assets/SEE/Controls/Actions/MoveAction.cs +++ b/Assets/SEE/Controls/Actions/MoveAction.cs @@ -84,7 +84,14 @@ public void Grab(GameObject gameObject) if (gameObject != null) { GrabbedGameObject = gameObject; - originalParent = gameObject.transform.parent; + if (SceneSettings.InputType == PlayerInputType.VRPlayer) + { + originalParent = XRSEEActions.oldParent; + } + else + { + originalParent = gameObject.transform.parent; + } originalLocalScale = gameObject.transform.localScale; originalWorldPosition = gameObject.transform.position; IsGrabbed = true; @@ -516,49 +523,91 @@ private void RestoreOriginalAppearance() /// true if completed public override bool Update() { - if (UserIsGrabbing()) // start to grab the object or continue to move the grabbed object + if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - if (!grabbedObject.IsGrabbed) + if (XRSEEActions.Selected) { - // User is starting dragging the currently hovered object. - InteractableObject hoveredObject = InteractableObject.HoveredObjectWithWorldFlag; - // An object to be grabbed must be representing a node that is not the root. - if (hoveredObject && hoveredObject.gameObject.TryGetNode(out Node node) && !node.IsRoot()) + if (!grabbedObject.IsGrabbed) { - grabbedObject.Grab(hoveredObject.gameObject); - AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.PickupSound, hoveredObject.gameObject); - // Remember the current distance from the pointing device to the grabbed object. - distanceToUser = Vector3.Distance(Raycasting.UserPointsTo().origin, grabbedObject.Position); - CurrentState = IReversibleAction.Progress.InProgress; + InteractableObject hoveredObject = InteractableObject.HoveredObjectWithWorldFlag; + if (hoveredObject && hoveredObject.gameObject.TryGetNode(out Node node) && !node.IsRoot()) + { + grabbedObject.Grab(hoveredObject.gameObject); + AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.PickupSound, hoveredObject.gameObject); + Vector3 origin; + XRSEEActions.RayInteractor.GetLineOriginAndDirection(out origin, out _); + distanceToUser = Vector3.Distance(origin, grabbedObject.Position); + CurrentState = IReversibleAction.Progress.InProgress; + } + else + { + Vector3 origin; + XRSEEActions.RayInteractor.GetLineOriginAndDirection(out origin, out Vector3 direction); + grabbedObject.MoveTo(origin + distanceToUser * direction); + } + UpdateHierarchy(); } } - else // continue moving the grabbed object + else if (grabbedObject.IsGrabbed) // dragging has ended { - // The grabbed object will be moved on the surface of a sphere with - // radius distanceToUser in the direction the user is pointing to. - Ray ray = Raycasting.UserPointsTo(); - grabbedObject.MoveTo(ray.origin + distanceToUser * ray.direction); + // Finalize the action with the grabbed object. + if (grabbedObject.GrabbedGameObject != null) + { + AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound, grabbedObject.GrabbedGameObject); + } + grabbedObject.UnGrab(); + // Action is finished. + CurrentState = IReversibleAction.Progress.Completed; + return true; } - - // The grabbed node is not yet at its final destination. The user is still moving - // it. We will run a what-if reflexion analysis to give immediate feedback on the - // consequences if the user were putting the grabbed node onto the node the user - // is currently aiming at. - UpdateHierarchy(); + return false; } - else if (grabbedObject.IsGrabbed) // dragging has ended + else { - // Finalize the action with the grabbed object. - if (grabbedObject.GrabbedGameObject != null) + if (UserIsGrabbing()) // start to grab the object or continue to move the grabbed object { - AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound, grabbedObject.GrabbedGameObject); + if (!grabbedObject.IsGrabbed) + { + // User is starting dragging the currently hovered object. + InteractableObject hoveredObject = InteractableObject.HoveredObjectWithWorldFlag; + // An object to be grabbed must be representing a node that is not the root. + if (hoveredObject && hoveredObject.gameObject.TryGetNode(out Node node) && !node.IsRoot()) + { + grabbedObject.Grab(hoveredObject.gameObject); + AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.PickupSound, hoveredObject.gameObject); + // Remember the current distance from the pointing device to the grabbed object. + distanceToUser = Vector3.Distance(Raycasting.UserPointsTo().origin, grabbedObject.Position); + CurrentState = IReversibleAction.Progress.InProgress; + } + } + else // continue moving the grabbed object + { + // The grabbed object will be moved on the surface of a sphere with + // radius distanceToUser in the direction the user is pointing to. + Ray ray = Raycasting.UserPointsTo(); + grabbedObject.MoveTo(ray.origin + distanceToUser * ray.direction); + } + + // The grabbed node is not yet at its final destination. The user is still moving + // it. We will run a what-if reflexion analysis to give immediate feedback on the + // consequences if the user were putting the grabbed node onto the node the user + // is currently aiming at. + UpdateHierarchy(); + } + else if (grabbedObject.IsGrabbed) // dragging has ended + { + // Finalize the action with the grabbed object. + if (grabbedObject.GrabbedGameObject != null) + { + AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound, grabbedObject.GrabbedGameObject); + } + grabbedObject.UnGrab(); + // Action is finished. + CurrentState = IReversibleAction.Progress.Completed; + return true; } - grabbedObject.UnGrab(); - // Action is finished. - CurrentState = IReversibleAction.Progress.Completed; - return true; + return false; } - return false; } /// diff --git a/Assets/SEE/Controls/Actions/ShowCodeAction.cs b/Assets/SEE/Controls/Actions/ShowCodeAction.cs index e181ef46d0..336430cb1c 100644 --- a/Assets/SEE/Controls/Actions/ShowCodeAction.cs +++ b/Assets/SEE/Controls/Actions/ShowCodeAction.cs @@ -288,33 +288,58 @@ public static CodeWindow ShowCode(GraphElementRef graphElementRef) public override bool Update() { - // Only allow local player to open new code windows - if (spaceManager.CurrentPlayer == WindowSpaceManager.LocalPlayer - && SEEInput.Select() - && Raycasting.RaycastGraphElement(out RaycastHit _, out GraphElementRef graphElementRef) != HitGraphElement.None) + if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - // If nothing is selected, there's nothing more we need to do - if (graphElementRef == null) + if (XRSEEActions.Selected) { - return false; - } - - // Edges of type Clone will be handled differently. For these, we will be - // showing a unified diff. - CodeWindow codeWindow = graphElementRef is EdgeRef { Value: { Type: "Clone" } } edgeRef + if (!XRSEEActions.hoveredGameObject.HasNodeRef()) + { + return false; + } + XRSEEActions.hoveredGameObject.TryGetComponent(out NodeRef nodeRef); + GraphElementRef elementRef = nodeRef; + CodeWindow codeWindow = elementRef is EdgeRef { Value: { Type: "Clone" } } edgeRef ? ShowUnifiedDiff(edgeRef) - : ShowCode(graphElementRef); - // Add code window to our space of code window, if it isn't in there yet - WindowSpace manager = spaceManager[WindowSpaceManager.LocalPlayer]; - if (!manager.Windows.Contains(codeWindow)) - { - manager.AddWindow(codeWindow); + : ShowCode(elementRef); + // Add code window to our space of code window, if it isn't in there yet + WindowSpace manager = spaceManager[WindowSpaceManager.LocalPlayer]; + if (!manager.Windows.Contains(codeWindow)) + { + manager.AddWindow(codeWindow); + } + manager.ActiveWindow = codeWindow; } - manager.ActiveWindow = codeWindow; - // TODO (#669): Set font size etc in settings (maybe, or maybe that's too much) + return false; } + else + { + // Only allow local player to open new code windows + if (spaceManager.CurrentPlayer == WindowSpaceManager.LocalPlayer + && SEEInput.Select() + && Raycasting.RaycastGraphElement(out RaycastHit _, out GraphElementRef graphElementRef) != HitGraphElement.None) + { + // If nothing is selected, there's nothing more we need to do + if (graphElementRef == null) + { + return false; + } - return false; + // Edges of type Clone will be handled differently. For these, we will be + // showing a unified diff. + CodeWindow codeWindow = graphElementRef is EdgeRef { Value: { Type: "Clone" } } edgeRef + ? ShowUnifiedDiff(edgeRef) + : ShowCode(graphElementRef); + // Add code window to our space of code window, if it isn't in there yet + WindowSpace manager = spaceManager[WindowSpaceManager.LocalPlayer]; + if (!manager.Windows.Contains(codeWindow)) + { + manager.AddWindow(codeWindow); + } + manager.ActiveWindow = codeWindow; + // TODO (#669): Set font size etc in settings (maybe, or maybe that's too much) + } + return false; + } } } } diff --git a/Assets/SEE/Controls/Actions/ShowTree.cs b/Assets/SEE/Controls/Actions/ShowTree.cs index 63a5343bad..523eff4ea8 100644 --- a/Assets/SEE/Controls/Actions/ShowTree.cs +++ b/Assets/SEE/Controls/Actions/ShowTree.cs @@ -95,15 +95,17 @@ private void HideTreeView() private void Update() { - if (SEEInput.ToggleTreeView()) + if (SEEInput.ToggleTreeView() || XRSEEActions.ContextMenu) { if (treeWindows.Count == 0) { ShowTreeView(); + XRSEEActions.ContextMenu = false; } else { HideTreeView(); + XRSEEActions.ContextMenu = false; } } } diff --git a/Assets/SEE/UI/Notification/Notification.cs b/Assets/SEE/UI/Notification/Notification.cs index 89b3d2b809..52aba16d3f 100644 --- a/Assets/SEE/UI/Notification/Notification.cs +++ b/Assets/SEE/UI/Notification/Notification.cs @@ -228,6 +228,11 @@ protected override void StartDesktop() } } + protected override void StartVR() + { + StartDesktop(); + } + protected override void UpdateDesktop() { // We only perform this expensive comparison once every 50 frames, if necessary @@ -238,5 +243,10 @@ protected override void UpdateDesktop() Destroyer.Destroy(gameObject); } } + + protected override void UpdateVR() + { + UpdateDesktop(); + } } } diff --git a/Assets/SEE/UI/PopupMenu/PopupMenu.cs b/Assets/SEE/UI/PopupMenu/PopupMenu.cs index 708316d63b..3f16cb74ff 100644 --- a/Assets/SEE/UI/PopupMenu/PopupMenu.cs +++ b/Assets/SEE/UI/PopupMenu/PopupMenu.cs @@ -108,6 +108,11 @@ protected override void StartDesktop() menu.gameObject.SetActive(false); } + protected override void StartVR() + { + StartDesktop(); + } + /// /// Adds a new to the menu. /// diff --git a/Assets/SEE/UI/StateIndicator/StateIndicator.cs b/Assets/SEE/UI/StateIndicator/StateIndicator.cs index 6082147455..fd3330fd59 100644 --- a/Assets/SEE/UI/StateIndicator/StateIndicator.cs +++ b/Assets/SEE/UI/StateIndicator/StateIndicator.cs @@ -21,5 +21,10 @@ protected override void StartDesktop() { StartDesktopInit(); } + + protected override void StartVR() + { + StartDesktop(); + } } } diff --git a/Assets/SEE/UI/Window/BaseWindow.cs b/Assets/SEE/UI/Window/BaseWindow.cs index ccbe9b6fe5..68d7d58257 100644 --- a/Assets/SEE/UI/Window/BaseWindow.cs +++ b/Assets/SEE/UI/Window/BaseWindow.cs @@ -75,6 +75,11 @@ protected override void StartDesktop() // TODO: Disable IDE Button if unused } + protected override void StartVR() + { + StartDesktop(); + } + /// /// Shows or hides the window, depending on the parameter. /// @@ -92,7 +97,7 @@ public void Show(bool show) ShowDesktop(show); break; case PlayerInputType.VRPlayer: - PlatformUnsupported(); + ShowDesktop(show); break; case PlayerInputType.None: // nothing needs to be done break; diff --git a/Assets/SEE/UI/Window/CodeWindow/DesktopCodeWindow.cs b/Assets/SEE/UI/Window/CodeWindow/DesktopCodeWindow.cs index a6135556c1..46a0d777af 100644 --- a/Assets/SEE/UI/Window/CodeWindow/DesktopCodeWindow.cs +++ b/Assets/SEE/UI/Window/CodeWindow/DesktopCodeWindow.cs @@ -108,6 +108,11 @@ protected override void StartDesktop() SetupBreakpoints(); } + protected override void StartVR() + { + StartDesktop(); + } + /// /// Sets up the breakpoints. /// @@ -193,6 +198,11 @@ protected override void UpdateDesktop() } } + protected override void UpdateVR() + { + UpdateDesktop(); + } + /// /// Recalculates the using the current window height and line height of the text. /// This method should be called every time the window height or the line height changes. diff --git a/Assets/SEE/UI/Window/DesktopWindowSpace.cs b/Assets/SEE/UI/Window/DesktopWindowSpace.cs index d5769bd6af..ecc97aee4c 100644 --- a/Assets/SEE/UI/Window/DesktopWindowSpace.cs +++ b/Assets/SEE/UI/Window/DesktopWindowSpace.cs @@ -1,9 +1,11 @@ using System; using System.Linq; using DynamicPanels; +using SEE.Controls; using SEE.GO; using SEE.Utils; using UnityEngine; +using UnityEngine.XR.Interaction.Toolkit.UI; namespace SEE.UI.Window { @@ -35,6 +37,17 @@ protected override void StartDesktop() space.SetActive(true); } + protected override void StartVR() + { + Canvas.MustGetComponent().renderMode = RenderMode.WorldSpace; + Canvas.transform.SetParent(GameObject.Find("XRTabletCanvas").transform, false); + Canvas.AddComponent(); + Canvas.GetComponent().localScale = new Vector3(0.001f, 0.001f, 0.001f); + Canvas.GetComponent().localRotation = Quaternion.Euler(0, -90, 0); + Canvas.GetComponent().localPosition = new Vector3(0.9f, 0, 0); + StartDesktop(); + } + /// ///

/// Sets the active tab of the panel to the . @@ -147,7 +160,10 @@ protected override void UpdateDesktop() // Rebuild layout panelsCanvas.ForceRebuildLayoutImmediate(); - window.RebuildLayout(); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + window.RebuildLayout(); + } } void CloseTab(PanelTab panelTab) @@ -169,6 +185,11 @@ void CloseTab(PanelTab panelTab) } } + protected override void UpdateVR() + { + UpdateDesktop(); + } + ///

/// Initializes the dynamic panel on the panel canvas. /// diff --git a/Assets/SEE/Utils/Raycasting.cs b/Assets/SEE/Utils/Raycasting.cs index 3bbbd93761..db6af9e01b 100644 --- a/Assets/SEE/Utils/Raycasting.cs +++ b/Assets/SEE/Utils/Raycasting.cs @@ -278,9 +278,19 @@ public static Ray UserPointsTo() { // FIXME: We need to an interaction for VR, too. Camera mainCamera = MainCamera.Camera; - return mainCamera != null + if (SceneSettings.InputType == PlayerInputType.VRPlayer) + { + XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit); + return mainCamera != null + ? mainCamera.ScreenPointToRay(mainCamera.WorldToScreenPoint(hit.point)) + : new Ray(origin: Vector3.zero, direction: Vector3.zero); + } + else + { + return mainCamera != null ? mainCamera.ScreenPointToRay(Input.mousePosition) : new Ray(origin: Vector3.zero, direction: Vector3.zero); + } } } } From 730383c9e55ae450c1230ca1ddbc36e89363445c Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Sun, 21 Jul 2024 16:57:09 +0200 Subject: [PATCH 03/35] Added AddEdgeAction/AddNodeAction/DeleteAction/DrawAction/AvatarAdapter/InteractionDecorator --- Assets/SEE/Controls/Actions/AddEdgeAction.cs | 38 +++++++++++---- Assets/SEE/Controls/Actions/AddNodeAction.cs | 19 +++++++- Assets/SEE/Controls/Actions/DeleteAction.cs | 49 ++++++++++++++------ Assets/SEE/Controls/Actions/DrawAction.cs | 30 +++++++++--- Assets/SEE/Game/Avatars/AvatarAdapter.cs | 28 ++++++++++- Assets/SEE/Game/InteractionDecorator.cs | 2 + 6 files changed, 136 insertions(+), 30 deletions(-) diff --git a/Assets/SEE/Controls/Actions/AddEdgeAction.cs b/Assets/SEE/Controls/Actions/AddEdgeAction.cs index a2abf39565..3008c9c919 100644 --- a/Assets/SEE/Controls/Actions/AddEdgeAction.cs +++ b/Assets/SEE/Controls/Actions/AddEdgeAction.cs @@ -133,22 +133,44 @@ public override void Stop() public override bool Update() { bool result = false; - // Assigning the game objects to be connected. // Checking whether the two game objects are not null and whether they are // actually nodes. // FIXME: We need an interaction for VR, too. - if (HoveredObject != null && Input.GetMouseButtonDown(0) && !Raycasting.IsMouseOverGUI() && HoveredObject.HasNodeRef()) + if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - if (from == null) + if (XRSEEActions.hoveredGameObject != null && XRSEEActions.Delete && XRSEEActions.hoveredGameObject.HasNodeRef()) { - // No source selected yet; this interaction is meant to set the source. - from = HoveredObject; + Debug.Log("nice"); + if (from == null) + { + from = XRSEEActions.hoveredGameObject; + XRSEEActions.Delete = false; + Debug.Log(from.name); + } + else if (to == null) + { + to = XRSEEActions.hoveredGameObject; + XRSEEActions.Delete = false; + Debug.Log(to.name); + } } - else if (to == null) + } + else + { + if (HoveredObject != null && Input.GetMouseButtonDown(0) && !Raycasting.IsMouseOverGUI() && HoveredObject.HasNodeRef()) { - // Source is already set; this interaction is meant to set the target. - to = HoveredObject; + Debug.Log("nice"); + if (from == null) + { + // No source selected yet; this interaction is meant to set the source. + from = HoveredObject; + } + else if (to == null) + { + // Source is already set; this interaction is meant to set the target. + to = HoveredObject; + } } } // Note: from == to may be possible. diff --git a/Assets/SEE/Controls/Actions/AddNodeAction.cs b/Assets/SEE/Controls/Actions/AddNodeAction.cs index 54b0eb62c7..c8cc224a38 100644 --- a/Assets/SEE/Controls/Actions/AddNodeAction.cs +++ b/Assets/SEE/Controls/Actions/AddNodeAction.cs @@ -23,7 +23,24 @@ internal class AddNodeAction : AbstractPlayerAction public override bool Update() { bool result = false; - + if (XRSEEActions.hoveredGameObject != null && XRSEEActions.Delete && XRSEEActions.hoveredGameObject.HasNodeRef() && + XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit res)) + { + GameObject parent = XRSEEActions.hoveredGameObject; + addedGameNode = GameNodeAdder.AddChild(parent); + // addedGameNode has the scale and position of parent. + // The position at which the parent was hit will be the center point of the addedGameNode. + addedGameNode.transform.position = res.point; + // PutOn makes sure addedGameNode fits into parent. + GameNodeMover.PutOn(child: addedGameNode.transform, parent: parent, true); + memento = new Memento(child: addedGameNode, parent: parent); + memento.NodeID = addedGameNode.name; + new AddNodeNetAction(parentID: memento.Parent.name, newNodeID: memento.NodeID, memento.Position, memento.Scale).Execute(); + result = true; + CurrentState = IReversibleAction.Progress.Completed; + XRSEEActions.Delete = false; + AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.NewNodeSound, parent); + } // FIXME: Needs adaptation for VR where no mouse is available. if (Input.GetMouseButtonDown(0) && Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) == HitGraphElement.Node) diff --git a/Assets/SEE/Controls/Actions/DeleteAction.cs b/Assets/SEE/Controls/Actions/DeleteAction.cs index 9c5a120a07..a5056cc4ac 100644 --- a/Assets/SEE/Controls/Actions/DeleteAction.cs +++ b/Assets/SEE/Controls/Actions/DeleteAction.cs @@ -115,23 +115,46 @@ public override void Stop() /// true if completed public override bool Update() { - // FIXME: Needs adaptation for VR where no mouse is available. - if (Input.GetMouseButtonDown(0) - && Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.None) + Debug.Log(XRSEEActions.Delete); + if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - // the hit object is the one to be deleted - hitGraphElement = raycastHit.collider.gameObject; - Assert.IsTrue(hitGraphElement.HasNodeRef() || hitGraphElement.HasEdgeRef()); - InteractableObject.UnselectAll(true); - (_, deletedGameObjects) = GameElementDeleter.Delete(hitGraphElement); - new DeleteNetAction(hitGraphElement.name).Execute(); - CurrentState = IReversibleAction.Progress.Completed; - AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound); - return true; // the selected objects are deleted and this action is done now + if (XRSEEActions.Delete) + { + GameObject DeleteGameObject = XRSEEActions.hoveredGameObject; + Assert.IsTrue(DeleteGameObject.HasNodeRef() || DeleteGameObject.HasEdgeRef()); + InteractableObject.UnselectAll(true); + (_, deletedGameObjects) = GameElementDeleter.Delete(DeleteGameObject); + new DeleteNetAction(DeleteGameObject.name).Execute(); + CurrentState = IReversibleAction.Progress.Completed; + AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound); + XRSEEActions.Delete = false; + return true; // the selected objects are deleted and this action is done now + } + else + { + return false; + } } else { - return false; + // FIXME: Needs adaptation for VR where no mouse is available. + if (Input.GetMouseButtonDown(0) + && Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.None) + { + // the hit object is the one to be deleted + hitGraphElement = raycastHit.collider.gameObject; + Assert.IsTrue(hitGraphElement.HasNodeRef() || hitGraphElement.HasEdgeRef()); + InteractableObject.UnselectAll(true); + (_, deletedGameObjects) = GameElementDeleter.Delete(hitGraphElement); + new DeleteNetAction(hitGraphElement.name).Execute(); + CurrentState = IReversibleAction.Progress.Completed; + AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound); + return true; // the selected objects are deleted and this action is done now + } + else + { + return false; + } } } diff --git a/Assets/SEE/Controls/Actions/DrawAction.cs b/Assets/SEE/Controls/Actions/DrawAction.cs index fcd0dfce83..99ec9d2faa 100644 --- a/Assets/SEE/Controls/Actions/DrawAction.cs +++ b/Assets/SEE/Controls/Actions/DrawAction.cs @@ -104,9 +104,9 @@ private void SetUpRenderer() ///
public override bool Update() { - if (!Raycasting.IsMouseOverGUI()) + if (!XRSEEActions.Selected || !Raycasting.IsMouseOverGUI()) { - if (Input.GetMouseButtonDown(0) || Input.GetMouseButton(0)) + if (Input.GetMouseButtonDown(0) || Input.GetMouseButton(0) || XRSEEActions.Selected) { // We create the line on demand so that there is no left-over // when the drawing action has never actually started to draw anything. @@ -116,9 +116,25 @@ public override bool Update() } // FIXME: This would needed to be adjusted to VR and AR. // The position at which to continue the line. - Vector3 newPosition = Input.mousePosition; - newPosition.z = 1.0f; - newPosition = Camera.main.ScreenToWorldPoint(newPosition); + Vector3 newPosition; + if (SceneSettings.InputType == PlayerInputType.VRPlayer) + { + if (XRSEEActions.hoveredGameObject.name == "pen") + { + newPosition = XRSEEActions.hoveredGameObject.transform.Find("Front").position; + } + else + { + XRSEEActions.RayInteractor.GetLineOriginAndDirection(out Vector3 origin, out Vector3 direction); + newPosition = origin; + } + } + else + { + newPosition = Input.mousePosition; + newPosition.z = 1.0f; + newPosition = Camera.main.ScreenToWorldPoint(newPosition); + } // Add newPosition to the line renderer. Vector3[] newPositions = new Vector3[positions.Length + 1]; @@ -130,12 +146,12 @@ public override bool Update() // The line has been continued so this action has had a visible effect. CurrentState = IReversibleAction.Progress.Completed; } - if (Input.GetMouseButtonDown(0)) + if (Input.GetMouseButtonDown(0) || XRSEEActions.Selected) { AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.ScribbleSound); } // The action is considered complete if the mouse button is no longer pressed. - return Input.GetMouseButtonUp(0); + return Input.GetMouseButtonUp(0) || !XRSEEActions.Selected; } return false; } diff --git a/Assets/SEE/Game/Avatars/AvatarAdapter.cs b/Assets/SEE/Game/Avatars/AvatarAdapter.cs index de3818e200..84b58015d2 100644 --- a/Assets/SEE/Game/Avatars/AvatarAdapter.cs +++ b/Assets/SEE/Game/Avatars/AvatarAdapter.cs @@ -8,6 +8,9 @@ using SEE.Utils; using Unity.Netcode; using UnityEngine; +using SEE.Controls.Actions; +using TMPro; +using UnityEngine.UI; #if ENABLE_VR using UnityEngine.Assertions; @@ -297,13 +300,36 @@ public IEnumerator StartXRCoroutine() GameObject rig = PrefabInstantiator.InstantiatePrefab(vrPlayerRigPrefab); rig.transform.position = gameObject.transform.position; + Debug.Log("aaaaaaaaaaaaaaaaaaa" + gameObject.name); + Debug.Log("aaaaaaaaaaaaaaaaaaa" + gameObject.name); + Debug.Log("aaaaaaaaaaaaaaaaaaa" + gameObject.name); + Debug.Log("aaaaaaaaaaaaaaaaaaa" + gameObject.name); + GameObject indicator = rig.transform.Find("Camera Offset/Left Controller/ModePanel/Canvas").transform.gameObject; + Debug.Log(indicator.name + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + ActionStateType firstType = ActionStateTypes.NewEdge; + GlobalActionHistory.Execute(ActionStateTypes.Delete); + // Initial state will be the first action state type + GlobalActionHistory.Execute(firstType); + indicator.name = "StateIndicator"; + foreach (AbstractActionStateType a in ActionStateTypes.AllRootTypes.AllElements()) + { + Debug.Log(a.Name); + GameObject mode = PrefabInstantiator.InstantiatePrefab("Prefabs/UI/XRButton", indicator.transform.Find("Scroll View/Viewport/Content").transform, false).transform.gameObject; + mode.transform.SetParent(indicator.transform.Find("Scroll View/Viewport/Content")); + mode.transform.Find("Text").gameObject.MustGetComponent().SetText(a.Name); + if (a is ActionStateType actionStateType) + { + mode.transform.gameObject.MustGetComponent
public override bool Update() { - if (!XRSEEActions.Selected || !Raycasting.IsMouseOverGUI()) + if (!Raycasting.IsMouseOverGUI()) { - if (Input.GetMouseButtonDown(0) || Input.GetMouseButton(0) || XRSEEActions.Selected) + if (Input.GetMouseButtonDown(0) || Input.GetMouseButton(0)) { // We create the line on demand so that there is no left-over // when the drawing action has never actually started to draw anything. @@ -116,25 +116,9 @@ public override bool Update() } // FIXME: This would needed to be adjusted to VR and AR. // The position at which to continue the line. - Vector3 newPosition; - if (SceneSettings.InputType == PlayerInputType.VRPlayer) - { - if (XRSEEActions.hoveredGameObject.name == "Pen(Clone)") - { - newPosition = XRSEEActions.hoveredGameObject.transform.Find("Front").position; - } - else - { - XRSEEActions.RayInteractor.GetLineOriginAndDirection(out Vector3 origin, out Vector3 direction); - newPosition = origin; - } - } - else - { - newPosition = Input.mousePosition; - newPosition.z = 1.0f; - newPosition = Camera.main.ScreenToWorldPoint(newPosition); - } + Vector3 newPosition = Input.mousePosition; + newPosition.z = 1.0f; + newPosition = Camera.main.ScreenToWorldPoint(newPosition); // Add newPosition to the line renderer. Vector3[] newPositions = new Vector3[positions.Length + 1]; @@ -146,12 +130,12 @@ public override bool Update() // The line has been continued so this action has had a visible effect. CurrentState = IReversibleAction.Progress.Completed; } - if (Input.GetMouseButtonDown(0) || XRSEEActions.Selected) + if (Input.GetMouseButtonDown(0)) { AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.ScribbleSound); } // The action is considered complete if the mouse button is no longer pressed. - return Input.GetMouseButtonUp(0) || !XRSEEActions.Selected; + return Input.GetMouseButtonUp(0); } return false; } diff --git a/Assets/SEE/Controls/Actions/MoveAction.cs b/Assets/SEE/Controls/Actions/MoveAction.cs index a31de35543..249141e5db 100644 --- a/Assets/SEE/Controls/Actions/MoveAction.cs +++ b/Assets/SEE/Controls/Actions/MoveAction.cs @@ -527,6 +527,7 @@ public override bool Update() { if (XRSEEActions.Selected) { + XRSEEActions.CloseTreeView = true; if (!grabbedObject.IsGrabbed) { InteractableObject hoveredObject = InteractableObject.HoveredObjectWithWorldFlag; diff --git a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs index e0754dc2e3..0089bf3745 100644 --- a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs +++ b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs @@ -21,7 +21,10 @@ internal abstract class NodeManipulationAction : AbstractPlayerAction ///
public NodeManipulationAction() : base() { - RTGInitializer.Enable(); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + RTGInitializer.Enable(); + } } #region ReversibleAction Overrides @@ -44,7 +47,10 @@ public override HashSet GetChangedObjects() public override void Start() { base.Start(); - RTGInitializer.Enable(); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + RTGInitializer.Enable(); + } } /// @@ -53,8 +59,11 @@ public override void Start() public override void Stop() { base.Stop(); - RTGInitializer.Disable(); - UsedGizmo.Disable(); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + RTGInitializer.Disable(); + UsedGizmo.Disable(); + } } #endregion ReversibleAction Overrides @@ -106,81 +115,85 @@ public override void Redo() /// true if completed public override bool Update() { - if (UsedGizmo.IsHovered()) + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) { - // Transformation via the gizmo is in progress. - if (GameNodeSelected && HasChanges()) + if (UsedGizmo.IsHovered()) { - CurrentState = IReversibleAction.Progress.InProgress; + // Transformation via the gizmo is in progress. + if (GameNodeSelected && HasChanges()) + { + CurrentState = IReversibleAction.Progress.InProgress; + } + return false; } - return false; - } - if (SEEInput.Select()) - { - if (Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.Node) + if (SEEInput.Select()) { - // An object different from a graph node was selected. - if (GameNodeSelected && HasChanges()) + if (Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.Node) { - // An object to be manipulated was selected already and it was changed. - // The action is finished. - FinalizeAction(); - return true; + // An object different from a graph node was selected. + if (GameNodeSelected && HasChanges()) + { + // An object to be manipulated was selected already and it was changed. + // The action is finished. + FinalizeAction(); + return true; + } + else + { + // No game node has been selected yet or the previously selected game node + // has had no changes. The action is continued. + return false; + } } else { - // No game node has been selected yet or the previously selected game node - // has had no changes. The action is continued. - return false; - } - } - else - { - // A game node was selected by the user. - // Has the user already selected a game node in a previous iteration? - if (GameNodeSelected) - { - // The user has already selected a game node in a previous iteration. - // Are the two game nodes different? - if (GameNodeSelected != raycastHit.collider.gameObject) + // A game node was selected by the user. + // Has the user already selected a game node in a previous iteration? + if (GameNodeSelected) { - // The newly and previously selected nodes are different. - // Have we had any changes yet? If not, we assume the user wants - // to manipulate the newly game node instead. - if (!HasChanges()) + // The user has already selected a game node in a previous iteration. + // Are the two game nodes different? + if (GameNodeSelected != raycastHit.collider.gameObject) { - StartAction(raycastHit.collider.gameObject); - return false; + // The newly and previously selected nodes are different. + // Have we had any changes yet? If not, we assume the user wants + // to manipulate the newly game node instead. + if (!HasChanges()) + { + StartAction(raycastHit.collider.gameObject); + return false; + } + else + { + // This action is considered finished and a different action should + // be started to continue with the newly selected node. + FinalizeAction(); + GameNodeToBeContinuedInNextAction = raycastHit.collider.gameObject; + return true; + } } else { - // This action is considered finished and a different action should - // be started to continue with the newly selected node. - FinalizeAction(); - GameNodeToBeContinuedInNextAction = raycastHit.collider.gameObject; - return true; + // The user has selected the same node again. + // Nothing to be done. + return false; } } else { - // The user has selected the same node again. - // Nothing to be done. + // It's the first time, a game node was selected. The action starts. + StartAction(raycastHit.collider.gameObject); return false; } } - else - { - // It's the first time, a game node was selected. The action starts. - StartAction(raycastHit.collider.gameObject); - return false; - } } - } - else if (SEEInput.ToggleMenu() || SEEInput.Cancel()) - { - FinalizeAction(); - return true; + else if (SEEInput.ToggleMenu() || SEEInput.Cancel()) + { + FinalizeAction(); + return true; + } + return false; } return false; } diff --git a/Assets/SEE/Controls/Actions/RotateAction.cs b/Assets/SEE/Controls/Actions/RotateAction.cs index f95c959e48..76e13fd3de 100644 --- a/Assets/SEE/Controls/Actions/RotateAction.cs +++ b/Assets/SEE/Controls/Actions/RotateAction.cs @@ -1,4 +1,5 @@ using RTG; +using SEE.GO; using SEE.Net.Actions; using SEE.Utils.History; using UnityEngine; @@ -21,7 +22,10 @@ internal class RotateAction : NodeManipulationAction private RotateAction(GameObject gameNodeToBeContinuedWith) : base() { Initialize(); - StartAction(gameNodeToBeContinuedWith); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + StartAction(gameNodeToBeContinuedWith); + } } /// @@ -40,7 +44,10 @@ private RotateAction() : base() private void Initialize() { CurrentState = IReversibleAction.Progress.NoEffect; - UsedGizmo = new RotateGizmo(); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + UsedGizmo = new RotateGizmo(); + } } #endregion Constructors diff --git a/Assets/SEE/Controls/Actions/ScaleNodeAction.cs b/Assets/SEE/Controls/Actions/ScaleNodeAction.cs index 566a60bc42..70dd96f7ba 100644 --- a/Assets/SEE/Controls/Actions/ScaleNodeAction.cs +++ b/Assets/SEE/Controls/Actions/ScaleNodeAction.cs @@ -3,6 +3,7 @@ using RTG; using UnityEngine; using SEE.Utils.History; +using SEE.GO; namespace SEE.Controls.Actions { @@ -164,7 +165,10 @@ private class ScaleGizmo : Gizmo /// public ScaleGizmo() { - ObjectTransformationGizmo = RTGizmosEngine.Get.CreateObjectScaleGizmo(); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + ObjectTransformationGizmo = RTGizmosEngine.Get.CreateObjectScaleGizmo(); + } } } #endregion diff --git a/Assets/SEE/Controls/Actions/ShowCodeAction.cs b/Assets/SEE/Controls/Actions/ShowCodeAction.cs index 336430cb1c..41871fdfdf 100644 --- a/Assets/SEE/Controls/Actions/ShowCodeAction.cs +++ b/Assets/SEE/Controls/Actions/ShowCodeAction.cs @@ -308,6 +308,7 @@ public override bool Update() manager.AddWindow(codeWindow); } manager.ActiveWindow = codeWindow; + XRSEEActions.Selected = false; } return false; } diff --git a/Assets/SEE/Controls/Actions/ShowTree.cs b/Assets/SEE/Controls/Actions/ShowTree.cs index 523eff4ea8..63a5343bad 100644 --- a/Assets/SEE/Controls/Actions/ShowTree.cs +++ b/Assets/SEE/Controls/Actions/ShowTree.cs @@ -95,17 +95,15 @@ private void HideTreeView() private void Update() { - if (SEEInput.ToggleTreeView() || XRSEEActions.ContextMenu) + if (SEEInput.ToggleTreeView()) { if (treeWindows.Count == 0) { ShowTreeView(); - XRSEEActions.ContextMenu = false; } else { HideTreeView(); - XRSEEActions.ContextMenu = false; } } } diff --git a/Assets/SEE/Game/Avatars/AvatarAdapter.cs b/Assets/SEE/Game/Avatars/AvatarAdapter.cs index b37b3ea4b5..9d13949d56 100644 --- a/Assets/SEE/Game/Avatars/AvatarAdapter.cs +++ b/Assets/SEE/Game/Avatars/AvatarAdapter.cs @@ -332,7 +332,7 @@ public IEnumerator StartXRCoroutine() // Note: AddComponents() must be run before TurnOffAvatarAimingSystem() because the latter // will remove components, the former must query. - VRIKActions.AddComponents(gameObject, IsLocalPlayer); + //VRIKActions.AddComponents(gameObject, IsLocalPlayer); VRIKActions.TurnOffAvatarAimingSystem(gameObject); VRIKActions.ReplaceAnimator(gameObject, animatorForVRIKPrefab); diff --git a/Assets/SEE/GameObjects/Menu/PlayerMenu.cs b/Assets/SEE/GameObjects/Menu/PlayerMenu.cs index 24cf11407e..eaea337710 100644 --- a/Assets/SEE/GameObjects/Menu/PlayerMenu.cs +++ b/Assets/SEE/GameObjects/Menu/PlayerMenu.cs @@ -220,7 +220,12 @@ private void Update() SetPlayerMenu(currentAction.Name); indicator.ChangeActionState(currentAction); } - + if (RadialSelection.IndicatorChange) + { + ActionStateType currentAction = GlobalActionHistory.Current(); + indicator.ChangeActionState(currentAction); + RadialSelection.IndicatorChange = false; + } GlobalActionHistory.Update(); } diff --git a/Assets/SEE/UI/Window/DesktopWindowSpace.cs b/Assets/SEE/UI/Window/DesktopWindowSpace.cs index d8a2ac825c..3bbf33f46c 100644 --- a/Assets/SEE/UI/Window/DesktopWindowSpace.cs +++ b/Assets/SEE/UI/Window/DesktopWindowSpace.cs @@ -2,6 +2,7 @@ using System.Linq; using DynamicPanels; using SEE.Controls; +using SEE.Controls.Actions; using SEE.GO; using SEE.Utils; using UnityEngine; @@ -104,6 +105,17 @@ private void UpdateActiveTab() protected override void UpdateDesktop() { + // In VR the TreeView could be open, while the user moves a Node. In this case, the TreeView + // would update the entire time, the user is moving the Node, which is causing laggs. Thats + // why we close it. + if (GlobalActionHistory.Current() == ActionStateTypes.Move && XRSEEActions.CloseTreeView) + { + foreach (BaseWindow window in currentWindows) + { + CloseWindow(window); + } + XRSEEActions.CloseTreeView = false; + } if (panel && !windows.Any()) { // We need to destroy the panel now diff --git a/Assets/SEE/XR/KeyboardInputHandler.cs b/Assets/SEE/XR/KeyboardInputHandler.cs index 226dd452e5..06d8fe1dca 100644 --- a/Assets/SEE/XR/KeyboardInputHandler.cs +++ b/Assets/SEE/XR/KeyboardInputHandler.cs @@ -1,13 +1,20 @@ using SEE.Controls; using SEE.GO; -using System.Collections; -using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.EventSystems; +/// +/// This class changes the inputfield for the VR-Keyboard and +/// also activates the keyboard. +/// public class KeyboardInputHandler : MonoBehaviour, IPointerClickHandler { + /// + /// This method gets called, when the user clicks the input field. + /// If the user is in VR, the keyboard gets activated. + /// + /// Event data associated with the event, when the user clicks on the inputfield. public void OnPointerClick(PointerEventData eventdata) { if (SceneSettings.InputType == PlayerInputType.VRPlayer) diff --git a/Assets/SEE/XR/KeyboardKey.cs b/Assets/SEE/XR/KeyboardKey.cs index 838a2d4c25..c4ad209bde 100644 --- a/Assets/SEE/XR/KeyboardKey.cs +++ b/Assets/SEE/XR/KeyboardKey.cs @@ -1,19 +1,32 @@ -using System.Collections; -using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; - +/// +/// This class handles a single keyboard key. +/// public class KeyboardKey : MonoBehaviour { + /// + /// The character of the key. + /// public string character; + /// + /// The shifted character. + /// public string shiftCharacter; - + /// + /// Is true, when the key is shifted. + /// private bool isShifted = false; - + /// + /// The actual keyboard key. + /// private Button key; + /// + /// The label of the keyboard key. + /// public TextMeshProUGUI keyLabel; - + // Start is called before the first frame update private void Start() { KeyboardManager.instance.shiftButton.onClick.AddListener(HandleShift); @@ -29,7 +42,11 @@ private void Start() shiftCharacter = GetShiftCharacter(); } } - + /// + /// This method returns the shifted equivalent to + /// each number on the keyboard. + /// + /// The shifted equivalent to each number. private string GetShiftCharacter() { switch (keyLabel.text) @@ -59,7 +76,10 @@ private string GetShiftCharacter() } return string.Empty; } - + /// + /// This method handles the shifting of the + /// alphabetical characters. + /// private void HandleShift() { isShifted = !isShifted; @@ -73,7 +93,9 @@ private void HandleShift() keyLabel.text = character; } } - + /// + /// This method transfer the character to the inputfield. + /// private void TypeKey() { if (isShifted) diff --git a/Assets/SEE/XR/KeyboardManager.cs b/Assets/SEE/XR/KeyboardManager.cs index 82f64166a8..45339b76c0 100644 --- a/Assets/SEE/XR/KeyboardManager.cs +++ b/Assets/SEE/XR/KeyboardManager.cs @@ -1,20 +1,46 @@ -using System.Collections; -using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; +/// +/// This class manages the input for all buttons on the keyboard. +/// public class KeyboardManager : MonoBehaviour { + /// + /// The instance of the keyboardmanager. + /// public static KeyboardManager instance; + /// + /// The shiftbutton, which performs the shift-action. + /// public Button shiftButton; + /// + /// The deletebutton, which performs the delete-action. + /// public Button deleteButton; + /// + /// The spacebuttom, which performs the space-action. + /// public Button spaceButton; + /// + /// The enterbutton, which performs the enter-action. + /// public Button enterButton; + /// + /// The inputfield, which is accessed by the keyboard. + /// public TMP_InputField inputField; + /// + /// This image is used, to tell the user, that the shiftbutton is active. + /// private Image shiftButtonImage; + /// + /// Is true, when the shiftbutton is active. + /// private bool isShifted = false; + // Awake is always called before any Start functions. private void Awake() { if (instance == null) @@ -27,18 +53,24 @@ private void Awake() enterButton.onClick.AddListener(Enter); shiftButtonImage = shiftButton.gameObject.GetComponent(); } - + /// + /// This method performs the space-action on the keyboard. + /// private void Space() { inputField.text += " "; } - + /// + /// This method performs the enter-action on the keyboard. + /// private void Enter() { inputField.onSubmit.Invoke(inputField.text); gameObject.transform.Find("Keyboard").gameObject.SetActive(false); } - + /// + /// This method performs the delete-action on the keyboard. + /// private void Delete() { int length = inputField.text.Length; @@ -48,6 +80,9 @@ private void Delete() inputField.text = inputField.text.Substring(0, length); } } + /// + /// This method performs the shift-action on the keyboard. + /// private void Shifted() { isShifted = !isShifted; diff --git a/Assets/SEE/XR/RadialSelection.cs b/Assets/SEE/XR/RadialSelection.cs index 37cade9fbb..bf1bc8a5c2 100644 --- a/Assets/SEE/XR/RadialSelection.cs +++ b/Assets/SEE/XR/RadialSelection.cs @@ -1,6 +1,5 @@ using SEE.Controls.Actions; using SEE.GO; -using System.Collections; using System.Collections.Generic; using System.Linq; using TMPro; @@ -11,26 +10,80 @@ using UnityEngine.XR.Interaction.Toolkit; using SEE.Utils; +/// +/// This class is used, to open a +/// radial menu in VR. +/// public class RadialSelection : MonoBehaviour { + /// + /// The number of radial-parts. + /// int numberOfRadialPart = 0; + /// + /// The prefab for the single radial-parts. + /// public GameObject radialPartPrefab; + /// + /// The canvas on which the radial should be shown. + /// public Transform radialPartCanvas; + /// + /// The angle between the radial-parts. + /// public float angleBetweenPart = 10; + /// + /// The radial-parts which got spawned. + /// private List spawnedParts = new List(); + /// + /// The transform of the controller. + /// public Transform handTransform; + /// + /// The currently selected radial-part. + /// private int currentSelectedRadialPart = -1; + /// + /// The selected radial-part. + /// public UnityEvent OnPartSelected; + /// + /// The button, which triggers the radial-menu. + /// public InputActionReference radialMenu; + /// + /// All actions, which are currently available. + /// List actions = new(); + /// + /// Alle submenus and their entries. + /// List<(string, List)> subMenus = new List<(string, List)>(); + /// + /// All entry of a submenu. + /// List menuEntrys = new(); - Dictionary> menus; + /// + /// This is used for the rotate-action, because in this case we + /// have a dial, which is getting spawned, and should be despawned, when the action changes. + /// GameObject actionObject; + /// + /// The position of the submenu. + /// It should be the same, as the position from the mainmenu. + /// Vector3? subMenuPosition; + /// + /// The rotation of the submenu. + /// It should be the same, as the rotation from the mainmenu. + /// Quaternion? subMenuRotation; + /// + /// The hide-action is a special action, because it has sub-actions, which we need to access. + /// public static HideModeSelector HideMode { get; set; } - // Start is called before the first frame update + // Awake is always called before any Start functions. private void Awake() { radialMenu.action.performed += RadialMenu; @@ -89,54 +142,55 @@ bool Visit(AbstractActionStateType child, AbstractActionStateType parent) return true; } } - - public static bool RadialMenuTrigger { get; set; } + /// + /// Is true, when the radial menu is open. + /// + bool radialMenuTrigger; + /// + /// This method gets called, when the button for the radial-menu is pressed. + /// + /// Information provided to action callbacks about what triggered an action. private void RadialMenu(InputAction.CallbackContext context) { - Debug.Log("uuuuuuuuuuuuuuuuuu"); - RadialMenuTrigger = true; + radialMenuTrigger = true; } // Update is called once per frame void Update() { - if (RadialMenuTrigger && !spawned) + if (radialMenuTrigger && !spawned) { SpawnRadialPart(); - RadialMenuTrigger = false; + radialMenuTrigger = false; } if (spawned) { GetSelectedRadialPart(); } - if (RadialMenuTrigger && spawned) + if (radialMenuTrigger && spawned) { spawned = false; - RadialMenuTrigger = false; + radialMenuTrigger = false; HideAndTriggerSelected(); } } - - bool select; - bool unselect; - - public void OnSelectEnter(SelectEnterEventArgs args) - { - Debug.Log("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"); - select = true; - } - - public void OnSelectExited(SelectExitEventArgs args) - { - unselect = true; - } - + /// + /// Is true, when the current action changed. + /// It is being used, to trigger the update for the GlobalActionHistory. + /// + public static bool IndicatorChange { set; get; } + /// + /// This method activates the selected action and deactivates the radial menu. + /// public void HideAndTriggerSelected() { radialPartCanvas.gameObject.SetActive(false); OnPartSelected.Invoke(currentSelectedRadialPart); + IndicatorChange = true; } - + /// + /// This method calculates, at which radial-part the user is aiming at. + /// public void GetSelectedRadialPart() { Vector3 centerToHand = handTransform.position - radialPartCanvas.position; @@ -164,17 +218,21 @@ public void GetSelectedRadialPart() { spawnedParts[i].GetComponent().color = Color.white; spawnedParts[i].transform.localScale = Vector3.one; - spawnedParts[i].gameObject.transform.Find("TextField").gameObject.MustGetComponent().color = Color.white; + spawnedParts[i].gameObject.transform.Find("TextField").gameObject.MustGetComponent().color = Color.black; spawnedParts[i].gameObject.transform.Find("TextField").gameObject.MustGetComponent().fontStyle = (FontStyles)FontStyle.Normal; } } } - + /// + /// Is true if the radial menu is open or not. + /// bool spawned; - + /// + /// This method activates the selected action, or opens a submenu/mainmenu. + /// + /// public void SelectAction(int i) { - Debug.Log("cwazy"); if (menuEntrys.Count != 0) { if (actions[i] == "Back") @@ -182,14 +240,13 @@ public void SelectAction(int i) actions.Clear(); actions.AddRange(menuEntrys); numberOfRadialPart = actions.Count(); - RadialMenuTrigger = true; + radialMenuTrigger = true; menuEntrys.Clear(); subMenuPosition = radialPartCanvas.position; subMenuRotation = radialPartCanvas.rotation; } if (actions[i] == "HideAll") { - Debug.Log("letsgo"); HideMode = HideModeSelector.HideAll; GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); return; @@ -241,7 +298,6 @@ public void SelectAction(int i) } else { - Debug.Log("komisch" + actions[0] + numberOfRadialPart); GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == actions[i])); } } @@ -265,7 +321,7 @@ public void SelectAction(int i) actions.Add("HideAllTransitiveClosure"); actions.Add("HighlightEdges"); actions.Add("Back"); - RadialMenuTrigger = true; + radialMenuTrigger = true; subMenuPosition = radialPartCanvas.position; subMenuRotation = radialPartCanvas.rotation; } @@ -275,11 +331,7 @@ public void SelectAction(int i) { Destroy(actionObject); } - Debug.Log("jjjjjjjjjjjj"); actionObject = PrefabInstantiator.InstantiatePrefab("Prefabs/Dial").transform.gameObject; - Debug.Log("spawned" + actionObject.name); - Debug.Log("location" + actionObject.transform.position); - Debug.Log("handlocation" + handTransform.position); actionObject.transform.position = handTransform.position; actionObject.SetActive(true); } @@ -289,11 +341,7 @@ public void SelectAction(int i) { Destroy(actionObject); } - Debug.Log("jjjjjjjjjjjj"); actionObject = PrefabInstantiator.InstantiatePrefab("Prefabs/Pen").transform.gameObject; - Debug.Log("spawned" + actionObject.name); - Debug.Log("location" + actionObject.transform.position); - Debug.Log("handlocation" + handTransform.position); actionObject.transform.position = handTransform.position; actionObject.SetActive(true); } @@ -313,16 +361,17 @@ public void SelectAction(int i) actions.Clear(); actions.AddRange(subMenus[i].Item2); actions.Add("Back"); - RadialMenuTrigger = true; + radialMenuTrigger = true; subMenuPosition = radialPartCanvas.position; subMenuRotation = radialPartCanvas.rotation; } } } - + /// + /// This method spawns all radial-parts for the current menu. + /// public void SpawnRadialPart() { - Debug.Log("SoViele:" + numberOfRadialPart); radialPartCanvas.gameObject.SetActive(true); if (subMenuPosition != null && subMenuRotation != null) { @@ -362,7 +411,6 @@ public void SpawnRadialPart() spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().alignment = TextAlignmentOptions.Right; } spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().text = actions[i]; - Debug.Log("Action:" + actions[i]); spawnedParts.Add(spawnRadialPart); } spawned = true; diff --git a/Assets/SEE/XR/Rotator.cs b/Assets/SEE/XR/Rotator.cs index e958e6b2fa..537f3ed91d 100644 --- a/Assets/SEE/XR/Rotator.cs +++ b/Assets/SEE/XR/Rotator.cs @@ -1,29 +1,47 @@ -using System.Collections; -using System.Collections.Generic; using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; using UnityEngine.XR.Interaction.Toolkit.Interactables; using UnityEngine.XR.Interaction.Toolkit.Interactors; - +/// +/// This class is used to rotate nodes in VR. +/// This script is based on this tutorial: "https://www.youtube.com/watch?v=vIrgCMNsE3s". +/// public class Rotator : MonoBehaviour { + /// + /// The actual dial, which gets rotated. + /// [SerializeField] Transform linkedDial; + /// + /// The amount of degrees the dial gets roated each time. + /// [SerializeField] private int snapRotationAmount = 25; + /// + /// The amount of degrees at which the dial is starting to rotate. + /// [SerializeField] private float angleTolerance; - [SerializeField] - private GameObject RighthandModel; - [SerializeField] - private GameObject LefthandModel; - [SerializeField] - bool shouldUseDummyHands; - + /// + /// The controller, which is used to rotated the dial/node. + /// private XRBaseInteractor interactor; + /// + /// The base angle. + /// private float startAngle; + /// + /// This bool is used, to determin if it is the first rotation, or not. + /// private bool requiresStartAngle = true; + /// + /// Is true, when the dial/node should be roated according to the hand-rotation. + /// private bool shouldGetHandRotation = false; + /// + /// The grab-interactor of the dial. + /// private XRGrabInteractable grabInteractor => GetComponent(); private void OnEnable() @@ -37,13 +55,19 @@ private void OnDisable() grabInteractor.selectEntered.RemoveListener(GrabbedBy); grabInteractor.selectExited.RemoveListener(GrabEnd); } - + /// + /// This method gets called, when the user stops to use the dial. + /// + /// Event data associated with the event when an Interactor stops selecting an Interactable. private void GrabEnd(SelectExitEventArgs args) { shouldGetHandRotation = false; requiresStartAngle = true; } - + /// + /// This method gets called, when the user begins to use the dial. + /// + /// Event data associated with the event when an Interactor first initiates selecting an Interactable. private void GrabbedBy(SelectEnterEventArgs args) { interactor = (XRBaseInteractor)GetComponent().interactorsSelecting[0]; @@ -52,12 +76,6 @@ private void GrabbedBy(SelectEnterEventArgs args) } - // Start is called before the first frame update - void Start() - { - - } - // Update is called once per frame void Update() { @@ -67,9 +85,15 @@ void Update() GetRotationDistance(rotationAngle); } } - + /// + /// Gets the current roation-angle from the controller. + /// + /// The current rotation-angle from the controller. public float GetInteractorRotation() => interactor.GetComponent().eulerAngles.z; - + /// + /// Determins in which direction and how much the dial/node should be rotated. + /// + /// The current angle from the controller. private void GetRotationDistance(float currentAngle) { if (!requiresStartAngle) @@ -128,16 +152,26 @@ private void GetRotationDistance(float currentAngle) startAngle = currentAngle; } } - + /// + /// This method is used, to calculate, if the rotation angle is significant enough, to rotate the dial/node. + /// + /// The current angle of the controller. + /// The base angle. + /// private float CheckAngle(float currentAngle, float startAngle) => (360f - currentAngle) + startAngle; + /// + /// This method is used, to rotate the dial/node clockwise. + /// private void RotateDialClockwise() { linkedDial.localEulerAngles = new Vector3(linkedDial.localEulerAngles.x, linkedDial.localEulerAngles.y + snapRotationAmount, linkedDial.localEulerAngles.z); Transform rotateObject = XRSEEActions.RotateObject.transform; rotateObject.localEulerAngles = new Vector3(rotateObject.localEulerAngles.x, linkedDial.localEulerAngles.y, rotateObject.localEulerAngles.z); } - + /// + /// This method is used, to rotate the dial/node anticlockwise. + /// private void RotateDialAntiClockwise() { linkedDial.localEulerAngles = new Vector3(linkedDial.localEulerAngles.x, linkedDial.localEulerAngles.y - snapRotationAmount, linkedDial.localEulerAngles.z); diff --git a/Assets/SEE/XR/XRSEEActions.cs b/Assets/SEE/XR/XRSEEActions.cs index 6bfeb40566..ad3419b2f1 100644 --- a/Assets/SEE/XR/XRSEEActions.cs +++ b/Assets/SEE/XR/XRSEEActions.cs @@ -1,99 +1,95 @@ using SEE.Controls; using SEE.Controls.Actions; -using SEE.DataModel.DG; using SEE.GO; using SEE.Utils; -using System; -using System.Collections; -using System.Collections.Generic; using UnityEngine; -using UnityEngine.Assertions; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.XR.Interaction.Toolkit; -using UnityEngine.XR.Interaction.Toolkit.Inputs; -using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers; -using UnityEngine.XR.Interaction.Toolkit.Interactables; using UnityEngine.XR.Interaction.Toolkit.Interactors; -using UnityEngine.XR.Interaction.Toolkit.Locomotion; public class XRSEEActions : MonoBehaviour { - [SerializeField] - [Tooltip("Reads input data from the right hand controller. Input Action must be a Value action type (Vector 2).")] - XRInputValueReader m_RightHandTurnInput = new XRInputValueReader("Right Hand Snap Turn"); - /// - /// Reads input data from the right hand controller. Input Action must be a Value action type (Vector 2). + /// The RayInteractor, which will be accessed. /// - public XRInputValueReader rightHandTurnInput - { - get => m_RightHandTurnInput; - set => XRInputReaderUtility.SetInputProperty(ref m_RightHandTurnInput, value, this); - } - - public InputHelpers.Button rightTurnButton = InputHelpers.Button.PrimaryAxis2DRight; - public InputHelpers.Button leftTurnButton = InputHelpers.Button.SecondaryAxis2DLeft; - [SerializeField] XRRayInteractor m_RayInteractor; + /// + /// The RayInteractor, which we use, to get the current position of the Laserpointer. + /// public static XRRayInteractor RayInteractor { get; set; } - + /// + /// Is true, when the user is hovering over an interactable. + /// private bool hovering = false; - public static GameObject hoveredGameObject { get; set; } - - public static Transform oldParent { get; set; } - + /// + /// The GameObject the user is currently hovering over. + /// + public static GameObject hoveredGameObject { get; private set; } + /// + /// The old parent of a node. We need this for the MoveAction. + /// + public static Transform oldParent { get; private set; } + /// + /// The button, which is used for the primary actions. + /// public InputActionReference inputAction; - - public InputActionReference openContextMenu; - + /// + /// The button, which is used for the undo-action. + /// public InputActionReference undo; + /// + /// The button, which is used for the redo-action. + /// public InputActionReference redo; - public InputActionReference radialMenu; - public InputActionReference scale; + /// + /// The button, which is used, to open the tooltip. + /// public InputActionReference tooltip; + /// + /// Shows the source name of the hovered or selected object as a text label above the + /// object. In between that label and the game object, a connecting bar + /// will be shown. + /// private ShowLabel showLabel; - private Vector3 originalScale; - private Vector3 originalPoint; - private Vector3 updatedScale; - public bool allowTurn { get; set; } - // Start is called before the first frame update + // Awake is always called before any Start functions. private void Awake() { - //radialMenu.action.performed += RadialMenu; tooltip.action.performed += Tooltip; undo.action.performed += Undo; redo.action.performed += Redo; - //rightHandTurnInput.inputAction.performed += Rotate; inputAction.action.Enable(); inputAction.action.performed += Action; - //scale.action.performed += Scale; - //openContextMenu.action.Enable(); - //openContextMenu.action.performed += OpenContexMenu; RayInteractor = m_RayInteractor; + Selected = false; } - + /// + /// This method gets called, when the user begins to hover over an interactable. + /// + /// Event data associated with the event when an Interactor first initiates hovering over an Interactable. public void OnHoverEnter(HoverEnterEventArgs args) { - Debug.Log($"{args.interactorObject} hovered over {args.interactableObject}", this); hovering = true; hoveredGameObject = args.interactableObject.transform.gameObject; - oldParent = args.interactableObject.transform.parent; - Debug.Log(oldParent.name + "cool"); + if (GlobalActionHistory.Current() == ActionStateTypes.Move) + { + oldParent = args.interactableObject.transform.parent; + } hoveredGameObject.transform.TryGetComponent(out showLabel); showLabel?.On(); if (hoveredGameObject.transform.TryGetComponent(out InteractableObject io)) { - allowTurn = false; io.SetHoverFlag(HoverFlag.World, true, true); } } - + /// + /// This method gets called, when the user stops hovering over an interactable. + /// + /// Event data associated with the event when an Interactor stops hovering over an Interactable. public void OnHoverExited(HoverExitEventArgs args) { - Debug.Log($"{args.interactorObject} stopped hovered over {args.interactableObject}", this); hovering = false; hoveredGameObject = args.interactableObject.transform.gameObject; oldParent = null; @@ -101,15 +97,24 @@ public void OnHoverExited(HoverExitEventArgs args) showLabel?.Off(); if (hoveredGameObject.transform.TryGetComponent(out InteractableObject io)) { - allowTurn = true; io.SetHoverFlag(HoverFlag.World, false, true); } } - - public void OnSelectEnter(SelectEnterEventArgs args) + /// + /// Is true, when the button for the primary actions is pressed. + /// + public static bool Selected { get; set; } + /// + /// The GameObject, which should be rotated. + /// + public static GameObject RotateObject { get; private set; } + /// + /// This method gets called, when the button for the primary actions is pressed. + /// + /// Information provided to action callbacks about what triggered an action. + private void Action(InputAction.CallbackContext context) { - if(GlobalActionHistory.Current() == ActionStateTypes.Move || GlobalActionHistory.Current() == ActionStateTypes.Draw || GlobalActionHistory.Current() == ActionStateTypes.ShowCode - || GlobalActionHistory.Current() == ActionStateTypes.Rotate) + if (hovering) { if (GlobalActionHistory.Current() == ActionStateTypes.Move && Selected == true) { @@ -119,47 +124,45 @@ public void OnSelectEnter(SelectEnterEventArgs args) { Selected = true; } - } - } - - public void OnSelectExited(SelectExitEventArgs args) - { - if (GlobalActionHistory.Current() == ActionStateTypes.Draw || GlobalActionHistory.Current() == ActionStateTypes.ShowCode - || GlobalActionHistory.Current() == ActionStateTypes.Rotate) - { - Selected = false; - } - } - public static bool Selected { get; set; } - public static bool Delete { get; set; } - public static GameObject RotateObject { get; set; } - private void Action(InputAction.CallbackContext context) - { - if (hovering) - { - Delete = true; if (GlobalActionHistory.Current() == ActionStateTypes.NewEdge || GlobalActionHistory.Current() == ActionStateTypes.Delete || GlobalActionHistory.Current() == ActionStateTypes.NewNode || GlobalActionHistory.Current() == ActionStateTypes.AcceptDivergence) { - Delete = true; + Selected = true; } if (GlobalActionHistory.Current() == ActionStateTypes.Rotate) { RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit); RotateObject = hit.collider.transform.gameObject; - Delete = true; + Selected = true; } } } - + /// + /// Will be true, when the TreeView is open, while the user tries to move a node. + /// + public static bool CloseTreeView { get; set; } + /// + /// Is true, when the Tooltip is getting activated. + /// public static bool TooltipToggle { get; set; } - + /// + /// Is true, while the Tooltip is open. + /// This bool is used, to close the Tooltip, if the user does not want + /// to use an action. + /// public static bool OnSelectToggle { get; set; } - + /// + /// Is true, when the user opens the tooltip in the treeview. + /// public static bool OnTreeViewToggle { get; set; } - + /// + /// The treview-entry for which the tooltip should be shown. + /// public static GameObject TreeViewEntry { get; set; } - + /// + /// This method gets called, when the button for the tooltip is pressed. + /// + /// Information provided to action callbacks about what triggered an action. private void Tooltip(InputAction.CallbackContext context) { if (OnTreeViewToggle) @@ -177,79 +180,29 @@ private void Tooltip(InputAction.CallbackContext context) TooltipToggle = true; } } - + /// + /// Is true, when the button for the undo-action is pressed. + /// public static bool UndoToggle { get; set; } + /// + /// This method gets called, when the button for the undo-action is pressed. + /// + /// Information provided to action callbacks about what triggered an action. private void Undo(InputAction.CallbackContext context) { UndoToggle = true; } + /// + /// Is true, when the button for the redo-action is pressed. + /// public static bool RedoToggle { get; set; } - private void Redo(InputAction.CallbackContext context) - { - RedoToggle = true; - } - public static bool RadialMenuTrigger { get; set; } - private void RadialMenu(InputAction.CallbackContext context) - { - RadialMenuTrigger = true; - } - - public static bool ContextMenu { get; set; } - - private void OpenContexMenu(InputAction.CallbackContext context) - { - if (hovering) - { - if (hoveredGameObject.gameObject.TryGetNode(out Node node)) - { - ContextMenu = true; - } - } - } - - private void Rotate(InputAction.CallbackContext context) - { - if (hovering && GlobalActionHistory.Current() == ActionStateTypes.Rotate) - { - if (hoveredGameObject.gameObject.TryGetNode(out Node node)) - { - var amount = GetTurnAmount(m_RightHandTurnInput.ReadValue()); - if (Math.Abs(amount) > 0f) - { - hoveredGameObject.transform.Rotate(0, amount * Time.deltaTime, 0); - } - ContextMenu = true; - } - } - } - /// - /// Determines the turn amount in degrees for the given vector. + /// This method gets called, when the button for the redo-action is pressed. /// - /// Input vector, such as from a thumbstick. - /// Returns the turn amount in degrees for the given vector. - protected virtual float GetTurnAmount(Vector2 input) + /// Information provided to action callbacks about what triggered an action. + private void Redo(InputAction.CallbackContext context) { - if (input == Vector2.zero) - return 0f; - - var cardinal = CardinalUtility.GetNearestCardinal(input); - switch (cardinal) - { - case Cardinal.North: - break; - case Cardinal.South: - break; - case Cardinal.East: - return 45f; - case Cardinal.West: - return -45f; - default: - Assert.IsTrue(false, $"Unhandled {nameof(Cardinal)}={cardinal}"); - break; - } - - return 0f; + RedoToggle = true; } // Update is called once per frame From edbcedf13b02be028a66d91e1adf9b948e7278af Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Sat, 5 Oct 2024 10:55:07 +0200 Subject: [PATCH 17/35] Fixed VR-Interactions after merge. --- .../Actions/AcceptDivergenceAction.cs | 51 +++++++++++-------- Assets/SEE/Controls/Actions/AddNodeAction.cs | 9 +++- .../SEE/Controls/Actions/ContextMenuAction.cs | 39 +++++++++++--- Assets/SEE/Controls/Actions/DeleteAction.cs | 9 +++- Assets/SEE/Controls/Actions/MoveAction.cs | 31 +++++++++-- Assets/SEE/Controls/Actions/SelectAction.cs | 8 +-- Assets/SEE/Controls/Actions/ShowCodeAction.cs | 2 +- Assets/SEE/Controls/Actions/ShowLabel.cs | 14 ++++- .../PropertyDialog/DesktopPropertyDialog.cs | 15 ++++++ .../SEE/UI/PropertyDialog/PropertyDialog.cs | 2 + Assets/SEE/UI/PropertyDialog/PropertyGroup.cs | 5 ++ .../UI/PropertyDialog/SelectionProperty.cs | 5 ++ .../SEE/UI/PropertyDialog/StringProperty.cs | 5 ++ Assets/SEE/UI/Window/DesktopWindowSpace.cs | 1 + .../UI/Window/TreeWindow/DesktopTreeWindow.cs | 34 ++++++++----- Assets/SEE/Utils/Raycasting.cs | 9 +++- Assets/SEE/XR/RadialSelection.cs | 10 ---- 17 files changed, 182 insertions(+), 67 deletions(-) diff --git a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs index 2a8b8c84ac..69c9185f02 100644 --- a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs +++ b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs @@ -141,12 +141,12 @@ public override bool Update() // we have both source and target of the edge and use a memento struct // to remember which edge we have added memento = new Memento(source, target, Edge.SourceDependency); - + mementoList.Add(memento); // create the edge - createdEdge = CreateConvergentEdge(memento); + createdEdgeList.Add(CreateConvergentEdge(memento)); // check whether edge creation was successful - bool divergenceSolved = createdEdge != null; + bool divergenceSolved = createdEdgeList[0] != null; // add audio cue to the appearance of the new architecture edge AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.NewEdgeSound); @@ -166,6 +166,12 @@ public override bool Update() ShowNotification.Warn("Not an edge", $"Selected Element {divergentEdge.name} is not an edge.\n"); } } + if (ExecuteViaContextMenu) + { + bool divergenceSolved = createdEdgeList.All(e => e != null); + CurrentState = divergenceSolved ? IReversibleAction.Progress.Completed : IReversibleAction.Progress.NoEffect; + return true; + } return false; } else @@ -195,15 +201,15 @@ public override bool Update() // or implicitly mapped to Node target = graph.MapsTo(selectedEdge.Target); - // we have both source and target of the edge and use a memento struct - // to remember which edge we have added - memento = new Memento(source, target, Edge.SourceDependency); - mementoList.Add(memento); - // create the edge - createdEdgeList.Add(CreateConvergentEdge(memento)); + // we have both source and target of the edge and use a memento struct + // to remember which edge we have added + memento = new Memento(source, target, Edge.SourceDependency); + mementoList.Add(memento); + // create the edge + createdEdgeList.Add(CreateConvergentEdge(memento)); - // check whether edge creation was successful - bool divergenceSolved = createdEdgeList[0] != null; + // check whether edge creation was successful + bool divergenceSolved = createdEdgeList[0] != null; // add audio cue to the appearance of the new architecture edge AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.NewEdgeSound); @@ -212,22 +218,23 @@ public override bool Update() // (required in order to register as an undo-able action) CurrentState = divergenceSolved ? IReversibleAction.Progress.Completed : IReversibleAction.Progress.NoEffect; - // the selected object is synced and this action is done - return true; + // the selected object is synced and this action is done + return true; + } + } + else + { + ShowNotification.Warn("Not an edge", $"Selected Element {divergentEdge.name} is not an edge.\n"); } } - else + if (ExecuteViaContextMenu) { - ShowNotification.Warn("Not an edge", $"Selected Element {divergentEdge.name} is not an edge.\n"); + bool divergenceSolved = createdEdgeList.All(e => e != null); + CurrentState = divergenceSolved ? IReversibleAction.Progress.Completed : IReversibleAction.Progress.NoEffect; + return true; } + return false; } - if (ExecuteViaContextMenu) - { - bool divergenceSolved = createdEdgeList.All(e => e != null); - CurrentState = divergenceSolved ? IReversibleAction.Progress.Completed : IReversibleAction.Progress.NoEffect; - return true; - } - return false; } /// diff --git a/Assets/SEE/Controls/Actions/AddNodeAction.cs b/Assets/SEE/Controls/Actions/AddNodeAction.cs index 686696132f..2e027399b1 100644 --- a/Assets/SEE/Controls/Actions/AddNodeAction.cs +++ b/Assets/SEE/Controls/Actions/AddNodeAction.cs @@ -67,13 +67,20 @@ public override bool Update() switch (progress) { case ProgressState.NoNodeSelected: - if (Input.GetMouseButtonDown(0) + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer && Input.GetMouseButtonDown(0) && Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) == HitGraphElement.Node) { // the hit object is the parent in which to create the new node GameObject parent = raycastHit.collider.gameObject; AddNode(raycastHit.collider.gameObject, raycastHit.point); } + else if (SceneSettings.InputType == PlayerInputType.VRPlayer && XRSEEActions.hoveredGameObject != null && XRSEEActions.Selected && XRSEEActions.hoveredGameObject.HasNodeRef() && + XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit res)) + { + // the hit object is the parent in which to create the new node + GameObject parent = res.collider.gameObject; + AddNode(res.collider.gameObject, res.point); + } else if (ExecuteViaContextMenu) { AddNode(parent, position); diff --git a/Assets/SEE/Controls/Actions/ContextMenuAction.cs b/Assets/SEE/Controls/Actions/ContextMenuAction.cs index 9f4da7d197..341f8e46c8 100644 --- a/Assets/SEE/Controls/Actions/ContextMenuAction.cs +++ b/Assets/SEE/Controls/Actions/ContextMenuAction.cs @@ -55,18 +55,27 @@ private void Start() popupMenu = gameObject.AddComponent(); } + /// + /// Is true, when the context-menu is open. + /// This is used in VR, to open and close the menu. + /// bool onSelect; private void Update() { - if (SEEInput.OpenContextMenuStart()) + if (SEEInput.OpenContextMenuStart() || (XRSEEActions.TooltipToggle && !onSelect)) { if (InteractableObject.SelectedObjects.Count <= 1) { Raycasting.RaycastInteractableObject(out _, out InteractableObject o); startObject = o; - startMousePosition = Input.mousePosition; + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + startMousePosition = Input.mousePosition; + } multiselection = false; + XRSEEActions.TooltipToggle = false; + onSelect = true; } else { @@ -74,7 +83,7 @@ private void Update() multiselection = true; } } - if (SEEInput.OpenContextMenuEnd()) + if (SEEInput.OpenContextMenuEnd() || (XRSEEActions.TooltipToggle && onSelect)) { if (!multiselection) { @@ -83,10 +92,20 @@ private void Update() { return; } - if (o == startObject && (Input.mousePosition - startMousePosition).magnitude < 1) + if ((o == startObject && (Input.mousePosition - startMousePosition).magnitude < 1) || SceneSettings.InputType == PlayerInputType.VRPlayer) { - position = Input.mousePosition; + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + position = Input.mousePosition; + } + else + { + XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit res); + position = res.point; + } IEnumerable entries = GetApplicableOptions(popupMenu, position, raycastHit.point, o.GraphElemRef.Elem, o.gameObject); + XRSEEActions.TooltipToggle = false; + onSelect = false; popupMenu.ShowWith(entries, position); } } @@ -99,7 +118,15 @@ private void Update() } if (InteractableObject.SelectedObjects.Contains(o)) { - position = Input.mousePosition; + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + position = Input.mousePosition; + } + else + { + XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit res); + position = res.point; + } IEnumerable entries = GetApplicableOptionsForMultiselection(popupMenu, InteractableObject.SelectedObjects); popupMenu.ShowWith(entries, position); } diff --git a/Assets/SEE/Controls/Actions/DeleteAction.cs b/Assets/SEE/Controls/Actions/DeleteAction.cs index d4a49b7fd3..30c953d6e8 100644 --- a/Assets/SEE/Controls/Actions/DeleteAction.cs +++ b/Assets/SEE/Controls/Actions/DeleteAction.cs @@ -113,13 +113,20 @@ public override void Stop() public override bool Update() { // FIXME: Needs adaptation for VR where no mouse is available. - if (Input.GetMouseButtonDown(0) + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer && Input.GetMouseButtonDown(0) && Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.None) { // the hit object is the one to be deleted hitGraphElements.Add(raycastHit.collider.gameObject); return Delete(); // the selected objects are deleted and this action is done now } + else if (SceneSettings.InputType == PlayerInputType.VRPlayer && XRSEEActions.Selected) + { + // the hit object is the one to be deleted + hitGraphElements.Add(XRSEEActions.hoveredGameObject); + XRSEEActions.Selected = false; + return Delete(); // the selected objects are deleted and this action is done now + } else if (ExecuteViaContextMenu) { return Delete(); diff --git a/Assets/SEE/Controls/Actions/MoveAction.cs b/Assets/SEE/Controls/Actions/MoveAction.cs index 882b0de9be..7825002e66 100644 --- a/Assets/SEE/Controls/Actions/MoveAction.cs +++ b/Assets/SEE/Controls/Actions/MoveAction.cs @@ -74,6 +74,12 @@ public override ActionStateType GetActionStateType() return ActionStateTypes.Move; } + /// + /// This is true, if the user is dragging an object. + /// We use this in VR, to activate/deactivate this action. + /// + bool activeAction; + /// /// Reacts to the user interactions. An object can be grabbed and moved /// around. If it is put onto another node, it will be re-parented onto this @@ -96,7 +102,7 @@ public override bool Update() bool mouseHeldDown = Queries.LeftMouseInteraction(); if (!grabbedObject.IsGrabbed) // grab object { - if (Queries.LeftMouseDown() && !ExecuteViaContextMenu) + if ((Queries.LeftMouseDown() || (XRSEEActions.Selected && !activeAction)) && !ExecuteViaContextMenu) { // User starts dragging the currently hovered object. InteractableObject hoveredObject = InteractableObject.HoveredObjectWithWorldFlag; @@ -105,21 +111,28 @@ public override bool Update() return false; } - if (Raycasting.RaycastGraphElement(out RaycastHit grabbedObjectHit, out GraphElementRef _) + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer && Raycasting.RaycastGraphElement(out RaycastHit grabbedObjectHit, out GraphElementRef _) == HitGraphElement.Node) { cursorOffset = grabbedObjectHit.point - hoveredObject.transform.position; } + if (SceneSettings.InputType == PlayerInputType.VRPlayer && XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit raycasthit)) + { + cursorOffset = raycasthit.point - hoveredObject.transform.position; + } // An object to be grabbed must be representing a node that is not the root. if (hoveredObject.gameObject.TryGetNode(out Node node) && !node.IsRoot()) { + XRSEEActions.Selected = false; + activeAction = true; grabbedObject.Grab(hoveredObject.gameObject); AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.PickupSound, hoveredObject.gameObject); CurrentState = IReversibleAction.Progress.InProgress; } else { + XRSEEActions.Selected = false; return false; } @@ -144,7 +157,7 @@ public override bool Update() CurrentState = IReversibleAction.Progress.InProgress; } } - else if (mouseHeldDown ^ ExecuteViaContextMenu) // drag grabbed object + else if ((!XRSEEActions.Selected && activeAction) || mouseHeldDown ^ ExecuteViaContextMenu) // drag grabbed object { Raycasting.RaycastLowestNode(out RaycastHit? targetObjectHit, out Node _, grabbedObject.Node); if (targetObjectHit.HasValue) @@ -164,7 +177,8 @@ public override bool Update() { AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound, grabbedObject.GrabbedGameObject); } - + XRSEEActions.Selected = false; + activeAction = false; bool wasMoved = grabbedObject.UnGrab(); // Action is finished. // Prevent instant re-grab if the action was started via context menu and is not completed @@ -240,7 +254,14 @@ public void Grab(GameObject gameObject) if (gameObject != null) { GrabbedGameObject = gameObject; - originalParent = gameObject.transform.parent; + if (SceneSettings.InputType == PlayerInputType.VRPlayer) + { + originalParent = XRSEEActions.oldParent; + } + else + { + originalParent = gameObject.transform.parent; + } originalWorldPosition = gameObject.transform.position; IsGrabbed = true; if (gameObject.TryGetComponent(out InteractableObject interactableObject)) diff --git a/Assets/SEE/Controls/Actions/SelectAction.cs b/Assets/SEE/Controls/Actions/SelectAction.cs index 561a20f3f3..fef7d0faf4 100644 --- a/Assets/SEE/Controls/Actions/SelectAction.cs +++ b/Assets/SEE/Controls/Actions/SelectAction.cs @@ -35,25 +35,25 @@ private void Update() InteractableObject.UnselectAll(true); AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound); } - else if (SEEInput.Select() || (SceneSettings.InputType == PlayerInputType.VRPlayer && XRSEEActions.Delete)) + else if (SEEInput.Select() || (SceneSettings.InputType == PlayerInputType.VRPlayer && XRSEEActions.Selected)) { InteractableObject obj = null; if (Raycasting.RaycastInteractableObject(out _, out InteractableObject o) != HitGraphElement.None) { obj = o; } - if (Input.GetKey(KeyCode.LeftControl) || (SceneSettings.InputType == PlayerInputType.VRPlayer && XRSEEActions.Delete)) + if (Input.GetKey(KeyCode.LeftControl) || (SceneSettings.InputType == PlayerInputType.VRPlayer && XRSEEActions.Selected)) { if (obj != null) { obj.SetSelect(!obj.IsSelected, true); - XRSEEActions.Delete = false; + XRSEEActions.Selected = false; } } else { InteractableObject.ReplaceSelection(obj, true); - XRSEEActions.Delete = false; + XRSEEActions.Selected = false; } } else if (IDEIntegration.Instance != null && IDEIntegration.Instance.PendingSelectionsAction()) diff --git a/Assets/SEE/Controls/Actions/ShowCodeAction.cs b/Assets/SEE/Controls/Actions/ShowCodeAction.cs index 5c6e77f668..78c294e06f 100644 --- a/Assets/SEE/Controls/Actions/ShowCodeAction.cs +++ b/Assets/SEE/Controls/Actions/ShowCodeAction.cs @@ -318,7 +318,7 @@ public override bool Update() { // Only allow local player to open new code windows if (spaceManager.CurrentPlayer == WindowSpaceManager.LocalPlayer - && SEEInput.Select() + && (SEEInput.Select() || XRSEEActions.Selected) && Raycasting.RaycastGraphElement(out RaycastHit _, out GraphElementRef graphElementRef) != HitGraphElement.None) { // If nothing is selected, there's nothing more we need to do diff --git a/Assets/SEE/Controls/Actions/ShowLabel.cs b/Assets/SEE/Controls/Actions/ShowLabel.cs index 2c339b20d3..a24c896cd8 100644 --- a/Assets/SEE/Controls/Actions/ShowLabel.cs +++ b/Assets/SEE/Controls/Actions/ShowLabel.cs @@ -160,9 +160,19 @@ public void On() if (nodeOperator.Node != null) { LabelAttributes settings = GetLabelSettings(nodeOperator.Node, nodeOperator.City); - if (settings.Show && pointer.Value.On && nodeOperator.LabelIsNotEmpty()) + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) { - nodeOperator.FadeLabel(settings.LabelAlpha, pointer.Value.LastHit, settings.AnimationFactor); + if (settings.Show && pointer.Value.On && nodeOperator.LabelIsNotEmpty()) + { + nodeOperator.FadeLabel(settings.LabelAlpha, pointer.Value.LastHit, settings.AnimationFactor); + } + } + else + { + if (settings.Show && XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit raycasthit) && nodeOperator.LabelIsNotEmpty()) + { + nodeOperator.FadeLabel(settings.LabelAlpha, raycasthit.point, settings.AnimationFactor); + } } } } diff --git a/Assets/SEE/UI/PropertyDialog/DesktopPropertyDialog.cs b/Assets/SEE/UI/PropertyDialog/DesktopPropertyDialog.cs index c969a053e8..a77003fb71 100644 --- a/Assets/SEE/UI/PropertyDialog/DesktopPropertyDialog.cs +++ b/Assets/SEE/UI/PropertyDialog/DesktopPropertyDialog.cs @@ -1,4 +1,5 @@ using Michsky.UI.ModernUIPack; +using SEE.Controls; using SEE.GO; using SEE.Utils; using System; @@ -53,6 +54,10 @@ protected override void StartDesktop() try { dialog = PrefabInstantiator.InstantiatePrefab(dialogPrefab, Canvas.transform, false); + if (SceneSettings.InputType == PlayerInputType.VRPlayer) + { + dialog.transform.Find("Background").gameObject.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f); + } } catch (Exception e) { @@ -105,6 +110,11 @@ protected override void StartDesktop() } } + protected override void StartVR() + { + StartDesktop(); + } + /// /// Enables or disables the dialog depending on . /// @@ -128,6 +138,11 @@ protected override void UpdateDesktop() } } + protected override void UpdateVR() + { + UpdateDesktop(); + } + /// /// Calls for each group in . /// diff --git a/Assets/SEE/UI/PropertyDialog/PropertyDialog.cs b/Assets/SEE/UI/PropertyDialog/PropertyDialog.cs index 74b7cc7ed1..aaffa6c0c4 100644 --- a/Assets/SEE/UI/PropertyDialog/PropertyDialog.cs +++ b/Assets/SEE/UI/PropertyDialog/PropertyDialog.cs @@ -82,6 +82,7 @@ public void AllowClosing(bool allow) case PlayerInputType.TouchGamepadPlayer: break; case PlayerInputType.VRPlayer: + AllowClosingDesktop(allow); break; case PlayerInputType.None: // no UI has to be rendered break; @@ -105,6 +106,7 @@ public void Close() case PlayerInputType.TouchGamepadPlayer: break; case PlayerInputType.VRPlayer: + CloseDesktop(); break; case PlayerInputType.None: // no UI has to be rendered break; diff --git a/Assets/SEE/UI/PropertyDialog/PropertyGroup.cs b/Assets/SEE/UI/PropertyDialog/PropertyGroup.cs index 664081114d..ab322f573e 100644 --- a/Assets/SEE/UI/PropertyDialog/PropertyGroup.cs +++ b/Assets/SEE/UI/PropertyDialog/PropertyGroup.cs @@ -100,6 +100,11 @@ protected override void StartDesktop() } } + protected override void StartVR() + { + StartDesktop(); + } + /// /// Returns a new instantiation of prefab /// with given . diff --git a/Assets/SEE/UI/PropertyDialog/SelectionProperty.cs b/Assets/SEE/UI/PropertyDialog/SelectionProperty.cs index 3c8a65cb6f..8f2d4854ea 100644 --- a/Assets/SEE/UI/PropertyDialog/SelectionProperty.cs +++ b/Assets/SEE/UI/PropertyDialog/SelectionProperty.cs @@ -105,6 +105,11 @@ static HorizontalSelector GetHorizontalSelector(GameObject field) #endregion } + protected override void StartVR() + { + StartDesktop(); + } + /// /// Sets as the parent of the . /// diff --git a/Assets/SEE/UI/PropertyDialog/StringProperty.cs b/Assets/SEE/UI/PropertyDialog/StringProperty.cs index 8686cb354f..972faac69b 100644 --- a/Assets/SEE/UI/PropertyDialog/StringProperty.cs +++ b/Assets/SEE/UI/PropertyDialog/StringProperty.cs @@ -107,6 +107,11 @@ static TMP_InputField GetInputField(GameObject field) #endregion } + protected override void StartVR() + { + StartDesktop(); + } + /// /// Sets as the parent of the . /// diff --git a/Assets/SEE/UI/Window/DesktopWindowSpace.cs b/Assets/SEE/UI/Window/DesktopWindowSpace.cs index bbbbf9a3fd..723df0bd1f 100644 --- a/Assets/SEE/UI/Window/DesktopWindowSpace.cs +++ b/Assets/SEE/UI/Window/DesktopWindowSpace.cs @@ -7,6 +7,7 @@ using SEE.GO; using SEE.Utils; using UnityEngine; +using UnityEngine.XR.Interaction.Toolkit.UI; using CollectionExtensions = SEE.Utils.CollectionExtensions; namespace SEE.UI.Window diff --git a/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs b/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs index c24d1cb032..e3cc961014 100644 --- a/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs +++ b/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs @@ -407,16 +407,22 @@ void RegisterClickHandler() } // We want all applicable actions for the element, except ones where the element - // element is shown in the TreeWindow, since we are already in the TreeWindow. - IEnumerable actions = ContextMenuAction - .GetApplicableOptions(representedGraphElement, - representedGameObject) - .Where(x => !x.Name.Contains("TreeWindow")); - actions = actions.Append(new PopupMenuAction("Hide in TreeWindow", () => + // is shown in the TreeWindow, since we are already in the TreeWindow. + List appends = new() { - searcher.Filter.ExcludeElements.Add(representedGraphElement); - Rebuild(); - }, Icons.Hide)); + new("Hide in TreeWindow", () => + { + searcher.Filter.ExcludeElements.Add(representedGraphElement); + Rebuild(); + }, Icons.Hide) + }; + IEnumerable actions = ContextMenuAction + .GetOptionsForTreeView(contextMenu.ContextMenu, + e.position, + representedGraphElement, + representedGameObject, + appends); + actions = actions.Concat(appends); XRSEEActions.TooltipToggle = false; XRSEEActions.OnSelectToggle = true; XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit ray); @@ -438,13 +444,13 @@ void RegisterClickHandler() // We want all applicable actions for the element, except ones where the element // is shown in the TreeWindow, since we are already in the TreeWindow. - List appends = new () + List appends = new() { new("Hide in TreeWindow", () => - { - searcher.Filter.ExcludeElements.Add(representedGraphElement); - Rebuild(); - }, Icons.Hide) + { + searcher.Filter.ExcludeElements.Add(representedGraphElement); + Rebuild(); + }, Icons.Hide) }; IEnumerable actions = ContextMenuAction .GetOptionsForTreeView(contextMenu.ContextMenu, diff --git a/Assets/SEE/Utils/Raycasting.cs b/Assets/SEE/Utils/Raycasting.cs index 0fb8b1cd5d..7ded7fb4f3 100644 --- a/Assets/SEE/Utils/Raycasting.cs +++ b/Assets/SEE/Utils/Raycasting.cs @@ -106,7 +106,14 @@ public static bool RaycastAnything(out RaycastHit raycastHit, float maxDistance { raycastHit = new RaycastHit(); Physics.queriesHitBackfaces = true; - return !IsMouseOverGUI() && Physics.Raycast(UserPointsTo(), out raycastHit, maxDistance); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + return !IsMouseOverGUI() && Physics.Raycast(UserPointsTo(), out raycastHit, maxDistance); + } + else + { + return Physics.Raycast(UserPointsTo(), out raycastHit, maxDistance); + } } /// diff --git a/Assets/SEE/XR/RadialSelection.cs b/Assets/SEE/XR/RadialSelection.cs index bf1bc8a5c2..9f969d1ca8 100644 --- a/Assets/SEE/XR/RadialSelection.cs +++ b/Assets/SEE/XR/RadialSelection.cs @@ -335,16 +335,6 @@ public void SelectAction(int i) actionObject.transform.position = handTransform.position; actionObject.SetActive(true); } - else if (actions[i] == ActionStateTypes.Draw.Name) - { - if (actionObject != null) - { - Destroy(actionObject); - } - actionObject = PrefabInstantiator.InstantiatePrefab("Prefabs/Pen").transform.gameObject; - actionObject.transform.position = handTransform.position; - actionObject.SetActive(true); - } else { if (actionObject != null) From 70e0a23307a7c6b9e1a7c3f6ad78d336210d7b34 Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Wed, 9 Oct 2024 21:22:44 +0200 Subject: [PATCH 18/35] Fixed Undo/Redo for the RotateAction in VR and fixed a Bug according to the playermenu in VR --- .../Actions/NodeManipulationAction.cs | 126 ++++++++++-------- Assets/SEE/Controls/Actions/RotateAction.cs | 5 +- Assets/SEE/Game/Avatars/AvatarAdapter.cs | 14 ++ Assets/SEE/UI/HelpSystem/HelpSystemEntry.cs | 10 ++ Assets/SEE/Utils/Raycasting.cs | 2 +- Assets/SEE/XR/Rotator.cs | 7 +- 6 files changed, 99 insertions(+), 65 deletions(-) diff --git a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs index 6279a3c27e..7b26ef2a2e 100644 --- a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs +++ b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs @@ -111,91 +111,93 @@ public override void Redo() /// protected GameObject GameNodeToBeContinuedInNextAction; + bool a; + /// . /// /// true if completed public override bool Update() { - if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && UsedGizmo.IsHovered()) || (SceneSettings.InputType == PlayerInputType.VRPlayer && Rotator.shouldGetHandRotation)) { - if (UsedGizmo.IsHovered()) + // Transformation via the gizmo is in progress. + if (GameNodeSelected && HasChanges()) { - // Transformation via the gizmo is in progress. - if (GameNodeSelected && HasChanges()) - { - CurrentState = IReversibleAction.Progress.InProgress; - } - return false; + a = true; + CurrentState = IReversibleAction.Progress.InProgress; } + return false; + } - if (SEEInput.Select()) + if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && SEEInput.Select()) || (SceneSettings.InputType == PlayerInputType.VRPlayer && (XRSEEActions.Selected || (!Rotator.shouldGetHandRotation && a)))) + { + Debug.Log("zweite Schzleife"); + if (Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.Node) { - if (Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.Node) + // An object different from a graph node was selected. + if (GameNodeSelected && HasChanges()) { - // An object different from a graph node was selected. - if (GameNodeSelected && HasChanges()) - { - // An object to be manipulated was selected already and it was changed. - // The action is finished. - FinalizeAction(); - return true; - } - else - { - // No game node has been selected yet or the previously selected game node - // has had no changes. The action is continued. - return false; - } + // An object to be manipulated was selected already and it was changed. + // The action is finished. + a = false; + FinalizeAction(); + return true; } else { - // A game node was selected by the user. - // Has the user already selected a game node in a previous iteration? - if (GameNodeSelected) + // No game node has been selected yet or the previously selected game node + // has had no changes. The action is continued. + return false; + } + } + else + { + // A game node was selected by the user. + // Has the user already selected a game node in a previous iteration? + if (GameNodeSelected) + { + // The user has already selected a game node in a previous iteration. + // Are the two game nodes different? + if (GameNodeSelected != raycastHit.collider.gameObject) { - // The user has already selected a game node in a previous iteration. - // Are the two game nodes different? - if (GameNodeSelected != raycastHit.collider.gameObject) + // The newly and previously selected nodes are different. + // Have we had any changes yet? If not, we assume the user wants + // to manipulate the newly game node instead. + if (!HasChanges()) { - // The newly and previously selected nodes are different. - // Have we had any changes yet? If not, we assume the user wants - // to manipulate the newly game node instead. - if (!HasChanges()) - { - StartAction(raycastHit.collider.gameObject); - return false; - } - else - { - // This action is considered finished and a different action should - // be started to continue with the newly selected node. - FinalizeAction(); - GameNodeToBeContinuedInNextAction = raycastHit.collider.gameObject; - return true; - } + StartAction(raycastHit.collider.gameObject); + return false; } else { - // The user has selected the same node again. - // Nothing to be done. - return false; + // This action is considered finished and a different action should + // be started to continue with the newly selected node. + FinalizeAction(); + GameNodeToBeContinuedInNextAction = raycastHit.collider.gameObject; + return true; } } else { - // It's the first time, a game node was selected. The action starts. - StartAction(raycastHit.collider.gameObject); + // The user has selected the same node again. + // Nothing to be done. return false; } } + else + { + XRSEEActions.Selected = false; + // It's the first time, a game node was selected. The action starts. + StartAction(raycastHit.collider.gameObject); + return false; + } } - else if (SEEInput.ToggleMenu() || SEEInput.Cancel()) - { - FinalizeAction(); - return true; - } - return false; + } + else if (SEEInput.ToggleMenu() || SEEInput.Cancel()) + { + FinalizeAction(); + return true; } return false; } @@ -221,7 +223,10 @@ protected void StartAction(GameObject gameNode) GameNodeSelected = gameNode; GameNodeMemento = CreateMemento(GameNodeSelected); - UsedGizmo.Enable(GameNodeSelected); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + UsedGizmo.Enable(GameNodeSelected); + } AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.PickupSound, GameNodeSelected); } @@ -234,7 +239,10 @@ protected void StartAction(GameObject gameNode) protected virtual void FinalizeAction() { UnityEngine.Assertions.Assert.IsNotNull(GameNodeSelected); - UsedGizmo.Disable(); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + UsedGizmo.Disable(); + } CurrentState = IReversibleAction.Progress.Completed; AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound, GameNodeSelected); } diff --git a/Assets/SEE/Controls/Actions/RotateAction.cs b/Assets/SEE/Controls/Actions/RotateAction.cs index 76e13fd3de..ba77d7968a 100644 --- a/Assets/SEE/Controls/Actions/RotateAction.cs +++ b/Assets/SEE/Controls/Actions/RotateAction.cs @@ -22,10 +22,7 @@ internal class RotateAction : NodeManipulationAction private RotateAction(GameObject gameNodeToBeContinuedWith) : base() { Initialize(); - if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) - { - StartAction(gameNodeToBeContinuedWith); - } + StartAction(gameNodeToBeContinuedWith); } /// diff --git a/Assets/SEE/Game/Avatars/AvatarAdapter.cs b/Assets/SEE/Game/Avatars/AvatarAdapter.cs index 258d6bcece..91f49eae28 100644 --- a/Assets/SEE/Game/Avatars/AvatarAdapter.cs +++ b/Assets/SEE/Game/Avatars/AvatarAdapter.cs @@ -11,6 +11,7 @@ using SEE.Controls.Actions; using TMPro; using UnityEngine.UI; +using SEE.GO.Menu; #if ENABLE_VR using UnityEngine.Assertions; @@ -53,6 +54,19 @@ private void Start() gameObject.AddComponent(); } + if (SceneSettings.InputType == PlayerInputType.VRPlayer) + { + if (!gameObject.TryGetComponent(out PlayerMenu _)) + { + gameObject.AddComponent(); + } + + if (!gameObject.TryGetComponent(out DrawableSurfacesRef _)) + { + gameObject.AddComponent(); + } + } + switch (SceneSettings.InputType) { case PlayerInputType.DesktopPlayer: diff --git a/Assets/SEE/UI/HelpSystem/HelpSystemEntry.cs b/Assets/SEE/UI/HelpSystem/HelpSystemEntry.cs index c792236477..4f2cab28cb 100644 --- a/Assets/SEE/UI/HelpSystem/HelpSystemEntry.cs +++ b/Assets/SEE/UI/HelpSystem/HelpSystemEntry.cs @@ -155,6 +155,11 @@ protected override void StartDesktop() instructionsDisplay = GetTextField(); } + protected override void StartVR() + { + StartDesktop(); + } + /// /// Instantiates the help system entry and the space if necessary, that is, /// if is null. @@ -230,6 +235,11 @@ protected override void UpdateDesktop() } } + protected override void UpdateVR() + { + UpdateDesktop(); + } + /// /// Sets the TextMeshPro text which represents the progress of the video. It is /// split in parts of texts, e.g., 1/5 or 2/5, instead of time. diff --git a/Assets/SEE/Utils/Raycasting.cs b/Assets/SEE/Utils/Raycasting.cs index 7ded7fb4f3..7f3781db60 100644 --- a/Assets/SEE/Utils/Raycasting.cs +++ b/Assets/SEE/Utils/Raycasting.cs @@ -66,7 +66,7 @@ public static class Raycasting /// public static HitGraphElement RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef elementRef) { - if (!IsMouseOverGUI() && Physics.Raycast(UserPointsTo(), out RaycastHit hit)) + if (((SceneSettings.InputType == PlayerInputType.DesktopPlayer && !IsMouseOverGUI()) || SceneSettings.InputType == PlayerInputType.VRPlayer) && Physics.Raycast(UserPointsTo(), out RaycastHit hit)) { raycastHit = hit; if (hit.transform.TryGetComponent(out NodeRef nodeRef)) diff --git a/Assets/SEE/XR/Rotator.cs b/Assets/SEE/XR/Rotator.cs index 537f3ed91d..86f034f4de 100644 --- a/Assets/SEE/XR/Rotator.cs +++ b/Assets/SEE/XR/Rotator.cs @@ -1,3 +1,4 @@ +using SEE.Controls.Actions; using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; using UnityEngine.XR.Interaction.Toolkit.Interactables; @@ -38,7 +39,7 @@ public class Rotator : MonoBehaviour /// /// Is true, when the dial/node should be roated according to the hand-rotation. /// - private bool shouldGetHandRotation = false; + public static bool shouldGetHandRotation { get; private set; } = false; /// /// The grab-interactor of the dial. /// @@ -79,6 +80,10 @@ private void GrabbedBy(SelectEnterEventArgs args) // Update is called once per frame void Update() { + if (GlobalActionHistory.Current() != ActionStateTypes.Rotate) + { + Destroy(gameObject); + } if (shouldGetHandRotation) { var rotationAngle = GetInteractorRotation(); From 7cd91bfec9b8cd0900a76c0a77c6f170849d0b07 Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Wed, 9 Oct 2024 21:37:04 +0200 Subject: [PATCH 19/35] Added documentation for the VR-Keyboard and the NodeManipulationAction in VR --- .../SEE/Controls/Actions/NodeManipulationAction.cs | 12 ++++++++---- Assets/SEE/XR/KeyboardInputHandler.cs | 1 + Assets/SEE/XR/KeyboardKey.cs | 1 + Assets/SEE/XR/KeyboardManager.cs | 1 + Assets/SEE/XR/RadialSelection.cs | 1 + 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs index 7b26ef2a2e..4f5470f290 100644 --- a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs +++ b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs @@ -111,7 +111,11 @@ public override void Redo() /// protected GameObject GameNodeToBeContinuedInNextAction; - bool a; + /// + /// This is true, if the user is rotating an object. + /// We use this in VR, to finish this action. + /// + bool inProgress; /// . @@ -124,13 +128,13 @@ public override bool Update() // Transformation via the gizmo is in progress. if (GameNodeSelected && HasChanges()) { - a = true; + inProgress = true; CurrentState = IReversibleAction.Progress.InProgress; } return false; } - if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && SEEInput.Select()) || (SceneSettings.InputType == PlayerInputType.VRPlayer && (XRSEEActions.Selected || (!Rotator.shouldGetHandRotation && a)))) + if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && SEEInput.Select()) || (SceneSettings.InputType == PlayerInputType.VRPlayer && (XRSEEActions.Selected || (!Rotator.shouldGetHandRotation && inProgress)))) { Debug.Log("zweite Schzleife"); if (Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.Node) @@ -140,7 +144,7 @@ public override bool Update() { // An object to be manipulated was selected already and it was changed. // The action is finished. - a = false; + inProgress = false; FinalizeAction(); return true; } diff --git a/Assets/SEE/XR/KeyboardInputHandler.cs b/Assets/SEE/XR/KeyboardInputHandler.cs index 06d8fe1dca..bcd4371a7e 100644 --- a/Assets/SEE/XR/KeyboardInputHandler.cs +++ b/Assets/SEE/XR/KeyboardInputHandler.cs @@ -7,6 +7,7 @@ /// /// This class changes the inputfield for the VR-Keyboard and /// also activates the keyboard. +/// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g /// public class KeyboardInputHandler : MonoBehaviour, IPointerClickHandler { diff --git a/Assets/SEE/XR/KeyboardKey.cs b/Assets/SEE/XR/KeyboardKey.cs index c4ad209bde..903e95de18 100644 --- a/Assets/SEE/XR/KeyboardKey.cs +++ b/Assets/SEE/XR/KeyboardKey.cs @@ -3,6 +3,7 @@ using UnityEngine.UI; /// /// This class handles a single keyboard key. +/// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g /// public class KeyboardKey : MonoBehaviour { diff --git a/Assets/SEE/XR/KeyboardManager.cs b/Assets/SEE/XR/KeyboardManager.cs index 45339b76c0..c9c81659f2 100644 --- a/Assets/SEE/XR/KeyboardManager.cs +++ b/Assets/SEE/XR/KeyboardManager.cs @@ -4,6 +4,7 @@ /// /// This class manages the input for all buttons on the keyboard. +/// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g /// public class KeyboardManager : MonoBehaviour { diff --git a/Assets/SEE/XR/RadialSelection.cs b/Assets/SEE/XR/RadialSelection.cs index 9f969d1ca8..72ebe13fd4 100644 --- a/Assets/SEE/XR/RadialSelection.cs +++ b/Assets/SEE/XR/RadialSelection.cs @@ -13,6 +13,7 @@ /// /// This class is used, to open a /// radial menu in VR. +/// This script is based on this tutorial: https://www.youtube.com/watch?v=n-xPN1v3dvA /// public class RadialSelection : MonoBehaviour { From 3304383d4794c3447eb4d58fe1673cb181e2020c Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Thu, 10 Oct 2024 03:26:10 +0200 Subject: [PATCH 20/35] VR-Interactions over the context-menu are fixed now / VR-Keyboard is now for more UIs available / Debug-Cleanup --- .../UI/InputFields/StringInputField.prefab | 13 +++++++++++ .../Prefabs/UI/PropertyWindow.prefab | 13 +++++++++++ Assets/SEE/Controls/Actions/AddNodeAction.cs | 2 +- .../SEE/Controls/Actions/ContextMenuAction.cs | 15 ++++++++++--- Assets/SEE/Controls/Actions/MoveAction.cs | 6 ++--- .../Actions/NodeManipulationAction.cs | 1 - Assets/SEE/Game/Avatars/AvatarAdapter.cs | 22 ------------------- Assets/SEE/Game/InteractionDecorator.cs | 5 ++++- .../PropertyWindowContextMenu.cs | 9 ++++++++ .../UI/Window/TreeWindow/DesktopTreeWindow.cs | 9 ++++++++ Assets/SEE/XR/XRSEEActions.cs | 2 +- 11 files changed, 65 insertions(+), 32 deletions(-) diff --git a/Assets/Resources/Prefabs/UI/InputFields/StringInputField.prefab b/Assets/Resources/Prefabs/UI/InputFields/StringInputField.prefab index 5403828a86..2161c60f97 100644 --- a/Assets/Resources/Prefabs/UI/InputFields/StringInputField.prefab +++ b/Assets/Resources/Prefabs/UI/InputFields/StringInputField.prefab @@ -270,6 +270,7 @@ GameObject: - component: {fileID: 8077329023944246151} - component: {fileID: 8077329023944246148} - component: {fileID: 278820234878124279} + - component: {fileID: 3756390358347190417} m_Layer: 5 m_Name: StringInputField m_TagString: Untagged @@ -509,6 +510,18 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 52dd8aaa3b5d4058ac1b1e9242d35f57, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!114 &3756390358347190417 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8077329023944246153} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8200dca4f813a2949a733aadb413d2f7, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &8077329024008539462 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Resources/Prefabs/UI/PropertyWindow.prefab b/Assets/Resources/Prefabs/UI/PropertyWindow.prefab index 6381ad29ce..554710b3b4 100644 --- a/Assets/Resources/Prefabs/UI/PropertyWindow.prefab +++ b/Assets/Resources/Prefabs/UI/PropertyWindow.prefab @@ -2474,6 +2474,7 @@ GameObject: - component: {fileID: 790196186597391616} - component: {fileID: 8874951550066978760} - component: {fileID: 9004278678232372259} + - component: {fileID: 8056112077780593223} m_Layer: 5 m_Name: SearchField m_TagString: Untagged @@ -2684,6 +2685,18 @@ MonoBehaviour: m_FlexibleWidth: 100 m_FlexibleHeight: -1 m_LayoutPriority: 3 +--- !u!114 &8056112077780593223 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7475176147733851298} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8200dca4f813a2949a733aadb413d2f7, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &8709522067248902145 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/SEE/Controls/Actions/AddNodeAction.cs b/Assets/SEE/Controls/Actions/AddNodeAction.cs index 2e027399b1..826a4f340d 100644 --- a/Assets/SEE/Controls/Actions/AddNodeAction.cs +++ b/Assets/SEE/Controls/Actions/AddNodeAction.cs @@ -248,8 +248,8 @@ public override void Undo() base.Undo(); if (addedGameNode != null) { - new DeleteNetAction(addedGameNode.name).Execute(); GameElementDeleter.RemoveNodeFromGraph(addedGameNode); + new DeleteNetAction(addedGameNode.name).Execute(); Destroyer.Destroy(addedGameNode); addedGameNode = null; } diff --git a/Assets/SEE/Controls/Actions/ContextMenuAction.cs b/Assets/SEE/Controls/Actions/ContextMenuAction.cs index 341f8e46c8..3a986bbb78 100644 --- a/Assets/SEE/Controls/Actions/ContextMenuAction.cs +++ b/Assets/SEE/Controls/Actions/ContextMenuAction.cs @@ -63,7 +63,7 @@ private void Start() private void Update() { - if (SEEInput.OpenContextMenuStart() || (XRSEEActions.TooltipToggle && !onSelect)) + if (SEEInput.OpenContextMenuStart() || (XRSEEActions.TooltipToggle && !XRSEEActions.OnSelectToggle)) { if (InteractableObject.SelectedObjects.Count <= 1) { @@ -75,15 +75,19 @@ private void Update() } multiselection = false; XRSEEActions.TooltipToggle = false; + XRSEEActions.OnSelectToggle = true; onSelect = true; } else { startObject = null; multiselection = true; + XRSEEActions.TooltipToggle = false; + XRSEEActions.OnSelectToggle = true; + onSelect = true; } } - if (SEEInput.OpenContextMenuEnd() || (XRSEEActions.TooltipToggle && onSelect)) + if (SEEInput.OpenContextMenuEnd() || (XRSEEActions.OnSelectToggle && onSelect)) { if (!multiselection) { @@ -104,7 +108,6 @@ private void Update() position = res.point; } IEnumerable entries = GetApplicableOptions(popupMenu, position, raycastHit.point, o.GraphElemRef.Elem, o.gameObject); - XRSEEActions.TooltipToggle = false; onSelect = false; popupMenu.ShowWith(entries, position); } @@ -128,6 +131,7 @@ private void Update() position = res.point; } IEnumerable entries = GetApplicableOptionsForMultiselection(popupMenu, InteractableObject.SelectedObjects); + onSelect = false; popupMenu.ShowWith(entries, position); } } @@ -604,6 +608,11 @@ void RotateNode() UpdatePlayerMenu(); RotateAction action = (RotateAction)GlobalActionHistory.CurrentAction(); action.ContextMenuExecution(gameObject); + if (SceneSettings.InputType == PlayerInputType.VRPlayer) + { + PrefabInstantiator.InstantiatePrefab("Prefabs/Dial").transform.position = gameObject.transform.position + new Vector3(0, 1, 0); + XRSEEActions.RotateObject = gameObject; + } ExcecutePreviousActionAsync(action, previousAction).Forget(); } diff --git a/Assets/SEE/Controls/Actions/MoveAction.cs b/Assets/SEE/Controls/Actions/MoveAction.cs index 7825002e66..b843524877 100644 --- a/Assets/SEE/Controls/Actions/MoveAction.cs +++ b/Assets/SEE/Controls/Actions/MoveAction.cs @@ -137,7 +137,7 @@ public override bool Update() } } - else if (ExecuteViaContextMenu && !mouseHeldDown) + else if ((SceneSettings.InputType == PlayerInputType.VRPlayer && ExecuteViaContextMenu) || (ExecuteViaContextMenu && !mouseHeldDown)) { // User starts dragging object selected via context menu. // Override the initial cursorOffset based on new mouse position to reduce jump @@ -152,12 +152,12 @@ public override bool Update() anchorPosition.z = Mathf.Clamp(anchorPosition.z, objectPosition.z - 0.5f * objectSize.z, objectPosition.z + 0.5f * objectSize.z); cursorOffset = anchorPosition - objectPosition; } - grabbedObject.Grab(contextMenuObjectToMove); + activeAction = true; CurrentState = IReversibleAction.Progress.InProgress; } } - else if ((!XRSEEActions.Selected && activeAction) || mouseHeldDown ^ ExecuteViaContextMenu) // drag grabbed object + else if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && (mouseHeldDown ^ ExecuteViaContextMenu)) || (SceneSettings.InputType == PlayerInputType.VRPlayer && !XRSEEActions.Selected && activeAction)) // drag grabbed object { Raycasting.RaycastLowestNode(out RaycastHit? targetObjectHit, out Node _, grabbedObject.Node); if (targetObjectHit.HasValue) diff --git a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs index 4f5470f290..0f1fabd196 100644 --- a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs +++ b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs @@ -136,7 +136,6 @@ public override bool Update() if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && SEEInput.Select()) || (SceneSettings.InputType == PlayerInputType.VRPlayer && (XRSEEActions.Selected || (!Rotator.shouldGetHandRotation && inProgress)))) { - Debug.Log("zweite Schzleife"); if (Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.Node) { // An object different from a graph node was selected. diff --git a/Assets/SEE/Game/Avatars/AvatarAdapter.cs b/Assets/SEE/Game/Avatars/AvatarAdapter.cs index 91f49eae28..e93224115e 100644 --- a/Assets/SEE/Game/Avatars/AvatarAdapter.cs +++ b/Assets/SEE/Game/Avatars/AvatarAdapter.cs @@ -314,28 +314,6 @@ public IEnumerator StartXRCoroutine() GameObject rig = PrefabInstantiator.InstantiatePrefab(vrPlayerRigPrefab); rig.transform.position = gameObject.transform.position; - Debug.Log("aaaaaaaaaaaaaaaaaaa" + gameObject.name); - Debug.Log("aaaaaaaaaaaaaaaaaaa" + gameObject.name); - Debug.Log("aaaaaaaaaaaaaaaaaaa" + gameObject.name); - Debug.Log("aaaaaaaaaaaaaaaaaaa" + gameObject.name); - GameObject indicator = rig.transform.Find("Camera Offset/Left Controller/ModePanel/XRCanvas").transform.gameObject; - Debug.Log(indicator.name + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - ActionStateType firstType = ActionStateTypes.NewEdge; - GlobalActionHistory.Execute(ActionStateTypes.Delete); - // Initial state will be the first action state type - GlobalActionHistory.Execute(firstType); - indicator.name = "StateIndicator"; - foreach (AbstractActionStateType a in ActionStateTypes.AllRootTypes.AllElements()) - { - Debug.Log(a.Name); - GameObject mode = PrefabInstantiator.InstantiatePrefab("Prefabs/UI/XRButton", indicator.transform.Find("Scroll View/Viewport/Content").transform, false).transform.gameObject; - mode.transform.SetParent(indicator.transform.Find("Scroll View/Viewport/Content")); - mode.transform.Find("Text").gameObject.MustGetComponent().SetText(a.Name); - if (a is ActionStateType actionStateType) - { - mode.transform.gameObject.MustGetComponent private void Start() { - SetupCameraDropdown(); - StartCoroutine(GetToken()); + if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) + { + SetupCameraDropdown(); + StartCoroutine(GetToken()); + } + else + { + gameObject.SetActive(false); + } } /// diff --git a/Assets/SEE/XR/XRSEEActions.cs b/Assets/SEE/XR/XRSEEActions.cs index d58222ce2f..532b274c45 100644 --- a/Assets/SEE/XR/XRSEEActions.cs +++ b/Assets/SEE/XR/XRSEEActions.cs @@ -30,7 +30,7 @@ public class XRSEEActions : MonoBehaviour /// /// The old parent of a node. We need this for the MoveAction. /// - public static Transform oldParent { get; private set; } + public static Transform oldParent { get; set; } /// /// The button, which is used for the primary actions. /// @@ -92,7 +92,6 @@ public void OnHoverExited(HoverExitEventArgs args) { hovering = false; hoveredGameObject = args.interactableObject.transform.gameObject; - oldParent = null; hoveredGameObject.transform.TryGetComponent(out showLabel); showLabel?.Off(); if (hoveredGameObject.transform.TryGetComponent(out InteractableObject io)) @@ -104,6 +103,8 @@ public void OnHoverExited(HoverExitEventArgs args) /// Is true, when the button for the primary actions is pressed. /// public static bool Selected { get; set; } + + public static bool SelectedFlag { get; set; } /// /// The GameObject, which should be rotated. /// @@ -119,6 +120,7 @@ private void Action(InputAction.CallbackContext context) if (GlobalActionHistory.Current() == ActionStateTypes.Move && Selected == true) { Selected = false; + SelectedFlag = false; } else { @@ -126,10 +128,19 @@ private void Action(InputAction.CallbackContext context) { RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit); RotateObject = hit.collider.transform.gameObject; + Selected = true; } Selected = true; + if (GlobalActionHistory.Current() != ActionStateTypes.Move) + { + SelectedFlag = true; + } } } + else + { + InteractableObject.ReplaceSelection(null, true); + } } /// /// Will be true, when the TreeView is open, while the user tries to move a node. From 66253953df5d0df64b3b009535a33e9c282d2b37 Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Thu, 10 Oct 2024 12:18:24 +0200 Subject: [PATCH 24/35] OpenXR disabled, because of Linux builds --- Assets/XR/XRGeneralSettings.asset | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Assets/XR/XRGeneralSettings.asset b/Assets/XR/XRGeneralSettings.asset index 3435575a8f..7c6a2978b4 100644 --- a/Assets/XR/XRGeneralSettings.asset +++ b/Assets/XR/XRGeneralSettings.asset @@ -75,5 +75,4 @@ MonoBehaviour: m_RequiresSettingsUpdate: 0 m_AutomaticLoading: 0 m_AutomaticRunning: 0 - m_Loaders: - - {fileID: 11400000, guid: 52e3c795d65ebf94492ecc68b6423ce3, type: 2} + m_Loaders: [] From 596a34179c7195510269d467d6d105647a3632c5 Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Thu, 10 Oct 2024 12:53:08 +0200 Subject: [PATCH 25/35] Fixed bad patterns, that the github-actions-bot detected. --- Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs | 1 - Assets/SEE/XR/RadialSelection.cs | 6 +++--- Assets/SEE/XR/Rotator.cs | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs index 69c9185f02..51f5ec1329 100644 --- a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs +++ b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs @@ -176,7 +176,6 @@ public override bool Update() } else { - // FIXME: Needs adaptation for VR where no mouse is available. if (Input.GetMouseButtonDown(0) && Raycasting.RaycastGraphElement( out RaycastHit raycastHit, diff --git a/Assets/SEE/XR/RadialSelection.cs b/Assets/SEE/XR/RadialSelection.cs index 72ebe13fd4..2ed6cad07d 100644 --- a/Assets/SEE/XR/RadialSelection.cs +++ b/Assets/SEE/XR/RadialSelection.cs @@ -330,7 +330,7 @@ public void SelectAction(int i) { if (actionObject != null) { - Destroy(actionObject); + Destroyer.Destroy(actionObject); } actionObject = PrefabInstantiator.InstantiatePrefab("Prefabs/Dial").transform.gameObject; actionObject.transform.position = handTransform.position; @@ -380,7 +380,7 @@ public void SpawnRadialPart() foreach (GameObject item in spawnedParts) { - Destroy(item); + Destroyer.Destroy(item); } spawnedParts.Clear(); @@ -397,7 +397,7 @@ public void SpawnRadialPart() spawnRadialPart.GetComponent().fillAmount = (1 / (float)numberOfRadialPart) - (angleBetweenPart / 360); if (i > (numberOfRadialPart / 2)) { - spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().rotation = + spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().rotation = spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().rotation * Quaternion.Euler(0, 0, 180); spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().alignment = TextAlignmentOptions.Right; } diff --git a/Assets/SEE/XR/Rotator.cs b/Assets/SEE/XR/Rotator.cs index 86f034f4de..b29bd413d7 100644 --- a/Assets/SEE/XR/Rotator.cs +++ b/Assets/SEE/XR/Rotator.cs @@ -1,4 +1,5 @@ using SEE.Controls.Actions; +using SEE.Utils; using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; using UnityEngine.XR.Interaction.Toolkit.Interactables; @@ -82,7 +83,7 @@ void Update() { if (GlobalActionHistory.Current() != ActionStateTypes.Rotate) { - Destroy(gameObject); + Destroyer.Destroy(gameObject); } if (shouldGetHandRotation) { From 2733f0d3625171c755ede07d4e22fffd1dc1f6ae Mon Sep 17 00:00:00 2001 From: Falko Galperin Date: Wed, 16 Oct 2024 18:57:44 +0200 Subject: [PATCH 26/35] ActionStateTypes: Remove unused static `Draw` type This should also fix the failing tests. --- Assets/SEE/Controls/Actions/ActionStateTypes.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Assets/SEE/Controls/Actions/ActionStateTypes.cs b/Assets/SEE/Controls/Actions/ActionStateTypes.cs index b753e1ee54..52cee76a7f 100644 --- a/Assets/SEE/Controls/Actions/ActionStateTypes.cs +++ b/Assets/SEE/Controls/Actions/ActionStateTypes.cs @@ -299,7 +299,7 @@ static ActionStateTypes() parent: Drawable); } - // IMPORTANT NOTE: The order of the following field declarations must be exaclty the same + // IMPORTANT NOTE: The order of the following field declarations must be exactly the same // as the order of their assignments in the static constructor above. public static readonly ActionStateType Move; @@ -312,7 +312,6 @@ static ActionStateTypes() public static readonly ActionStateType ScaleNode; public static readonly ActionStateType Delete; public static readonly ActionStateType ShowCode; - public static readonly ActionStateType Draw; public static readonly ActionStateType AcceptDivergence; public static readonly ActionStateTypeGroup MetricBoard; From 05986a169e73652074d85f8fc4302f2228d29731 Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Sat, 12 Oct 2024 14:58:45 +0200 Subject: [PATCH 27/35] Made changes according to the review of @falko17 (relate to documentation/style) --- .../Actions/AcceptDivergenceAction.cs | 4 +- .../SEE/Controls/Actions/ContextMenuAction.cs | 8 +-- Assets/SEE/Controls/Actions/MoveAction.cs | 4 +- .../Actions/NodeManipulationAction.cs | 8 +-- Assets/SEE/Controls/SEEInput.cs | 22 +++--- Assets/SEE/Game/Avatars/AvatarAdapter.cs | 22 +----- Assets/SEE/UI/PopupMenu/PopupMenu.cs | 7 +- Assets/SEE/UI/Window/DesktopWindowSpace.cs | 11 +-- .../UI/Window/TreeWindow/DesktopTreeWindow.cs | 57 +++++++-------- Assets/SEE/Utils/PointerHelper.cs | 4 ++ Assets/SEE/Utils/Raycasting.cs | 41 +++++------ Assets/SEE/XR/KeyboardInputHandler.cs | 17 ++++- Assets/SEE/XR/KeyboardKey.cs | 52 +++++++------- Assets/SEE/XR/KeyboardManager.cs | 26 ++++--- Assets/SEE/XR/Rotator.cs | 72 +++++++++++-------- 15 files changed, 184 insertions(+), 171 deletions(-) diff --git a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs index 51f5ec1329..846173d9df 100644 --- a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs +++ b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs @@ -116,8 +116,8 @@ public override bool Update() { if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - if (XRSEEActions.Selected - && XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit)) + if (XRSEEActions.Selected + && XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit)) { // find the edge representing the divergence that should be solved. GameObject divergentEdge = hit.collider.gameObject; diff --git a/Assets/SEE/Controls/Actions/ContextMenuAction.cs b/Assets/SEE/Controls/Actions/ContextMenuAction.cs index 3a986bbb78..48c8c6c462 100644 --- a/Assets/SEE/Controls/Actions/ContextMenuAction.cs +++ b/Assets/SEE/Controls/Actions/ContextMenuAction.cs @@ -56,8 +56,8 @@ private void Start() } /// - /// Is true, when the context-menu is open. - /// This is used in VR, to open and close the menu. + /// Is true when the context-menu is open. + /// This is used in VR to open and close the menu. /// bool onSelect; @@ -96,7 +96,7 @@ private void Update() { return; } - if ((o == startObject && (Input.mousePosition - startMousePosition).magnitude < 1) || SceneSettings.InputType == PlayerInputType.VRPlayer) + if (SceneSettings.InputType == PlayerInputType.VRPlayer || (o == startObject && (Input.mousePosition - startMousePosition).magnitude < 1)) { if (SceneSettings.InputType == PlayerInputType.DesktopPlayer) { @@ -610,7 +610,7 @@ void RotateNode() action.ContextMenuExecution(gameObject); if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - PrefabInstantiator.InstantiatePrefab("Prefabs/Dial").transform.position = gameObject.transform.position + new Vector3(0, 1, 0); + PrefabInstantiator.InstantiatePrefab("Prefabs/Dial").transform.position = gameObject.transform.position + Vector3.up; XRSEEActions.RotateObject = gameObject; } ExcecutePreviousActionAsync(action, previousAction).Forget(); diff --git a/Assets/SEE/Controls/Actions/MoveAction.cs b/Assets/SEE/Controls/Actions/MoveAction.cs index dead39d39b..d9befd1768 100644 --- a/Assets/SEE/Controls/Actions/MoveAction.cs +++ b/Assets/SEE/Controls/Actions/MoveAction.cs @@ -75,8 +75,8 @@ public override ActionStateType GetActionStateType() } /// - /// This is true, if the user is dragging an object. - /// We use this in VR, to activate/deactivate this action. + /// This is true if the user is dragging an object. + /// We use this in VR to activate/deactivate this action. /// bool activeAction; diff --git a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs index 0f1fabd196..309f602b1a 100644 --- a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs +++ b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs @@ -112,8 +112,8 @@ public override void Redo() protected GameObject GameNodeToBeContinuedInNextAction; /// - /// This is true, if the user is rotating an object. - /// We use this in VR, to finish this action. + /// This is true if the user is rotating an object. + /// We use this in VR to finish this action. /// bool inProgress; @@ -123,7 +123,7 @@ public override void Redo() /// true if completed public override bool Update() { - if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && UsedGizmo.IsHovered()) || (SceneSettings.InputType == PlayerInputType.VRPlayer && Rotator.shouldGetHandRotation)) + if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && UsedGizmo.IsHovered()) || (SceneSettings.InputType == PlayerInputType.VRPlayer && Rotator.ShouldGetHandRotation)) { // Transformation via the gizmo is in progress. if (GameNodeSelected && HasChanges()) @@ -134,7 +134,7 @@ public override bool Update() return false; } - if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && SEEInput.Select()) || (SceneSettings.InputType == PlayerInputType.VRPlayer && (XRSEEActions.Selected || (!Rotator.shouldGetHandRotation && inProgress)))) + if ((SceneSettings.InputType == PlayerInputType.DesktopPlayer && SEEInput.Select()) || (SceneSettings.InputType == PlayerInputType.VRPlayer && (XRSEEActions.Selected || (!Rotator.ShouldGetHandRotation && inProgress)))) { if (Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.Node) { diff --git a/Assets/SEE/Controls/SEEInput.cs b/Assets/SEE/Controls/SEEInput.cs index 9d59733740..d48ca989d5 100644 --- a/Assets/SEE/Controls/SEEInput.cs +++ b/Assets/SEE/Controls/SEEInput.cs @@ -125,14 +125,11 @@ public static bool DigitKeyPressed(int digit) /// true if the user requests this action and public static bool Undo() { - if (SceneSettings.InputType == PlayerInputType.VRPlayer) + if (SceneSettings.InputType == PlayerInputType.VRPlayer && XRSEEActions.UndoToggle) { - if (XRSEEActions.UndoToggle) - { - bool undo = XRSEEActions.UndoToggle; - XRSEEActions.UndoToggle = false; - return undo; - } + bool undo = XRSEEActions.UndoToggle; + XRSEEActions.UndoToggle = false; + return undo; } #if UNITY_EDITOR == false // Ctrl keys are not available when running the game in the editor @@ -155,14 +152,11 @@ public static bool Undo() /// true if the user requests this action and public static bool Redo() { - if (SceneSettings.InputType == PlayerInputType.VRPlayer) + if (SceneSettings.InputType == PlayerInputType.VRPlayer && XRSEEActions.RedoToggle) { - if (XRSEEActions.RedoToggle) - { - bool redo = XRSEEActions.RedoToggle; - XRSEEActions.RedoToggle = false; - return redo; - } + bool redo = XRSEEActions.RedoToggle; + XRSEEActions.RedoToggle = false; + return redo; } #if UNITY_EDITOR == false // Ctrl keys are not available when running the game in the editor diff --git a/Assets/SEE/Game/Avatars/AvatarAdapter.cs b/Assets/SEE/Game/Avatars/AvatarAdapter.cs index e93224115e..99f05218f4 100644 --- a/Assets/SEE/Game/Avatars/AvatarAdapter.cs +++ b/Assets/SEE/Game/Avatars/AvatarAdapter.cs @@ -56,15 +56,8 @@ private void Start() if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - if (!gameObject.TryGetComponent(out PlayerMenu _)) - { - gameObject.AddComponent(); - } - - if (!gameObject.TryGetComponent(out DrawableSurfacesRef _)) - { - gameObject.AddComponent(); - } + gameObject.AddOrGetComponent(); + gameObject.AddOrGetComponent(); } switch (SceneSettings.InputType) @@ -314,17 +307,6 @@ public IEnumerator StartXRCoroutine() GameObject rig = PrefabInstantiator.InstantiatePrefab(vrPlayerRigPrefab); rig.transform.position = gameObject.transform.position; - //indicator.transform.Find("Text (TMP)").GetComponent().SetText(firstType.Name); - // FIXME: Only the server is allowed to spawn objects. - //rig.AddComponent().Spawn(); - //rig.AddComponent(); - - //gameObject.transform.SetParent(rig.transform); - //gameObject.transform.position = Vector3.zero; - - // Note: AddComponents() must be run before TurnOffAvatarAimingSystem() because the latter - // will remove components, the former must query. - //VRIKActions.AddComponents(gameObject, IsLocalPlayer); VRIKActions.TurnOffAvatarAimingSystem(gameObject); VRIKActions.ReplaceAnimator(gameObject, animatorForVRIKPrefab); diff --git a/Assets/SEE/UI/PopupMenu/PopupMenu.cs b/Assets/SEE/UI/PopupMenu/PopupMenu.cs index 8abec0904b..f5da016dcc 100644 --- a/Assets/SEE/UI/PopupMenu/PopupMenu.cs +++ b/Assets/SEE/UI/PopupMenu/PopupMenu.cs @@ -23,6 +23,11 @@ public class PopupMenu : PlatformDependentComponent /// private const string menuPrefabPath = "Prefabs/UI/PopupMenu"; + /// + /// Path to the gameobject that should be used as the popup menu in VR. + /// + private const string xrMenuPrefabPath = "XRRig(Clone)/Camera Offset/Right Controller/Popup/XRCanvas/PopupMenu"; + /// /// The root transform of the popup menu. /// @@ -104,7 +109,7 @@ protected override void StartDesktop() // Instantiate the menu. if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - menu = (RectTransform)GameObject.Find("XRRig(Clone)/Camera Offset/Right Controller/Popup/XRCanvas/PopupMenu").transform; + menu = (RectTransform)GameObject.Find(xrMenuPrefabPath).transform; } else { diff --git a/Assets/SEE/UI/Window/DesktopWindowSpace.cs b/Assets/SEE/UI/Window/DesktopWindowSpace.cs index 723df0bd1f..d18a41b025 100644 --- a/Assets/SEE/UI/Window/DesktopWindowSpace.cs +++ b/Assets/SEE/UI/Window/DesktopWindowSpace.cs @@ -43,12 +43,13 @@ protected override void StartDesktop() protected override void StartVR() { Canvas.MustGetComponent().renderMode = RenderMode.WorldSpace; - Canvas.transform.SetParent(GameObject.Find("XRTabletCanvas(Clone)").transform.Find("Screen").transform, false); + Canvas.transform.SetParent(GameObject.Find("XRTabletCanvas(Clone)/Screen").transform, false); Canvas.AddComponent(); - Canvas.GetComponent().localScale = new Vector3(0.001f, 0.001f, 0.001f); - Canvas.GetComponent().sizeDelta = new Vector2(950, 950); - Canvas.GetComponent().localRotation = Quaternion.Euler(0, -90, 0); - Canvas.GetComponent().localPosition = new Vector3(0.9f, 0, 0); + RectTransform canvasTransform = Canvas.GetComponent(); + canvasTransform.localScale = new Vector3(0.001f, 0.001f, 0.001f); + canvasTransform.sizeDelta = new Vector2(950, 950); + canvasTransform.localRotation = Quaternion.Euler(0, -90, 0); + canvasTransform.localPosition = new Vector3(0.9f, 0, 0); StartDesktop(); } diff --git a/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs b/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs index 6d2c869e72..1a7a17f345 100644 --- a/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs +++ b/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs @@ -394,8 +394,12 @@ void RegisterClickHandler() { if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - pointerHelper.EnterEvent.AddListener(e => { XRSEEActions.OnTreeViewToggle = true; XRSEEActions.TreeViewEntry = item; }); - pointerHelper.ExitEvent.AddListener(e => XRSEEActions.OnTreeViewToggle = false); + pointerHelper.EnterEvent.AddListener(_ => + { + XRSEEActions.OnTreeViewToggle = true; + XRSEEActions.TreeViewEntry = item; + }); + pointerHelper.ExitEvent.AddListener(_ => XRSEEActions.OnTreeViewToggle = false); pointerHelper.ThumbstickEvent.AddListener(e => { if (XRSEEActions.TooltipToggle) @@ -408,21 +412,7 @@ void RegisterClickHandler() // We want all applicable actions for the element, except ones where the element // is shown in the TreeWindow, since we are already in the TreeWindow. - List appends = new() - { - new("Hide in TreeWindow", () => - { - searcher.Filter.ExcludeElements.Add(representedGraphElement); - Rebuild(); - }, Icons.Hide) - }; - IEnumerable actions = ContextMenuAction - .GetOptionsForTreeView(contextMenu.ContextMenu, - e.position, - representedGraphElement, - representedGameObject, - appends); - actions = actions.Concat(appends); + IEnumerable actions = CreateContextMenuActions(contextMenu, e.position, representedGraphElement, representedGameObject); XRSEEActions.TooltipToggle = false; XRSEEActions.OnSelectToggle = true; XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit ray); @@ -444,21 +434,7 @@ void RegisterClickHandler() // We want all applicable actions for the element, except ones where the element // is shown in the TreeWindow, since we are already in the TreeWindow. - List appends = new() - { - new("Hide in TreeWindow", () => - { - searcher.Filter.ExcludeElements.Add(representedGraphElement); - Rebuild(); - }, Icons.Hide) - }; - IEnumerable actions = ContextMenuAction - .GetOptionsForTreeView(contextMenu.ContextMenu, - e.position, - representedGraphElement, - representedGameObject, - appends); - actions = actions.Concat(appends); + IEnumerable actions = CreateContextMenuActions(contextMenu, e.position, representedGraphElement, representedGameObject); contextMenu.ShowWith(actions, e.position); } else @@ -474,6 +450,23 @@ void RegisterClickHandler() } }); } + + IEnumerable CreateContextMenuActions (TreeWindowContextMenu contextMenu, Vector2 position, GraphElement representedGraphElement, GameObject representedGameObject) + { + List appends = new() + { + new("Hide in TreeWindow", () => + { + searcher.Filter.ExcludeElements.Add(representedGraphElement); + Rebuild(); + }, Icons.Hide) + }; + + IEnumerable actions = ContextMenuAction + .GetOptionsForTreeView(contextMenu.ContextMenu, position, representedGraphElement, representedGameObject, appends); + + return actions.Concat(appends); + } } } diff --git a/Assets/SEE/Utils/PointerHelper.cs b/Assets/SEE/Utils/PointerHelper.cs index 70401f4cf5..f57af47926 100644 --- a/Assets/SEE/Utils/PointerHelper.cs +++ b/Assets/SEE/Utils/PointerHelper.cs @@ -31,6 +31,10 @@ public class PointerHelper: MonoBehaviour, IPointerExitHandler, IPointerEnterHan /// public readonly UnityEvent ClickEvent = new(); + /// + /// This event will be triggered whenever the user clicks the GameObject + /// this component is attached to with the thumbstickbutton in VR. + /// public readonly UnityEvent ThumbstickEvent = new(); /// diff --git a/Assets/SEE/Utils/Raycasting.cs b/Assets/SEE/Utils/Raycasting.cs index 7f3781db60..7c0e7f5553 100644 --- a/Assets/SEE/Utils/Raycasting.cs +++ b/Assets/SEE/Utils/Raycasting.cs @@ -200,6 +200,18 @@ public static HitGraphElement RaycastInteractableObject(out RaycastHit raycastHi { HitGraphElement result = HitGraphElement.None; + static HitGraphElement DetermineHit(InteractableObject element) + { + HitGraphElement result = element.GraphElemRef.Elem switch + { + null => HitGraphElement.None, + Node => HitGraphElement.Node, + Edge => HitGraphElement.Edge, + _ => throw new System.ArgumentOutOfRangeException() + }; + return result; + } + raycastHit = new RaycastHit(); obj = null; if (SceneSettings.InputType == PlayerInputType.VRPlayer) @@ -209,13 +221,7 @@ public static HitGraphElement RaycastInteractableObject(out RaycastHit raycastHi raycastHit = ray; if (ray.transform.TryGetComponent(out InteractableObject io)) { - result = io.GraphElemRef.Elem switch - { - null => HitGraphElement.None, - Node => HitGraphElement.Node, - Edge => HitGraphElement.Edge, - _ => throw new System.ArgumentOutOfRangeException() - }; + DetermineHit(io); obj = io; } } @@ -227,13 +233,7 @@ public static HitGraphElement RaycastInteractableObject(out RaycastHit raycastHi raycastHit = hit; if (hit.transform.TryGetComponent(out InteractableObject io)) { - result = io.GraphElemRef.Elem switch - { - null => HitGraphElement.None, - Node => HitGraphElement.Node, - Edge => HitGraphElement.Edge, - _ => throw new System.ArgumentOutOfRangeException() - }; + DetermineHit(io); obj = io; } } @@ -323,19 +323,20 @@ public static Ray UserPointsTo() { // FIXME: We need to an interaction for VR, too. Camera mainCamera = MainCamera.Camera; + Vector3 screenPoint; if (SceneSettings.InputType == PlayerInputType.VRPlayer) { XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit); - return mainCamera != null - ? mainCamera.ScreenPointToRay(mainCamera.WorldToScreenPoint(hit.point)) - : new Ray(origin: Vector3.zero, direction: Vector3.zero); + screenPoint = mainCamera.WorldToScreenPoint(hit.point); } else { - return mainCamera != null - ? mainCamera.ScreenPointToRay(Input.mousePosition) - : new Ray(origin: Vector3.zero, direction: Vector3.zero); + screenPoint = Input.mousePosition; } + + return mainCamera != null + ? mainCamera.ScreenPointToRay(screenPoint) + : new Ray(origin: Vector3.zero, direction: Vector3.zero); } } } diff --git a/Assets/SEE/XR/KeyboardInputHandler.cs b/Assets/SEE/XR/KeyboardInputHandler.cs index bcd4371a7e..b44b526a8d 100644 --- a/Assets/SEE/XR/KeyboardInputHandler.cs +++ b/Assets/SEE/XR/KeyboardInputHandler.cs @@ -12,16 +12,27 @@ public class KeyboardInputHandler : MonoBehaviour, IPointerClickHandler { /// - /// This method gets called, when the user clicks the input field. + /// The reference to the keyboard GameObject. + /// + private GameObject keyboardGameObject; + + private void Start() + { + // Cache the reference to the keyboard GameObject + keyboardGameObject = KeyboardManager.instance.gameObject.transform.Find("Keyboard").gameObject; + } + + /// + /// This method gets called when the user clicks the input field. /// If the user is in VR, the keyboard gets activated. /// - /// Event data associated with the event, when the user clicks on the inputfield. + /// Event data associated with the event when the user clicks on the inputfield. public void OnPointerClick(PointerEventData eventdata) { if (SceneSettings.InputType == PlayerInputType.VRPlayer) { KeyboardManager.instance.inputField = GetComponent(); - KeyboardManager.instance.gameObject.transform.Find("Keyboard").gameObject.SetActive(true); + keyboardGameObject.SetActive(true); } } } diff --git a/Assets/SEE/XR/KeyboardKey.cs b/Assets/SEE/XR/KeyboardKey.cs index 903e95de18..89b242eb88 100644 --- a/Assets/SEE/XR/KeyboardKey.cs +++ b/Assets/SEE/XR/KeyboardKey.cs @@ -1,6 +1,7 @@ using TMPro; using UnityEngine; using UnityEngine.UI; + /// /// This class handles a single keyboard key. /// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g @@ -11,23 +12,27 @@ public class KeyboardKey : MonoBehaviour /// The character of the key. /// public string character; + /// /// The shifted character. /// public string shiftCharacter; + /// - /// Is true, when the key is shifted. + /// Is true when the key is shifted. /// private bool isShifted = false; + /// /// The actual keyboard key. /// private Button key; + /// /// The label of the keyboard key. /// public TextMeshProUGUI keyLabel; - // Start is called before the first frame update + private void Start() { KeyboardManager.instance.shiftButton.onClick.AddListener(HandleShift); @@ -43,6 +48,7 @@ private void Start() shiftCharacter = GetShiftCharacter(); } } + /// /// This method returns the shifted equivalent to /// each number on the keyboard. @@ -50,33 +56,22 @@ private void Start() /// The shifted equivalent to each number. private string GetShiftCharacter() { - switch (keyLabel.text) + return keyLabel.text switch { - case "1": - return "!"; - case "2": - return "."; - case "3": - return "#"; - case "4": - return "?"; - case "5": - return "%"; - case "6": - return "&"; - case "7": - return "+"; - case "8": - return "("; - case "9": - return ")"; - case "0": - return "="; - default: - break; - } - return string.Empty; + "1" => "!", + "2" => ".", + "3" => "#", + "4" => "?", + "5" => "%", + "6" => "&", + "7" => "+", + "8" => "(", + "9" => ")", + "0" => "=", + _ => string.Empty + }; } + /// /// This method handles the shifting of the /// alphabetical characters. @@ -94,8 +89,9 @@ private void HandleShift() keyLabel.text = character; } } + /// - /// This method transfer the character to the inputfield. + /// This method transfers the character to the inputfield. /// private void TypeKey() { diff --git a/Assets/SEE/XR/KeyboardManager.cs b/Assets/SEE/XR/KeyboardManager.cs index c9c81659f2..62671b85fd 100644 --- a/Assets/SEE/XR/KeyboardManager.cs +++ b/Assets/SEE/XR/KeyboardManager.cs @@ -12,36 +12,42 @@ public class KeyboardManager : MonoBehaviour /// The instance of the keyboardmanager. /// public static KeyboardManager instance; + /// - /// The shiftbutton, which performs the shift-action. + /// The shiftbutton, which shifts a character. /// public Button shiftButton; + /// - /// The deletebutton, which performs the delete-action. + /// The deletebutton, which deletes a character. /// public Button deleteButton; + /// - /// The spacebuttom, which performs the space-action. + /// The spacebuttom, which adds a space. /// public Button spaceButton; + /// - /// The enterbutton, which performs the enter-action. + /// The enterbutton, which finalizes the input. /// public Button enterButton; + /// /// The inputfield, which is accessed by the keyboard. /// public TMP_InputField inputField; + /// - /// This image is used, to tell the user, that the shiftbutton is active. + /// This image is used to tell the user that the shiftbutton is active. /// private Image shiftButtonImage; /// - /// Is true, when the shiftbutton is active. + /// Is true when the shiftbutton is active. /// private bool isShifted = false; - // Awake is always called before any Start functions. + private void Awake() { if (instance == null) @@ -54,13 +60,15 @@ private void Awake() enterButton.onClick.AddListener(Enter); shiftButtonImage = shiftButton.gameObject.GetComponent(); } + /// - /// This method performs the space-action on the keyboard. + /// This method adds a whitespace character. /// private void Space() { inputField.text += " "; } + /// /// This method performs the enter-action on the keyboard. /// @@ -69,6 +77,7 @@ private void Enter() inputField.onSubmit.Invoke(inputField.text); gameObject.transform.Find("Keyboard").gameObject.SetActive(false); } + /// /// This method performs the delete-action on the keyboard. /// @@ -81,6 +90,7 @@ private void Delete() inputField.text = inputField.text.Substring(0, length); } } + /// /// This method performs the shift-action on the keyboard. /// diff --git a/Assets/SEE/XR/Rotator.cs b/Assets/SEE/XR/Rotator.cs index b29bd413d7..8b91c22920 100644 --- a/Assets/SEE/XR/Rotator.cs +++ b/Assets/SEE/XR/Rotator.cs @@ -4,6 +4,7 @@ using UnityEngine.XR.Interaction.Toolkit; using UnityEngine.XR.Interaction.Toolkit.Interactables; using UnityEngine.XR.Interaction.Toolkit.Interactors; + /// /// This class is used to rotate nodes in VR. /// This script is based on this tutorial: "https://www.youtube.com/watch?v=vIrgCMNsE3s". @@ -14,37 +15,49 @@ public class Rotator : MonoBehaviour /// The actual dial, which gets rotated. /// [SerializeField] - Transform linkedDial; + private Transform linkedDial; + /// /// The amount of degrees the dial gets roated each time. /// [SerializeField] private int snapRotationAmount = 25; + /// /// The amount of degrees at which the dial is starting to rotate. /// [SerializeField] private float angleTolerance; + /// /// The controller, which is used to rotated the dial/node. /// private XRBaseInteractor interactor; + /// /// The base angle. /// private float startAngle; + /// - /// This bool is used, to determin if it is the first rotation, or not. + /// Whether this is the first rotation. /// - private bool requiresStartAngle = true; + private bool firstRotation = true; + /// - /// Is true, when the dial/node should be roated according to the hand-rotation. + /// Whether the dial/node should be rotated according to the hand-rotation. /// - public static bool shouldGetHandRotation { get; private set; } = false; + public static bool ShouldGetHandRotation { get; private set; } = false; + /// /// The grab-interactor of the dial. /// - private XRGrabInteractable grabInteractor => GetComponent(); + private XRGrabInteractable grabInteractor; + + private void Awake() + { + grabInteractor = GetComponent(); + } private void OnEnable() { @@ -57,54 +70,56 @@ private void OnDisable() grabInteractor.selectEntered.RemoveListener(GrabbedBy); grabInteractor.selectExited.RemoveListener(GrabEnd); } + /// - /// This method gets called, when the user stops to use the dial. + /// This method gets called when the user stops using the dial. /// /// Event data associated with the event when an Interactor stops selecting an Interactable. private void GrabEnd(SelectExitEventArgs args) { - shouldGetHandRotation = false; - requiresStartAngle = true; + ShouldGetHandRotation = false; + firstRotation = true; } + /// - /// This method gets called, when the user begins to use the dial. + /// This method gets called when the user begins using the dial. /// /// Event data associated with the event when an Interactor first initiates selecting an Interactable. private void GrabbedBy(SelectEnterEventArgs args) { interactor = (XRBaseInteractor)GetComponent().interactorsSelecting[0]; - shouldGetHandRotation = true; + ShouldGetHandRotation = true; startAngle = 0f; - } - // Update is called once per frame - void Update() + private void Update() { if (GlobalActionHistory.Current() != ActionStateTypes.Rotate) { Destroyer.Destroy(gameObject); } - if (shouldGetHandRotation) + if (ShouldGetHandRotation) { - var rotationAngle = GetInteractorRotation(); + float rotationAngle = GetInteractorRotation(); GetRotationDistance(rotationAngle); } } + /// - /// Gets the current roation-angle from the controller. + /// Returns the current rotation-angle from the controller. /// /// The current rotation-angle from the controller. - public float GetInteractorRotation() => interactor.GetComponent().eulerAngles.z; + public float GetInteractorRotation() => interactor.transform.eulerAngles.z; + /// - /// Determins in which direction and how much the dial/node should be rotated. + /// Determines in which direction and how much the dial/node should be rotated. /// /// The current angle from the controller. private void GetRotationDistance(float currentAngle) { - if (!requiresStartAngle) + if (!firstRotation) { - var angleDifference = Mathf.Abs(startAngle - currentAngle); + float angleDifference = Mathf.Abs(startAngle - currentAngle); if (angleDifference > angleTolerance) { if (angleDifference > 270f) @@ -142,32 +157,32 @@ private void GetRotationDistance(float currentAngle) if (startAngle < currentAngle) { RotateDialAntiClockwise(); - startAngle = currentAngle; } else if (startAngle > currentAngle) { RotateDialClockwise(); - startAngle = currentAngle; } + startAngle = currentAngle; } } } else { - requiresStartAngle = false; + firstRotation = false; startAngle = currentAngle; } } + /// - /// This method is used, to calculate, if the rotation angle is significant enough, to rotate the dial/node. + /// Calculates the current rotation angle of the controller. /// /// The current angle of the controller. /// The base angle. - /// + /// the amount of rotation private float CheckAngle(float currentAngle, float startAngle) => (360f - currentAngle) + startAngle; /// - /// This method is used, to rotate the dial/node clockwise. + /// Rotates the dial/node clockwise. /// private void RotateDialClockwise() { @@ -175,8 +190,9 @@ private void RotateDialClockwise() Transform rotateObject = XRSEEActions.RotateObject.transform; rotateObject.localEulerAngles = new Vector3(rotateObject.localEulerAngles.x, linkedDial.localEulerAngles.y, rotateObject.localEulerAngles.z); } + /// - /// This method is used, to rotate the dial/node anticlockwise. + /// Rotates the dial/node anticlockwise. /// private void RotateDialAntiClockwise() { From 924b7b0cab4ee42c0708bd0bd921383eb5fd709a Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Tue, 15 Oct 2024 21:39:46 +0200 Subject: [PATCH 28/35] Fixed RadialSelection and XRSEEAction according to the suggestions of @falko17. --- Assets/SEE/XR/RadialSelection.cs | 206 +++++++++++++++++++------------ Assets/SEE/XR/XRSEEActions.cs | 101 ++++++++++----- 2 files changed, 194 insertions(+), 113 deletions(-) diff --git a/Assets/SEE/XR/RadialSelection.cs b/Assets/SEE/XR/RadialSelection.cs index 2ed6cad07d..5863fec673 100644 --- a/Assets/SEE/XR/RadialSelection.cs +++ b/Assets/SEE/XR/RadialSelection.cs @@ -11,8 +11,7 @@ using SEE.Utils; /// -/// This class is used, to open a -/// radial menu in VR. +/// This component is used to open a radial menu in VR. /// This script is based on this tutorial: https://www.youtube.com/watch?v=n-xPN1v3dvA /// public class RadialSelection : MonoBehaviour @@ -20,74 +19,94 @@ public class RadialSelection : MonoBehaviour /// /// The number of radial-parts. /// - int numberOfRadialPart = 0; + private int numberOfRadialParts = 0; + /// /// The prefab for the single radial-parts. /// public GameObject radialPartPrefab; + /// /// The canvas on which the radial should be shown. /// public Transform radialPartCanvas; + /// /// The angle between the radial-parts. /// public float angleBetweenPart = 10; + /// /// The radial-parts which got spawned. /// - private List spawnedParts = new List(); + private List spawnedParts = new(); + /// /// The transform of the controller. /// public Transform handTransform; + /// /// The currently selected radial-part. /// private int currentSelectedRadialPart = -1; + /// /// The selected radial-part. /// public UnityEvent OnPartSelected; + /// - /// The button, which triggers the radial-menu. + /// The button that triggers the radial-menu. /// - public InputActionReference radialMenu; + public InputActionReference RadialMenuActionRef; + /// /// All actions, which are currently available. /// List actions = new(); + /// /// Alle submenus and their entries. /// - List<(string, List)> subMenus = new List<(string, List)>(); + List<(string, List)> subMenus = new(); + /// /// All entry of a submenu. + /// This list is used, to swap between the main-menu and sub-menus. + /// The main menu contains all "main-actions" and the sub-menus more + /// specific sub-actions. We safe the main-actions here for the case, + /// that the user wants to go back to the main-menu. /// - List menuEntrys = new(); + List menuEntries = new(); + /// /// This is used for the rotate-action, because in this case we /// have a dial, which is getting spawned, and should be despawned, when the action changes. /// GameObject actionObject; + /// /// The position of the submenu. /// It should be the same, as the position from the mainmenu. /// Vector3? subMenuPosition; + /// /// The rotation of the submenu. /// It should be the same, as the rotation from the mainmenu. /// Quaternion? subMenuRotation; + /// + /// The selected HideMode. /// The hide-action is a special action, because it has sub-actions, which we need to access. /// public static HideModeSelector HideMode { get; set; } - // Awake is always called before any Start functions. + private void Awake() { - radialMenu.action.performed += RadialMenu; + RadialMenuActionRef.action.performed += RadialMenu; ActionStateTypes.AllRootTypes.PreorderTraverse(Visit); bool Visit(AbstractActionStateType child, AbstractActionStateType parent) { @@ -107,8 +126,8 @@ bool Visit(AbstractActionStateType child, AbstractActionStateType parent) } // If child is not a root (i.e., has a parent), we will add the entry to - // the InnerEntries of the NestedMenuEntry corresponding to the parent. - // We know that such a NestedMenuEntry must exist, because we are doing a + // the InnerEntries of the menu corresponding to the parent. + // We know that such a menu must exist, because we are doing a // preorder traversal. if (parent != null) { @@ -137,16 +156,18 @@ bool Visit(AbstractActionStateType child, AbstractActionStateType parent) { subMenus.Add((name, null)); actions.Add(name); - numberOfRadialPart += 1; + numberOfRadialParts += 1; } // Continue with the traversal. return true; } } + /// - /// Is true, when the radial menu is open. + /// Whether the radial menu is open. /// bool radialMenuTrigger; + /// /// This method gets called, when the button for the radial-menu is pressed. /// @@ -156,8 +177,7 @@ private void RadialMenu(InputAction.CallbackContext context) radialMenuTrigger = true; } - // Update is called once per frame - void Update() + private void Update() { if (radialMenuTrigger && !spawned) { @@ -166,7 +186,7 @@ void Update() } if (spawned) { - GetSelectedRadialPart(); + UpdateSelectedRadialPart(); } if (radialMenuTrigger && spawned) { @@ -175,11 +195,13 @@ void Update() HideAndTriggerSelected(); } } + /// - /// Is true, when the current action changed. - /// It is being used, to trigger the update for the GlobalActionHistory. + /// Whether the current action changed. + /// It is used to trigger the update for the GlobalActionHistory. /// public static bool IndicatorChange { set; get; } + /// /// This method activates the selected action and deactivates the radial menu. /// @@ -189,10 +211,13 @@ public void HideAndTriggerSelected() OnPartSelected.Invoke(currentSelectedRadialPart); IndicatorChange = true; } + /// - /// This method calculates, at which radial-part the user is aiming at. + /// This method calculates at which radial-part the user is aiming at. + /// The selected radial part is saved in currentSelectedRadialPart, so that + /// if the user decides to activate this part, the matching action gets triggered. /// - public void GetSelectedRadialPart() + public void UpdateSelectedRadialPart() { Vector3 centerToHand = handTransform.position - radialPartCanvas.position; Vector3 centerToHandProjected = Vector3.ProjectOnPlane(centerToHand, radialPartCanvas.forward); @@ -204,7 +229,7 @@ public void GetSelectedRadialPart() angle += 360; } - currentSelectedRadialPart = (int)angle * numberOfRadialPart / 360; + currentSelectedRadialPart = (int)angle * numberOfRadialParts / 360; for (int i = 0; i < spawnedParts.Count; i++) { @@ -212,90 +237,112 @@ public void GetSelectedRadialPart() { spawnedParts[i].GetComponent().color = Color.yellow; spawnedParts[i].transform.localScale = 1.1f * Vector3.one; - spawnedParts[i].gameObject.transform.Find("TextField").gameObject.MustGetComponent().color = Color.yellow; - spawnedParts[i].gameObject.transform.Find("TextField").gameObject.MustGetComponent().fontStyle = (FontStyles)FontStyle.Bold; + TextMeshProUGUI tmpGUI = spawnedParts[i].gameObject.transform.Find("TextField").gameObject.MustGetComponent(); + tmpGUI.color = Color.yellow; + tmpGUI.fontStyle = (FontStyles)FontStyle.Bold; } else { spawnedParts[i].GetComponent().color = Color.white; spawnedParts[i].transform.localScale = Vector3.one; - spawnedParts[i].gameObject.transform.Find("TextField").gameObject.MustGetComponent().color = Color.black; - spawnedParts[i].gameObject.transform.Find("TextField").gameObject.MustGetComponent().fontStyle = (FontStyles)FontStyle.Normal; + TextMeshProUGUI tmpGUI = spawnedParts[i].gameObject.transform.Find("TextField").gameObject.MustGetComponent(); + tmpGUI.color = Color.black; + tmpGUI.fontStyle = (FontStyles)FontStyle.Normal; } } } + /// - /// Is true if the radial menu is open or not. + /// Is true if the radial menu is open. /// bool spawned; + + /// + /// This list represents all hideActions. + /// + static List hideActions = new() + { + "HideAll", + "HideSelected", + "HideUnselected", + "HideOutgoing", + "HideIncoming", + "HideAllEdgesOfSelected", + "HideForwardTransitiveClosure", + "HideBackwardTransitiveClosure", + "HideAllTransitiveClosure", + "HighlightEdges", + "Back" + }; + + /// /// This method activates the selected action, or opens a submenu/mainmenu. /// - /// + /// the current selected radialPart. public void SelectAction(int i) { - if (menuEntrys.Count != 0) + if (menuEntries.Count != 0) { if (actions[i] == "Back") { actions.Clear(); - actions.AddRange(menuEntrys); - numberOfRadialPart = actions.Count(); + actions.AddRange(menuEntries); + numberOfRadialParts = actions.Count(); radialMenuTrigger = true; - menuEntrys.Clear(); + menuEntries.Clear(); subMenuPosition = radialPartCanvas.position; subMenuRotation = radialPartCanvas.rotation; } - if (actions[i] == "HideAll") + else if (actions[i] == "HideAll") { HideMode = HideModeSelector.HideAll; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); - return; + TriggerHideAction(); } - if (actions[i] == "HideSelected") + else if (actions[i] == "HideSelected") { HideMode = HideModeSelector.HideSelected; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + TriggerHideAction(); } - if (actions[i] == "HideUnselected") + else if (actions[i] == "HideUnselected") { HideMode = HideModeSelector.HideUnselected; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + TriggerHideAction(); } - if (actions[i] == "HideOutgoing") + else if (actions[i] == "HideOutgoing") { HideMode = HideModeSelector.HideOutgoing; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + TriggerHideAction(); } - if (actions[i] == "HideIncoming") + else if (actions[i] == "HideIncoming") { HideMode = HideModeSelector.HideIncoming; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + TriggerHideAction(); } - if (actions[i] == "HideAllEdgesOfSelected") + else if (actions[i] == "HideAllEdgesOfSelected") { HideMode = HideModeSelector.HideAllEdgesOfSelected; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + TriggerHideAction(); } - if (actions[i] == "HideForwardTransitiveClosure") + else if (actions[i] == "HideForwardTransitiveClosure") { HideMode = HideModeSelector.HideForwardTransitiveClosure; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + TriggerHideAction(); } - if (actions[i] == "HideBackwardTransitiveClosure") + else if (actions[i] == "HideBackwardTransitiveClosure") { HideMode = HideModeSelector.HideBackwardTransitiveClosure; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + TriggerHideAction(); } - if (actions[i] == "HideAllTransitiveClosure") + else if (actions[i] == "HideAllTransitiveClosure") { HideMode = HideModeSelector.HideAllTransitiveClosure; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + TriggerHideAction(); } - if (actions[i] == "HighlightEdges") + else if (actions[i] == "HighlightEdges") { HideMode = HideModeSelector.HighlightEdges; - GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + TriggerHideAction(); } else { @@ -308,20 +355,10 @@ public void SelectAction(int i) { if (subMenus[i].Item1 == "Hide") { - numberOfRadialPart = 11; - menuEntrys.AddRange(actions); + numberOfRadialParts = hideActions.Count(); + menuEntries.AddRange(actions); actions.Clear(); - actions.Add("HideAll"); - actions.Add("HideSelected"); - actions.Add("HideUnselected"); - actions.Add("HideOutgoing"); - actions.Add("HideIncoming"); - actions.Add("HideAllEdgesOfSelected"); - actions.Add("HideForwardTransitiveClosure"); - actions.Add("HideBackwardTransitiveClosure"); - actions.Add("HideAllTransitiveClosure"); - actions.Add("HighlightEdges"); - actions.Add("Back"); + actions.AddRange(hideActions); radialMenuTrigger = true; subMenuPosition = radialPartCanvas.position; subMenuRotation = radialPartCanvas.rotation; @@ -336,19 +373,16 @@ public void SelectAction(int i) actionObject.transform.position = handTransform.position; actionObject.SetActive(true); } - else + else if (actionObject != null) { - if (actionObject != null) - { - actionObject.SetActive(false); - } + actionObject.SetActive(false); } GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == subMenus[i].Item1)); } else { - numberOfRadialPart = subMenus[i].Item2.Count() + 1; - menuEntrys.AddRange(actions); + numberOfRadialParts = subMenus[i].Item2.Count() + 1; + menuEntries.AddRange(actions); actions.Clear(); actions.AddRange(subMenus[i].Item2); actions.Add("Back"); @@ -358,6 +392,15 @@ public void SelectAction(int i) } } } + + /// + /// This triggers the HideAction. + /// + private void TriggerHideAction() + { + GlobalActionHistory.Execute((ActionStateType)ActionStateTypes.AllRootTypes.AllElements().FirstOrDefault(a => a.Name == "Hide")); + } + /// /// This method spawns all radial-parts for the current menu. /// @@ -385,23 +428,24 @@ public void SpawnRadialPart() spawnedParts.Clear(); - for (int i = 0; i < numberOfRadialPart; i++) + for (int i = 0; i < numberOfRadialParts; i++) { - float angle = -i * 360 / numberOfRadialPart - angleBetweenPart / 2; + float angle = -i * 360 / numberOfRadialParts - angleBetweenPart / 2; Vector3 radialPartEulerAngle = new Vector3(0, 0, angle); GameObject spawnRadialPart = Instantiate(radialPartPrefab, radialPartCanvas); spawnRadialPart.transform.position = radialPartCanvas.position; spawnRadialPart.transform.localEulerAngles = radialPartEulerAngle; - spawnRadialPart.GetComponent().fillAmount = (1 / (float)numberOfRadialPart) - (angleBetweenPart / 360); - if (i > (numberOfRadialPart / 2)) + spawnRadialPart.GetComponent().fillAmount = (1 / (float)numberOfRadialParts) - (angleBetweenPart / 360); + TextMeshProUGUI tmpUGUI = spawnRadialPart.transform.Find("TextField").gameObject.MustGetComponent(); + if (i > (numberOfRadialParts / 2)) { - spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().rotation = - spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().rotation * Quaternion.Euler(0, 0, 180); - spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().alignment = TextAlignmentOptions.Right; + RectTransform rectTransform = spawnRadialPart.transform.Find("TextField").gameObject.MustGetComponent(); + rectTransform.rotation *= Quaternion.Euler(0, 0, 180); + tmpUGUI.alignment = TextAlignmentOptions.Right; } - spawnRadialPart.gameObject.transform.Find("TextField").gameObject.MustGetComponent().text = actions[i]; + tmpUGUI.text = actions[i]; spawnedParts.Add(spawnRadialPart); } spawned = true; diff --git a/Assets/SEE/XR/XRSEEActions.cs b/Assets/SEE/XR/XRSEEActions.cs index 532b274c45..5accf42b64 100644 --- a/Assets/SEE/XR/XRSEEActions.cs +++ b/Assets/SEE/XR/XRSEEActions.cs @@ -8,45 +8,53 @@ using UnityEngine.XR.Interaction.Toolkit; using UnityEngine.XR.Interaction.Toolkit.Interactors; +/// +/// This is a central control class for VR. +/// It is used to provide the input from the controllers +/// for all actions in SEE. +/// public class XRSEEActions : MonoBehaviour { /// - /// The RayInteractor, which will be accessed. - /// - [SerializeField] - XRRayInteractor m_RayInteractor; - /// - /// The RayInteractor, which we use, to get the current position of the Laserpointer. + /// The RayInteractor that we use to get the current position of the laserpointer. /// public static XRRayInteractor RayInteractor { get; set; } + /// - /// Is true, when the user is hovering over an interactable. + /// Whether the user is hovering over an interactable. /// private bool hovering = false; + /// /// The GameObject the user is currently hovering over. /// public static GameObject hoveredGameObject { get; private set; } + /// /// The old parent of a node. We need this for the MoveAction. /// public static Transform oldParent { get; set; } + /// - /// The button, which is used for the primary actions. + /// The button that's used for the primary actions. /// public InputActionReference inputAction; + /// - /// The button, which is used for the undo-action. + /// The button that's used for the undo-action. /// public InputActionReference undo; + /// - /// The button, which is used for the redo-action. + /// The button that's used for the redo-action. /// public InputActionReference redo; + /// - /// The button, which is used, to open the tooltip. + /// The button that's used to open the tooltip. /// public InputActionReference tooltip; + /// /// Shows the source name of the hovered or selected object as a text label above the /// object. In between that label and the game object, a connecting bar @@ -54,7 +62,6 @@ public class XRSEEActions : MonoBehaviour /// private ShowLabel showLabel; - // Awake is always called before any Start functions. private void Awake() { tooltip.action.performed += Tooltip; @@ -62,11 +69,13 @@ private void Awake() redo.action.performed += Redo; inputAction.action.Enable(); inputAction.action.performed += Action; - RayInteractor = m_RayInteractor; + RayInteractor = GameObject.Find("XRRig(Clone)/Camera Offset/Right Controller/XRRay").MustGetComponent(); Selected = false; } + /// /// This method gets called, when the user begins to hover over an interactable. + /// It provides the input data from the controller with regard to the start of a hover over an object in the CodeCity. /// /// Event data associated with the event when an Interactor first initiates hovering over an Interactable. public void OnHoverEnter(HoverEnterEventArgs args) @@ -77,15 +86,19 @@ public void OnHoverEnter(HoverEnterEventArgs args) { oldParent = args.interactableObject.transform.parent; } - hoveredGameObject.transform.TryGetComponent(out showLabel); - showLabel?.On(); + if (hoveredGameObject.transform.TryGetComponent(out showLabel)) + { + showLabel.On(); + } if (hoveredGameObject.transform.TryGetComponent(out InteractableObject io)) { io.SetHoverFlag(HoverFlag.World, true, true); } } + /// /// This method gets called, when the user stops hovering over an interactable. + /// It provides the input data from the controller with regard to the end of a hover over an object in the CodeCity. /// /// Event data associated with the event when an Interactor stops hovering over an Interactable. public void OnHoverExited(HoverExitEventArgs args) @@ -93,31 +106,41 @@ public void OnHoverExited(HoverExitEventArgs args) hovering = false; hoveredGameObject = args.interactableObject.transform.gameObject; hoveredGameObject.transform.TryGetComponent(out showLabel); - showLabel?.Off(); + if (hoveredGameObject.transform.TryGetComponent(out showLabel)) + { + showLabel.Off(); + } if (hoveredGameObject.transform.TryGetComponent(out InteractableObject io)) { io.SetHoverFlag(HoverFlag.World, false, true); } } + /// - /// Is true, when the button for the primary actions is pressed. + /// Whether the button for the primary actions is pressed. /// public static bool Selected { get; set; } + /// + /// Whether an object in the code city should marked as selected. + /// public static bool SelectedFlag { get; set; } + /// /// The GameObject, which should be rotated. /// public static GameObject RotateObject { get; set; } + /// /// This method gets called, when the button for the primary actions is pressed. + /// After the button got pressed, the desired action will be performed if all conditions are matching. /// /// Information provided to action callbacks about what triggered an action. private void Action(InputAction.CallbackContext context) { if (hovering) { - if (GlobalActionHistory.Current() == ActionStateTypes.Move && Selected == true) + if (GlobalActionHistory.Current() == ActionStateTypes.Move && Selected) { Selected = false; SelectedFlag = false; @@ -128,7 +151,6 @@ private void Action(InputAction.CallbackContext context) { RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit); RotateObject = hit.collider.transform.gameObject; - Selected = true; } Selected = true; if (GlobalActionHistory.Current() != ActionStateTypes.Move) @@ -142,30 +164,41 @@ private void Action(InputAction.CallbackContext context) InteractableObject.ReplaceSelection(null, true); } } + /// - /// Will be true, when the TreeView is open, while the user tries to move a node. + /// Whether the TreeView is open while the user tries to move a node. + /// We need to know if the user has the TreeView open, while moving a node, + /// because this can caus lags, which we prevent by closing the TreeView, befor + /// the node gets moved. /// public static bool CloseTreeView { get; set; } + /// - /// Is true, when the Tooltip is getting activated. + /// Whether the Tooltip is getting activated. /// public static bool TooltipToggle { get; set; } + /// - /// Is true, while the Tooltip is open. - /// This bool is used, to close the Tooltip, if the user does not want - /// to use an action. + /// Whether the Tooltip is open. + /// This is used to close the Tooltip if the user does not want to use an action. /// public static bool OnSelectToggle { get; set; } + /// - /// Is true, when the user opens the tooltip in the treeview. + /// Whether the user opened the tooltip in the treeview. /// public static bool OnTreeViewToggle { get; set; } + /// - /// The treview-entry for which the tooltip should be shown. + /// The treeview entry for which the tooltip should be shown. /// public static GameObject TreeViewEntry { get; set; } + /// /// This method gets called, when the button for the tooltip is pressed. + /// When the user points at an object in the CodeCity, or an entry in the TreeView and + /// presses the corresponding button, it opens up and after another + /// button-press or the selection of an entry it gets closed. /// /// Information provided to action callbacks about what triggered an action. private void Tooltip(InputAction.CallbackContext context) @@ -173,8 +206,7 @@ private void Tooltip(InputAction.CallbackContext context) if (OnTreeViewToggle) { TooltipToggle = true; - TreeViewEntry.TryGetComponentOrLog(out PointerHelper pointerHelper); - pointerHelper.ThumbstickEvent.Invoke(new PointerEventData(EventSystem.current)); + TreeViewEntry.MustGetComponent().ThumbstickEvent.Invoke(new PointerEventData(EventSystem.current)); } else if (hovering && !OnTreeViewToggle) { @@ -185,24 +217,30 @@ private void Tooltip(InputAction.CallbackContext context) TooltipToggle = true; } } + /// - /// Is true, when the button for the undo-action is pressed. + /// Whether the button for the undo-action is pressed. /// public static bool UndoToggle { get; set; } + /// /// This method gets called, when the button for the undo-action is pressed. + /// It will undo the last action. /// /// Information provided to action callbacks about what triggered an action. private void Undo(InputAction.CallbackContext context) { UndoToggle = true; } + /// - /// Is true, when the button for the redo-action is pressed. + /// Whether the button for the redo-action is pressed. /// public static bool RedoToggle { get; set; } + /// /// This method gets called, when the button for the redo-action is pressed. + /// It will redo the last action that was undone. /// /// Information provided to action callbacks about what triggered an action. private void Redo(InputAction.CallbackContext context) @@ -210,8 +248,7 @@ private void Redo(InputAction.CallbackContext context) RedoToggle = true; } - // Update is called once per frame - void Update() + private void Update() { GlobalActionHistory.Update(); } From 837bb4b4f1d228669cec3f64ee22c9920ea0b704 Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Wed, 16 Oct 2024 00:49:03 +0200 Subject: [PATCH 29/35] Fixed small bug regarding the SelectAction from 05986a169e73652074d85f8fc4302f2228d29731 --- Assets/SEE/Utils/Raycasting.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/SEE/Utils/Raycasting.cs b/Assets/SEE/Utils/Raycasting.cs index 7c0e7f5553..8f57f8a24b 100644 --- a/Assets/SEE/Utils/Raycasting.cs +++ b/Assets/SEE/Utils/Raycasting.cs @@ -221,7 +221,7 @@ static HitGraphElement DetermineHit(InteractableObject element) raycastHit = ray; if (ray.transform.TryGetComponent(out InteractableObject io)) { - DetermineHit(io); + result = DetermineHit(io); obj = io; } } @@ -233,7 +233,7 @@ static HitGraphElement DetermineHit(InteractableObject element) raycastHit = hit; if (hit.transform.TryGetComponent(out InteractableObject io)) { - DetermineHit(io); + result = DetermineHit(io); obj = io; } } From 8d9eb13e54e60f54595350de9ec9649338a42517 Mon Sep 17 00:00:00 2001 From: Andreas Gawe Date: Wed, 16 Oct 2024 01:10:38 +0200 Subject: [PATCH 30/35] Added missing XR namespace. --- Assets/Resources/Prefabs/Players/XRRig.prefab | 3 +- .../Actions/AcceptDivergenceAction.cs | 3 +- Assets/SEE/Controls/Actions/AddEdgeAction.cs | 1 + Assets/SEE/Controls/Actions/AddNodeAction.cs | 1 + .../SEE/Controls/Actions/ContextMenuAction.cs | 1 + Assets/SEE/Controls/Actions/DeleteAction.cs | 1 + Assets/SEE/Controls/Actions/HideAction.cs | 1 + Assets/SEE/Controls/Actions/MoveAction.cs | 1 + .../Actions/NodeManipulationAction.cs | 1 + Assets/SEE/Controls/Actions/SelectAction.cs | 1 + Assets/SEE/Controls/Actions/ShowCodeAction.cs | 1 + Assets/SEE/Controls/Actions/ShowLabel.cs | 1 + Assets/SEE/Controls/SEEInput.cs | 1 + Assets/SEE/GameObjects/Menu/PlayerMenu.cs | 1 + Assets/SEE/UI/PopupMenu/PopupMenu.cs | 1 + .../PropertyWindowContextMenu.cs | 1 + .../UI/Window/TreeWindow/DesktopTreeWindow.cs | 1 + Assets/SEE/Utils/Raycasting.cs | 1 + Assets/SEE/XR/KeyboardInputHandler.cs | 49 +- Assets/SEE/XR/KeyboardKey.cs | 167 ++-- Assets/SEE/XR/KeyboardManager.cs | 167 ++-- Assets/SEE/XR/RadialSelection.cs | 748 +++++++++--------- Assets/SEE/XR/Rotator.cs | 323 ++++---- Assets/SEE/XR/XRSEEActions.cs | 401 +++++----- 24 files changed, 955 insertions(+), 922 deletions(-) diff --git a/Assets/Resources/Prefabs/Players/XRRig.prefab b/Assets/Resources/Prefabs/Players/XRRig.prefab index d82cf3b294..957f67085f 100644 --- a/Assets/Resources/Prefabs/Players/XRRig.prefab +++ b/Assets/Resources/Prefabs/Players/XRRig.prefab @@ -3324,7 +3324,7 @@ MonoBehaviour: m_StringArgument: m_BoolArgument: 0 m_CallState: 2 - radialMenu: {fileID: -5982496924579745919, guid: c348712bda248c246b8c49b3db54643f, + RadialMenuActionRef: {fileID: -5982496924579745919, guid: c348712bda248c246b8c49b3db54643f, type: 3} --- !u!1 &7806924667908141112 GameObject: @@ -5196,7 +5196,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: df5e8c7d8b96d6944aaafef73a15f5b0, type: 3} m_Name: m_EditorClassIdentifier: - m_RayInteractor: {fileID: 645861538944586177} inputAction: {fileID: -1379689384741821685, guid: c348712bda248c246b8c49b3db54643f, type: 3} undo: {fileID: -989603093121905207, guid: c348712bda248c246b8c49b3db54643f, type: 3} diff --git a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs index 846173d9df..1f522ff174 100644 --- a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs +++ b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; +using SEE.XR; namespace SEE.Controls.Actions { @@ -116,7 +117,7 @@ public override bool Update() { if (SceneSettings.InputType == PlayerInputType.VRPlayer) { - if (XRSEEActions.Selected + if (XRSEEActions.Selected && XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit)) { // find the edge representing the divergence that should be solved. diff --git a/Assets/SEE/Controls/Actions/AddEdgeAction.cs b/Assets/SEE/Controls/Actions/AddEdgeAction.cs index fd8a0f42aa..48ee5a6596 100644 --- a/Assets/SEE/Controls/Actions/AddEdgeAction.cs +++ b/Assets/SEE/Controls/Actions/AddEdgeAction.cs @@ -10,6 +10,7 @@ using SEE.Audio; using SEE.DataModel.DG; using SEE.Game.SceneManipulation; +using SEE.XR; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/Actions/AddNodeAction.cs b/Assets/SEE/Controls/Actions/AddNodeAction.cs index 826a4f340d..3265bf9b74 100644 --- a/Assets/SEE/Controls/Actions/AddNodeAction.cs +++ b/Assets/SEE/Controls/Actions/AddNodeAction.cs @@ -9,6 +9,7 @@ using System; using SEE.UI.PropertyDialog; using SEE.DataModel.DG; +using SEE.XR; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/Actions/ContextMenuAction.cs b/Assets/SEE/Controls/Actions/ContextMenuAction.cs index 48c8c6c462..fd5f318e2d 100644 --- a/Assets/SEE/Controls/Actions/ContextMenuAction.cs +++ b/Assets/SEE/Controls/Actions/ContextMenuAction.cs @@ -17,6 +17,7 @@ using SEE.GO.Menu; using SEE.UI.Menu.Drawable; using SEE.UI.Window.PropertyWindow; +using SEE.XR; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/Actions/DeleteAction.cs b/Assets/SEE/Controls/Actions/DeleteAction.cs index 30c953d6e8..4f0b192c7b 100644 --- a/Assets/SEE/Controls/Actions/DeleteAction.cs +++ b/Assets/SEE/Controls/Actions/DeleteAction.cs @@ -8,6 +8,7 @@ using SEE.Audio; using SEE.Game.SceneManipulation; using SEE.Utils.History; +using SEE.XR; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/Actions/HideAction.cs b/Assets/SEE/Controls/Actions/HideAction.cs index cce5ac7909..55ff682297 100644 --- a/Assets/SEE/Controls/Actions/HideAction.cs +++ b/Assets/SEE/Controls/Actions/HideAction.cs @@ -7,6 +7,7 @@ using UnityEngine; using UnityEngine.Assertions; using SEE.Utils.History; +using SEE.XR; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/Actions/MoveAction.cs b/Assets/SEE/Controls/Actions/MoveAction.cs index d9befd1768..64b24523bd 100644 --- a/Assets/SEE/Controls/Actions/MoveAction.cs +++ b/Assets/SEE/Controls/Actions/MoveAction.cs @@ -15,6 +15,7 @@ using SEE.Utils.History; using UnityEngine; using UnityEngine.EventSystems; +using SEE.XR; namespace SEE.Controls.Actions diff --git a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs index 309f602b1a..c63b6425b0 100644 --- a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs +++ b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs @@ -9,6 +9,7 @@ using SEE.Utils.History; using System.Collections.Generic; using UnityEngine; +using SEE.XR; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/Actions/SelectAction.cs b/Assets/SEE/Controls/Actions/SelectAction.cs index b0606a0f9b..11ec58733a 100644 --- a/Assets/SEE/Controls/Actions/SelectAction.cs +++ b/Assets/SEE/Controls/Actions/SelectAction.cs @@ -4,6 +4,7 @@ using UnityEngine; using SEE.Audio; using SEE.GO; +using SEE.XR; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/Actions/ShowCodeAction.cs b/Assets/SEE/Controls/Actions/ShowCodeAction.cs index 78c294e06f..463e533975 100644 --- a/Assets/SEE/Controls/Actions/ShowCodeAction.cs +++ b/Assets/SEE/Controls/Actions/ShowCodeAction.cs @@ -14,6 +14,7 @@ using SEE.Utils.History; using SEE.Game.City; using SEE.VCS; +using SEE.XR; using GraphElementRef = SEE.GO.GraphElementRef; using Range = SEE.DataModel.DG.Range; diff --git a/Assets/SEE/Controls/Actions/ShowLabel.cs b/Assets/SEE/Controls/Actions/ShowLabel.cs index a24c896cd8..2c2b49b77d 100644 --- a/Assets/SEE/Controls/Actions/ShowLabel.cs +++ b/Assets/SEE/Controls/Actions/ShowLabel.cs @@ -6,6 +6,7 @@ using SEE.Game.Operator; using SEE.GO; using UnityEngine; +using SEE.XR; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/SEEInput.cs b/Assets/SEE/Controls/SEEInput.cs index d48ca989d5..4949199b23 100644 --- a/Assets/SEE/Controls/SEEInput.cs +++ b/Assets/SEE/Controls/SEEInput.cs @@ -2,6 +2,7 @@ using SEE.Utils; using UnityEngine; using SEE.GO; +using SEE.XR; namespace SEE.Controls { diff --git a/Assets/SEE/GameObjects/Menu/PlayerMenu.cs b/Assets/SEE/GameObjects/Menu/PlayerMenu.cs index f4c338cb43..4813ba0500 100644 --- a/Assets/SEE/GameObjects/Menu/PlayerMenu.cs +++ b/Assets/SEE/GameObjects/Menu/PlayerMenu.cs @@ -8,6 +8,7 @@ using SEE.UI.StateIndicator; using SEE.Utils; using UnityEngine; +using SEE.XR; namespace SEE.GO.Menu { diff --git a/Assets/SEE/UI/PopupMenu/PopupMenu.cs b/Assets/SEE/UI/PopupMenu/PopupMenu.cs index f5da016dcc..0fb9636d27 100644 --- a/Assets/SEE/UI/PopupMenu/PopupMenu.cs +++ b/Assets/SEE/UI/PopupMenu/PopupMenu.cs @@ -10,6 +10,7 @@ using TMPro; using UnityEngine; using UnityEngine.UI; +using SEE.XR; namespace SEE.UI.PopupMenu { diff --git a/Assets/SEE/UI/Window/PropertyWindow/PropertyWindowContextMenu.cs b/Assets/SEE/UI/Window/PropertyWindow/PropertyWindowContextMenu.cs index 34e49db663..7c17499681 100644 --- a/Assets/SEE/UI/Window/PropertyWindow/PropertyWindowContextMenu.cs +++ b/Assets/SEE/UI/Window/PropertyWindow/PropertyWindowContextMenu.cs @@ -10,6 +10,7 @@ using TMPro; using UnityEngine; using UnityEngine.Events; +using SEE.XR; namespace SEE.UI.Window.PropertyWindow { diff --git a/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs b/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs index 1a7a17f345..3bb2287793 100644 --- a/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs +++ b/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs @@ -16,6 +16,7 @@ using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; +using SEE.XR; using ArgumentException = System.ArgumentException; using Edge = SEE.DataModel.DG.Edge; using Node = SEE.DataModel.DG.Node; diff --git a/Assets/SEE/Utils/Raycasting.cs b/Assets/SEE/Utils/Raycasting.cs index 8f57f8a24b..45362bf88b 100644 --- a/Assets/SEE/Utils/Raycasting.cs +++ b/Assets/SEE/Utils/Raycasting.cs @@ -7,6 +7,7 @@ using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.InputSystem.UI; +using SEE.XR; namespace SEE.Utils { diff --git a/Assets/SEE/XR/KeyboardInputHandler.cs b/Assets/SEE/XR/KeyboardInputHandler.cs index b44b526a8d..fd71ee5660 100644 --- a/Assets/SEE/XR/KeyboardInputHandler.cs +++ b/Assets/SEE/XR/KeyboardInputHandler.cs @@ -4,35 +4,38 @@ using UnityEngine; using UnityEngine.EventSystems; -/// -/// This class changes the inputfield for the VR-Keyboard and -/// also activates the keyboard. -/// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g -/// -public class KeyboardInputHandler : MonoBehaviour, IPointerClickHandler +namespace SEE.XR { /// - /// The reference to the keyboard GameObject. + /// This class changes the inputfield for the VR-Keyboard and + /// also activates the keyboard. + /// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g /// - private GameObject keyboardGameObject; - - private void Start() + public class KeyboardInputHandler : MonoBehaviour, IPointerClickHandler { - // Cache the reference to the keyboard GameObject - keyboardGameObject = KeyboardManager.instance.gameObject.transform.Find("Keyboard").gameObject; - } + /// + /// The reference to the keyboard GameObject. + /// + private GameObject keyboardGameObject; - /// - /// This method gets called when the user clicks the input field. - /// If the user is in VR, the keyboard gets activated. - /// - /// Event data associated with the event when the user clicks on the inputfield. - public void OnPointerClick(PointerEventData eventdata) - { - if (SceneSettings.InputType == PlayerInputType.VRPlayer) + private void Start() + { + // Cache the reference to the keyboard GameObject + keyboardGameObject = KeyboardManager.instance.gameObject.transform.Find("Keyboard").gameObject; + } + + /// + /// This method gets called when the user clicks the input field. + /// If the user is in VR, the keyboard gets activated. + /// + /// Event data associated with the event when the user clicks on the inputfield. + public void OnPointerClick(PointerEventData eventdata) { - KeyboardManager.instance.inputField = GetComponent(); - keyboardGameObject.SetActive(true); + if (SceneSettings.InputType == PlayerInputType.VRPlayer) + { + KeyboardManager.instance.inputField = GetComponent(); + keyboardGameObject.SetActive(true); + } } } } diff --git a/Assets/SEE/XR/KeyboardKey.cs b/Assets/SEE/XR/KeyboardKey.cs index 89b242eb88..5684eb8311 100644 --- a/Assets/SEE/XR/KeyboardKey.cs +++ b/Assets/SEE/XR/KeyboardKey.cs @@ -2,106 +2,109 @@ using UnityEngine; using UnityEngine.UI; -/// -/// This class handles a single keyboard key. -/// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g -/// -public class KeyboardKey : MonoBehaviour +namespace SEE.XR { /// - /// The character of the key. + /// This class handles a single keyboard key. + /// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g /// - public string character; - - /// - /// The shifted character. - /// - public string shiftCharacter; - - /// - /// Is true when the key is shifted. - /// - private bool isShifted = false; + public class KeyboardKey : MonoBehaviour + { + /// + /// The character of the key. + /// + public string character; - /// - /// The actual keyboard key. - /// - private Button key; + /// + /// The shifted character. + /// + public string shiftCharacter; - /// - /// The label of the keyboard key. - /// - public TextMeshProUGUI keyLabel; + /// + /// Is true when the key is shifted. + /// + private bool isShifted = false; - private void Start() - { - KeyboardManager.instance.shiftButton.onClick.AddListener(HandleShift); - key = GetComponent