version 2.0.0
This commit is contained in:
8
com.htc.upm.vive.openxr/Runtime/Raycast/Resources.meta
Normal file
8
com.htc.upm.vive.openxr/Runtime/Raycast/Resources.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58897b8e0a7d7b24999d04f6ab923561
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6f2f61f885577442b9a4cafff9a62e6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,85 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: GazePointerDef
|
||||
m_Shader: {fileID: 4800000, guid: 105680c0299ff144b972446cb07a81c5, type: 3}
|
||||
m_ShaderKeywords:
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _BumpScale: 1
|
||||
- _ColorMask: 15
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UVSec: 0
|
||||
- _UseUIAlphaClip: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58d737ad5355ee04eb3f7f57de71df1e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 571cc732a01df91488bf9c7a5909fead
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
Shader "VIVE/OpenXR/Raycast/GazePointerDef"
|
||||
{
|
||||
Properties{
|
||||
_MainTex("Font Texture", 2D) = "white" {}
|
||||
_Color("Text Color", Color) = (1,1,1,1)
|
||||
|
||||
_StencilComp("Stencil Comparison", Float) = 8
|
||||
_Stencil("Stencil ID", Float) = 0
|
||||
_StencilOp("Stencil Operation", Float) = 0
|
||||
_StencilWriteMask("Stencil Write Mask", Float) = 255
|
||||
_StencilReadMask("Stencil Read Mask", Float) = 255
|
||||
|
||||
_ColorMask("Color Mask", Float) = 15
|
||||
}
|
||||
|
||||
SubShader {
|
||||
|
||||
Tags
|
||||
{
|
||||
"Queue"="Transparent"
|
||||
"IgnoreProjector"="True"
|
||||
"RenderType"="Transparent"
|
||||
"PreviewType"="Plane"
|
||||
}
|
||||
|
||||
Stencil
|
||||
{
|
||||
Ref [_Stencil]
|
||||
Comp [_StencilComp]
|
||||
Pass [_StencilOp]
|
||||
ReadMask [_StencilReadMask]
|
||||
WriteMask [_StencilWriteMask]
|
||||
}
|
||||
|
||||
Lighting Off
|
||||
Cull Off
|
||||
ZTest Off
|
||||
ZWrite Off
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
ColorMask [_ColorMask]
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct appdata_t {
|
||||
float4 vertex : POSITION;
|
||||
fixed4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f {
|
||||
float4 vertex : SV_POSITION;
|
||||
fixed4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
uniform float4 _MainTex_ST;
|
||||
uniform fixed4 _Color;
|
||||
|
||||
v2f vert (appdata_t v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.color = v.color * _Color;
|
||||
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
|
||||
#ifdef UNITY_HALF_TEXEL_OFFSET
|
||||
o.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
|
||||
#endif
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag (v2f i) : SV_Target
|
||||
{
|
||||
fixed4 col = i.color;
|
||||
col.a *= tex2D(_MainTex, i.texcoord).a;
|
||||
clip (col.a - 0.01);
|
||||
return col;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 105680c0299ff144b972446cb07a81c5
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
com.htc.upm.vive.openxr/Runtime/Raycast/Scripts.meta
Normal file
8
com.htc.upm.vive.openxr/Runtime/Raycast/Scripts.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 827931b60f4bb6c438a0a3473ead12a5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
public static class CanvasProvider
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.CanvasProvider";
|
||||
private static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
|
||||
private static List<Canvas> s_TargetCanvases = new List<Canvas>();
|
||||
|
||||
public static bool RegisterTargetCanvas(Canvas canvas)
|
||||
{
|
||||
if (canvas != null && !s_TargetCanvases.Contains(canvas))
|
||||
{
|
||||
DEBUG("RegisterTargetCanvas() " + canvas.gameObject.name);
|
||||
s_TargetCanvases.Add(canvas);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public static bool RemoveTargetCanvas(Canvas canvas)
|
||||
{
|
||||
if (canvas != null && s_TargetCanvases.Contains(canvas))
|
||||
{
|
||||
DEBUG("RemoveTargetCanvas() " + canvas.gameObject.name);
|
||||
s_TargetCanvases.Remove(canvas);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public static Canvas[] GetTargetCanvas() { return s_TargetCanvases.ToArray(); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c68c9c4b6f2ba4843ac07957f9727968
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
public class ControllerRaycastPointer : RaycastPointer
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.ControllerRaycastPointer";
|
||||
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
|
||||
#region Inspector
|
||||
[SerializeField]
|
||||
private InputActionReference m_IsTracked = null;
|
||||
public InputActionReference IsTracked { get => m_IsTracked; set => m_IsTracked = value; }
|
||||
|
||||
[Tooltip("Keys for control.")]
|
||||
[SerializeField]
|
||||
private List<InputActionReference> m_ActionsKeys = new List<InputActionReference>();
|
||||
public List<InputActionReference> ActionKeys { get { return m_ActionsKeys; } set { m_ActionsKeys = value; } }
|
||||
bool getBool(InputActionReference actionReference)
|
||||
{
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
if (actionReference.action.activeControl.valueType == typeof(bool))
|
||||
return actionReference.action.ReadValue<bool>();
|
||||
if (actionReference.action.activeControl.valueType == typeof(float))
|
||||
return actionReference.action.ReadValue<float>() > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[Tooltip("To show the ray anymore.")]
|
||||
[SerializeField]
|
||||
private bool m_AlwaysEnable = false;
|
||||
public bool AlwaysEnable { get { return m_AlwaysEnable; } set { m_AlwaysEnable = value; } }
|
||||
#endregion
|
||||
|
||||
#region MonoBehaviour overrides
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
}
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!IsInteractable()) { return; }
|
||||
|
||||
UpdateButtonStates();
|
||||
}
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
DEBUG("Start()");
|
||||
}
|
||||
private void OnApplicationPause(bool pause)
|
||||
{
|
||||
DEBUG("OnApplicationPause() " + pause);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool IsInteractable()
|
||||
{
|
||||
bool enabled = RaycastSwitch.Controller.Enabled;
|
||||
bool validPose = getBool(m_IsTracked);
|
||||
|
||||
m_Interactable = (m_AlwaysEnable || enabled) && validPose;
|
||||
|
||||
if (printIntervalLog)
|
||||
{
|
||||
DEBUG("IsInteractable() enabled: " + enabled
|
||||
+ ", validPose: " + validPose
|
||||
+ ", m_AlwaysEnable: " + m_AlwaysEnable
|
||||
+ ", m_Interactable: " + m_Interactable);
|
||||
}
|
||||
|
||||
return m_Interactable;
|
||||
}
|
||||
|
||||
private void UpdateButtonStates()
|
||||
{
|
||||
if (m_ActionsKeys == null) { return; }
|
||||
|
||||
down = false;
|
||||
for (int i = 0; i < m_ActionsKeys.Count; i++)
|
||||
{
|
||||
if (!hold)
|
||||
{
|
||||
down |= getBool(m_ActionsKeys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
hold = false;
|
||||
for (int i = 0; i < m_ActionsKeys.Count; i++)
|
||||
{
|
||||
hold |= getBool(m_ActionsKeys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#region RaycastImpl Actions overrides
|
||||
internal bool down = false, hold = false;
|
||||
protected override bool OnDown()
|
||||
{
|
||||
return down;
|
||||
}
|
||||
protected override bool OnHold()
|
||||
{
|
||||
return hold;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0ed4455b73284942bc407f2d8a98eee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,268 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.XR;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
public class GazeRaycastRing : RaycastRing
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.GazeRaycastRing";
|
||||
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
void INTERVAL(string msg) { if (printIntervalLog) { DEBUG(msg); } }
|
||||
|
||||
#region Inspector
|
||||
[SerializeField]
|
||||
[Tooltip("Use Eye Tracking data for Gaze.")]
|
||||
private bool m_EyeTracking = false;
|
||||
public bool EyeTracking { get { return m_EyeTracking; } set { m_EyeTracking = value; } }
|
||||
|
||||
[SerializeField]
|
||||
private InputActionReference m_EyePose = null;
|
||||
public InputActionReference EyePose { get => m_EyePose; set => m_EyePose = value; }
|
||||
bool getTracked(InputActionReference actionReference)
|
||||
{
|
||||
bool tracked = false;
|
||||
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.InputSystem.XR.PoseState))
|
||||
#else
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.XR.OpenXR.Input.Pose))
|
||||
#endif
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
tracked = actionReference.action.ReadValue<UnityEngine.InputSystem.XR.PoseState>().isTracked;
|
||||
#else
|
||||
tracked = actionReference.action.ReadValue<UnityEngine.XR.OpenXR.Input.Pose>().isTracked;
|
||||
#endif
|
||||
INTERVAL("getTracked(" + tracked + ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
INTERVAL("getTracked() invalid input: " + value);
|
||||
}
|
||||
|
||||
return tracked;
|
||||
}
|
||||
InputTrackingState getTrackingState(InputActionReference actionReference)
|
||||
{
|
||||
InputTrackingState state = InputTrackingState.None;
|
||||
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.InputSystem.XR.PoseState))
|
||||
#else
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.XR.OpenXR.Input.Pose))
|
||||
#endif
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
state = actionReference.action.ReadValue<UnityEngine.InputSystem.XR.PoseState>().trackingState;
|
||||
#else
|
||||
state = actionReference.action.ReadValue<UnityEngine.XR.OpenXR.Input.Pose>().trackingState;
|
||||
#endif
|
||||
INTERVAL("getTrackingState(" + state + ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
INTERVAL("getTrackingState() invalid input: " + value);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
Vector3 getDirection(InputActionReference actionReference)
|
||||
{
|
||||
Quaternion rotation = Quaternion.identity;
|
||||
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.InputSystem.XR.PoseState))
|
||||
#else
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.XR.OpenXR.Input.Pose))
|
||||
#endif
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
rotation = actionReference.action.ReadValue<UnityEngine.InputSystem.XR.PoseState>().rotation;
|
||||
#else
|
||||
rotation = actionReference.action.ReadValue<UnityEngine.XR.OpenXR.Input.Pose>().rotation;
|
||||
#endif
|
||||
INTERVAL("getDirection(" + rotation.x.ToString() + ", " + rotation.y.ToString() + ", " + rotation.z.ToString() + ", " + rotation.w.ToString() + ")");
|
||||
return (rotation * Vector3.forward);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
INTERVAL("getDirection() invalid input: " + value);
|
||||
}
|
||||
|
||||
return Vector3.forward;
|
||||
}
|
||||
Vector3 getOrigin(InputActionReference actionReference)
|
||||
{
|
||||
var origin = Vector3.zero;
|
||||
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.InputSystem.XR.PoseState))
|
||||
#else
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.XR.OpenXR.Input.Pose))
|
||||
#endif
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
origin = actionReference.action.ReadValue<UnityEngine.InputSystem.XR.PoseState>().position;
|
||||
#else
|
||||
origin = actionReference.action.ReadValue<UnityEngine.XR.OpenXR.Input.Pose>().position;
|
||||
#endif
|
||||
INTERVAL("getOrigin(" + origin.x.ToString() + ", " + origin.y.ToString() + ", " + origin.z.ToString() + ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
INTERVAL("getOrigin() invalid input: " + value);
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
|
||||
[Tooltip("Event triggered by gaze.")]
|
||||
[SerializeField]
|
||||
private GazeEvent m_InputEvent = GazeEvent.Down;
|
||||
public GazeEvent InputEvent { get { return m_InputEvent; } set { m_InputEvent = value; } }
|
||||
|
||||
[Tooltip("Keys for control.")]
|
||||
[SerializeField]
|
||||
private List<InputActionReference> m_ActionsKeys = new List<InputActionReference>();
|
||||
public List<InputActionReference> ActionKeys { get { return m_ActionsKeys; } set { m_ActionsKeys = value; } }
|
||||
|
||||
bool getButton(InputActionReference actionReference)
|
||||
{
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
if (actionReference.action.activeControl.valueType == typeof(bool))
|
||||
return actionReference.action.ReadValue<bool>();
|
||||
if (actionReference.action.activeControl.valueType == typeof(float))
|
||||
return actionReference.action.ReadValue<float>() > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
INTERVAL("getButton() invalid input: " + value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool m_AlwaysEnable = false;
|
||||
public bool AlwaysEnable { get { return m_AlwaysEnable; } set { m_AlwaysEnable = value; } }
|
||||
#endregion
|
||||
|
||||
#region MonoBehaviour overrides
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
}
|
||||
|
||||
private bool m_KeyDown = false;
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!IsInteractable()) { return; }
|
||||
|
||||
m_KeyDown = ButtonPressed();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool IsInteractable()
|
||||
{
|
||||
bool enabled = RaycastSwitch.Gaze.Enabled;
|
||||
|
||||
m_Interactable = (m_AlwaysEnable || enabled);
|
||||
|
||||
if (printIntervalLog)
|
||||
{
|
||||
DEBUG("IsInteractable() enabled: " + enabled + ", m_AlwaysEnable: " + m_AlwaysEnable);
|
||||
}
|
||||
|
||||
return m_Interactable;
|
||||
}
|
||||
|
||||
internal bool m_Down = false, m_Hold = false;
|
||||
private bool ButtonPressed()
|
||||
{
|
||||
if (m_ActionsKeys == null) { return false; }
|
||||
|
||||
bool keyDown = false;
|
||||
for (int i = 0; i < m_ActionsKeys.Count; i++)
|
||||
{
|
||||
var pressed = getButton(m_ActionsKeys[i]);
|
||||
if (pressed)
|
||||
DEBUG("ButtonPressed()" + m_ActionsKeys[i].name + " is pressed.");
|
||||
keyDown |= pressed;
|
||||
}
|
||||
|
||||
m_Down = false;
|
||||
if (!m_Hold) { m_Down |= keyDown; }
|
||||
m_Hold = keyDown;
|
||||
|
||||
return m_Down;
|
||||
}
|
||||
|
||||
protected override bool UseEyeData(out Vector3 direction)
|
||||
{
|
||||
bool tracked = getTracked(m_EyePose);
|
||||
bool useEye = m_EyeTracking && tracked;
|
||||
|
||||
getTrackingState(m_EyePose);
|
||||
getOrigin(m_EyePose);
|
||||
direction = getDirection(m_EyePose);
|
||||
|
||||
INTERVAL("UseEyeData() m_EyeTracking: " + m_EyeTracking + ", tracked: " + tracked);
|
||||
|
||||
if (!useEye) { return base.UseEyeData(out direction); }
|
||||
|
||||
return useEye;
|
||||
}
|
||||
|
||||
#region RaycastImpl Actions overrides
|
||||
protected override bool OnDown()
|
||||
{
|
||||
if (m_InputEvent != GazeEvent.Down) { return false; }
|
||||
|
||||
bool down = false;
|
||||
if (m_RingPercent >= 100 || m_KeyDown)
|
||||
{
|
||||
m_RingPercent = 0;
|
||||
m_GazeOnTime = Time.unscaledTime;
|
||||
down = true;
|
||||
DEBUG("OnDown()");
|
||||
}
|
||||
|
||||
return down;
|
||||
}
|
||||
protected override bool OnSubmit()
|
||||
{
|
||||
if (m_InputEvent != GazeEvent.Submit) { return false; }
|
||||
|
||||
bool submit = false;
|
||||
if (m_RingPercent >= 100 || m_KeyDown)
|
||||
{
|
||||
m_RingPercent = 0;
|
||||
m_GazeOnTime = Time.unscaledTime;
|
||||
submit = true;
|
||||
DEBUG("OnSubmit()");
|
||||
}
|
||||
|
||||
return submit;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78ba47d171a9d604e87df3c8c0304fde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,285 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR;
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
public class HandRaycastPointer : RaycastPointer
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.HandRaycastPointer ";
|
||||
|
||||
#region Inspector
|
||||
public bool IsLeft = false;
|
||||
|
||||
[Tooltip("To apply poses on the raycast pointer.")]
|
||||
[SerializeField]
|
||||
private bool m_UsePose = true;
|
||||
public bool UsePose { get { return m_UsePose; } set { m_UsePose = value; } }
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
[SerializeField]
|
||||
private InputActionReference m_AimPose = null;
|
||||
public InputActionReference AimPose { get { return m_AimPose; } set { m_AimPose = value; } }
|
||||
bool getAimTracked(InputActionReference actionReference)
|
||||
{
|
||||
bool tracked = false;
|
||||
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.InputSystem.XR.PoseState))
|
||||
#else
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.XR.OpenXR.Input.Pose))
|
||||
#endif
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
tracked = actionReference.action.ReadValue<UnityEngine.InputSystem.XR.PoseState>().isTracked;
|
||||
#else
|
||||
tracked = actionReference.action.ReadValue<UnityEngine.XR.OpenXR.Input.Pose>().isTracked;
|
||||
#endif
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getAimTracked(").Append(tracked).Append(")");
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getAimTracked() invalid input: ").Append(value);
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
|
||||
return tracked;
|
||||
}
|
||||
InputTrackingState getAimTrackingState(InputActionReference actionReference)
|
||||
{
|
||||
InputTrackingState state = InputTrackingState.None;
|
||||
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.InputSystem.XR.PoseState))
|
||||
#else
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.XR.OpenXR.Input.Pose))
|
||||
#endif
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
state = actionReference.action.ReadValue<UnityEngine.InputSystem.XR.PoseState>().trackingState;
|
||||
#else
|
||||
state = actionReference.action.ReadValue<UnityEngine.XR.OpenXR.Input.Pose>().trackingState;
|
||||
#endif
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getAimTrackingState(").Append(state).Append(")");
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getAimTrackingState() invalid input: ").Append(value);
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
Vector3 getAimPosition(InputActionReference actionReference)
|
||||
{
|
||||
var position = Vector3.zero;
|
||||
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.InputSystem.XR.PoseState))
|
||||
#else
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.XR.OpenXR.Input.Pose))
|
||||
#endif
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
position = actionReference.action.ReadValue<UnityEngine.InputSystem.XR.PoseState>().position;
|
||||
#else
|
||||
position = actionReference.action.ReadValue<UnityEngine.XR.OpenXR.Input.Pose>().position;
|
||||
#endif
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getAimPosition(").Append(position.x).Append(", ").Append(position.y).Append(", ").Append(position.z).Append(")");
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getAimPosition() invalid input: ").Append(value);
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
Quaternion getAimRotation(InputActionReference actionReference)
|
||||
{
|
||||
var rotation = Quaternion.identity;
|
||||
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.InputSystem.XR.PoseState))
|
||||
#else
|
||||
if (actionReference.action.activeControl.valueType == typeof(UnityEngine.XR.OpenXR.Input.Pose))
|
||||
#endif
|
||||
{
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
rotation = actionReference.action.ReadValue<UnityEngine.InputSystem.XR.PoseState>().rotation;
|
||||
#else
|
||||
rotation = actionReference.action.ReadValue<UnityEngine.XR.OpenXR.Input.Pose>().rotation;
|
||||
#endif
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getAimRotation(").Append(rotation.x).Append(", ").Append(rotation.y).Append(", ").Append(rotation.z).Append(", ").Append(rotation.w).Append(")");
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getAimRotation() invalid input: ").Append(value);
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
|
||||
return rotation;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private InputActionReference m_PinchStrength = null;
|
||||
public InputActionReference PinchStrength { get => m_PinchStrength; set => m_PinchStrength = value; }
|
||||
float getStrength(InputActionReference actionReference)
|
||||
{
|
||||
float strength = 0;
|
||||
|
||||
if (OpenXRHelper.VALIDATE(actionReference, out string value))
|
||||
{
|
||||
if (actionReference.action.activeControl.valueType == typeof(float))
|
||||
{
|
||||
strength = actionReference.action.ReadValue<float>();
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getStrength(").Append(strength).Append(")");
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("getStrength() invalid input: ").Append(value);
|
||||
DEBUG(sb);
|
||||
}
|
||||
}
|
||||
|
||||
return strength;
|
||||
}
|
||||
#endif
|
||||
|
||||
[Tooltip("Pinch strength to trigger events.")]
|
||||
[SerializeField]
|
||||
private float m_PinchThreshold = .5f;
|
||||
public float PinchThreshold { get { return m_PinchThreshold; } set { m_PinchThreshold = value; } }
|
||||
|
||||
[SerializeField]
|
||||
private bool m_AlwaysEnable = false;
|
||||
public bool AlwaysEnable { get { return m_AlwaysEnable; } set { m_AlwaysEnable = value; } }
|
||||
#endregion
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
validPose = getAimTracked(m_AimPose);
|
||||
trackingStatus = getAimTrackingState(m_AimPose);
|
||||
var origin = getAimPosition(m_AimPose);
|
||||
var rotation = getAimRotation(m_AimPose);
|
||||
strength = getStrength(m_PinchStrength);
|
||||
|
||||
if (!IsInteractable()) { return; }
|
||||
|
||||
if (m_UsePose)
|
||||
{
|
||||
transform.localPosition = origin;
|
||||
transform.localRotation = rotation;
|
||||
}
|
||||
}
|
||||
|
||||
bool validPose = false;
|
||||
InputTrackingState trackingStatus = InputTrackingState.None;
|
||||
private bool IsInteractable()
|
||||
{
|
||||
bool enabled = RaycastSwitch.Hand.Enabled;
|
||||
bool positionTracked = ((trackingStatus & InputTrackingState.Position) != 0);
|
||||
bool rotationTracked = ((trackingStatus & InputTrackingState.Rotation) != 0);
|
||||
|
||||
m_Interactable = (m_AlwaysEnable || enabled)
|
||||
&& validPose
|
||||
&& positionTracked
|
||||
&& rotationTracked;
|
||||
|
||||
if (printIntervalLog)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("IsInteractable() m_Interactable: ").Append(m_Interactable)
|
||||
.Append(", enabled: ").Append(enabled)
|
||||
.Append(", validPose: ").Append(validPose)
|
||||
.Append(", positionTracked: ").Append(positionTracked)
|
||||
.Append(", rotationTracked: ").Append(rotationTracked)
|
||||
.Append(", m_AlwaysEnable: ").Append(m_AlwaysEnable);
|
||||
DEBUG(sb);
|
||||
}
|
||||
|
||||
return m_Interactable;
|
||||
}
|
||||
#endif
|
||||
|
||||
#region RaycastImpl Actions overrides
|
||||
bool eligibleForClick = false;
|
||||
float strength = 0;
|
||||
protected override bool OnDown()
|
||||
{
|
||||
if (!eligibleForClick)
|
||||
{
|
||||
bool down = strength > m_PinchThreshold;
|
||||
if (down)
|
||||
{
|
||||
eligibleForClick = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
protected override bool OnHold()
|
||||
{
|
||||
bool hold = strength > m_PinchThreshold;
|
||||
if (!hold)
|
||||
eligibleForClick = false;
|
||||
return hold;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d1efc78bbff2cc4eb930103b4111b67
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
public class RaycastEventData : PointerEventData
|
||||
{
|
||||
/// <summary> The actor sends an event. </summary>
|
||||
public GameObject Actor { get { return m_Actor; } }
|
||||
private GameObject m_Actor = null;
|
||||
|
||||
public RaycastEventData(EventSystem eventSystem, GameObject actor)
|
||||
: base(eventSystem)
|
||||
{
|
||||
m_Actor = actor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The object which receives events should implement this interface.
|
||||
/// </summary>
|
||||
public interface IHoverHandler : IEventSystemHandler
|
||||
{
|
||||
void OnHover(RaycastEventData eventData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Objects will use
|
||||
/// ExecuteEvents.Execute (GameObject, BaseEventData, RayastEvents.pointerXXXXHandler)
|
||||
/// to send XXXX events.
|
||||
/// </summary>
|
||||
public static class RaycastEvents
|
||||
{
|
||||
#region Event Executor of Hover
|
||||
/// Use ExecuteEvents.Execute (GameObject, BaseEventData, RaycastEvents.pointerHoverHandler)
|
||||
private static void HoverExecutor(IHoverHandler handler, BaseEventData eventData)
|
||||
{
|
||||
handler.OnHover(ExecuteEvents.ValidateEventData<RaycastEventData>(eventData));
|
||||
}
|
||||
public static ExecuteEvents.EventFunction<IHoverHandler> pointerHoverHandler
|
||||
{
|
||||
get { return HoverExecutor; }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34e90154ac6fdd64f85236ba9b967440
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
593
com.htc.upm.vive.openxr/Runtime/Raycast/Scripts/RaycastImpl.cs
Normal file
593
com.htc.upm.vive.openxr/Runtime/Raycast/Scripts/RaycastImpl.cs
Normal file
@@ -0,0 +1,593 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(Camera))]
|
||||
public class RaycastImpl : BaseRaycaster
|
||||
{
|
||||
private StringBuilder m_sb = null;
|
||||
protected StringBuilder sb {
|
||||
get {
|
||||
if (m_sb == null) { m_sb = new StringBuilder(); }
|
||||
return m_sb;
|
||||
}
|
||||
}
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.RaycastImpl ";
|
||||
protected void DEBUG(StringBuilder msg) { Debug.Log(msg); }
|
||||
|
||||
#region Inspector
|
||||
[SerializeField]
|
||||
private bool m_IgnoreReversedGraphics = false;
|
||||
public bool IgnoreReversedGraphics { get { return m_IgnoreReversedGraphics; } set { m_IgnoreReversedGraphics = value; } }
|
||||
[SerializeField]
|
||||
private float m_PhysicsCastDistance = 100;
|
||||
public float PhysicsCastDistance { get { return m_PhysicsCastDistance; } set { m_PhysicsCastDistance = value; } }
|
||||
[SerializeField]
|
||||
private LayerMask m_PhysicsEventMask = ~0;
|
||||
public LayerMask PhysicsEventMask { get { return m_PhysicsEventMask; } set { m_PhysicsEventMask = value; } }
|
||||
#endregion
|
||||
|
||||
private Camera m_Camera = null;
|
||||
public override Camera eventCamera { get { return m_Camera; } }
|
||||
|
||||
#region MonoBehaviour overrides
|
||||
protected override void OnEnable()
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("OnEnable()"); DEBUG(sb);
|
||||
base.OnEnable();
|
||||
|
||||
/// 1. Set up the event camera.
|
||||
m_Camera = GetComponent<Camera>();
|
||||
m_Camera.stereoTargetEye = StereoTargetEyeMask.None;
|
||||
m_Camera.enabled = false;
|
||||
|
||||
/// 2. Set up the EventSystem.
|
||||
if (EventSystem.current == null)
|
||||
{
|
||||
var eventSystemObject = new GameObject("EventSystem");
|
||||
eventSystemObject.AddComponent<EventSystem>();
|
||||
}
|
||||
}
|
||||
protected override void OnDisable()
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("OnDisable()"); DEBUG(sb);
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
int printFrame = 0;
|
||||
protected bool printIntervalLog = false;
|
||||
|
||||
protected bool m_Interactable = true;
|
||||
protected virtual void Update()
|
||||
{
|
||||
printFrame++;
|
||||
printFrame %= 300;
|
||||
printIntervalLog = (printFrame == 0);
|
||||
|
||||
if (!m_Interactable) return;
|
||||
|
||||
/// Use the event camera and EventSystem to reset PointerEventData.
|
||||
ResetEventData();
|
||||
|
||||
/// Update the raycast results
|
||||
resultAppendList.Clear();
|
||||
Raycast(pointerData, resultAppendList);
|
||||
|
||||
pointerData.pointerCurrentRaycast = currentRaycastResult;
|
||||
|
||||
/// Send events
|
||||
HandleRaycastEvent();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Raycast Result Handling
|
||||
static readonly Comparison<RaycastResult> rrComparator = RaycastResultComparator;
|
||||
private RaycastResult GetFirstRaycastResult(List<RaycastResult> results)
|
||||
{
|
||||
RaycastResult rr = default;
|
||||
|
||||
results.Sort(rrComparator);
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
if (results[i].isValid)
|
||||
{
|
||||
rr = results[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rr;
|
||||
}
|
||||
private static int RaycastResultComparator(RaycastResult lhs, RaycastResult rhs)
|
||||
{
|
||||
if (lhs.module != rhs.module)
|
||||
{
|
||||
if (lhs.module.eventCamera != null && rhs.module.eventCamera != null && lhs.module.eventCamera.depth != rhs.module.eventCamera.depth)
|
||||
{
|
||||
// need to reverse the standard compareTo
|
||||
if (lhs.module.eventCamera.depth < rhs.module.eventCamera.depth) { return 1; }
|
||||
if (lhs.module.eventCamera.depth == rhs.module.eventCamera.depth) { return 0; }
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
|
||||
{
|
||||
return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);
|
||||
}
|
||||
|
||||
if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
|
||||
{
|
||||
return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
|
||||
}
|
||||
}
|
||||
|
||||
if (lhs.sortingLayer != rhs.sortingLayer)
|
||||
{
|
||||
// Uses the layer value to properly compare the relative order of the layers.
|
||||
var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
|
||||
var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
|
||||
return rid.CompareTo(lid);
|
||||
}
|
||||
|
||||
if (lhs.sortingOrder != rhs.sortingOrder)
|
||||
{
|
||||
return rhs.sortingOrder.CompareTo(lhs.sortingOrder);
|
||||
}
|
||||
|
||||
if (!Mathf.Approximately(lhs.distance, rhs.distance))
|
||||
{
|
||||
return lhs.distance.CompareTo(rhs.distance);
|
||||
}
|
||||
|
||||
if (lhs.depth != rhs.depth)
|
||||
{
|
||||
return rhs.depth.CompareTo(lhs.depth);
|
||||
}
|
||||
|
||||
return lhs.index.CompareTo(rhs.index);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#if UNITY_EDITOR
|
||||
bool drawDebugLine = false;
|
||||
#endif
|
||||
|
||||
#region Raycast
|
||||
protected virtual bool UseEyeData(out Vector3 direction)
|
||||
{
|
||||
direction = Vector3.forward;
|
||||
return false;
|
||||
}
|
||||
protected PointerEventData pointerData = null;
|
||||
protected Vector3 pointerLocalOffset = Vector3.forward;
|
||||
private Vector3 physicsWorldPosition = Vector3.zero;
|
||||
private Vector2 graphicScreenPosition = Vector2.zero;
|
||||
private void UpdatePointerDataPosition()
|
||||
{
|
||||
/// 1. Calculate the pointer offset in "local" space.
|
||||
pointerLocalOffset = Vector3.forward;
|
||||
if (UseEyeData(out Vector3 direction))
|
||||
{
|
||||
pointerLocalOffset = direction;
|
||||
|
||||
// Revise the offset from World space to Local space.
|
||||
// OpenXR always uses World space.
|
||||
pointerLocalOffset = Quaternion.Inverse(transform.rotation) * pointerLocalOffset;
|
||||
}
|
||||
|
||||
/// 2. Calculate the pointer position in "world" space.
|
||||
Vector3 rotated_offset = transform.rotation * pointerLocalOffset;
|
||||
physicsWorldPosition = transform.position + rotated_offset;
|
||||
graphicScreenPosition = m_Camera.WorldToScreenPoint(physicsWorldPosition);
|
||||
// The graphicScreenPosition.x should be equivalent to (0.5f * Screen.width);
|
||||
// The graphicScreenPosition.y should be equivalent to (0.5f * Screen.height);
|
||||
}
|
||||
private void ResetEventData()
|
||||
{
|
||||
if (pointerData == null) { pointerData = new RaycastEventData(EventSystem.current, gameObject); }
|
||||
|
||||
UpdatePointerDataPosition();
|
||||
pointerData.position = graphicScreenPosition;
|
||||
}
|
||||
List<RaycastResult> resultAppendList = new List<RaycastResult>();
|
||||
private RaycastResult currentRaycastResult = default;
|
||||
protected GameObject raycastObject = null;
|
||||
protected List<GameObject> s_raycastObjects = new List<GameObject>();
|
||||
protected GameObject raycastObjectEx = null;
|
||||
protected List<GameObject> s_raycastObjectsEx = new List<GameObject>();
|
||||
/**
|
||||
* Call to
|
||||
* GraphicRaycast(Canvas canvas, Camera eventCamera, Vector2 screenPosition, List<RaycastResult> resultAppendList)
|
||||
* PhysicsRaycast(Ray ray, Camera eventCamera, List<RaycastResult> resultAppendList)
|
||||
**/
|
||||
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
|
||||
{
|
||||
// --------------- Previous Results ---------------
|
||||
raycastObjectEx = raycastObject;
|
||||
s_raycastObjects.Clear();
|
||||
|
||||
// --------------- Graphic Raycast ---------------
|
||||
Canvas[] canvases = CanvasProvider.GetTargetCanvas();
|
||||
if (canvases.Length <= 0) { canvases = FindObjectsOfType<Canvas>(); } // note: GC.Alloc
|
||||
for (int i = 0; i < canvases.Length; i++)
|
||||
{
|
||||
GraphicRaycast(canvases[i], m_Camera, eventData.position, resultAppendList);
|
||||
}
|
||||
|
||||
// --------------- Physics Raycast ---------------
|
||||
Ray ray = new Ray(transform.position, (physicsWorldPosition - transform.position));
|
||||
PhysicsRaycast(ray, m_Camera, resultAppendList);
|
||||
|
||||
currentRaycastResult = GetFirstRaycastResult(resultAppendList);
|
||||
|
||||
// --------------- Current Results ---------------
|
||||
raycastObject = currentRaycastResult.gameObject;
|
||||
|
||||
GameObject raycastTarget = currentRaycastResult.gameObject;
|
||||
while (raycastTarget != null)
|
||||
{
|
||||
s_raycastObjects.Add(raycastTarget);
|
||||
raycastTarget = (raycastTarget.transform.parent != null ? raycastTarget.transform.parent.gameObject : null);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (drawDebugLine)
|
||||
{
|
||||
Vector3 end = transform.position + (transform.forward * 100);
|
||||
Debug.DrawLine(transform.position, end, Color.red, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Ray ray = new Ray();
|
||||
protected virtual void GraphicRaycast(Canvas canvas, Camera eventCamera, Vector2 screenPosition, List<RaycastResult> resultAppendList)
|
||||
{
|
||||
if (canvas == null)
|
||||
return;
|
||||
|
||||
IList<Graphic> foundGraphics = GraphicRegistry.GetGraphicsForCanvas(canvas);
|
||||
if (foundGraphics == null || foundGraphics.Count == 0)
|
||||
return;
|
||||
|
||||
int displayIndex = 0;
|
||||
var currentEventCamera = eventCamera; // Property can call Camera.main, so cache the reference
|
||||
|
||||
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay || currentEventCamera == null)
|
||||
displayIndex = canvas.targetDisplay;
|
||||
else
|
||||
displayIndex = currentEventCamera.targetDisplay;
|
||||
|
||||
if (currentEventCamera != null)
|
||||
ray = currentEventCamera.ScreenPointToRay(screenPosition);
|
||||
|
||||
// Necessary for the event system
|
||||
for (int i = 0; i < foundGraphics.Count; ++i)
|
||||
{
|
||||
Graphic graphic = foundGraphics[i];
|
||||
|
||||
// -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
|
||||
if (!graphic.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1) { continue; }
|
||||
|
||||
if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, screenPosition, currentEventCamera)) { continue; }
|
||||
|
||||
if (currentEventCamera != null && currentEventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > currentEventCamera.farClipPlane) { continue; }
|
||||
|
||||
if (graphic.Raycast(screenPosition, currentEventCamera))
|
||||
{
|
||||
var go = graphic.gameObject;
|
||||
bool appendGraphic = true;
|
||||
|
||||
if (m_IgnoreReversedGraphics)
|
||||
{
|
||||
if (currentEventCamera == null)
|
||||
{
|
||||
// If we dont have a camera we know that we should always be facing forward
|
||||
var dir = go.transform.rotation * Vector3.forward;
|
||||
appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have a camera compare the direction against the cameras forward.
|
||||
var cameraForward = currentEventCamera.transform.rotation * Vector3.forward * currentEventCamera.nearClipPlane;
|
||||
appendGraphic = Vector3.Dot(go.transform.position - currentEventCamera.transform.position - cameraForward, go.transform.forward) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (appendGraphic)
|
||||
{
|
||||
float distance = 0;
|
||||
Transform trans = go.transform;
|
||||
Vector3 transForward = trans.forward;
|
||||
|
||||
if (currentEventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
distance = 0;
|
||||
else
|
||||
{
|
||||
// http://geomalgorithms.com/a06-_intersect-2.html
|
||||
distance = (Vector3.Dot(transForward, trans.position - ray.origin) / Vector3.Dot(transForward, ray.direction));
|
||||
|
||||
// Check to see if the go is behind the camera.
|
||||
if (distance < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
resultAppendList.Add(new RaycastResult
|
||||
{
|
||||
gameObject = go,
|
||||
module = this,
|
||||
distance = distance,
|
||||
screenPosition = screenPosition,
|
||||
displayIndex = displayIndex,
|
||||
index = resultAppendList.Count,
|
||||
depth = graphic.depth,
|
||||
sortingLayer = canvas.sortingLayerID,
|
||||
sortingOrder = canvas.sortingOrder,
|
||||
worldPosition = ray.origin + ray.direction * distance,
|
||||
worldNormal = -transForward
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 hitScreenPos = Vector3.zero;
|
||||
Vector2 hitScreenPos2D = Vector2.zero;
|
||||
static readonly RaycastHit[] hits = new RaycastHit[255];
|
||||
protected virtual void PhysicsRaycast(Ray ray, Camera eventCamera, List<RaycastResult> resultAppendList)
|
||||
{
|
||||
var hitCount = Physics.RaycastNonAlloc(ray, hits, m_PhysicsCastDistance, m_PhysicsEventMask);
|
||||
|
||||
for (int i = 0; i < hitCount; ++i)
|
||||
{
|
||||
hitScreenPos = eventCamera.WorldToScreenPoint(hits[i].point);
|
||||
hitScreenPos2D.x = hitScreenPos.x;
|
||||
hitScreenPos2D.y = hitScreenPos.y;
|
||||
|
||||
resultAppendList.Add(new RaycastResult
|
||||
{
|
||||
gameObject = hits[i].collider.gameObject,
|
||||
module = this,
|
||||
distance = hits[i].distance,
|
||||
worldPosition = hits[i].point,
|
||||
worldNormal = hits[i].normal,
|
||||
screenPosition = hitScreenPos2D,
|
||||
index = resultAppendList.Count,
|
||||
sortingLayer = 0,
|
||||
sortingOrder = 0
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Event
|
||||
private void CopyList(List<GameObject> src, List<GameObject> dst)
|
||||
{
|
||||
dst.Clear();
|
||||
for (int i = 0; i < src.Count; i++)
|
||||
dst.Add(src[i]);
|
||||
}
|
||||
|
||||
private void ExitEnterHandler(ref List<GameObject> enterObjects, ref List<GameObject> exitObjects)
|
||||
{
|
||||
if (exitObjects.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < exitObjects.Count; i++)
|
||||
{
|
||||
if (exitObjects[i] != null && !enterObjects.Contains(exitObjects[i]))
|
||||
{
|
||||
ExecuteEvents.Execute(exitObjects[i], pointerData, ExecuteEvents.pointerExitHandler);
|
||||
sb.Clear().Append(LOG_TAG).Append("ExitEnterHandler() Exit: ").Append(exitObjects[i].name); DEBUG(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enterObjects.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < enterObjects.Count; i++)
|
||||
{
|
||||
if (enterObjects[i] != null && !exitObjects.Contains(enterObjects[i]))
|
||||
{
|
||||
ExecuteEvents.Execute(enterObjects[i], pointerData, ExecuteEvents.pointerEnterHandler);
|
||||
sb.Clear().Append(LOG_TAG).Append("ExitEnterHandler() Enter: ").Append(enterObjects[i].name).Append(", camera: ").Append(pointerData.enterEventCamera); DEBUG(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CopyList(enterObjects, exitObjects);
|
||||
}
|
||||
private void HoverHandler()
|
||||
{
|
||||
if (raycastObject != null && (raycastObject == raycastObjectEx))
|
||||
{
|
||||
if (printIntervalLog) { sb.Clear().Append(LOG_TAG).Append("HoverHandler() Hover: ").Append(raycastObject.name); DEBUG(sb); }
|
||||
ExecuteEvents.ExecuteHierarchy(raycastObject, pointerData, RaycastEvents.pointerHoverHandler);
|
||||
}
|
||||
}
|
||||
private void DownHandler()
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("DownHandler()");DEBUG(sb);
|
||||
if (raycastObject == null) { return; }
|
||||
|
||||
pointerData.pressPosition = pointerData.position;
|
||||
pointerData.pointerPressRaycast = pointerData.pointerCurrentRaycast;
|
||||
pointerData.pointerPress =
|
||||
ExecuteEvents.ExecuteHierarchy(raycastObject, pointerData, ExecuteEvents.pointerDownHandler)
|
||||
?? ExecuteEvents.GetEventHandler<IPointerClickHandler>(raycastObject);
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append("DownHandler() Down: ").Append(pointerData.pointerPress).Append(", raycastObject: ").Append(raycastObject.name); DEBUG(sb);
|
||||
|
||||
// If Drag Handler exists, send initializePotentialDrag event.
|
||||
pointerData.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(raycastObject);
|
||||
if (pointerData.pointerDrag != null)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("DownHandler() Send initializePotentialDrag to ").Append(pointerData.pointerDrag.name).Append(", current GameObject is ").Append(raycastObject.name); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.initializePotentialDrag);
|
||||
}
|
||||
|
||||
// press happened (even not handled) object.
|
||||
pointerData.rawPointerPress = raycastObject;
|
||||
// allow to send Pointer Click event
|
||||
pointerData.eligibleForClick = true;
|
||||
// reset the screen position of press, can be used to estimate move distance
|
||||
pointerData.delta = Vector2.zero;
|
||||
// current Down, reset drag state
|
||||
pointerData.dragging = false;
|
||||
pointerData.useDragThreshold = true;
|
||||
// record the count of Pointer Click should be processed, clean when Click event is sent.
|
||||
pointerData.clickCount = 1;
|
||||
// set clickTime to current time of Pointer Down instead of Pointer Click.
|
||||
// since Down & Up event should not be sent too closely. (< kClickInterval)
|
||||
pointerData.clickTime = Time.unscaledTime;
|
||||
}
|
||||
private void UpHandler()
|
||||
{
|
||||
if (!pointerData.eligibleForClick && !pointerData.dragging)
|
||||
{
|
||||
// 1. no pending click
|
||||
// 2. no dragging
|
||||
// Mean user has finished all actions and do NOTHING in current frame.
|
||||
return;
|
||||
}
|
||||
|
||||
// raycastObject may be different with pointerData.pointerDrag so we don't check null
|
||||
|
||||
if (pointerData.pointerPress != null)
|
||||
{
|
||||
// In the frame of button is pressed -> unpressed, send Pointer Up
|
||||
sb.Clear().Append(LOG_TAG).Append("UpHandler() Send Pointer Up to ").Append(pointerData.pointerPress.name); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerPress, pointerData, ExecuteEvents.pointerUpHandler);
|
||||
}
|
||||
|
||||
if (pointerData.eligibleForClick)
|
||||
{
|
||||
GameObject objectToClick = ExecuteEvents.GetEventHandler<IPointerClickHandler>(raycastObject);
|
||||
if (objectToClick != null)
|
||||
{
|
||||
if (objectToClick == pointerData.pointerPress)
|
||||
{
|
||||
// In the frame of button from being pressed to unpressed, send Pointer Click if Click is pending.
|
||||
sb.Clear().Append(LOG_TAG).Append("UpHandler() Send Pointer Click to ").Append(pointerData.pointerPress.name); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerPress, pointerData, ExecuteEvents.pointerClickHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("UpHandler() pointer down object ").Append(pointerData.pointerPress).Append(" is different with click object ").Append(objectToClick.name); DEBUG(sb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pointerData.dragging)
|
||||
{
|
||||
GameObject _pointerDrop = ExecuteEvents.GetEventHandler<IDropHandler>(raycastObject);
|
||||
if (_pointerDrop == pointerData.pointerDrag)
|
||||
{
|
||||
// In next frame of button from being pressed to unpressed, send Drop and EndDrag if dragging.
|
||||
sb.Clear().Append(LOG_TAG).Append("UpHandler() Send Pointer Drop to ").Append(pointerData.pointerDrag); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.dropHandler);
|
||||
}
|
||||
sb.Clear().Append(LOG_TAG).Append("UpHandler() Send Pointer endDrag to ").Append(pointerData.pointerDrag); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.endDragHandler);
|
||||
|
||||
pointerData.dragging = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initializePotentialDrag was sent when IDragHandler exists.
|
||||
pointerData.pointerDrag = null;
|
||||
// Down of pending Click object.
|
||||
pointerData.pointerPress = null;
|
||||
// press happened (even not handled) object.
|
||||
pointerData.rawPointerPress = null;
|
||||
// clear pending state.
|
||||
pointerData.eligibleForClick = false;
|
||||
// Click is processed, clearcount.
|
||||
pointerData.clickCount = 0;
|
||||
// Up is processed thus clear the time limitation of Down event.
|
||||
pointerData.clickTime = 0;
|
||||
}
|
||||
|
||||
// After selecting an object over this duration, the drag action will be taken.
|
||||
const float kTimeToDrag = 0.2f;
|
||||
private void DragHandler()
|
||||
{
|
||||
if (Time.unscaledTime - pointerData.clickTime < kTimeToDrag) { return; }
|
||||
if (pointerData.pointerDrag == null) { return; }
|
||||
|
||||
if (!pointerData.dragging)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("DragHandler() Send BeginDrag to ").Append(pointerData.pointerDrag.name); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.beginDragHandler);
|
||||
pointerData.dragging = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.dragHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void SubmitHandler()
|
||||
{
|
||||
if (raycastObject == null) { return; }
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append("SubmitHandler() Submit: ").Append(raycastObject.name); DEBUG(sb);
|
||||
ExecuteEvents.ExecuteHierarchy(raycastObject, pointerData, ExecuteEvents.submitHandler);
|
||||
}
|
||||
|
||||
// Do NOT allow event DOWN being sent multiple times during kClickInterval
|
||||
// since UI element of Unity needs time to perform transitions.
|
||||
const float kClickInterval = 0.2f;
|
||||
private void HandleRaycastEvent()
|
||||
{
|
||||
ExitEnterHandler(ref s_raycastObjects, ref s_raycastObjectsEx);
|
||||
HoverHandler();
|
||||
|
||||
bool submit = OnSubmit();
|
||||
if (submit)
|
||||
{
|
||||
SubmitHandler();
|
||||
return;
|
||||
}
|
||||
|
||||
bool down = OnDown();
|
||||
bool hold = OnHold();
|
||||
if (!down && hold)
|
||||
{
|
||||
// Hold means to Drag.
|
||||
DragHandler();
|
||||
}
|
||||
else if (Time.unscaledTime - pointerData.clickTime < kClickInterval)
|
||||
{
|
||||
// Delay new events until kClickInterval has passed.
|
||||
}
|
||||
else if (down && !pointerData.eligibleForClick)
|
||||
{
|
||||
// 1. Not Down -> Down
|
||||
// 2. No pending Click should be procced.
|
||||
DownHandler();
|
||||
}
|
||||
else if (!hold)
|
||||
{
|
||||
// 1. If Down before, send Up event and clear Down state.
|
||||
// 2. If Dragging, send Drop & EndDrag event and clear Dragging state.
|
||||
// 3. If no Down or Dragging state, do NOTHING.
|
||||
UpHandler();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Actions
|
||||
protected virtual bool OnDown() { return false; }
|
||||
protected virtual bool OnHold() { return false; }
|
||||
protected virtual bool OnSubmit() { return false; }
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74b5628be71247e4f80cb6ddc4d7025b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,208 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
[RequireComponent(typeof(LineRenderer))]
|
||||
public class RaycastPointer : RaycastImpl
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.RaycastPointer ";
|
||||
|
||||
#region Inspector
|
||||
[Tooltip("To show the ray which presents the casting direction.")]
|
||||
[SerializeField]
|
||||
private bool m_ShowRay = true;
|
||||
public bool ShowRay { get { return m_ShowRay; } set { m_ShowRay = value; } }
|
||||
|
||||
[SerializeField]
|
||||
private float m_RayStartWidth = 0.01f;
|
||||
public float RayStartWidth { get { return m_RayStartWidth; } set { m_RayStartWidth = value; } }
|
||||
|
||||
[SerializeField]
|
||||
private float m_RayEndWidth = 0.01f;
|
||||
public float RayEndWidth { get { return m_RayEndWidth; } set { m_RayEndWidth = value; } }
|
||||
|
||||
[SerializeField]
|
||||
private Material m_RayMaterial = null;
|
||||
public Material RayMaterial { get { return m_RayMaterial; } set { m_RayMaterial = value; } }
|
||||
|
||||
[SerializeField]
|
||||
private GameObject m_Pointer = null;
|
||||
public GameObject Pointer { get { return m_Pointer; } set { m_Pointer = value; } }
|
||||
#endregion
|
||||
|
||||
LineRenderer m_Ray = null;
|
||||
Vector3 m_PointerScale = new Vector3(.15f, .15f, .15f);
|
||||
|
||||
#region MonoBehaviour overrides
|
||||
protected override void OnEnable()
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("OnEnable()"); DEBUG(sb);
|
||||
base.OnEnable();
|
||||
|
||||
if (m_Ray == null) { m_Ray = GetComponent<LineRenderer>(); }
|
||||
if (m_Pointer != null)
|
||||
{
|
||||
m_PointerScale = m_Pointer.transform.localScale;
|
||||
sb.Clear().Append(LOG_TAG).Append("OnEnable() Get default pointer scale (").Append(m_PointerScale.x).Append(", ").Append(m_PointerScale.y).Append(", ").Append(m_PointerScale.z).Append(")"); DEBUG(sb);
|
||||
}
|
||||
}
|
||||
protected override void OnDisable()
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("OnDisable()"); DEBUG(sb);
|
||||
base.OnDisable();
|
||||
|
||||
ActivatePointer(false);
|
||||
ActivateRay(false);
|
||||
}
|
||||
protected override void Update()
|
||||
{
|
||||
/// Raycast
|
||||
base.Update();
|
||||
if (printIntervalLog)
|
||||
{
|
||||
if (m_Ray != null)
|
||||
sb.Clear().Append(LOG_TAG).Append("Update() ").Append(gameObject.name).Append(", m_Ray enabled: ").Append(m_Ray.enabled); DEBUG(sb);
|
||||
if (m_Pointer != null)
|
||||
sb.Clear().Append(LOG_TAG).Append("Update() ").Append(gameObject.name).Append(", m_Pointer enabled: ").Append(m_Pointer.activeSelf); DEBUG(sb);
|
||||
}
|
||||
|
||||
if (!IsInteractable()) { return; }
|
||||
|
||||
/// Draw the ray and pointer.
|
||||
DrawRayPointer();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Ray and Pointer
|
||||
private void ActivatePointer(bool active)
|
||||
{
|
||||
if (m_Pointer != null)
|
||||
{
|
||||
if (m_Pointer.activeSelf != active) { sb.Clear().Append(LOG_TAG).Append("ActivatePointer() ").Append(gameObject.name).Append(" ").Append(active); DEBUG(sb); }
|
||||
m_Pointer.SetActive(active);
|
||||
}
|
||||
}
|
||||
private void ActivateRay(bool active)
|
||||
{
|
||||
if (m_Ray != null)
|
||||
{
|
||||
if (m_Ray.enabled != active) { sb.Clear().Append(LOG_TAG).Append("ActivateRay() ").Append(gameObject.name).Append(" ").Append(active); DEBUG(sb); }
|
||||
m_Ray.enabled = active;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 GetIntersectionPosition(Camera cam, RaycastResult raycastResult)
|
||||
{
|
||||
if (cam == null)
|
||||
return Vector3.zero;
|
||||
|
||||
float intersectionDistance = raycastResult.distance + cam.nearClipPlane;
|
||||
Vector3 intersectionPosition = cam.transform.forward * intersectionDistance + cam.transform.position;
|
||||
return intersectionPosition;
|
||||
}
|
||||
|
||||
Vector3 rayStart = Vector3.zero, rayEnd = Vector3.zero;
|
||||
const float kRayLengthMin = 0.5f;
|
||||
private float m_RayLength = 10;
|
||||
protected Vector3 pointerPosition = Vector3.zero;
|
||||
private void DrawRayPointer()
|
||||
{
|
||||
Vector3 hit = GetIntersectionPosition(eventCamera, pointerData.pointerCurrentRaycast);
|
||||
|
||||
rayStart = transform.position;
|
||||
if (raycastObject != null)
|
||||
{
|
||||
m_RayLength = Vector3.Distance(hit, rayStart);
|
||||
m_RayLength = m_RayLength > kRayLengthMin ? m_RayLength : kRayLengthMin;
|
||||
}
|
||||
|
||||
if (LockPointer())
|
||||
{
|
||||
Vector3 middle = new Vector3(0, 0, (m_RayLength - 0.2f) / 4);
|
||||
DrawCurveRay(rayStart, middle, rayEnd, m_RayStartWidth, m_RayEndWidth, m_RayMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
rayEnd = rayStart + (transform.forward * (m_RayLength - 0.2f));
|
||||
pointerPosition = rayStart + (transform.forward * m_RayLength);
|
||||
DrawRay(rayStart, rayEnd, m_RayStartWidth, m_RayEndWidth, m_RayMaterial);
|
||||
}
|
||||
|
||||
DrawPointer(pointerPosition);
|
||||
}
|
||||
const float kPointerDistance = 10;
|
||||
private void DrawPointer(Vector3 position)
|
||||
{
|
||||
if (m_Pointer == null) { return; }
|
||||
|
||||
m_Pointer.transform.position = position;
|
||||
m_Pointer.transform.rotation = Camera.main.transform.rotation;
|
||||
float distance = Vector3.Distance(position, Camera.main.transform.position);
|
||||
m_Pointer.transform.localScale = m_PointerScale * (distance / kPointerDistance);
|
||||
}
|
||||
|
||||
private void DrawRay(Vector3 start, Vector3 end, float startWidth, float endWidth, Material material)
|
||||
{
|
||||
if (m_Ray == null) { return; }
|
||||
|
||||
Vector3[] positions = new Vector3[] { start, end };
|
||||
m_Ray.positionCount = positions.Length;
|
||||
m_Ray.SetPositions(positions);
|
||||
m_Ray.startWidth = startWidth;
|
||||
m_Ray.endWidth = endWidth;
|
||||
m_Ray.material = material;
|
||||
m_Ray.useWorldSpace = true;
|
||||
}
|
||||
|
||||
private void DrawCurveRay(Vector3 start, Vector3 middle, Vector3 end, float startWidth, float endWidth, Material material)
|
||||
{
|
||||
if (m_Ray == null) { m_Ray = GetComponent<LineRenderer>(); }
|
||||
|
||||
Vector3[] positions = GenerateBezierCurve3(50, start, middle, end);
|
||||
m_Ray.positionCount = positions.Length;
|
||||
m_Ray.SetPositions(positions);
|
||||
m_Ray.startWidth = startWidth;
|
||||
m_Ray.endWidth = endWidth;
|
||||
m_Ray.material = material;
|
||||
m_Ray.useWorldSpace = true;
|
||||
}
|
||||
Vector3[] GenerateBezierCurve2(int iteration, Vector3 start, Vector3 end)
|
||||
{
|
||||
Vector3[] points = new Vector3[iteration + 1];
|
||||
for (int i = 0; i < iteration + 1; i++)
|
||||
{
|
||||
points.SetValue(start + ((end - start).normalized * (end - start).magnitude * i / iteration), i);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
Vector3[] GenerateBezierCurve3(int iteration, Vector3 start, Vector3 middle, Vector3 end)
|
||||
{
|
||||
Vector3[] points1 = GenerateBezierCurve2(iteration, start, middle);
|
||||
Vector3[] points2 = GenerateBezierCurve2(iteration, start, end);
|
||||
Vector3[] points = new Vector3[iteration + 1];
|
||||
for (int i = 0; i < iteration + 1; i++)
|
||||
{
|
||||
points.SetValue(points1[i] + ((points2[i] - points1[i]).normalized * (points2[i] - points1[i]).magnitude * i / iteration), i);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool IsInteractable()
|
||||
{
|
||||
ActivatePointer(m_Interactable);
|
||||
ActivateRay(m_Interactable && m_ShowRay);
|
||||
|
||||
return m_Interactable;
|
||||
}
|
||||
|
||||
/// <summary> For DrawRayPointer(), controls whether locking the pointer or not. </summary>
|
||||
protected virtual bool LockPointer()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e8ff0b4ae49c1a4ea049f9e8afe0230
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
335
com.htc.upm.vive.openxr/Runtime/Raycast/Scripts/RaycastRing.cs
Normal file
335
com.htc.upm.vive.openxr/Runtime/Raycast/Scripts/RaycastRing.cs
Normal file
@@ -0,0 +1,335 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
/// <summary>
|
||||
/// To draw a ring pointer to indicate the gazed space.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
|
||||
public class RaycastRing : RaycastImpl
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.RaycastRing";
|
||||
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
|
||||
public enum GazeEvent
|
||||
{
|
||||
Down = 0,
|
||||
Submit = 1
|
||||
}
|
||||
|
||||
#region Inspector
|
||||
// ----------- Width of ring -----------
|
||||
const float kRingWidthDefault = 0.005f;
|
||||
const float kRingWidthMinimal = 0.001f;
|
||||
[Tooltip("Set the width of the pointer's ring.")]
|
||||
[SerializeField]
|
||||
private float m_PointerRingWidth = kRingWidthDefault;
|
||||
public float PointerRingWidth { get { return m_PointerRingWidth; } set { m_PointerRingWidth = value; } }
|
||||
|
||||
// ----------- Radius of inner circle -----------
|
||||
const float kInnerCircleRadiusDefault = 0.005f;
|
||||
const float kInnerCircleRadiusMinimal = 0.001f;
|
||||
[Tooltip("Set the radius of the pointer's inner circle.")]
|
||||
[SerializeField]
|
||||
private float m_PointerCircleRadius = kInnerCircleRadiusDefault;
|
||||
public float PointerCircleRadius { get { return m_PointerCircleRadius; } set { m_PointerCircleRadius = value; } }
|
||||
|
||||
// ----------- Z distance of ring -----------
|
||||
const float kPointerDistanceDefault = 1;
|
||||
const float kPointerDistanceMinimal = 0.1f;
|
||||
[Tooltip("Set the z-coordinate of the pointer.")]
|
||||
[SerializeField]
|
||||
private float m_PointerDistance = kPointerDistanceDefault;
|
||||
public float PointerDistance { get { return m_PointerDistance; } set { m_PointerDistance = value; } }
|
||||
|
||||
/// The offset from the pointer to the pointer-mounted object.
|
||||
private Vector3 ringOffset = Vector3.zero;
|
||||
/// The offset from the pointer to the pointer-mounted object in every frame.
|
||||
private Vector3 ringFrameOffset = Vector3.zero;
|
||||
/// The pointer world position.
|
||||
private Vector3 ringWorldPosition = Vector3.zero;
|
||||
|
||||
// ----------- Color of ring -----------
|
||||
/// Color of ring background.
|
||||
[Tooltip("Set the ring background color.")]
|
||||
[SerializeField]
|
||||
private Color m_PointerColor = Color.white;
|
||||
public Color PointerColor { get { return m_PointerColor; } set { m_PointerColor = value; } }
|
||||
|
||||
/// Color of ring foreground
|
||||
[Tooltip("Set the ring foreground progess color.")]
|
||||
[SerializeField]
|
||||
private Color m_ProgressColor = new Color(0, 245, 255);
|
||||
public Color ProgressColor { get { return m_ProgressColor; } set { m_ProgressColor = value; } }
|
||||
|
||||
// ----------- Material and Mesh -----------
|
||||
private Mesh m_Mesh = null;
|
||||
|
||||
const string kPointerMaterial = "Materials/GazePointerDef";
|
||||
[Tooltip("Empty for using the default material or set a customized material.")]
|
||||
[SerializeField]
|
||||
private Material m_PointerMaterial = null;
|
||||
public Material PointerMaterial { get { return m_PointerMaterial; } set { m_PointerMaterial = value; } }
|
||||
private Material pointerMaterialInstance = null;
|
||||
|
||||
private MeshFilter m_MeshFilter = null;
|
||||
private MeshRenderer m_MeshRenderer = null;
|
||||
|
||||
const int kMaterialRenderQueueMin = 1000;
|
||||
const int kMaterialRenderQueueMax = 5000;
|
||||
/// The material's renderQueue.
|
||||
[Tooltip("Set the Material's renderQueue.")]
|
||||
[SerializeField]
|
||||
private int m_PointerRenderQueue = kMaterialRenderQueueMax;
|
||||
public int PointerRenderQueue { get { return m_PointerRenderQueue; } set { m_PointerRenderQueue = value; } }
|
||||
|
||||
/// The MeshRenderer's sortingOrder.
|
||||
[Tooltip("Set the MeshRenderer's sortingOrder.")]
|
||||
[SerializeField]
|
||||
private int m_PointerSortingOrder = 32767;
|
||||
public int PointerSortingOrder { get { return m_PointerSortingOrder; } set { m_PointerSortingOrder = value; } }
|
||||
|
||||
protected int m_RingPercent = 0;
|
||||
public int RingPercent { get { return m_RingPercent; } set { m_RingPercent = value; } }
|
||||
|
||||
[Tooltip("Gaze timer to trigger gaze events.")]
|
||||
[SerializeField]
|
||||
private float m_TimeToGaze = 1.5f;
|
||||
public float TimeToGaze { get { return m_TimeToGaze; } set { m_TimeToGaze = value; } }
|
||||
|
||||
private void ValidateParameters()
|
||||
{
|
||||
if (m_PointerRingWidth < kRingWidthMinimal)
|
||||
m_PointerRingWidth = kRingWidthDefault;
|
||||
|
||||
if (m_PointerCircleRadius < kInnerCircleRadiusMinimal)
|
||||
m_PointerCircleRadius = kInnerCircleRadiusDefault;
|
||||
|
||||
if (m_PointerDistance < kPointerDistanceMinimal)
|
||||
m_PointerDistance = kPointerDistanceDefault;
|
||||
|
||||
if (m_PointerRenderQueue < kMaterialRenderQueueMin || m_PointerRenderQueue > kMaterialRenderQueueMax)
|
||||
m_PointerRenderQueue = kMaterialRenderQueueMax;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region MonoBehaviour overrides
|
||||
private bool mEnabled = false;
|
||||
protected override void OnEnable()
|
||||
{
|
||||
DEBUG("OnEnable()");
|
||||
base.OnEnable();
|
||||
|
||||
if (!mEnabled)
|
||||
{
|
||||
// 1. Texture or Mesh < Material < < MeshFilter < MeshRenderer, we don't use the texture.
|
||||
if (m_Mesh == null)
|
||||
m_Mesh = new Mesh();
|
||||
if (m_Mesh != null)
|
||||
m_Mesh.name = gameObject.name + " Mesh";
|
||||
|
||||
// 2. Load the Material RingUnlitTransparentMat.
|
||||
if (m_PointerMaterial == null)
|
||||
m_PointerMaterial = Resources.Load(kPointerMaterial) as Material;
|
||||
if (m_PointerMaterial != null)
|
||||
{
|
||||
pointerMaterialInstance = Instantiate(m_PointerMaterial);
|
||||
DEBUG("OnEnable() Loaded resource " + pointerMaterialInstance.name);
|
||||
}
|
||||
|
||||
// 3. Get the MeshFilter.
|
||||
m_MeshFilter = GetComponent<MeshFilter>();
|
||||
|
||||
// 4. Get the MeshRenderer.
|
||||
m_MeshRenderer = GetComponent<MeshRenderer>();
|
||||
m_MeshRenderer.sortingOrder = m_PointerSortingOrder;
|
||||
m_MeshRenderer.material = pointerMaterialInstance;
|
||||
m_MeshRenderer.material.renderQueue = PointerRenderQueue;
|
||||
|
||||
mEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
DEBUG("OnDisable()");
|
||||
base.OnDisable();
|
||||
|
||||
if (mEnabled)
|
||||
{
|
||||
if (m_MeshFilter != null)
|
||||
{
|
||||
Mesh mesh = m_MeshFilter.mesh;
|
||||
mesh.Clear();
|
||||
}
|
||||
Destroy(pointerMaterialInstance);
|
||||
|
||||
mEnabled = false;
|
||||
DEBUG("OnDisable()");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!IsInteractable()) { return; }
|
||||
|
||||
ValidateParameters();
|
||||
UpdatePointerOffset();
|
||||
|
||||
ringFrameOffset = ringOffset;
|
||||
ringFrameOffset.z = ringFrameOffset.z < kPointerDistanceMinimal ? kPointerDistanceDefault : ringFrameOffset.z;
|
||||
|
||||
// Calculate the pointer world position
|
||||
Vector3 rotated_direction = transform.rotation * ringFrameOffset;
|
||||
ringWorldPosition = transform.position + rotated_direction;
|
||||
//DEBUG("ringWorldPosition: " + ringWorldPosition.x + ", " + ringWorldPosition.y + ", " + ringWorldPosition.z);
|
||||
|
||||
float calcRingWidth = m_PointerRingWidth * (ringFrameOffset.z / kPointerDistanceDefault);
|
||||
float calcInnerCircleRadius = m_PointerCircleRadius * (ringFrameOffset.z / kPointerDistanceDefault);
|
||||
|
||||
UpdateRingPercent();
|
||||
DrawRingRoll(calcRingWidth + calcInnerCircleRadius, calcInnerCircleRadius, ringFrameOffset, m_RingPercent);
|
||||
|
||||
if (printIntervalLog)
|
||||
{
|
||||
DEBUG("Update() " + gameObject.name + " is " + (m_MeshRenderer.enabled ? "shown" : "hidden")
|
||||
+ ", ringFrameOffset (" + ringFrameOffset.x + ", " + ringFrameOffset.y + ", " + ringFrameOffset.z + ")");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool IsInteractable()
|
||||
{
|
||||
ActivatePointer(m_Interactable);
|
||||
return m_Interactable;
|
||||
}
|
||||
private void ActivatePointer(bool active)
|
||||
{
|
||||
if (m_MeshRenderer == null)
|
||||
return;
|
||||
|
||||
if (m_MeshRenderer.enabled != active)
|
||||
{
|
||||
m_MeshRenderer.enabled = active;
|
||||
DEBUG("ActivatePointer() " + m_MeshRenderer.enabled);
|
||||
if (m_MeshRenderer.enabled)
|
||||
{
|
||||
m_MeshRenderer.sortingOrder = m_PointerSortingOrder;
|
||||
if (pointerMaterialInstance != null)
|
||||
{
|
||||
m_MeshRenderer.material = pointerMaterialInstance;
|
||||
m_MeshRenderer.material.renderQueue = PointerRenderQueue;
|
||||
}
|
||||
// The MeshFilter's mesh is updated in DrawRingRoll(), not here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePointerOffset()
|
||||
{
|
||||
ringOffset = pointerLocalOffset;
|
||||
// Moves the pointer onto the gazed object.
|
||||
if (raycastObject != null)
|
||||
{
|
||||
Vector3 rotated_direction = pointerData.pointerCurrentRaycast.worldPosition - gameObject.transform.position;
|
||||
ringOffset = Quaternion.Inverse(transform.rotation) * rotated_direction;
|
||||
}
|
||||
}
|
||||
|
||||
protected float m_GazeOnTime = 0;
|
||||
private void UpdateRingPercent()
|
||||
{
|
||||
if (raycastObject != raycastObjectEx)
|
||||
{
|
||||
m_RingPercent = 0;
|
||||
if (raycastObject != null)
|
||||
m_GazeOnTime = Time.unscaledTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hovering
|
||||
if (raycastObject != null)
|
||||
m_RingPercent = (int)(((Time.unscaledTime - m_GazeOnTime) / m_TimeToGaze) * 100);
|
||||
}
|
||||
}
|
||||
|
||||
const int kRingVertexCount = 400; // 100 percents * 2 + 2, ex: 80% ring -> 80 * 2 + 2
|
||||
private Vector3[] ringVert = new Vector3[kRingVertexCount];
|
||||
private Color[] ringColor = new Color[kRingVertexCount];
|
||||
|
||||
const int kRingTriangleCount = 100 * 6; // 100 percents * 6, ex: 80% ring -> 80 * 6
|
||||
private int[] ringTriangle = new int[kRingTriangleCount];
|
||||
private Vector2[] ringUv = new Vector2[kRingVertexCount];
|
||||
|
||||
const float kPercentAngle = 3.6f; // 100% = 100 * 3.6f = 360 degrees.
|
||||
|
||||
private void DrawRingRoll(float radius, float innerRadius, Vector3 offset, int percent)
|
||||
{
|
||||
if (m_MeshFilter == null)
|
||||
return;
|
||||
|
||||
percent = percent >= 100 ? 100 : percent;
|
||||
|
||||
// vertices and colors
|
||||
float start_angle = 90; // Start angle of drawing ring.
|
||||
for (int i = 0; i < kRingVertexCount; i += 2)
|
||||
{
|
||||
float radian_cur = start_angle * Mathf.Deg2Rad;
|
||||
float cosA = Mathf.Cos(radian_cur);
|
||||
float sinA = Mathf.Sin(radian_cur);
|
||||
|
||||
ringVert[i].x = offset.x + radius * cosA;
|
||||
ringVert[i].y = offset.y + radius * sinA;
|
||||
ringVert[i].z = offset.z;
|
||||
|
||||
ringColor[i] = (i <= (percent * 2) && i > 0) ? m_ProgressColor : m_PointerColor;
|
||||
|
||||
ringVert[i + 1].x = offset.x + innerRadius * cosA;
|
||||
ringVert[i + 1].y = offset.y + innerRadius * sinA;
|
||||
ringVert[i + 1].z = offset.z;
|
||||
|
||||
ringColor[i + 1] = (i <= (percent * 2) && i > 0) ? m_ProgressColor : m_PointerColor;
|
||||
|
||||
start_angle -= kPercentAngle;
|
||||
}
|
||||
|
||||
// triangles
|
||||
for (int i = 0, vi = 0; i < kRingTriangleCount; i += 6, vi += 2)
|
||||
{
|
||||
ringTriangle[i] = vi;
|
||||
ringTriangle[i + 1] = vi + 3;
|
||||
ringTriangle[i + 2] = vi + 1;
|
||||
|
||||
ringTriangle[i + 3] = vi + 2;
|
||||
ringTriangle[i + 4] = vi + 3;
|
||||
ringTriangle[i + 5] = vi;
|
||||
}
|
||||
|
||||
// uv
|
||||
for (int i = 0; i < kRingVertexCount; i++)
|
||||
{
|
||||
ringUv[i].x = ringVert[i].x / radius / 2 + 0.5f;
|
||||
ringUv[i].y = ringVert[i].z / radius / 2 + 0.5f;
|
||||
}
|
||||
|
||||
m_Mesh.Clear();
|
||||
|
||||
m_Mesh.vertices = ringVert;
|
||||
m_Mesh.colors = ringColor;
|
||||
m_Mesh.triangles = ringTriangle;
|
||||
m_Mesh.uv = ringUv;
|
||||
m_MeshFilter.mesh = m_Mesh;
|
||||
}
|
||||
|
||||
#region External Functions
|
||||
public Vector3 GetPointerPosition()
|
||||
{
|
||||
return ringWorldPosition;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 928994fe36775ae4782045d0503ba99f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
104
com.htc.upm.vive.openxr/Runtime/Raycast/Scripts/RaycastSwitch.cs
Normal file
104
com.htc.upm.vive.openxr/Runtime/Raycast/Scripts/RaycastSwitch.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class RaycastSwitch : MonoBehaviour
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.RaycastSwitch";
|
||||
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
|
||||
[Serializable]
|
||||
public class GazeSettings
|
||||
{
|
||||
public bool Enabled = false;
|
||||
}
|
||||
[SerializeField]
|
||||
private GazeSettings m_GazeRaycast = new GazeSettings();
|
||||
public GazeSettings GazeRaycast { get { return m_GazeRaycast; } set { m_GazeRaycast = value; } }
|
||||
public static GazeSettings Gaze { get { return Instance.GazeRaycast; } }
|
||||
|
||||
[Serializable]
|
||||
public class ControllerSettings
|
||||
{
|
||||
public bool Enabled = true;
|
||||
}
|
||||
[SerializeField]
|
||||
private ControllerSettings m_ControllerRaycast = new ControllerSettings();
|
||||
public ControllerSettings ControllerRaycast { get { return m_ControllerRaycast; } set { m_ControllerRaycast = value; } }
|
||||
public static ControllerSettings Controller { get { return Instance.ControllerRaycast; } }
|
||||
|
||||
[Serializable]
|
||||
public class HandSettings
|
||||
{
|
||||
public bool Enabled = true;
|
||||
}
|
||||
[SerializeField]
|
||||
private HandSettings m_HandRaycast = new HandSettings();
|
||||
public HandSettings HandRaycast { get { return m_HandRaycast; } set { m_HandRaycast = value; } }
|
||||
public static HandSettings Hand { get { return Instance.HandRaycast; } }
|
||||
|
||||
private static RaycastSwitch m_Instance = null;
|
||||
public static RaycastSwitch Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Instance == null)
|
||||
{
|
||||
var rs = new GameObject("RaycastSwitch");
|
||||
m_Instance = rs.AddComponent<RaycastSwitch>();
|
||||
// This object should survive all scene transitions.
|
||||
DontDestroyOnLoad(rs);
|
||||
}
|
||||
return m_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
m_Instance = this;
|
||||
}
|
||||
private bool m_Enabled = false;
|
||||
private void OnEnable()
|
||||
{
|
||||
if (!m_Enabled)
|
||||
{
|
||||
DEBUG("OnEnable()");
|
||||
m_Enabled = true;
|
||||
}
|
||||
}
|
||||
private void OnDisable()
|
||||
{
|
||||
if (m_Enabled)
|
||||
{
|
||||
DEBUG("OnDisable()");
|
||||
m_Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
int printFrame = 0;
|
||||
bool printLog = false;
|
||||
private void Update()
|
||||
{
|
||||
printFrame++;
|
||||
printFrame %= 300;
|
||||
printLog = (printFrame == 0);
|
||||
|
||||
CheckSettings();
|
||||
|
||||
if (printLog)
|
||||
{
|
||||
DEBUG("Update() Gaze.Enabled: " + GazeRaycast.Enabled
|
||||
+ ", Controller.Enabled: " + ControllerRaycast.Enabled
|
||||
+ ", Hand.Enabled: " + HandRaycast.Enabled);
|
||||
}
|
||||
}
|
||||
/// <summary> Updates Gaze, Controller and Hand settings in runtime. </summary>
|
||||
private void CheckSettings()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1caf898641806b3448ff86ad2e1b0727
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
public class TargetCanvas : MonoBehaviour
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.TargetCanvas";
|
||||
private void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + gameObject.name + ", " + msg); }
|
||||
|
||||
Canvas m_Canvas = null;
|
||||
private void Awake()
|
||||
{
|
||||
m_Canvas = GetComponent<Canvas>();
|
||||
}
|
||||
private void OnEnable()
|
||||
{
|
||||
DEBUG("OnEnable()");
|
||||
if (m_Canvas != null)
|
||||
{
|
||||
DEBUG("OnEnable() RegisterTargetCanvas.");
|
||||
CanvasProvider.RegisterTargetCanvas(m_Canvas);
|
||||
}
|
||||
}
|
||||
private void OnDisable()
|
||||
{
|
||||
DEBUG("OnDisable()");
|
||||
if (m_Canvas != null)
|
||||
{
|
||||
DEBUG("OnDisable() RemoveTargetCanvas.");
|
||||
CanvasProvider.RemoveTargetCanvas(m_Canvas);
|
||||
}
|
||||
}
|
||||
|
||||
Canvas[] s_ChildrenCanvas = null;
|
||||
private void Update()
|
||||
{
|
||||
Canvas[] canvases = GetComponentsInChildren<Canvas>();
|
||||
if (canvases != null && canvases.Length > 0) // find children canvas
|
||||
{
|
||||
s_ChildrenCanvas = canvases;
|
||||
|
||||
for (int i = 0; i < s_ChildrenCanvas.Length; i++)
|
||||
CanvasProvider.RegisterTargetCanvas(s_ChildrenCanvas[i]);
|
||||
|
||||
return;
|
||||
}
|
||||
if (s_ChildrenCanvas != null && s_ChildrenCanvas.Length > 0) // remove old children canvas
|
||||
{
|
||||
for (int i = 0; i < s_ChildrenCanvas.Length; i++)
|
||||
CanvasProvider.RemoveTargetCanvas(s_ChildrenCanvas[i]);
|
||||
|
||||
s_ChildrenCanvas = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fe9581cc98d0ff4fb8250646cc0359f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user