diff --git a/Runtime/Components.meta b/Runtime/Components.meta
new file mode 100644
index 0000000..1ba3713
--- /dev/null
+++ b/Runtime/Components.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f5f45af3905a2e249a868a9d96a0816e
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Components/HPUIInteractorLRVisual.cs b/Runtime/Components/HPUIInteractorLRVisual.cs
new file mode 100644
index 0000000..bb1d680
--- /dev/null
+++ b/Runtime/Components/HPUIInteractorLRVisual.cs
@@ -0,0 +1,87 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using UnityEngine.XR.Interaction.Toolkit;
+using UnityEngine.XR.Interaction.Toolkit.Interactors;
+using UnityEngine.XR.Interaction.Toolkit.Interactables;
+using UnityEngine.XR.Interaction.Toolkit.Utilities;
+using UnityEngine.XR.Hands;
+using Unity.XR.CoreUtils;
+using UnityEngine.Pool;
+using ubco.ovilab.HPUI.Interaction;
+
+namespace ubco.ovilab.HPUI.Components
+{
+ public class HPUIInteractorLRVisual: MonoBehaviour
+ {
+ [SerializeField, Tooltip("The line renderer object to manage")]
+ private LineRenderer lineRenderer;
+
+ [SerializeField, Tooltip("The target interactor for event to subscribe to")]
+ private HPUIInteractor hpuiInteractor;
+
+ ///
+ private void OnEnable()
+ {
+ if (hpuiInteractor == null)
+ {
+ hpuiInteractor = GetComponent();
+ }
+
+ if (lineRenderer == null)
+ {
+ lineRenderer = gameObject.AddComponent();
+ }
+
+ if (hpuiInteractor != null)
+ {
+ hpuiInteractor.HoverUpdateEvent.AddListener(OnHoverUpdate);
+ hpuiInteractor.hoverEntered.AddListener(OnHoverEntered);
+ hpuiInteractor.hoverExited.AddListener(OnHoverExited);
+ }
+ }
+
+ ///
+ private void OnDisable()
+ {
+ if (hpuiInteractor != null)
+ {
+ hpuiInteractor.HoverUpdateEvent.RemoveListener(OnHoverUpdate);
+ hpuiInteractor.hoverEntered.RemoveListener(OnHoverEntered);
+ hpuiInteractor.hoverExited.RemoveListener(OnHoverExited);
+ }
+ }
+
+ ///
+ /// Callback for
+ ///
+ private void OnHoverUpdate(HPUIHoverUpdateEventArgs arg)
+ {
+ lineRenderer.SetPosition(0, arg.attachPoint);
+ lineRenderer.SetPosition(1, arg.hoverPoint);
+ }
+
+ ///
+ /// Callback for
+ ///
+ private void OnHoverEntered(HoverEnterEventArgs args)
+ {
+ if (lineRenderer != null)
+ {
+ lineRenderer.enabled = true;
+ }
+ }
+
+ ///
+ /// Callback for
+ ///
+ private void OnHoverExited(HoverExitEventArgs args)
+ {
+ if (lineRenderer != null)
+ {
+ lineRenderer.enabled = false;
+ }
+ }
+ }
+}
diff --git a/Runtime/Components/HPUIInteractorLRVisual.cs.meta b/Runtime/Components/HPUIInteractorLRVisual.cs.meta
new file mode 100644
index 0000000..4078f8c
--- /dev/null
+++ b/Runtime/Components/HPUIInteractorLRVisual.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 79451953501e8b045bfd05a4fdff6247
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Components/HPUIInteractorTransformVisual.cs b/Runtime/Components/HPUIInteractorTransformVisual.cs
new file mode 100644
index 0000000..5cfa632
--- /dev/null
+++ b/Runtime/Components/HPUIInteractorTransformVisual.cs
@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using UnityEngine.XR.Interaction.Toolkit;
+using UnityEngine.XR.Interaction.Toolkit.Interactors;
+using UnityEngine.XR.Interaction.Toolkit.Interactables;
+using UnityEngine.XR.Interaction.Toolkit.Utilities;
+using UnityEngine.XR.Hands;
+using Unity.XR.CoreUtils;
+using UnityEngine.Pool;
+using ubco.ovilab.HPUI.Interaction;
+
+namespace ubco.ovilab.HPUI.Components
+{
+ public class HPUIInteractorTransformVisual: MonoBehaviour
+ {
+ [SerializeField, Tooltip("The line renderer object to manage")]
+ private Transform visualTransform;
+
+ [SerializeField, Tooltip("The target interactor for event to subscribe to")]
+ private HPUIInteractor hpuiInteractor;
+
+ ///
+ private void OnEnable()
+ {
+ if (hpuiInteractor == null)
+ {
+ hpuiInteractor = GetComponent();
+ }
+
+ if (hpuiInteractor != null)
+ {
+ hpuiInteractor.HoverUpdateEvent.AddListener(OnHoverUpdate);
+ hpuiInteractor.hoverEntered.AddListener(OnHoverEntered);
+ hpuiInteractor.hoverExited.AddListener(OnHoverExited);
+ }
+ }
+
+ ///
+ private void OnDisable()
+ {
+ if (hpuiInteractor != null)
+ {
+ hpuiInteractor.HoverUpdateEvent.RemoveListener(OnHoverUpdate);
+ hpuiInteractor.hoverEntered.RemoveListener(OnHoverEntered);
+ hpuiInteractor.hoverExited.RemoveListener(OnHoverExited);
+ }
+ }
+
+ ///
+ /// Callback for
+ ///
+ private void OnHoverUpdate(HPUIHoverUpdateEventArgs arg)
+ {
+ visualTransform.position = arg.hoverPoint;
+ }
+
+ ///
+ /// Callback for
+ ///
+ private void OnHoverEntered(HoverEnterEventArgs args)
+ {
+ if (visualTransform != null)
+ {
+ visualTransform.gameObject.SetActive(true);
+ }
+ }
+
+ ///
+ /// Callback for
+ ///
+ private void OnHoverExited(HoverExitEventArgs args)
+ {
+ if (visualTransform != null)
+ {
+ visualTransform.gameObject.SetActive(false);
+ }
+ }
+ }
+}
diff --git a/Runtime/Components/HPUIInteractorTransformVisual.cs.meta b/Runtime/Components/HPUIInteractorTransformVisual.cs.meta
new file mode 100644
index 0000000..d139f1f
--- /dev/null
+++ b/Runtime/Components/HPUIInteractorTransformVisual.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 97e88c4eb70da9a40b5aa8003f63a998
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: