diff --git a/Assets/Plugins/XRUIInputModuleFix.meta b/Assets/Plugins/XRUIInputModuleFix.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f65cd25124f89d21bfc9adeec3d29176ff18af8c
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0258d149f294b034797c229c3eb6a8e3
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Plugins/XRUIInputModuleFix/IUIInteractorRegisterer.cs b/Assets/Plugins/XRUIInputModuleFix/IUIInteractorRegisterer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b6725adce0535138def6502ca8d8882f7453b9c8
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/IUIInteractorRegisterer.cs
@@ -0,0 +1,34 @@
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.XR.Interaction.Toolkit.UI
+{
+    [RequireComponent(typeof(IUIInteractor))]
+    public class IUIInteractorRegisterer : MonoBehaviour
+    {
+        void OnEnable ()
+        {
+            XRUIInputModuleFix module = EventSystem.current?.GetComponent<XRUIInputModuleFix>();
+
+            if (module != null)
+            {
+                IUIInteractor[] interactors = GetComponents<IUIInteractor>();
+
+                foreach (IUIInteractor interactor in interactors)
+                    module.RegisterInteractor(interactor);
+            }
+        }
+
+        void OnDisable ()
+        {
+            XRUIInputModuleFix module = EventSystem.current?.GetComponent<XRUIInputModuleFix>();
+
+            if (module != null)
+            {
+                IUIInteractor[] interactors = GetComponents<IUIInteractor>();
+
+                foreach (IUIInteractor interactor in interactors)
+                    module.UnregisterInteractor(interactor);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/XRUIInputModuleFix/IUIInteractorRegisterer.cs.meta b/Assets/Plugins/XRUIInputModuleFix/IUIInteractorRegisterer.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9048d512c03fcbc77d2b7e0ef16202e6f071cae6
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/IUIInteractorRegisterer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fb62641662b746c4ba1436abd6f193eb
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Plugins/XRUIInputModuleFix/JoystickModel.cs b/Assets/Plugins/XRUIInputModuleFix/JoystickModel.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6d7bc2155c95123c267321b9d6955e97f29bfce5
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/JoystickModel.cs
@@ -0,0 +1,114 @@
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.XR.Interaction.Toolkit.UI
+{
+    /// <summary>
+    /// Represents the state of a joystick in the Unity UI (UGUI) system. Keeps track of various book-keeping regarding UI selection, and move and button states.
+    /// </summary>
+    struct JoystickModel
+    {
+        public struct ImplementationData
+        {
+            /// <summary>
+            /// Bookkeeping values for Unity UI (UGUI) that tracks the number of sequential move commands in the same direction that have been sent.  Used to handle proper repeat timing.
+            /// </summary>
+            public int consecutiveMoveCount { get; set; }
+
+            /// <summary>
+            /// Bookkeeping values for Unity UI (UGUI) that tracks the direction of the last move command.  Used to handle proper repeat timing.
+            /// </summary>
+            public MoveDirection lastMoveDirection { get; set; }
+
+            /// <summary>
+            /// Bookkeeping values for Unity UI (UGUI) that tracks the last time a move command was sent.  Used to handle proper repeat timing.
+            /// </summary>
+            public float lastMoveTime { get; set; }
+
+            /// <summary>
+            /// Resets this object to its default, unused state.
+            /// </summary>
+            public void Reset()
+            {
+                consecutiveMoveCount = 0;
+                lastMoveTime = 0.0f;
+                lastMoveDirection = MoveDirection.None;
+            }
+        }
+
+        /// <summary>
+        /// A 2D Vector that represents a UI Selection movement command.  Think moving up and down in options menus or highlighting options.
+        /// </summary>
+        public Vector2 move { get; set; }
+
+        /// <summary>
+        /// Tracks the current state of the submit or 'move forward' button.  Setting this also updates the <see cref="submitButtonDelta"/> to track if a press or release occurred in the frame.
+        /// </summary>
+        public bool submitButtonDown
+        {
+            get => m_SubmitButtonDown;
+            set
+            {
+                if (m_SubmitButtonDown != value)
+                {
+                    submitButtonDelta = value ? ButtonDeltaState.Pressed : ButtonDeltaState.Released;
+                    m_SubmitButtonDown = value;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Tracks the changes in <see cref="submitButtonDown"/> between calls to <see cref="OnFrameFinished"/>.
+        /// </summary>
+        internal ButtonDeltaState submitButtonDelta { get; private set; }
+
+        /// <summary>
+        /// Tracks the current state of the submit or 'move backward' button.  Setting this also updates the <see cref="cancelButtonDelta"/> to track if a press or release occurred in the frame.
+        /// </summary>
+        public bool cancelButtonDown
+        {
+            get => m_CancelButtonDown;
+            set
+            {
+                if (m_CancelButtonDown != value)
+                {
+                    cancelButtonDelta = value ? ButtonDeltaState.Pressed : ButtonDeltaState.Released;
+                    m_CancelButtonDown = value;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Tracks the changes in <see cref="cancelButtonDown"/> between calls to <see cref="OnFrameFinished"/>.
+        /// </summary>
+        internal ButtonDeltaState cancelButtonDelta { get; private set; }
+
+        /// <summary>
+        /// Internal bookkeeping data used by the Unity UI (UGUI) system.
+        /// </summary>
+        internal ImplementationData implementationData { get; set; }
+
+        /// <summary>
+        /// Resets this object to it's default, unused state.
+        /// </summary>
+        public void Reset()
+        {
+            move = Vector2.zero;
+            m_SubmitButtonDown = m_CancelButtonDown = false;
+            submitButtonDelta = cancelButtonDelta = ButtonDeltaState.NoChange;
+
+            implementationData.Reset();
+        }
+
+        /// <summary>
+        /// Call this at the end of polling for per-frame changes.  This resets delta values, such as <see cref="submitButtonDelta"/> and <see cref="cancelButtonDelta"/>.
+        /// </summary>
+        public void OnFrameFinished()
+        {
+            submitButtonDelta = ButtonDeltaState.NoChange;
+            cancelButtonDelta = ButtonDeltaState.NoChange;
+        }
+
+        bool m_SubmitButtonDown;
+        bool m_CancelButtonDown;
+    }
+}
diff --git a/Assets/Plugins/XRUIInputModuleFix/JoystickModel.cs.meta b/Assets/Plugins/XRUIInputModuleFix/JoystickModel.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..640e48bbb97734f92c4b54381e7c72d611411c09
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/JoystickModel.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9b69cdbfe87c13b49831433b8ec8c50e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Plugins/XRUIInputModuleFix/MouseModel.cs b/Assets/Plugins/XRUIInputModuleFix/MouseModel.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2c2e60c189b474ccee5c08356d3f6acf1256bc9d
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/MouseModel.cs
@@ -0,0 +1,362 @@
+using System.Collections.Generic;
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.XR.Interaction.Toolkit.UI
+{
+    /// <summary>
+    /// Represents the state of a single mouse button within the Unity UI (UGUI) system. Keeps track of various book-keeping regarding clicks, drags, and presses.
+    /// Can be converted to and from PointerEventData for sending into Unity UI (UGUI).
+    /// </summary>
+    public struct MouseButtonModel
+    {
+        internal struct ImplementationData
+        {
+            /// <summary>
+            /// Used to cache whether or not the current mouse button is being dragged.
+            /// </summary>
+            /// <seealso cref="PointerEventData.dragging"/>
+            public bool isDragging { get; set; }
+
+            /// <summary>
+            /// Used to cache the last time this button was pressed.
+            /// </summary>
+            /// <seealso cref="PointerEventData.clickTime"/>
+            public float pressedTime { get; set; }
+
+            /// <summary>
+            /// The position on the screen that this button was last pressed.
+            /// In the same scale as <see cref="MouseModel.position"/>, and caches the same value as <see cref="PointerEventData.pressPosition"/>.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pressPosition"/>
+            public Vector2 pressedPosition { get; set; }
+
+            /// <summary>
+            /// The Raycast data from the time it was pressed.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pointerPressRaycast"/>
+            public RaycastResult pressedRaycast { get; set; }
+
+            /// <summary>
+            /// The last GameObject pressed on that can handle press or click events.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pointerPress"/>
+            public GameObject pressedGameObject { get; set; }
+
+            /// <summary>
+            /// The last GameObject pressed on regardless of whether it can handle events or not.
+            /// </summary>
+            /// <seealso cref="PointerEventData.rawPointerPress"/>
+            public GameObject pressedGameObjectRaw { get; set; }
+
+            /// <summary>
+            /// The GameObject currently being dragged if any.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pointerDrag"/>
+            public GameObject draggedGameObject { get; set; }
+
+            /// <summary>
+            /// Resets this object to it's default, unused state.
+            /// </summary>
+            public void Reset()
+            {
+                isDragging = false;
+                pressedTime = 0f;
+                pressedPosition = Vector2.zero;
+                pressedRaycast = new RaycastResult();
+                pressedGameObject = pressedGameObjectRaw = draggedGameObject = null;
+            }
+        }
+
+        /// <summary>
+        /// Used to store the current binary state of the button. When set, will also track the changes between calls of <see cref="OnFrameFinished"/> in <see cref="lastFrameDelta"/>.
+        /// </summary>
+        public bool isDown
+        {
+            get => m_IsDown;
+            set
+            {
+                if (m_IsDown != value)
+                {
+                    m_IsDown = value;
+                    lastFrameDelta |= value ? ButtonDeltaState.Pressed : ButtonDeltaState.Released;
+                }
+            }
+        }
+
+        /// <summary>
+        /// A set of flags to identify the changes that have occurred between calls of <see cref="OnFrameFinished"/>.
+        /// </summary>
+        internal ButtonDeltaState lastFrameDelta { get; private set; }
+
+        /// <summary>
+        /// Resets this object to it's default, unused state.
+        /// </summary>
+        public void Reset()
+        {
+            lastFrameDelta = ButtonDeltaState.NoChange;
+            m_IsDown = false;
+
+            m_ImplementationData.Reset();
+        }
+
+        /// <summary>
+        /// Call this on each frame in order to reset properties that detect whether or not a certain condition was met this frame.
+        /// </summary>
+        public void OnFrameFinished() => lastFrameDelta = ButtonDeltaState.NoChange;
+
+        /// <summary>
+        /// Fills a <see cref="PointerEventData"/> with this mouse button's internally cached values.
+        /// </summary>
+        /// <param name="eventData">These objects are used to send data through the Unity UI (UGUI) system.</param>
+        public void CopyTo(PointerEventData eventData)
+        {
+            eventData.dragging = m_ImplementationData.isDragging;
+            eventData.clickTime = m_ImplementationData.pressedTime;
+            eventData.pressPosition = m_ImplementationData.pressedPosition;
+            eventData.pointerPressRaycast = m_ImplementationData.pressedRaycast;
+            eventData.pointerPress = m_ImplementationData.pressedGameObject;
+            eventData.rawPointerPress = m_ImplementationData.pressedGameObjectRaw;
+            eventData.pointerDrag = m_ImplementationData.draggedGameObject;
+        }
+
+        /// <summary>
+        /// Fills this object with the values from a <see cref="PointerEventData"/>.
+        /// </summary>
+        /// <param name="eventData">These objects are used to send data through the Unity UI (UGUI) system.</param>
+        public void CopyFrom(PointerEventData eventData)
+        {
+            m_ImplementationData.isDragging = eventData.dragging;
+            m_ImplementationData.pressedTime = eventData.clickTime;
+            m_ImplementationData.pressedPosition = eventData.pressPosition;
+            m_ImplementationData.pressedRaycast = eventData.pointerPressRaycast;
+            m_ImplementationData.pressedGameObject = eventData.pointerPress;
+            m_ImplementationData.pressedGameObjectRaw = eventData.rawPointerPress;
+            m_ImplementationData.draggedGameObject = eventData.pointerDrag;
+        }
+
+        bool m_IsDown;
+        ImplementationData m_ImplementationData;
+    }
+
+    struct MouseModel
+    {
+        internal struct InternalData
+        {
+            /// <summary>
+            /// This tracks the current GUI targets being hovered over.
+            /// </summary>
+            /// <seealso cref="PointerEventData.hovered"/>
+            public List<GameObject> hoverTargets { get; set; }
+
+            /// <summary>
+            ///  Tracks the current enter/exit target being hovered over at any given moment.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pointerEnter"/>
+            public GameObject pointerTarget { get; set; }
+
+            public void Reset()
+            {
+                pointerTarget = null;
+
+                if (hoverTargets == null)
+                    hoverTargets = new List<GameObject>();
+                else
+                    hoverTargets.Clear();
+            }
+        }
+
+        /// <summary>
+        /// An Id representing a unique pointer.
+        /// </summary>
+        public int pointerId { get; }
+
+        /// <summary>
+        /// A boolean value representing whether any mouse data has changed this frame, meaning that events should be processed.
+        /// </summary>
+        /// <remarks>
+        /// This only checks for changes in mouse state (<see cref="position"/>, <see cref="leftButton"/>, <see cref="rightButton"/>, <see cref="middleButton"/>, or <see cref="scrollPosition"/>).
+        /// </remarks>
+        public bool changedThisFrame { get; private set; }
+
+        Vector2 m_Position;
+
+        public Vector2 position
+        {
+            get => m_Position;
+            set
+            {
+                if (m_Position != value)
+                {
+                    deltaPosition = value - m_Position;
+                    m_Position = value;
+                    changedThisFrame = true;
+                }
+            }
+        }
+
+        /// <summary>
+        /// The pixel-space change in <see cref="position"/> since the last call to <see cref="OnFrameFinished"/>.
+        /// </summary>
+        public Vector2 deltaPosition { get; private set; }
+
+        Vector2 m_ScrollDelta;
+
+        /// <summary>
+        /// The amount of scroll since the last call to <see cref="OnFrameFinished"/>.
+        /// </summary>
+        public Vector2 scrollDelta
+        {
+            get => m_ScrollDelta;
+            set
+            {
+                if (m_ScrollDelta != value)
+                {
+                    m_ScrollDelta = value;
+                    changedThisFrame = true;
+                }
+            }
+        }
+
+        MouseButtonModel m_LeftButton;
+
+        /// <summary>
+        /// Cached data and button state representing a left mouse button on a mouse.
+        /// Used by Unity UI (UGUI) to keep track of persistent click, press, and drag states.
+        /// </summary>
+        public MouseButtonModel leftButton
+        {
+            get => m_LeftButton;
+            set
+            {
+                changedThisFrame |= (value.lastFrameDelta != ButtonDeltaState.NoChange);
+                m_LeftButton = value;
+            }
+        }
+
+        /// <summary>
+        /// Shorthand to set the pressed state of the left mouse button.
+        /// </summary>
+        public bool leftButtonPressed
+        {
+            set
+            {
+                changedThisFrame |= m_LeftButton.isDown != value;
+                m_LeftButton.isDown = value;
+            }
+        }
+
+        MouseButtonModel m_RightButton;
+
+        /// <summary>
+        /// Cached data and button state representing a right mouse button on a mouse.
+        /// Used by Unity UI (UGUI) to keep track of persistent click, press, and drag states.
+        /// </summary>
+        public MouseButtonModel rightButton
+        {
+            get => m_RightButton;
+            set
+            {
+                changedThisFrame |= (value.lastFrameDelta != ButtonDeltaState.NoChange);
+                m_RightButton = value;
+            }
+        }
+
+        /// <summary>
+        /// Shorthand to set the pressed state of the right mouse button.
+        /// </summary>
+        public bool rightButtonPressed
+        {
+            set
+            {
+                changedThisFrame |= m_RightButton.isDown != value;
+                m_RightButton.isDown = value;
+            }
+        }
+
+        MouseButtonModel m_MiddleButton;
+
+        /// <summary>
+        /// Cached data and button state representing a middle mouse button on a mouse.
+        /// Used by Unity UI (UGUI) to keep track of persistent click, press, and drag states.
+        /// </summary>
+        public MouseButtonModel middleButton
+        {
+            get => m_MiddleButton;
+            set
+            {
+                changedThisFrame |= (value.lastFrameDelta != ButtonDeltaState.NoChange);
+                m_MiddleButton = value;
+            }
+        }
+
+        /// <summary>
+        /// Shorthand to set the pressed state of the middle mouse button.
+        /// </summary>
+        public bool middleButtonPressed
+        {
+            set
+            {
+                changedThisFrame |= m_MiddleButton.isDown != value;
+                m_MiddleButton.isDown = value;
+            }
+        }
+
+        InternalData m_InternalData;
+
+        public MouseModel(int pointerId)
+        {
+            this.pointerId = pointerId;
+            changedThisFrame = false;
+            m_Position = Vector2.zero;
+            deltaPosition = Vector2.zero;
+            m_ScrollDelta = Vector2.zero;
+
+            m_LeftButton = new MouseButtonModel();
+            m_RightButton = new MouseButtonModel();
+            m_MiddleButton = new MouseButtonModel();
+            m_LeftButton.Reset();
+            m_RightButton.Reset();
+            m_MiddleButton.Reset();
+
+            m_InternalData = new InternalData();
+            m_InternalData.Reset();
+        }
+
+        /// <summary>
+        /// Call this at the end of polling for per-frame changes.  This resets delta values, such as <see cref="deltaPosition"/>, <see cref="scrollDelta"/>, and <see cref="MouseButtonModel.lastFrameDelta"/>.
+        /// </summary>
+        public void OnFrameFinished()
+        {
+            changedThisFrame = false;
+            deltaPosition = Vector2.zero;
+            m_ScrollDelta = Vector2.zero;
+            m_LeftButton.OnFrameFinished();
+            m_RightButton.OnFrameFinished();
+            m_MiddleButton.OnFrameFinished();
+        }
+
+        public void CopyTo(PointerEventData eventData)
+        {
+            eventData.pointerId = pointerId;
+            eventData.position = position;
+            eventData.delta = deltaPosition;
+            eventData.scrollDelta = scrollDelta;
+
+            eventData.pointerEnter = m_InternalData.pointerTarget;
+            eventData.hovered.Clear();
+            eventData.hovered.AddRange(m_InternalData.hoverTargets);
+
+            // This is unset in legacy systems and can safely assumed to stay true.
+            eventData.useDragThreshold = true;
+        }
+
+        public void CopyFrom(PointerEventData eventData)
+        {
+            var hoverTargets = m_InternalData.hoverTargets;
+            m_InternalData.hoverTargets.Clear();
+            m_InternalData.hoverTargets.AddRange(eventData.hovered);
+            m_InternalData.hoverTargets = hoverTargets;
+            m_InternalData.pointerTarget = eventData.pointerEnter;
+        }
+    }
+}
diff --git a/Assets/Plugins/XRUIInputModuleFix/MouseModel.cs.meta b/Assets/Plugins/XRUIInputModuleFix/MouseModel.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6597f44f4d3e51e1b1b545df57e4ad28196fb041
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/MouseModel.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cfe2a6fdc09d5f847ba128acac8cc882
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Plugins/XRUIInputModuleFix/TouchModel.cs b/Assets/Plugins/XRUIInputModuleFix/TouchModel.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e9c0186435db24866db0019e3699d735ef14e52d
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/TouchModel.cs
@@ -0,0 +1,198 @@
+using System.Collections.Generic;
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.XR.Interaction.Toolkit.UI
+{
+    struct TouchModel
+    {
+        internal struct ImplementationData
+        {
+            /// <summary>
+            /// This tracks the current GUI targets being hovered over.
+            /// </summary>
+            /// <seealso cref="PointerEventData.hovered"/>
+            public List<GameObject> hoverTargets { get; set; }
+
+            /// <summary>
+            /// Tracks the current enter/exit target being hovered over at any given moment.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pointerEnter"/>
+            public GameObject pointerTarget { get; set; }
+
+            /// <summary>
+            /// Used to cache whether or not the current mouse button is being dragged.
+            /// </summary>
+            /// <seealso cref="PointerEventData.dragging"/>
+            public bool isDragging { get; set; }
+
+            /// <summary>
+            /// Used to cache the last time this button was pressed.
+            /// </summary>
+            /// <seealso cref="PointerEventData.clickTime"/>
+            public float pressedTime { get; set; }
+
+            /// <summary>
+            /// The position on the screen that this button was last pressed.
+            /// In the same scale as <see cref="position"/>, and caches the same value as <see cref="PointerEventData.pressPosition"/>.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pressPosition"/>
+            public Vector2 pressedPosition { get; set; }
+
+            /// <summary>
+            /// The Raycast data from the time it was pressed.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pointerPressRaycast"/>
+            public RaycastResult pressedRaycast { get; set; }
+
+            /// <summary>
+            /// The last GameObject pressed on that can handle press or click events.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pointerPress"/>
+            public GameObject pressedGameObject { get; set; }
+
+            /// <summary>
+            /// The last GameObject pressed on regardless of whether it can handle events or not.
+            /// </summary>
+            /// <seealso cref="PointerEventData.rawPointerPress"/>
+            public GameObject pressedGameObjectRaw { get; set; }
+
+            /// <summary>
+            /// The GameObject currently being dragged if any.
+            /// </summary>
+            /// <seealso cref="PointerEventData.pointerDrag"/>
+            public GameObject draggedGameObject { get; set; }
+
+            /// <summary>
+            /// Resets this object to it's default, unused state.
+            /// </summary>
+            public void Reset()
+            {
+                isDragging = false;
+                pressedTime = 0f;
+                pressedPosition = Vector2.zero;
+                pressedRaycast = new RaycastResult();
+                pressedGameObject = pressedGameObjectRaw = draggedGameObject = null;
+
+                if (hoverTargets == null)
+                    hoverTargets = new List<GameObject>();
+                else
+                    hoverTargets.Clear();
+            }
+        }
+
+        public int pointerId { get; }
+
+        public TouchPhase selectPhase
+        {
+            get => m_SelectPhase;
+            set
+            {
+                if (m_SelectPhase != value)
+                {
+                    if (value == TouchPhase.Began)
+                        selectDelta |= ButtonDeltaState.Pressed;
+
+                    if (value == TouchPhase.Ended || value == TouchPhase.Canceled)
+                        selectDelta |= ButtonDeltaState.Released;
+
+                    m_SelectPhase = value;
+
+                    changedThisFrame = true;
+                }
+            }
+        }
+
+        public ButtonDeltaState selectDelta { get; private set; }
+
+        public bool changedThisFrame { get; private set; }
+
+        /// <summary>
+        /// The pixel-space position of the touch object.
+        /// </summary>
+        public Vector2 position
+        {
+            get => m_Position;
+            set
+            {
+                if (m_Position != value)
+                {
+                    deltaPosition = value - m_Position;
+                    m_Position = value;
+                    changedThisFrame = true;
+                }
+            }
+        }
+
+        /// <summary>
+        /// The pixel-space change in <see cref="position"/> since the last call to <see cref="OnFrameFinished"/>.
+        /// </summary>
+        public Vector2 deltaPosition { get; private set; }
+
+        TouchPhase m_SelectPhase;
+        Vector2 m_Position;
+        ImplementationData m_ImplementationData;
+
+        public TouchModel(int pointerId)
+        {
+            this.pointerId = pointerId;
+
+            m_Position = deltaPosition = Vector2.zero;
+
+            m_SelectPhase = TouchPhase.Canceled;
+            changedThisFrame = false;
+            selectDelta = ButtonDeltaState.NoChange;
+
+            m_ImplementationData = new ImplementationData();
+            m_ImplementationData.Reset();
+        }
+
+        public void Reset()
+        {
+            m_Position = deltaPosition = Vector2.zero;
+            changedThisFrame = false;
+            selectDelta = ButtonDeltaState.NoChange;
+            m_ImplementationData.Reset();
+        }
+
+        public void OnFrameFinished()
+        {
+            deltaPosition = Vector2.zero;
+            selectDelta = ButtonDeltaState.NoChange;
+            changedThisFrame = false;
+        }
+
+        public void CopyTo(PointerEventData eventData)
+        {
+            eventData.pointerId = pointerId;
+            eventData.position = position;
+            eventData.delta = ((selectDelta & ButtonDeltaState.Pressed) != 0) ? Vector2.zero : deltaPosition;
+
+            eventData.pointerEnter = m_ImplementationData.pointerTarget;
+            eventData.dragging = m_ImplementationData.isDragging;
+            eventData.clickTime = m_ImplementationData.pressedTime;
+            eventData.pressPosition = m_ImplementationData.pressedPosition;
+            eventData.pointerPressRaycast = m_ImplementationData.pressedRaycast;
+            eventData.pointerPress = m_ImplementationData.pressedGameObject;
+            eventData.rawPointerPress = m_ImplementationData.pressedGameObjectRaw;
+            eventData.pointerDrag = m_ImplementationData.draggedGameObject;
+
+            eventData.hovered.Clear();
+            eventData.hovered.AddRange(m_ImplementationData.hoverTargets);
+        }
+
+        public void CopyFrom(PointerEventData eventData)
+        {
+            m_ImplementationData.pointerTarget = eventData.pointerEnter;
+            m_ImplementationData.isDragging = eventData.dragging;
+            m_ImplementationData.pressedTime = eventData.clickTime;
+            m_ImplementationData.pressedPosition = eventData.pressPosition;
+            m_ImplementationData.pressedRaycast = eventData.pointerPressRaycast;
+            m_ImplementationData.pressedGameObject = eventData.pointerPress;
+            m_ImplementationData.pressedGameObjectRaw = eventData.rawPointerPress;
+            m_ImplementationData.draggedGameObject = eventData.pointerDrag;
+
+            m_ImplementationData.hoverTargets.Clear();
+            m_ImplementationData.hoverTargets.AddRange(eventData.hovered);
+        }
+    }
+}
diff --git a/Assets/Plugins/XRUIInputModuleFix/TouchModel.cs.meta b/Assets/Plugins/XRUIInputModuleFix/TouchModel.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4d73690f714cb2f674c96fab7ed305a940487d2a
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/TouchModel.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 09733180af2b84849b64e1380f4aec9a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.Events.cs b/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.Events.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5729bdfb0142ea846ee836b54c204793c7043d88
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.Events.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.XR.Interaction.Toolkit.UI
+{
+    public abstract partial class UIInputModuleFix
+    {
+        /// <summary>
+        /// Calls the methods in its invocation list after the input module collects a list of type <see cref="RaycastResult"/>, but before the results are used.
+        /// Note that not all fields of the event data are still valid or up to date at this point in the UI event processing.
+        /// This event can be used to read, modify, or reorder results.
+        /// After the event, the first result in the list with a non-null GameObject will be used.
+        /// </summary>
+        public event Action<PointerEventData, List<RaycastResult>> finalizeRaycastResults;
+
+        /// <summary>
+        /// This occurs when a UI pointer enters an element.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> pointerEnter;
+
+        /// <summary>
+        /// This occurs when a UI pointer exits an element.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> pointerExit;
+
+        /// <summary>
+        /// This occurs when a select button down occurs while a UI pointer is hovering an element.
+        /// This event is executed using ExecuteEvents.ExecuteHierarchy when sent to the target element.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> pointerDown;
+
+        /// <summary>
+        /// This occurs when a select button up occurs while a UI pointer is hovering an element.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> pointerUp;
+
+        /// <summary>
+        /// This occurs when a select button click occurs while a UI pointer is hovering an element.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> pointerClick;
+
+        /// <summary>
+        /// This occurs when a potential drag occurs on an element.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> initializePotentialDrag;
+
+        /// <summary>
+        /// This occurs when a drag first occurs on an element.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> beginDrag;
+
+        /// <summary>
+        /// This occurs every frame while dragging an element.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> drag;
+
+        /// <summary>
+        /// This occurs on the last frame an element is dragged.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> endDrag;
+
+        /// <summary>
+        /// This occurs when a dragged element is dropped on a drop handler.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> drop;
+
+        /// <summary>
+        /// This occurs when an element is scrolled
+        /// This event is executed using ExecuteEvents.ExecuteHierarchy when sent to the target element.
+        /// </summary>
+        public event Action<GameObject, PointerEventData> scroll;
+
+        /// <summary>
+        /// This occurs on update for the currently selected object.
+        /// </summary>
+        public event Action<GameObject, BaseEventData> updateSelected;
+
+        /// <summary>
+        /// This occurs when the move axis is activated.
+        /// </summary>
+        public event Action<GameObject, AxisEventData> move;
+
+        /// <summary>
+        /// This occurs when the submit button is pressed.
+        /// </summary>
+        public event Action<GameObject, BaseEventData> submit;
+
+        /// <summary>
+        /// This occurs when the cancel button is pressed.
+        /// </summary>
+        public event Action<GameObject, BaseEventData> cancel;
+    }
+}
diff --git a/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.Events.cs.meta b/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.Events.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7a098a19ac63c5e13a4a6ccc913f3cbe819a4c1a
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.Events.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1a975c68a753baa4ebb9ec53f42d332e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.cs b/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7c0adcaf58b94548a2d1c3e11a0fbfe30dfb19a3
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.cs
@@ -0,0 +1,639 @@
+using System;
+using UnityEngine.EventSystems;
+using UnityEngine.Serialization;
+
+namespace UnityEngine.XR.Interaction.Toolkit.UI
+{
+    /// <summary>
+    /// Base class for input modules that send UI input.
+    /// </summary>
+    /// <remarks>
+    /// Multiple input modules may be placed on the same event system. In such a setup,
+    /// the modules will synchronize with each other.
+    /// </remarks>
+    [DefaultExecutionOrder(XRInteractionUpdateOrder.k_UIInputModule)]
+    public abstract partial class UIInputModuleFix : BaseInputModule
+    {
+        [SerializeField, FormerlySerializedAs("clickSpeed")]
+        [Tooltip("The maximum time (in seconds) between two mouse presses for it to be consecutive click.")]
+        float m_ClickSpeed = 0.3f;
+        /// <summary>
+        /// The maximum time (in seconds) between two mouse presses for it to be consecutive click.
+        /// </summary>
+        public float clickSpeed
+        {
+            get => m_ClickSpeed;
+            set => m_ClickSpeed = value;
+        }
+
+        [SerializeField, FormerlySerializedAs("moveDeadzone")]
+        [Tooltip("The absolute value required by a move action on either axis required to trigger a move event.")]
+        float m_MoveDeadzone = 0.6f;
+        /// <summary>
+        /// The absolute value required by a move action on either axis required to trigger a move event.
+        /// </summary>
+        public float moveDeadzone
+        {
+            get => m_MoveDeadzone;
+            set => m_MoveDeadzone = value;
+        }
+
+        [SerializeField, FormerlySerializedAs("repeatDelay")]
+        [Tooltip("The Initial delay (in seconds) between an initial move action and a repeated move action.")]
+        float m_RepeatDelay = 0.5f;
+        /// <summary>
+        /// The Initial delay (in seconds) between an initial move action and a repeated move action.
+        /// </summary>
+        public float repeatDelay
+        {
+            get => m_RepeatDelay;
+            set => m_RepeatDelay = value;
+        }
+
+        [FormerlySerializedAs("repeatRate")]
+        [SerializeField, Tooltip("The speed (in seconds) that the move action repeats itself once repeating.")]
+        float m_RepeatRate = 0.1f;
+        /// <summary>
+        /// The speed (in seconds) that the move action repeats itself once repeating.
+        /// </summary>
+        public float repeatRate
+        {
+            get => m_RepeatRate;
+            set => m_RepeatRate = value;
+        }
+
+        [FormerlySerializedAs("trackedDeviceDragThresholdMultiplier")]
+        [SerializeField, Tooltip("Scales the EventSystem.pixelDragThreshold, for tracked devices, to make selection easier.")]
+        float m_TrackedDeviceDragThresholdMultiplier = 2f;
+        /// <summary>
+        /// Scales the <see cref="EventSystem.pixelDragThreshold"/>, for tracked devices, to make selection easier.
+        /// </summary>
+        public float trackedDeviceDragThresholdMultiplier
+        {
+            get => m_TrackedDeviceDragThresholdMultiplier;
+            set => m_TrackedDeviceDragThresholdMultiplier = value;
+        }
+
+        /// <summary>
+        /// The <see cref="Camera"/> that is used to perform 2D raycasts when determining the screen space location of a tracked device cursor.
+        /// </summary>
+        public Camera uiCamera { get; set; }
+
+        AxisEventData m_CachedAxisEvent;
+        PointerEventData m_CachedPointerEvent;
+        TrackedDeviceEventData m_CachedTrackedDeviceEventData;
+
+        /// <summary>
+        /// See <see cref="MonoBehaviour"/>.
+        /// </summary>
+        /// <remarks>
+        /// Processing is postponed from earlier in the frame (<see cref="EventSystem"/> has a
+        /// script execution order of <c>-1000</c>) until this Update to allow other systems to
+        /// update the poses that will be used to generate the raycasts used by this input module.
+        /// <br />
+        /// For Ray Interactor, it must wait until after the Controller pose updates and Locomotion
+        /// moves the Rig in order to generate the current sample points used to create the rays used
+        /// for this frame. Those positions will be determined during <see cref="DoProcess"/>.
+        /// Ray Interactor needs the UI raycasts to be completed by the time <see cref="XRInteractionManager"/>
+        /// calls into <see cref="XRBaseInteractor.GetValidTargets"/> since that is dependent on
+        /// whether a UI hit was closer than a 3D hit. This processing must therefore be done
+        /// between Locomotion and <see cref="XRBaseInteractor.ProcessInteractor"/> to minimize latency.
+        /// </remarks>
+        protected virtual void Update()
+        {
+            // Check to make sure that Process should still be called.
+            // It would likely cause unexpected results if processing was done
+            // when this module is no longer the current one.
+            if (eventSystem.IsActive() && eventSystem.currentInputModule == this && eventSystem == EventSystem.current)
+            {
+                DoProcess();
+            }
+        }
+
+        /// <summary>
+        /// Process the current tick for the module.
+        /// </summary>
+        /// <remarks>
+        /// Executed once per Update call. Override for custom processing.
+        /// </remarks>
+        /// <seealso cref="Process"/>
+        protected virtual void DoProcess()
+        {
+            SendUpdateEventToSelectedObject();
+        }
+
+        /// <inheritdoc />
+        public override void Process()
+        {
+            // Postpone processing until later in the frame
+        }
+
+        /// <summary>
+        /// Sends an update event to the currently selected object.
+        /// </summary>
+        /// <returns>Returns whether the update event was used by the selected object.</returns>
+        protected bool SendUpdateEventToSelectedObject()
+        {
+            var selectedGameObject = eventSystem.currentSelectedGameObject;
+            if (selectedGameObject == null)
+                return false;
+
+            var data = GetBaseEventData();
+            updateSelected?.Invoke(selectedGameObject, data);
+            ExecuteEvents.Execute(selectedGameObject, data, ExecuteEvents.updateSelectedHandler);
+            return data.used;
+        }
+
+        RaycastResult PerformRaycast(PointerEventData eventData)
+        {
+            if (eventData == null)
+                throw new ArgumentNullException(nameof(eventData));
+
+            eventSystem.RaycastAll(eventData, m_RaycastResultCache);
+            finalizeRaycastResults?.Invoke(eventData, m_RaycastResultCache);
+            var result = FindFirstRaycast(m_RaycastResultCache);
+            m_RaycastResultCache.Clear();
+            return result;
+        }
+
+        /// <summary>
+        /// Takes an existing <see cref="MouseModel"/> and dispatches all relevant changes through the event system.
+        /// It also updates the internal data of the <see cref="MouseModel"/>.
+        /// </summary>
+        /// <param name="mouseState">The mouse state you want to forward into the UI Event System.</param>
+        internal void ProcessMouse(ref MouseModel mouseState)
+        {
+            if (!mouseState.changedThisFrame)
+                return;
+
+            var eventData = GetOrCreateCachedPointerEvent();
+            eventData.Reset();
+
+            mouseState.CopyTo(eventData);
+
+            eventData.pointerCurrentRaycast = PerformRaycast(eventData);
+
+            // Left Mouse Button
+            // The left mouse button is 'dominant' and we want to also process hover and scroll events as if the occurred during the left click.
+            var buttonState = mouseState.leftButton;
+            buttonState.CopyTo(eventData);
+            ProcessMouseButton(buttonState.lastFrameDelta, eventData);
+
+            ProcessMouseMovement(eventData);
+            ProcessMouseScroll(eventData);
+
+            mouseState.CopyFrom(eventData);
+
+            ProcessMouseButtonDrag(eventData);
+
+            buttonState.CopyFrom(eventData);
+            mouseState.leftButton = buttonState;
+
+            // Right Mouse Button
+            buttonState = mouseState.rightButton;
+            buttonState.CopyTo(eventData);
+
+            ProcessMouseButton(buttonState.lastFrameDelta, eventData);
+            ProcessMouseButtonDrag(eventData);
+
+            buttonState.CopyFrom(eventData);
+            mouseState.rightButton = buttonState;
+
+            // Middle Mouse Button
+            buttonState = mouseState.middleButton;
+            buttonState.CopyTo(eventData);
+
+            ProcessMouseButton(buttonState.lastFrameDelta, eventData);
+            ProcessMouseButtonDrag(eventData);
+
+            buttonState.CopyFrom(eventData);
+            mouseState.middleButton = buttonState;
+
+            mouseState.OnFrameFinished();
+        }
+
+        void ProcessMouseMovement(PointerEventData eventData)
+        {
+            var currentPointerTarget = eventData.pointerCurrentRaycast.gameObject;
+
+            foreach (GameObject hovered in eventData.hovered)
+                ExecuteEvents.Execute(hovered, eventData, ExecuteEvents.pointerMoveHandler);
+            
+            // another way of triggering the pointerMoveHandler
+            // if (currentPointerTarget != null)
+            //     ExecuteEvents.Execute(currentPointerTarget, eventData, ExecuteEvents.pointerMoveHandler);
+
+            // If we have no target or pointerEnter has been deleted,
+            // we just send exit events to anything we are tracking
+            // and then exit.
+            if (currentPointerTarget == null || eventData.pointerEnter == null)
+            {
+                foreach (var hovered in eventData.hovered)
+                {
+                    pointerExit?.Invoke(hovered, eventData);
+                    ExecuteEvents.Execute(hovered, eventData, ExecuteEvents.pointerExitHandler);
+                }
+
+                eventData.hovered.Clear();
+
+                if (currentPointerTarget == null)
+                {
+                    eventData.pointerEnter = null;
+                    return;
+                }
+            }
+
+            if (eventData.pointerEnter == currentPointerTarget)
+                return;
+
+            var commonRoot = FindCommonRoot(eventData.pointerEnter, currentPointerTarget);
+
+            // We walk up the tree until a common root and the last entered and current entered object is found.
+            // Then send exit and enter events up to, but not including, the common root.
+            if (eventData.pointerEnter != null)
+            {
+                var target = eventData.pointerEnter.transform;
+
+                while (target != null)
+                {
+                    if (commonRoot != null && commonRoot.transform == target)
+                        break;
+
+                    var targetGameObject = target.gameObject;
+                    pointerExit?.Invoke(targetGameObject, eventData);
+                    ExecuteEvents.Execute(targetGameObject, eventData, ExecuteEvents.pointerExitHandler);
+
+                    eventData.hovered.Remove(targetGameObject);
+
+                    target = target.parent;
+                }
+            }
+
+            eventData.pointerEnter = currentPointerTarget;
+            // ReSharper disable once ConditionIsAlwaysTrueOrFalse -- Could be null if it was destroyed immediately after executing above
+            if (currentPointerTarget != null)
+            {
+                var target = currentPointerTarget.transform;
+
+                while (target != null && target.gameObject != commonRoot)
+                {
+                    var targetGameObject = target.gameObject;
+                    pointerEnter?.Invoke(targetGameObject, eventData);
+                    ExecuteEvents.Execute(targetGameObject, eventData, ExecuteEvents.pointerEnterHandler);
+
+                    eventData.hovered.Add(targetGameObject);
+
+                    target = target.parent;
+                }
+            }
+        }
+
+        void ProcessMouseButton(ButtonDeltaState mouseButtonChanges, PointerEventData eventData)
+        {
+            var hoverTarget = eventData.pointerCurrentRaycast.gameObject;
+
+            if ((mouseButtonChanges & ButtonDeltaState.Pressed) != 0)
+            {
+                eventData.eligibleForClick = true;
+                eventData.delta = Vector2.zero;
+                eventData.dragging = false;
+                eventData.pressPosition = eventData.position;
+                eventData.pointerPressRaycast = eventData.pointerCurrentRaycast;
+
+                var selectHandler = ExecuteEvents.GetEventHandler<ISelectHandler>(hoverTarget);
+
+                // If we have clicked something new, deselect the old thing
+                // and leave 'selection handling' up to the press event.
+                if (selectHandler != eventSystem.currentSelectedGameObject)
+                    eventSystem.SetSelectedGameObject(null, eventData);
+
+                // search for the control that will receive the press.
+                // if we can't find a press handler set the press
+                // handler to be what would receive a click.
+
+                pointerDown?.Invoke(hoverTarget, eventData);
+                var newPressed = ExecuteEvents.ExecuteHierarchy(hoverTarget, eventData, ExecuteEvents.pointerDownHandler);
+
+                // We didn't find a press handler, so we search for a click handler.
+                if (newPressed == null)
+                    newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(hoverTarget);
+
+                var time = Time.unscaledTime;
+
+                if (newPressed == eventData.lastPress && ((time - eventData.clickTime) < m_ClickSpeed))
+                    ++eventData.clickCount;
+                else
+                    eventData.clickCount = 1;
+
+                eventData.clickTime = time;
+
+                eventData.pointerPress = newPressed;
+                eventData.rawPointerPress = hoverTarget;
+
+                // Save the drag handler for drag events during this mouse down.
+                var dragObject = ExecuteEvents.GetEventHandler<IDragHandler>(hoverTarget);
+                eventData.pointerDrag = dragObject;
+
+                if (dragObject != null)
+                {
+                    initializePotentialDrag?.Invoke(dragObject, eventData);
+                    ExecuteEvents.Execute(dragObject, eventData, ExecuteEvents.initializePotentialDrag);
+                }
+            }
+
+            if ((mouseButtonChanges & ButtonDeltaState.Released) != 0)
+            {
+                var target = eventData.pointerPress;
+                pointerUp?.Invoke(target, eventData);
+                ExecuteEvents.Execute(target, eventData, ExecuteEvents.pointerUpHandler);
+
+                var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(hoverTarget);
+                var pointerDrag = eventData.pointerDrag;
+                if (target == pointerUpHandler && eventData.eligibleForClick)
+                {
+                    pointerClick?.Invoke(target, eventData);
+                    ExecuteEvents.Execute(target, eventData, ExecuteEvents.pointerClickHandler);
+                }
+                else if (eventData.dragging && pointerDrag != null)
+                {
+                    drop?.Invoke(hoverTarget, eventData);
+                    ExecuteEvents.ExecuteHierarchy(hoverTarget, eventData, ExecuteEvents.dropHandler);
+
+                    endDrag?.Invoke(pointerDrag, eventData);
+                    ExecuteEvents.Execute(pointerDrag, eventData, ExecuteEvents.endDragHandler);
+                }
+
+                eventData.eligibleForClick = eventData.dragging = false;
+                eventData.pointerPress = eventData.rawPointerPress = eventData.pointerDrag = null;
+            }
+        }
+
+        void ProcessMouseButtonDrag(PointerEventData eventData, float pixelDragThresholdMultiplier = 1.0f)
+        {
+            if (!eventData.IsPointerMoving() ||
+                Cursor.lockState == CursorLockMode.Locked ||
+                eventData.pointerDrag == null)
+            {
+                return;
+            }
+
+            if (!eventData.dragging)
+            {
+                if ((eventData.pressPosition - eventData.position).sqrMagnitude >= ((eventSystem.pixelDragThreshold * eventSystem.pixelDragThreshold) * pixelDragThresholdMultiplier))
+                {
+                    var target = eventData.pointerDrag;
+                    beginDrag?.Invoke(target, eventData);
+                    ExecuteEvents.Execute(target, eventData, ExecuteEvents.beginDragHandler);
+                    eventData.dragging = true;
+                }
+            }
+
+            if (eventData.dragging)
+            {
+                // If we moved from our initial press object, process an up for that object.
+                var target = eventData.pointerPress;
+                if (target != eventData.pointerDrag)
+                {
+                    pointerUp?.Invoke(target, eventData);
+                    ExecuteEvents.Execute(target, eventData, ExecuteEvents.pointerUpHandler);
+
+                    eventData.eligibleForClick = false;
+                    eventData.pointerPress = null;
+                    eventData.rawPointerPress = null;
+                }
+
+                drag?.Invoke(eventData.pointerDrag, eventData);
+                ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.dragHandler);
+            }
+        }
+
+        void ProcessMouseScroll(PointerEventData eventData)
+        {
+            var scrollDelta = eventData.scrollDelta;
+            if (!Mathf.Approximately(scrollDelta.sqrMagnitude, 0f))
+            {
+                var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler>(eventData.pointerEnter);
+                scroll?.Invoke(scrollHandler, eventData);
+                ExecuteEvents.ExecuteHierarchy(scrollHandler, eventData, ExecuteEvents.scrollHandler);
+            }
+        }
+
+        internal void ProcessTouch(ref TouchModel touchState)
+        {
+            if (!touchState.changedThisFrame)
+                return;
+
+            var eventData = GetOrCreateCachedPointerEvent();
+            eventData.Reset();
+
+            touchState.CopyTo(eventData);
+
+            eventData.pointerCurrentRaycast = (touchState.selectPhase == TouchPhase.Canceled) ? new RaycastResult() : PerformRaycast(eventData);
+            eventData.button = PointerEventData.InputButton.Left;
+
+            ProcessMouseButton(touchState.selectDelta, eventData);
+            ProcessMouseMovement(eventData);
+            ProcessMouseButtonDrag(eventData);
+
+            touchState.CopyFrom(eventData);
+
+            touchState.OnFrameFinished();
+        }
+
+        internal void ProcessTrackedDevice(ref TrackedDeviceModel deviceState, bool force = false)
+        {
+            if (!deviceState.changedThisFrame && !force)
+                return;
+
+            var eventData = GetOrCreateCachedTrackedDeviceEvent();
+            eventData.Reset();
+            deviceState.CopyTo(eventData);
+
+            eventData.button = PointerEventData.InputButton.Left;
+
+            // Demolish the screen position so we don't trigger any hits from a GraphicRaycaster component on a Canvas.
+            // The position value is not used by the TrackedDeviceGraphicRaycaster.
+            // Restore the original value after the Raycast is complete.
+            var savedPosition = eventData.position;
+            eventData.position = new Vector2(float.MinValue, float.MinValue);
+            eventData.pointerCurrentRaycast = PerformRaycast(eventData);
+            eventData.position = savedPosition;
+
+            // Get associated camera, or main-tagged camera, or camera from raycast, and if *nothing* exists, then abort processing this frame.
+            // ReSharper disable once LocalVariableHidesMember
+            var camera = uiCamera != null ? uiCamera : (uiCamera = Camera.main);
+            if (camera == null)
+            {
+                var module = eventData.pointerCurrentRaycast.module;
+                if (module != null)
+                    camera = module.eventCamera;
+            }
+
+            if (camera != null)
+            {
+                Vector2 screenPosition;
+                if (eventData.pointerCurrentRaycast.isValid)
+                {
+                    screenPosition = camera.WorldToScreenPoint(eventData.pointerCurrentRaycast.worldPosition);
+                }
+                else
+                {
+                    var endPosition = eventData.rayPoints.Count > 0 ? eventData.rayPoints[eventData.rayPoints.Count - 1] : Vector3.zero;
+                    screenPosition = camera.WorldToScreenPoint(endPosition);
+                    eventData.position = screenPosition;
+                }
+
+                var thisFrameDelta = screenPosition - eventData.position;
+                eventData.position = screenPosition;
+                eventData.delta = thisFrameDelta;
+
+                ProcessMouseButton(deviceState.selectDelta, eventData);
+                ProcessMouseMovement(eventData);
+                ProcessMouseScroll(eventData);
+                ProcessMouseButtonDrag(eventData, m_TrackedDeviceDragThresholdMultiplier);
+
+                deviceState.CopyFrom(eventData);
+            }
+
+            deviceState.OnFrameFinished();
+        }
+
+        // TODO Update UIInputModule to make use of unused ProcessJoystick method
+        /// <summary>
+        /// Takes an existing JoystickModel and dispatches all relevant changes through the event system.
+        /// It also updates the internal data of the JoystickModel.
+        /// </summary>
+        /// <param name="joystickState">The joystick state you want to forward into the UI Event System</param>
+        internal void ProcessJoystick(ref JoystickModel joystickState)
+        {
+            var implementationData = joystickState.implementationData;
+
+            var usedSelectionChange = false;
+            var selectedGameObject = eventSystem.currentSelectedGameObject;
+            if (selectedGameObject != null)
+            {
+                var data = GetBaseEventData();
+                updateSelected?.Invoke(selectedGameObject, data);
+                ExecuteEvents.Execute(selectedGameObject, data, ExecuteEvents.updateSelectedHandler);
+                usedSelectionChange = data.used;
+            }
+
+            // Don't send move events if disabled in the EventSystem.
+            if (!eventSystem.sendNavigationEvents)
+                return;
+
+            var movement = joystickState.move;
+            if (!usedSelectionChange && (!Mathf.Approximately(movement.x, 0f) || !Mathf.Approximately(movement.y, 0f)))
+            {
+                var time = Time.unscaledTime;
+
+                var moveVector = joystickState.move;
+
+                var moveDirection = MoveDirection.None;
+                if (moveVector.sqrMagnitude > m_MoveDeadzone * m_MoveDeadzone)
+                {
+                    if (Mathf.Abs(moveVector.x) > Mathf.Abs(moveVector.y))
+                        moveDirection = (moveVector.x > 0) ? MoveDirection.Right : MoveDirection.Left;
+                    else
+                        moveDirection = (moveVector.y > 0) ? MoveDirection.Up : MoveDirection.Down;
+                }
+
+                if (moveDirection != implementationData.lastMoveDirection)
+                {
+                    implementationData.consecutiveMoveCount = 0;
+                }
+
+                if (moveDirection != MoveDirection.None)
+                {
+                    var allow = true;
+                    if (implementationData.consecutiveMoveCount != 0)
+                    {
+                        if (implementationData.consecutiveMoveCount > 1)
+                            allow = (time > (implementationData.lastMoveTime + m_RepeatRate));
+                        else
+                            allow = (time > (implementationData.lastMoveTime + m_RepeatDelay));
+                    }
+
+                    if (allow)
+                    {
+                        var eventData = GetOrCreateCachedAxisEvent();
+                        eventData.Reset();
+
+                        eventData.moveVector = moveVector;
+                        eventData.moveDir = moveDirection;
+
+                        move?.Invoke(selectedGameObject, eventData);
+                        ExecuteEvents.Execute(selectedGameObject, eventData, ExecuteEvents.moveHandler);
+                        usedSelectionChange = eventData.used;
+
+                        implementationData.consecutiveMoveCount++;
+                        implementationData.lastMoveTime = time;
+                        implementationData.lastMoveDirection = moveDirection;
+                    }
+                }
+                else
+                    implementationData.consecutiveMoveCount = 0;
+            }
+            else
+                implementationData.consecutiveMoveCount = 0;
+
+            if (!usedSelectionChange)
+            {
+                if (selectedGameObject != null)
+                {
+                    var data = GetBaseEventData();
+                    if ((joystickState.submitButtonDelta & ButtonDeltaState.Pressed) != 0)
+                    {
+                        submit?.Invoke(selectedGameObject, data);
+                        ExecuteEvents.Execute(selectedGameObject, data, ExecuteEvents.submitHandler);
+                    }
+
+                    if (!data.used && (joystickState.cancelButtonDelta & ButtonDeltaState.Pressed) != 0)
+                    {
+                        cancel?.Invoke(selectedGameObject, data);
+                        ExecuteEvents.Execute(selectedGameObject, data, ExecuteEvents.cancelHandler);
+                    }
+                }
+            }
+
+            joystickState.implementationData = implementationData;
+            joystickState.OnFrameFinished();
+        }
+
+        PointerEventData GetOrCreateCachedPointerEvent()
+        {
+            var result = m_CachedPointerEvent;
+            if (result == null)
+            {
+                result = new PointerEventData(eventSystem);
+                m_CachedPointerEvent = result;
+            }
+
+            return result;
+        }
+
+        TrackedDeviceEventData GetOrCreateCachedTrackedDeviceEvent()
+        {
+            var result = m_CachedTrackedDeviceEventData;
+            if (result == null)
+            {
+                result = new TrackedDeviceEventData(eventSystem);
+                m_CachedTrackedDeviceEventData = result;
+            }
+
+            return result;
+        }
+
+        AxisEventData GetOrCreateCachedAxisEvent()
+        {
+            var result = m_CachedAxisEvent;
+            if (result == null)
+            {
+                result = new AxisEventData(eventSystem);
+                m_CachedAxisEvent = result;
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.cs.meta b/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e3cf61e2b74653916b3803895412dc9fc74f6ba0
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/UIInputModuleFix.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 35ac9e67600f67849810a275d97ed64c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Plugins/XRUIInputModuleFix/XRUIInputModuleFix.cs b/Assets/Plugins/XRUIInputModuleFix/XRUIInputModuleFix.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bba96e867a8309c3fa9641bd80ab4a0508c0b421
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/XRUIInputModuleFix.cs
@@ -0,0 +1,325 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine.InputSystem;
+using UnityEngine.EventSystems;
+using UnityEngine.UIElements;
+
+namespace UnityEngine.XR.Interaction.Toolkit.UI
+{
+    /// <summary>
+    /// Custom class for input modules that send UI input in XR. Adapted for UIToolkit runtime worldspace panels.
+    /// </summary>
+    public class XRUIInputModuleFix : UIInputModuleFix
+    {
+        struct RegisteredInteractor
+        {
+            public IUIInteractor interactor;
+            public TrackedDeviceModel model;
+
+            public RegisteredInteractor(IUIInteractor interactor, int deviceIndex)
+            {
+                this.interactor = interactor;
+                model = new TrackedDeviceModel(deviceIndex);
+            }
+        }
+
+        struct RegisteredTouch
+        {
+            public bool isValid;
+            public int touchId;
+            public TouchModel model;
+
+            public RegisteredTouch(Touch touch, int deviceIndex)
+            {
+                touchId = touch.fingerId;
+                model = new TouchModel(deviceIndex);
+                isValid = true;
+            }
+        }
+
+        [SerializeField, HideInInspector]
+        [Tooltip("The maximum distance to raycast with tracked devices to find hit objects.")]
+        float m_MaxTrackedDeviceRaycastDistance = 1000f;
+
+        /// <summary>
+        /// The maximum distance to raycast with tracked devices to find hit objects.
+        /// </summary>
+        [Obsolete("maxRaycastDistance has been deprecated. Its value was unused, calling this property is unnecessary and should be removed.")]
+        public float maxRaycastDistance
+        {
+            get => m_MaxTrackedDeviceRaycastDistance;
+            set => m_MaxTrackedDeviceRaycastDistance = value;
+        }
+
+        [SerializeField]
+        [Tooltip("If true, will forward 3D tracked device data to UI elements.")]
+        bool m_EnableXRInput = true;
+
+        /// <summary>
+        /// If <see langword="true"/>, will forward 3D tracked device data to UI elements.
+        /// </summary>
+        public bool enableXRInput
+        {
+            get => m_EnableXRInput;
+            set => m_EnableXRInput = value;
+        }
+
+        [SerializeField]
+        [Tooltip("If true, will forward 2D mouse data to UI elements.")]
+        bool m_EnableMouseInput = true;
+
+        /// <summary>
+        /// If <see langword="true"/>, will forward 2D mouse data to UI elements.
+        /// </summary>
+        public bool enableMouseInput
+        {
+            get => m_EnableMouseInput;
+            set => m_EnableMouseInput = value;
+        }
+
+        [SerializeField]
+        [Tooltip("If true, will forward 2D touch data to UI elements.")]
+        bool m_EnableTouchInput = true;
+
+        [SerializeField]
+        protected bool usePenPointerIdBase = false;
+
+        /// <summary>
+        /// If <see langword="true"/>, will forward 2D touch data to UI elements.
+        /// </summary>
+        public bool enableTouchInput
+        {
+            get => m_EnableTouchInput;
+            set => m_EnableTouchInput = value;
+        }
+
+        MouseModel m_Mouse;
+
+        readonly List<RegisteredTouch> m_RegisteredTouches = new List<RegisteredTouch>();
+
+        int m_RollingInteractorIndex = PointerId.touchPointerIdBase;
+        bool m_penBaseIdAssigned = false;
+
+        readonly List<RegisteredInteractor> m_RegisteredInteractors = new List<RegisteredInteractor>();
+
+        /// <inheritdoc />
+        protected override void OnEnable()
+        {
+            base.OnEnable();
+            m_Mouse = new MouseModel(0);
+        }
+
+        public override int ConvertUIToolkitPointerId(PointerEventData sourcePointerData)
+        {
+            return sourcePointerData.pointerId;
+        }
+
+        /// <summary>
+        /// Register an <see cref="IUIInteractor"/> with the UI system.
+        /// Calling this will enable it to start interacting with UI.
+        /// </summary>
+        /// <param name="interactor">The <see cref="IUIInteractor"/> to use.</param>
+        public void RegisterInteractor(IUIInteractor interactor)
+        {
+            for (var i = 0; i < m_RegisteredInteractors.Count; i++)
+            {
+                if (m_RegisteredInteractors[i].interactor == interactor)
+                    return;
+            }
+
+            if (usePenPointerIdBase && !m_penBaseIdAssigned)
+            {
+                m_penBaseIdAssigned = true;
+                m_RegisteredInteractors.Add(new RegisteredInteractor(interactor, PointerId.penPointerIdBase));
+            }
+            else
+            {
+                if (usePenPointerIdBase && m_RollingInteractorIndex == PointerId.penPointerIdBase)
+                    ++m_RollingInteractorIndex;
+                
+                m_RegisteredInteractors.Add(new RegisteredInteractor(interactor, m_RollingInteractorIndex++));
+            }
+        }
+
+        /// <summary>
+        /// Unregisters an <see cref="IUIInteractor"/> with the UI system.
+        /// This cancels all UI Interaction and makes the <see cref="IUIInteractor"/> no longer able to affect UI.
+        /// </summary>
+        /// <param name="interactor">The <see cref="IUIInteractor"/> to stop using.</param>
+        public void UnregisterInteractor(IUIInteractor interactor)
+        {
+            for (var i = 0; i < m_RegisteredInteractors.Count; i++)
+            {
+                if (m_RegisteredInteractors[i].interactor == interactor)
+                {
+                    var registeredInteractor = m_RegisteredInteractors[i];
+                    registeredInteractor.interactor = null;
+                    m_RegisteredInteractors[i] = registeredInteractor;
+                    return;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets an <see cref="IUIInteractor"/> from its corresponding Unity UI Pointer Id.
+        /// This can be used to identify individual Interactors from the underlying UI Events.
+        /// </summary>
+        /// <param name="pointerId">A unique integer representing an object that can point at UI.</param>
+        /// <returns>Returns the interactor associated with <paramref name="pointerId"/>.
+        /// Returns <see langword="null"/> if no Interactor is associated (e.g. if it's a mouse event).</returns>
+        public IUIInteractor GetInteractor(int pointerId)
+        {
+            for (var i = 0; i < m_RegisteredInteractors.Count; i++)
+            {
+                if (m_RegisteredInteractors[i].model.pointerId == pointerId)
+                {
+                    return m_RegisteredInteractors[i].interactor;
+                }
+            }
+
+            return null;
+        }
+
+        /// <summary>Retrieves the UI Model for a selected <see cref="IUIInteractor"/>.</summary>
+        /// <param name="interactor">The <see cref="IUIInteractor"/> you want the model for.</param>
+        /// <param name="model">The returned model that reflects the UI state of the <paramref name="interactor"/>.</param>
+        /// <returns>Returns <see langword="true"/> if the model was able to retrieved. Otherwise, returns <see langword="false"/>.</returns>
+        public bool GetTrackedDeviceModel(IUIInteractor interactor, out TrackedDeviceModel model)
+        {
+            for (var i = 0; i < m_RegisteredInteractors.Count; i++)
+            {
+                if (m_RegisteredInteractors[i].interactor == interactor)
+                {
+                    model = m_RegisteredInteractors[i].model;
+                    return true;
+                }
+            }
+
+            model = new TrackedDeviceModel(-1);
+            return false;
+        }
+
+        /// <inheritdoc />
+        protected override void DoProcess()
+        {
+            base.DoProcess();
+
+            if (m_EnableXRInput)
+            {
+                for (var i = 0; i < m_RegisteredInteractors.Count; i++)
+                {
+                    var registeredInteractor = m_RegisteredInteractors[i];
+
+                    // If device is removed, we send a default state to unclick any UI
+                    if (registeredInteractor.interactor == null)
+                    {
+                        registeredInteractor.model.Reset(false);
+                        ProcessTrackedDevice(ref registeredInteractor.model, true);
+                        m_RegisteredInteractors.RemoveAt(i--);
+                    }
+                    else
+                    {
+                        registeredInteractor.interactor.UpdateUIModel(ref registeredInteractor.model);
+                        ProcessTrackedDevice(ref registeredInteractor.model);
+                        m_RegisteredInteractors[i] = registeredInteractor;
+                    }
+                }
+            }
+
+            if (m_EnableMouseInput)
+                ProcessMouse();
+
+            if (m_EnableTouchInput)
+                ProcessTouches();
+        }
+
+        void ProcessMouse()
+        {
+            if (Mouse.current != null)
+            {
+                // The Input System reports scroll in pixels, whereas the old Input class reported in lines.
+                // Example, scrolling down by one notch of a mouse wheel for Input would be (0, -1),
+                // but would be (0, -120) from Input System.
+                // For consistency between the two Active Input Handling modes and with StandaloneInputModule,
+                // scale the scroll value to the range expected by UI.
+                const float kPixelsPerLine = 120f;
+                m_Mouse.position = Mouse.current.position.ReadValue();
+                m_Mouse.scrollDelta = Mouse.current.scroll.ReadValue() * (1 / kPixelsPerLine);
+                m_Mouse.leftButtonPressed = Mouse.current.leftButton.isPressed;
+                m_Mouse.rightButtonPressed = Mouse.current.rightButton.isPressed;
+                m_Mouse.middleButtonPressed = Mouse.current.middleButton.isPressed;
+
+                ProcessMouse(ref m_Mouse);
+            }
+            else if (Input.mousePresent)
+            {
+                m_Mouse.position = Input.mousePosition;
+                m_Mouse.scrollDelta = Input.mouseScrollDelta;
+                m_Mouse.leftButtonPressed = Input.GetMouseButton(0);
+                m_Mouse.rightButtonPressed = Input.GetMouseButton(1);
+                m_Mouse.middleButtonPressed = Input.GetMouseButton(2);
+
+                ProcessMouse(ref m_Mouse);
+            }
+        }
+
+        void ProcessTouches()
+        {
+            if (Input.touchCount > 0)
+            {
+                var touches = Input.touches;
+                foreach (var touch in touches)
+                {
+                    var registeredTouchIndex = -1;
+
+                    // Find if touch already exists
+                    for (var j = 0; j < m_RegisteredTouches.Count; j++)
+                    {
+                        if (touch.fingerId == m_RegisteredTouches[j].touchId)
+                        {
+                            registeredTouchIndex = j;
+                            break;
+                        }
+                    }
+
+                    if (registeredTouchIndex < 0)
+                    {
+                        // Not found, search empty pool
+                        for (var j = 0; j < m_RegisteredTouches.Count; j++)
+                        {
+                            if (!m_RegisteredTouches[j].isValid)
+                            {
+                                // Re-use the Id
+                                var pointerId = m_RegisteredTouches[j].model.pointerId;
+                                m_RegisteredTouches[j] = new RegisteredTouch(touch, pointerId);
+                                registeredTouchIndex = j;
+                                break;
+                            }
+                        }
+
+                        if (registeredTouchIndex < 0)
+                        {
+                            // No Empty slots, add one
+                            registeredTouchIndex = m_RegisteredTouches.Count;
+                            m_RegisteredTouches.Add(new RegisteredTouch(touch, m_RollingInteractorIndex++));
+                        }
+                    }
+
+                    var registeredTouch = m_RegisteredTouches[registeredTouchIndex];
+                    registeredTouch.model.selectPhase = touch.phase;
+                    registeredTouch.model.position = touch.position;
+                    m_RegisteredTouches[registeredTouchIndex] = registeredTouch;
+                }
+
+                for (var i = 0; i < m_RegisteredTouches.Count; i++)
+                {
+                    var registeredTouch = m_RegisteredTouches[i];
+                    ProcessTouch(ref registeredTouch.model);
+                    if (registeredTouch.model.selectPhase == TouchPhase.Ended || registeredTouch.model.selectPhase == TouchPhase.Canceled)
+                        registeredTouch.isValid = false;
+                    m_RegisteredTouches[i] = registeredTouch;
+                }
+            }
+        }
+    }
+}
diff --git a/Assets/Plugins/XRUIInputModuleFix/XRUIInputModuleFix.cs.meta b/Assets/Plugins/XRUIInputModuleFix/XRUIInputModuleFix.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e4739dc453952e3249986e04fd49c5e312313a15
--- /dev/null
+++ b/Assets/Plugins/XRUIInputModuleFix/XRUIInputModuleFix.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 428d96dc7d2b75c4d94bc24fe82df2b5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity
index 12f1e2b7527f3939ec72c4a656657f470ade75a7..c222e9b98853a6bc2fab2ac9cdbf1c71e67b2589 100644
--- a/Assets/Scenes/SampleScene.unity
+++ b/Assets/Scenes/SampleScene.unity
@@ -123,6 +123,77 @@ NavMeshSettings:
     debug:
       m_Flags: 0
   m_NavMeshData: {fileID: 0}
+--- !u!1 &649525595
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 649525598}
+  - component: {fileID: 649525597}
+  - component: {fileID: 649525596}
+  m_Layer: 0
+  m_Name: EventSystem
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &649525596
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 649525595}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 428d96dc7d2b75c4d94bc24fe82df2b5, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_SendPointerHoverToParent: 1
+  m_ClickSpeed: 0.1
+  m_MoveDeadzone: 0.6
+  m_RepeatDelay: 0.5
+  m_RepeatRate: 0.1
+  m_TrackedDeviceDragThresholdMultiplier: 2
+  m_MaxTrackedDeviceRaycastDistance: 1000
+  m_EnableXRInput: 1
+  m_EnableMouseInput: 1
+  m_EnableTouchInput: 1
+  usePenPointerIdBase: 0
+--- !u!114 &649525597
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 649525595}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_FirstSelected: {fileID: 0}
+  m_sendNavigationEvents: 1
+  m_DragThreshold: 10
+--- !u!4 &649525598
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 649525595}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0.8125458, y: -0.20923173, z: 1.4384191}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
 --- !u!1 &1019091453
 GameObject:
   m_ObjectHideFlags: 0
@@ -295,6 +366,23 @@ PrefabInstance:
       objectReference: {fileID: 0}
     m_RemovedComponents: []
   m_SourcePrefab: {fileID: 100100000, guid: 1159b36af7d7c11498e7a839d21a5268, type: 3}
+--- !u!1 &1871036619 stripped
+GameObject:
+  m_CorrespondingSourceObject: {fileID: 8170552307152042535, guid: 8cfe3f32eae0d304285287fececf058b, type: 3}
+  m_PrefabInstance: {fileID: 2094756388}
+  m_PrefabAsset: {fileID: 0}
+--- !u!114 &1871036628
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1871036619}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: fb62641662b746c4ba1436abd6f193eb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
 --- !u!1001 &2094756388
 PrefabInstance:
   m_ObjectHideFlags: 0
diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset
index d189f14dba4f405312c27df223427641de9c641b..b7c4fcb5ffbd9c6d356b90fe8b7b840ae44af6e6 100644
--- a/ProjectSettings/ProjectSettings.asset
+++ b/ProjectSettings/ProjectSettings.asset
@@ -137,8 +137,6 @@ PlayerSettings:
   bundleVersion: 0.1
   preloadedAssets:
   - {fileID: 0}
-  - {fileID: 2022272034958044215, guid: 9c92df50043c4c24988b77aaa754ec15, type: 2}
-  - {fileID: 11400000, guid: 63a6df10c42823e4593558dd07e5406f, type: 2}
   metroInputSource: 0
   wsaTransparentSwapchain: 0
   m_HolographicPauseOnTrackingLoss: 1