diff --git a/Prefabs/Pointers.meta b/Prefabs/Pointers.meta
new file mode 100644
index 00000000..5729c18f
--- /dev/null
+++ b/Prefabs/Pointers.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e16b50fc4d479c3479d4bd866fc004ec
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Prefabs/Pointers/SharedResources.meta b/Prefabs/Pointers/SharedResources.meta
new file mode 100644
index 00000000..a4bd6772
--- /dev/null
+++ b/Prefabs/Pointers/SharedResources.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 19ae4898bfbf1d54aad099b8387a0556
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Prefabs/Pointers/SharedResources/Materials.meta b/Prefabs/Pointers/SharedResources/Materials.meta
new file mode 100644
index 00000000..a47a6314
--- /dev/null
+++ b/Prefabs/Pointers/SharedResources/Materials.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b2277dc64d10b534b9bc36f9b3d2c609
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Prefabs/Pointers/SharedResources/Materials/PointerDefaultInvalid.mat b/Prefabs/Pointers/SharedResources/Materials/PointerDefaultInvalid.mat
new file mode 100644
index 00000000..872bf6c6
--- /dev/null
+++ b/Prefabs/Pointers/SharedResources/Materials/PointerDefaultInvalid.mat
@@ -0,0 +1,76 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: PointerDefaultInvalid
+ m_Shader: {fileID: 4800000, guid: c6488aa155d56e042b5ff808b89c7dda, 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
+ - _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
+ - _UVSec: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 1, g: 0, b: 0, a: 0.5882353}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Prefabs/Pointers/SharedResources/Materials/PointerDefaultInvalid.mat.meta b/Prefabs/Pointers/SharedResources/Materials/PointerDefaultInvalid.mat.meta
new file mode 100644
index 00000000..b4794be0
--- /dev/null
+++ b/Prefabs/Pointers/SharedResources/Materials/PointerDefaultInvalid.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3ea92609994834245baf997807c5677c
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Prefabs/Pointers/SharedResources/Materials/PointerDefaultValid.mat b/Prefabs/Pointers/SharedResources/Materials/PointerDefaultValid.mat
new file mode 100644
index 00000000..577bb9fd
--- /dev/null
+++ b/Prefabs/Pointers/SharedResources/Materials/PointerDefaultValid.mat
@@ -0,0 +1,76 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: PointerDefaultValid
+ m_Shader: {fileID: 4800000, guid: c6488aa155d56e042b5ff808b89c7dda, 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
+ - _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
+ - _UVSec: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 0, g: 1, b: 0, a: 0.5882353}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Prefabs/Pointers/SharedResources/Materials/PointerDefaultValid.mat.meta b/Prefabs/Pointers/SharedResources/Materials/PointerDefaultValid.mat.meta
new file mode 100644
index 00000000..ddbad4c8
--- /dev/null
+++ b/Prefabs/Pointers/SharedResources/Materials/PointerDefaultValid.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 45363d50487514f40be10a6626b8874d
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Prefabs/Pointers/SolidParabolicPointer.prefab b/Prefabs/Pointers/SolidParabolicPointer.prefab
new file mode 100644
index 00000000..fc70ea72
--- /dev/null
+++ b/Prefabs/Pointers/SolidParabolicPointer.prefab
@@ -0,0 +1,732 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1001 &100100000
+Prefab:
+ m_ObjectHideFlags: 1
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications: []
+ m_RemovedComponents: []
+ m_ParentPrefab: {fileID: 0}
+ m_RootGameObject: {fileID: 1897668919043294}
+ m_IsPrefabParent: 1
+--- !u!1 &1070217285575164
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4990198347560228}
+ m_Layer: 0
+ m_Name: Middle
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1116056273701772
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4573320480551876}
+ - component: {fileID: 33364052698962962}
+ - component: {fileID: 23996761392651784}
+ m_Layer: 0
+ m_Name: InvalidTracer
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1200714994868418
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4755503614305096}
+ - component: {fileID: 33677716766495152}
+ - component: {fileID: 23339715944455470}
+ m_Layer: 0
+ m_Name: InvalidCursor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1224850404600106
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4417887466734172}
+ - component: {fileID: 33038519180947900}
+ - component: {fileID: 23829347887679280}
+ m_Layer: 0
+ m_Name: ActualObject
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1309279854652330
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4273869243677338}
+ m_Layer: 0
+ m_Name: Cursor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1459395790049962
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4516217194393388}
+ - component: {fileID: 33603319313222156}
+ - component: {fileID: 23403056193514100}
+ m_Layer: 0
+ m_Name: ValidTracer
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1529330592552216
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4051388704589450}
+ m_Layer: 0
+ m_Name: Tracer
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1547702833113128
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4301982990135466}
+ - component: {fileID: 33635014065063646}
+ - component: {fileID: 23655827314840474}
+ m_Layer: 0
+ m_Name: ActualObject
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1571008921183954
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4331298706467906}
+ m_Layer: 0
+ m_Name: ValidMiddle
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1846780609845688
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4936146464785312}
+ m_Layer: 0
+ m_Name: InvalidMiddle
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1897668919043294
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4748404725061138}
+ - component: {fileID: 114967402694328888}
+ - component: {fileID: 114921987913562258}
+ - component: {fileID: 114802851438685276}
+ - component: {fileID: 114757610285119408}
+ - component: {fileID: 114043219088351336}
+ m_Layer: 0
+ m_Name: SolidParabolicPointer
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1923595575554056
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4767204780692922}
+ - component: {fileID: 33718780184033244}
+ - component: {fileID: 23433715969401368}
+ m_Layer: 0
+ m_Name: ValidCursor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4051388704589450
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1529330592552216}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4516217194393388}
+ - {fileID: 4573320480551876}
+ m_Father: {fileID: 4748404725061138}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4273869243677338
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1309279854652330}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4767204780692922}
+ - {fileID: 4755503614305096}
+ m_Father: {fileID: 4748404725061138}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4301982990135466
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1547702833113128}
+ m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.1, y: 1, z: 0.1}
+ m_Children: []
+ m_Father: {fileID: 4331298706467906}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
+--- !u!4 &4331298706467906
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1571008921183954}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4301982990135466}
+ m_Father: {fileID: 4990198347560228}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4417887466734172
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1224850404600106}
+ m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.1, y: 1, z: 0.1}
+ m_Children: []
+ m_Father: {fileID: 4936146464785312}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
+--- !u!4 &4516217194393388
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1459395790049962}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.05, y: 0.05, z: 0.05}
+ m_Children: []
+ m_Father: {fileID: 4051388704589450}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4573320480551876
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1116056273701772}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.05, y: 0.05, z: 0.05}
+ m_Children: []
+ m_Father: {fileID: 4051388704589450}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4748404725061138
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1897668919043294}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4051388704589450}
+ - {fileID: 4990198347560228}
+ - {fileID: 4273869243677338}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4755503614305096
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1200714994868418}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.05, y: 0.05, z: 0.05}
+ m_Children: []
+ m_Father: {fileID: 4273869243677338}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4767204780692922
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1923595575554056}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.05, y: 0.05, z: 0.05}
+ m_Children: []
+ m_Father: {fileID: 4273869243677338}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4936146464785312
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1846780609845688}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4417887466734172}
+ m_Father: {fileID: 4990198347560228}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4990198347560228
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1070217285575164}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4331298706467906}
+ - {fileID: 4936146464785312}
+ m_Father: {fileID: 4748404725061138}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!23 &23339715944455470
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1200714994868418}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 3ea92609994834245baf997807c5677c, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!23 &23403056193514100
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1459395790049962}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 45363d50487514f40be10a6626b8874d, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!23 &23433715969401368
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1923595575554056}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 45363d50487514f40be10a6626b8874d, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!23 &23655827314840474
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1547702833113128}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 45363d50487514f40be10a6626b8874d, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!23 &23829347887679280
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1224850404600106}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 3ea92609994834245baf997807c5677c, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!23 &23996761392651784
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1116056273701772}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 3ea92609994834245baf997807c5677c, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!33 &33038519180947900
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1224850404600106}
+ m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!33 &33364052698962962
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1116056273701772}
+ m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!33 &33603319313222156
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1459395790049962}
+ m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!33 &33635014065063646
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1547702833113128}
+ m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!33 &33677716766495152
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1200714994868418}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!33 &33718780184033244
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1923595575554056}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!114 &114043219088351336
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1897668919043294}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: a11862926720f544fb5d904995c2b57b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ process:
+ field: {fileID: 114967402694328888}
+ onlyProcessOnActiveAndEnabled: 1
+--- !u!114 &114757610285119408
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1897668919043294}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 5c0eab237e25807459af1c5caf4d3d33, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ momentToProcess: 5
+ processes:
+ - {fileID: 114043219088351336}
+--- !u!114 &114802851438685276
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1897668919043294}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 255407672fbf4289a8950abfa476e0b0, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ pointsCast: {fileID: 114967402694328888}
+ scaleDirection: {x: 0, y: 0, z: 1}
+ tracer:
+ validObject: {fileID: 1459395790049962}
+ invalidObject: {fileID: 1116056273701772}
+ visibility: 0
+ middle:
+ validObject: {fileID: 1571008921183954}
+ invalidObject: {fileID: 1846780609845688}
+ visibility: 0
+ cursor:
+ validObject: {fileID: 1923595575554056}
+ invalidObject: {fileID: 1200714994868418}
+ visibility: 0
+--- !u!114 &114921987913562258
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1897668919043294}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 5195ecf61608495884b10998b2c6d117, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ pointsCast: {fileID: 114967402694328888}
+ Entered:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: VRTK.Core.Pointer.Pointer+PointerUnityEvent, Assembly-CSharp, Version=0.0.0.0,
+ Culture=neutral, PublicKeyToken=null
+ Exited:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: VRTK.Core.Pointer.Pointer+PointerUnityEvent, Assembly-CSharp, Version=0.0.0.0,
+ Culture=neutral, PublicKeyToken=null
+ Hovering:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: VRTK.Core.Pointer.Pointer+PointerUnityEvent, Assembly-CSharp, Version=0.0.0.0,
+ Culture=neutral, PublicKeyToken=null
+--- !u!114 &114967402694328888
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1897668919043294}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f57b1a00a82f48d3a19652931fc4887d, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ physicsCast: {fileID: 0}
+ targetValidity: {fileID: 0}
+ CastResultsChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: VRTK.Core.Cast.PointsCast+PointsCastUnityEvent, Assembly-CSharp, Version=0.0.0.0,
+ Culture=neutral, PublicKeyToken=null
+ maximumLength: {x: 10, y: Infinity}
+ heightLimitAngle: 100
+ density: 10
+ collisionCheckFrequency: 10
+ curveOffset: 1
diff --git a/Prefabs/Pointers/SolidParabolicPointer.prefab.meta b/Prefabs/Pointers/SolidParabolicPointer.prefab.meta
new file mode 100644
index 00000000..072cd187
--- /dev/null
+++ b/Prefabs/Pointers/SolidParabolicPointer.prefab.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 85ab5ffc906134f4792a21f10e948a12
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 100100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Prefabs/Pointers/SolidStraightPointer.prefab b/Prefabs/Pointers/SolidStraightPointer.prefab
new file mode 100644
index 00000000..c38cace4
--- /dev/null
+++ b/Prefabs/Pointers/SolidStraightPointer.prefab
@@ -0,0 +1,495 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1001 &100100000
+Prefab:
+ m_ObjectHideFlags: 1
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications: []
+ m_RemovedComponents: []
+ m_ParentPrefab: {fileID: 0}
+ m_RootGameObject: {fileID: 1552269893497444}
+ m_IsPrefabParent: 1
+--- !u!1 &1151889711566436
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4618714272313756}
+ m_Layer: 0
+ m_Name: Tracer
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1195745717349208
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4470840868069594}
+ - component: {fileID: 33842711365439030}
+ - component: {fileID: 23813864600538518}
+ m_Layer: 0
+ m_Name: ValidCursor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1268476617082826
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4603804475465888}
+ - component: {fileID: 33314063943023140}
+ - component: {fileID: 23699266861947568}
+ m_Layer: 0
+ m_Name: InvalidTracer
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1496519822845236
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4579394868571944}
+ - component: {fileID: 33710724874815580}
+ - component: {fileID: 23383046084383780}
+ m_Layer: 0
+ m_Name: InvalidCursor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1552269893497444
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4768196633265156}
+ - component: {fileID: 114710422162132952}
+ - component: {fileID: 114112185610041394}
+ - component: {fileID: 114990274115864700}
+ - component: {fileID: 114712552237190026}
+ - component: {fileID: 114365101931089768}
+ m_Layer: 0
+ m_Name: SolidStraightPointer
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1558147321532568
+GameObject:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4065798878235614}
+ - component: {fileID: 33408914329589152}
+ - component: {fileID: 23017688154559558}
+ m_Layer: 0
+ m_Name: ValidTracer
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1833070260010384
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4121294435962586}
+ m_Layer: 0
+ m_Name: Cursor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4065798878235614
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1558147321532568}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.05, y: 0.05, z: 0.05}
+ m_Children: []
+ m_Father: {fileID: 4618714272313756}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4121294435962586
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1833070260010384}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4470840868069594}
+ - {fileID: 4579394868571944}
+ m_Father: {fileID: 4768196633265156}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4470840868069594
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1195745717349208}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.05, y: 0.05, z: 0.05}
+ m_Children: []
+ m_Father: {fileID: 4121294435962586}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4579394868571944
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1496519822845236}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.05, y: 0.05, z: 0.05}
+ m_Children: []
+ m_Father: {fileID: 4121294435962586}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4603804475465888
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1268476617082826}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0.005, y: 0.005, z: 0.005}
+ m_Children: []
+ m_Father: {fileID: 4618714272313756}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4618714272313756
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1151889711566436}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4065798878235614}
+ - {fileID: 4603804475465888}
+ m_Father: {fileID: 4768196633265156}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4768196633265156
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1552269893497444}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4618714272313756}
+ - {fileID: 4121294435962586}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!23 &23017688154559558
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1558147321532568}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 45363d50487514f40be10a6626b8874d, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!23 &23383046084383780
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1496519822845236}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 3ea92609994834245baf997807c5677c, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!23 &23699266861947568
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1268476617082826}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 3ea92609994834245baf997807c5677c, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!23 &23813864600538518
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1195745717349208}
+ m_Enabled: 1
+ m_CastShadows: 0
+ m_ReceiveShadows: 0
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 45363d50487514f40be10a6626b8874d, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!33 &33314063943023140
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1268476617082826}
+ m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!33 &33408914329589152
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1558147321532568}
+ m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!33 &33710724874815580
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1496519822845236}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!33 &33842711365439030
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1195745717349208}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!114 &114112185610041394
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1552269893497444}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 5195ecf61608495884b10998b2c6d117, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ pointsCast: {fileID: 114710422162132952}
+ Entered:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: VRTK.Core.Pointer.Pointer+PointerUnityEvent, Assembly-CSharp, Version=0.0.0.0,
+ Culture=neutral, PublicKeyToken=null
+ Exited:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: VRTK.Core.Pointer.Pointer+PointerUnityEvent, Assembly-CSharp, Version=0.0.0.0,
+ Culture=neutral, PublicKeyToken=null
+ Hovering:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: VRTK.Core.Pointer.Pointer+PointerUnityEvent, Assembly-CSharp, Version=0.0.0.0,
+ Culture=neutral, PublicKeyToken=null
+--- !u!114 &114365101931089768
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1552269893497444}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: a11862926720f544fb5d904995c2b57b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ process:
+ field: {fileID: 114710422162132952}
+ onlyProcessOnActiveAndEnabled: 1
+--- !u!114 &114710422162132952
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1552269893497444}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 7934e9d7f2f8434c9358d4d4e8a14b6a, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ physicsCast: {fileID: 0}
+ targetValidity: {fileID: 0}
+ CastResultsChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: VRTK.Core.Cast.PointsCast+PointsCastUnityEvent, Assembly-CSharp, Version=0.0.0.0,
+ Culture=neutral, PublicKeyToken=null
+ maximumLength: 100
+--- !u!114 &114712552237190026
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1552269893497444}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 5c0eab237e25807459af1c5caf4d3d33, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ momentToProcess: 5
+ processes:
+ - {fileID: 114365101931089768}
+--- !u!114 &114990274115864700
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1552269893497444}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 255407672fbf4289a8950abfa476e0b0, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ pointsCast: {fileID: 114710422162132952}
+ scaleDirection: {x: 0, y: 0, z: 1}
+ tracer:
+ validObject: {fileID: 1558147321532568}
+ invalidObject: {fileID: 1268476617082826}
+ visibility: 0
+ middle:
+ validObject: {fileID: 1558147321532568}
+ invalidObject: {fileID: 1268476617082826}
+ visibility: 0
+ cursor:
+ validObject: {fileID: 1195745717349208}
+ invalidObject: {fileID: 1496519822845236}
+ visibility: 0
diff --git a/Prefabs/Pointers/SolidStraightPointer.prefab.meta b/Prefabs/Pointers/SolidStraightPointer.prefab.meta
new file mode 100644
index 00000000..f414335d
--- /dev/null
+++ b/Prefabs/Pointers/SolidStraightPointer.prefab.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 88f3454742388c943bc473e342a64467
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 100100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Cast.meta b/Scripts/Cast.meta
new file mode 100644
index 00000000..12655a0e
--- /dev/null
+++ b/Scripts/Cast.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 156756a9266a4c42a4c9363aee845395
+timeCreated: 1526407015
\ No newline at end of file
diff --git a/Scripts/Cast/ParabolicLineCast.cs b/Scripts/Cast/ParabolicLineCast.cs
new file mode 100644
index 00000000..049f8438
--- /dev/null
+++ b/Scripts/Cast/ParabolicLineCast.cs
@@ -0,0 +1,178 @@
+namespace VRTK.Core.Cast
+{
+ using UnityEngine;
+ using VRTK.Core.Utility;
+
+ ///
+ /// Casts a parabolic line and creates points at the origin, the target and in between.
+ ///
+ public class ParabolicLineCast : PointsCast
+ {
+ ///
+ /// Used to move the points back and up a bit to prevent the cast clipping at the collision points.
+ ///
+ protected const float ADJUSTMENT_OFFSET = 0.0001f;
+
+ ///
+ /// The maximum length of the projected cast. The x value is the length of the forward cast, the y value is the length of the downward cast.
+ ///
+ [Tooltip("The maximum length of the projected cast. The x value is the length of the forward cast, the y value is the length of the downward cast.")]
+ public Vector2 maximumLength = new Vector2(10f, float.PositiveInfinity);
+ ///
+ /// The maximum angle in degrees of the origin before the cast line height is restricted. A lower angle setting will prevent the cast being projected high into the sky and curving back down.
+ ///
+ [Range(1, 100)]
+ [Tooltip("The maximum angle in degrees of the origin before the cast line height is restricted. A lower angle setting will prevent the cast being projected high into the sky and curving back down.")]
+ public float heightLimitAngle = 100f;
+ ///
+ /// The number of points to generate on the parabolic line.
+ ///
+ /// The higher the number, the more CPU intensive the point generation becomes.
+ [Tooltip("The number of points to generate on the parabolic line. The higher the number, the more CPU intensive the point generation becomes.")]
+ public int density = 10;
+ ///
+ /// The number of points along the parabolic line to check for an early cast collision. Useful if the parabolic line is appearing to clip through locations. 0 won't make any checks and it will be capped at .
+ ///
+ /// The higher the number, the more CPU intensive the checks become.
+ [Tooltip("The number of points along the parabolic line to check for an early cast collision. Useful if the parabolic line is appearing to clip through locations. 0 won't make any checks and it will be capped at the set density. The higher the number, the more CPU intensive the checks become.")]
+ public int collisionCheckFrequency;
+ ///
+ /// The amount of height offset to apply to the projected cast to generate a smoother line even when the cast is pointing straight.
+ ///
+ [Tooltip("The amount of height offset to apply to the projected cast to generate a smoother line even when the cast is pointing straight.")]
+ public float curveOffset = 1f;
+
+ ///
+ public override void CastPoints()
+ {
+ Vector3 forward = ProjectForward();
+ Vector3 down = ProjectDown(forward);
+ AdjustForEarlyCollisions(forward, down);
+ }
+
+ ///
+ /// Projects a straight line forward from the current .
+ ///
+ /// The collision point or the point being the furthest away on the cast line if nothing is hit.
+ protected virtual Vector3 ProjectForward()
+ {
+ float rotation = Vector3.Dot(Vector3.up, transform.forward.normalized);
+ float length = maximumLength.x;
+
+ if (rotation * 100f > heightLimitAngle)
+ {
+ float controllerRotationOffset = 1f - (rotation - heightLimitAngle / 100f);
+ length = maximumLength.x * controllerRotationOffset * controllerRotationOffset;
+ }
+
+ Ray ray = new Ray(transform.position, transform.forward);
+ RaycastHit hitData;
+ bool hasCollided = PhysicsCast.Raycast(physicsCast, ray, out hitData, length, Physics.IgnoreRaycastLayer);
+
+ // Adjust the cast length if something is blocking it.
+ if (hasCollided && hitData.distance < length)
+ {
+ length = hitData.distance;
+ }
+
+ // Use an offset to move the point back and up a bit to prevent the cast clipping at the collision point.
+ return ray.GetPoint(length - ADJUSTMENT_OFFSET) + Vector3.up * ADJUSTMENT_OFFSET;
+ }
+
+ ///
+ /// Projects a straight line downwards from the provided point.
+ ///
+ /// The origin of the projected line.
+ /// The collision point or the point being the furthest away on the cast line if nothing is hit.
+ protected virtual Vector3 ProjectDown(Vector3 origin)
+ {
+ Vector3 point = Vector3.zero;
+ Ray ray = new Ray(origin, Vector3.down);
+ RaycastHit hitData;
+
+ bool downRayHit = PhysicsCast.Raycast(physicsCast, ray, out hitData, maximumLength.y, Physics.IgnoreRaycastLayer);
+
+ if (!downRayHit || (TargetHit?.collider != null && TargetHit.Value.collider != hitData.collider))
+ {
+ TargetHit = null;
+ point = ray.GetPoint(0f);
+ }
+
+ if (downRayHit)
+ {
+ point = ray.GetPoint(hitData.distance);
+ TargetHit = hitData;
+ }
+
+ return point;
+ }
+
+ ///
+ /// Checks for early collisions along the parabolic line and generates the final points.
+ ///
+ /// The forward direction to use for the checks.
+ /// The downwards direction to use for the checks.
+ protected virtual void AdjustForEarlyCollisions(Vector3 forward, Vector3 down)
+ {
+ GeneratePoints(forward, down);
+
+ collisionCheckFrequency = Mathf.Clamp(collisionCheckFrequency, 0, density);
+ int step = density / (collisionCheckFrequency > 0 ? collisionCheckFrequency : 1);
+
+ for (int index = 0; index < density - step; index += step)
+ {
+ Vector3 currentPoint = points[index];
+ Vector3 nextPoint = index + step < points.Count ? points[index + step] : points[points.Count - 1];
+ Vector3 nextPointDirection = (nextPoint - currentPoint).normalized;
+ float nextPointDistance = Vector3.Distance(currentPoint, nextPoint);
+
+ Ray pointsRay = new Ray(currentPoint, nextPointDirection);
+ RaycastHit pointsHitData;
+
+ if (!PhysicsCast.Raycast(physicsCast, pointsRay, out pointsHitData, nextPointDistance, Physics.IgnoreRaycastLayer))
+ {
+ continue;
+ }
+
+ Vector3 collisionPoint = pointsRay.GetPoint(pointsHitData.distance);
+ Ray downwardRay = new Ray(collisionPoint + Vector3.up * 0.01f, Vector3.down);
+ RaycastHit downwardHitData;
+
+ if (!PhysicsCast.Raycast(physicsCast, downwardRay, out downwardHitData, float.PositiveInfinity, Physics.IgnoreRaycastLayer))
+ {
+ continue;
+ }
+
+ TargetHit = downwardHitData;
+
+ Vector3 newDownPosition = downwardRay.GetPoint(downwardHitData.distance);
+ Vector3 newJointPosition = newDownPosition.y < forward.y ? new Vector3(newDownPosition.x, forward.y, newDownPosition.z) : forward;
+ GeneratePoints(newJointPosition, newDownPosition);
+
+ break;
+ }
+
+ OnCastResultsChanged();
+ }
+
+ ///
+ /// Generates points on a parabolic line.
+ ///
+ /// The end point of the forward cast.
+ /// The end point of the down cast.
+ /// The generated points on the parabolic line.
+ protected virtual void GeneratePoints(Vector3 forward, Vector3 down)
+ {
+ Vector3[] curvePoints =
+ {
+ transform.position,
+ forward + new Vector3(0f, curveOffset, 0f),
+ down,
+ down
+ };
+
+ points.Clear();
+ points.AddRange(BezierCurveGenerator.GeneratePoints(density, curvePoints));
+ }
+ }
+}
diff --git a/Scripts/Cast/ParabolicLineCast.cs.meta b/Scripts/Cast/ParabolicLineCast.cs.meta
new file mode 100644
index 00000000..95258a9a
--- /dev/null
+++ b/Scripts/Cast/ParabolicLineCast.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: f57b1a00a82f48d3a19652931fc4887d
+timeCreated: 1526406952
\ No newline at end of file
diff --git a/Scripts/Utility/PhysicsCast.cs b/Scripts/Cast/PhysicsCast.cs
similarity index 98%
rename from Scripts/Utility/PhysicsCast.cs
rename to Scripts/Cast/PhysicsCast.cs
index bb161fab..a95974f7 100644
--- a/Scripts/Utility/PhysicsCast.cs
+++ b/Scripts/Cast/PhysicsCast.cs
@@ -1,201 +1,201 @@
-namespace VRTK.Core.Utility
-{
- using UnityEngine;
-
- ///
- /// The PhysicsCast allows customising of Unity Physics casting within other scripts by applying settings at edit time.
- ///
- public class PhysicsCast : MonoBehaviour
- {
- [Tooltip("The layers to ignore when casting.")]
- public LayerMask layersToIgnore = Physics.IgnoreRaycastLayer;
- [Tooltip("Determines whether the cast will interact with trigger colliders.")]
- public QueryTriggerInteraction triggerInteraction = QueryTriggerInteraction.UseGlobal;
-
- ///
- /// The Raycast method is used to generate a Raycast either from the given PhysicsCast object or a default Physics.Raycast.
- ///
- /// The optional object with customised cast parameters.
- /// The Ray to cast with.
- /// The Raycast hit data.
- /// The maximum length of the Raycast.
- /// A layermask of layers to ignore from the Raycast.
- /// Determines the trigger interaction level of the cast.
- /// Returns `true` if the Raycast successfully collides with a valid object.
- public static bool Raycast(PhysicsCast customCast, Ray ray, out RaycastHit hitData, float length, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
- {
- if (customCast != null)
- {
- return customCast.CustomRaycast(ray, out hitData, length);
- }
- else
- {
- return Physics.Raycast(ray, out hitData, length, ~ignoreLayers, affectTriggers);
- }
- }
-
- ///
- /// The Linecast method is used to generate a Linecast either from the given PhysicsCast object or a default Physics.Linecast.
- ///
- /// The optional object with customised cast parameters.
- /// The world position to start the Linecast from.
- /// The world position to end the Linecast at.
- /// The Linecast hit data.
- /// A layermask of layers to ignore from the Linecast.
- /// Determines the trigger interaction level of the cast.
- /// Returns `true` if the Linecast successfully collides with a valid object.
- public static bool Linecast(PhysicsCast customCast, Vector3 startPosition, Vector3 endPosition, out RaycastHit hitData, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
- {
- if (customCast != null)
- {
- return customCast.CustomLinecast(startPosition, endPosition, out hitData);
- }
- else
- {
- return Physics.Linecast(startPosition, endPosition, out hitData, ~ignoreLayers, affectTriggers);
- }
- }
-
- ///
- /// The SphereCast method is used to generate a SphereCast either from the given PhysicsCast object or a default Physics.SphereCast.
- ///
- /// The optional object with customised cast parameters.
- /// The origin point of the sphere to cast.
- /// The radius of the sphere.
- /// The direction into which to sweep the sphere.
- /// The SphereCast hit data.
- /// The max length of the sweep.
- /// A layermask of layers to ignore from the SphereCast.
- /// Determines the trigger interaction level of the cast.
- /// Returns `true` if the SphereCast successfully collides with a valid object.
- public static bool SphereCast(PhysicsCast customCast, Vector3 origin, float radius, Vector3 direction, out RaycastHit hitData, float maxDistance, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
- {
- if (customCast != null)
- {
- return customCast.CustomSphereCast(origin, radius, direction, out hitData, maxDistance);
- }
- else
- {
- return Physics.SphereCast(origin, radius, direction, out hitData, maxDistance, ~ignoreLayers, affectTriggers);
- }
- }
-
- ///
- /// The CapsuleCast method is used to generate a CapsuleCast either from the given PhysicsCast object or a default Physics.CapsuleCast.
- ///
- /// The optional object with customised cast parameters.
- /// The center of the sphere at the start of the capsule.
- /// The center of the sphere at the end of the capsule.
- /// The radius of the capsule.
- /// The direction into which to sweep the capsule.
- /// The CapsuleCast hit data.
- /// The max length of the sweep.
- /// A layermask of layers to ignore from the CapsuleCast.
- /// Determines the trigger interaction level of the cast.
- /// Returns `true` if the CapsuleCast successfully collides with a valid object.
- public static bool CapsuleCast(PhysicsCast customCast, Vector3 point1, Vector3 point2, float radius, Vector3 direction, out RaycastHit hitData, float maxDistance, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
- {
- if (customCast != null)
- {
- return customCast.CustomCapsuleCast(point1, point2, radius, direction, out hitData, maxDistance);
- }
- else
- {
- return Physics.CapsuleCast(point1, point2, radius, direction, out hitData, maxDistance, ~ignoreLayers, affectTriggers);
- }
- }
-
- ///
- /// The BoxCast method is used to generate a BoxCast either from the given PhysicsCast object or a default Physics.BoxCast.
- ///
- /// The optional object with customised cast parameters.
- /// The center of the box.
- /// Half the size of the box in each dimension.
- /// The direction in which to cast the box.
- /// The BoxCast hit data.
- /// The rotation of the box.
- /// The max length of the cast.
- /// A layermask of layers to ignore from the BoxCast.
- /// Determines the trigger interaction level of the cast.
- /// Returns `true` if the BoxCast successfully collides with a valid object.
- public static bool BoxCast(PhysicsCast customCast, Vector3 center, Vector3 halfExtents, Vector3 direction, out RaycastHit hitData, Quaternion orientation, float maxDistance, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
- {
- if (customCast != null)
- {
- return customCast.CustomBoxCast(center, halfExtents, direction, out hitData, orientation, maxDistance);
- }
- else
- {
- return Physics.BoxCast(center, halfExtents, direction, out hitData, orientation, maxDistance, ~ignoreLayers, affectTriggers);
- }
- }
-
- ///
- /// The CustomRaycast method is used to generate a Raycast based on the options defined in the PhysicsCast object.
- ///
- /// The Ray to cast with.
- /// The Raycast hit data.
- /// The maximum length of the Raycast.
- /// Returns `true` if the Raycast successfully collides with a valid object.
- public virtual bool CustomRaycast(Ray ray, out RaycastHit hitData, float length)
- {
- return Physics.Raycast(ray, out hitData, length, ~layersToIgnore, triggerInteraction);
- }
-
- ///
- /// The CustomLinecast method is used to generate a Linecast based on the options defined in the PhysicsCast object.
- ///
- /// The world position to start the Linecast from.
- /// The world position to end the Linecast at.
- /// The Linecast hit data.
- /// Returns `true` if the Linecast successfully collides with a valid object.
- public virtual bool CustomLinecast(Vector3 startPosition, Vector3 endPosition, out RaycastHit hitData)
- {
- return Physics.Linecast(startPosition, endPosition, out hitData, ~layersToIgnore, triggerInteraction);
- }
-
- ///
- /// The CustomSphereCast method is used to generate a SphereCast based on the options defined in the PhysicsCast object.
- ///
- /// The origin point of the sphere to cast.
- /// The radius of the sphere.
- /// The direction into which to sweep the sphere.
- /// The SphereCast hit data.
- /// The max length of the sweep.
- /// Returns `true` if the SphereCast successfully collides with a valid object.
- public virtual bool CustomSphereCast(Vector3 origin, float radius, Vector3 direction, out RaycastHit hitData, float maxDistance)
- {
- return Physics.SphereCast(origin, radius, direction, out hitData, maxDistance, ~layersToIgnore, triggerInteraction);
- }
-
- ///
- /// The CustomCapsuleCast method is used to generate a CapsuleCast based on the options defined in the PhysicsCast object.
- ///
- /// The center of the sphere at the start of the capsule.
- /// The center of the sphere at the end of the capsule.
- /// The radius of the capsule.
- /// The direction into which to sweep the capsule.
- /// The CapsuleCast hit data.
- /// The max length of the sweep.
- /// Returns `true` if the CapsuleCast successfully collides with a valid object.
- public virtual bool CustomCapsuleCast(Vector3 point1, Vector3 point2, float radius, Vector3 direction, out RaycastHit hitData, float maxDistance)
- {
- return Physics.CapsuleCast(point1, point2, radius, direction, out hitData, maxDistance, ~layersToIgnore, triggerInteraction);
- }
-
- ///
- /// The CustomBoxCast method is used to generate a BoxCast based on the options defined in the PhysicsCast object.
- ///
- /// The center of the box.
- /// Half the size of the box in each dimension.
- /// The direction in which to cast the box.
- /// The BoxCast hit data.
- /// The rotation of the box.
- /// The max length of the cast.
- /// Returns `true` if the box successfully collides with a valid object.
- public virtual bool CustomBoxCast(Vector3 center, Vector3 halfExtents, Vector3 direction, out RaycastHit hitData, Quaternion orientation, float maxDistance)
- {
- return Physics.BoxCast(center, halfExtents, direction, out hitData, orientation, maxDistance, ~layersToIgnore, triggerInteraction);
- }
- }
+namespace VRTK.Core.Cast
+{
+ using UnityEngine;
+
+ ///
+ /// The PhysicsCast allows customising of Unity Physics casting within other scripts by applying settings at edit time.
+ ///
+ public class PhysicsCast : MonoBehaviour
+ {
+ [Tooltip("The layers to ignore when casting.")]
+ public LayerMask layersToIgnore = Physics.IgnoreRaycastLayer;
+ [Tooltip("Determines whether the cast will interact with trigger colliders.")]
+ public QueryTriggerInteraction triggerInteraction = QueryTriggerInteraction.UseGlobal;
+
+ ///
+ /// The Raycast method is used to generate a Raycast either from the given PhysicsCast object or a default Physics.Raycast.
+ ///
+ /// The optional object with customised cast parameters.
+ /// The Ray to cast with.
+ /// The Raycast hit data.
+ /// The maximum length of the Raycast.
+ /// A layermask of layers to ignore from the Raycast.
+ /// Determines the trigger interaction level of the cast.
+ /// Returns `true` if the Raycast successfully collides with a valid object.
+ public static bool Raycast(PhysicsCast customCast, Ray ray, out RaycastHit hitData, float length, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
+ {
+ if (customCast != null)
+ {
+ return customCast.CustomRaycast(ray, out hitData, length);
+ }
+ else
+ {
+ return Physics.Raycast(ray, out hitData, length, ~ignoreLayers, affectTriggers);
+ }
+ }
+
+ ///
+ /// The Linecast method is used to generate a Linecast either from the given PhysicsCast object or a default Physics.Linecast.
+ ///
+ /// The optional object with customised cast parameters.
+ /// The world position to start the Linecast from.
+ /// The world position to end the Linecast at.
+ /// The Linecast hit data.
+ /// A layermask of layers to ignore from the Linecast.
+ /// Determines the trigger interaction level of the cast.
+ /// Returns `true` if the Linecast successfully collides with a valid object.
+ public static bool Linecast(PhysicsCast customCast, Vector3 startPosition, Vector3 endPosition, out RaycastHit hitData, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
+ {
+ if (customCast != null)
+ {
+ return customCast.CustomLinecast(startPosition, endPosition, out hitData);
+ }
+ else
+ {
+ return Physics.Linecast(startPosition, endPosition, out hitData, ~ignoreLayers, affectTriggers);
+ }
+ }
+
+ ///
+ /// The SphereCast method is used to generate a SphereCast either from the given PhysicsCast object or a default Physics.SphereCast.
+ ///
+ /// The optional object with customised cast parameters.
+ /// The origin point of the sphere to cast.
+ /// The radius of the sphere.
+ /// The direction into which to sweep the sphere.
+ /// The SphereCast hit data.
+ /// The max length of the sweep.
+ /// A layermask of layers to ignore from the SphereCast.
+ /// Determines the trigger interaction level of the cast.
+ /// Returns `true` if the SphereCast successfully collides with a valid object.
+ public static bool SphereCast(PhysicsCast customCast, Vector3 origin, float radius, Vector3 direction, out RaycastHit hitData, float maxDistance, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
+ {
+ if (customCast != null)
+ {
+ return customCast.CustomSphereCast(origin, radius, direction, out hitData, maxDistance);
+ }
+ else
+ {
+ return Physics.SphereCast(origin, radius, direction, out hitData, maxDistance, ~ignoreLayers, affectTriggers);
+ }
+ }
+
+ ///
+ /// The CapsuleCast method is used to generate a CapsuleCast either from the given PhysicsCast object or a default Physics.CapsuleCast.
+ ///
+ /// The optional object with customised cast parameters.
+ /// The center of the sphere at the start of the capsule.
+ /// The center of the sphere at the end of the capsule.
+ /// The radius of the capsule.
+ /// The direction into which to sweep the capsule.
+ /// The CapsuleCast hit data.
+ /// The max length of the sweep.
+ /// A layermask of layers to ignore from the CapsuleCast.
+ /// Determines the trigger interaction level of the cast.
+ /// Returns `true` if the CapsuleCast successfully collides with a valid object.
+ public static bool CapsuleCast(PhysicsCast customCast, Vector3 point1, Vector3 point2, float radius, Vector3 direction, out RaycastHit hitData, float maxDistance, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
+ {
+ if (customCast != null)
+ {
+ return customCast.CustomCapsuleCast(point1, point2, radius, direction, out hitData, maxDistance);
+ }
+ else
+ {
+ return Physics.CapsuleCast(point1, point2, radius, direction, out hitData, maxDistance, ~ignoreLayers, affectTriggers);
+ }
+ }
+
+ ///
+ /// The BoxCast method is used to generate a BoxCast either from the given PhysicsCast object or a default Physics.BoxCast.
+ ///
+ /// The optional object with customised cast parameters.
+ /// The center of the box.
+ /// Half the size of the box in each dimension.
+ /// The direction in which to cast the box.
+ /// The BoxCast hit data.
+ /// The rotation of the box.
+ /// The max length of the cast.
+ /// A layermask of layers to ignore from the BoxCast.
+ /// Determines the trigger interaction level of the cast.
+ /// Returns `true` if the BoxCast successfully collides with a valid object.
+ public static bool BoxCast(PhysicsCast customCast, Vector3 center, Vector3 halfExtents, Vector3 direction, out RaycastHit hitData, Quaternion orientation, float maxDistance, LayerMask ignoreLayers, QueryTriggerInteraction affectTriggers = QueryTriggerInteraction.UseGlobal)
+ {
+ if (customCast != null)
+ {
+ return customCast.CustomBoxCast(center, halfExtents, direction, out hitData, orientation, maxDistance);
+ }
+ else
+ {
+ return Physics.BoxCast(center, halfExtents, direction, out hitData, orientation, maxDistance, ~ignoreLayers, affectTriggers);
+ }
+ }
+
+ ///
+ /// The CustomRaycast method is used to generate a Raycast based on the options defined in the PhysicsCast object.
+ ///
+ /// The Ray to cast with.
+ /// The Raycast hit data.
+ /// The maximum length of the Raycast.
+ /// Returns `true` if the Raycast successfully collides with a valid object.
+ public virtual bool CustomRaycast(Ray ray, out RaycastHit hitData, float length)
+ {
+ return Physics.Raycast(ray, out hitData, length, ~layersToIgnore, triggerInteraction);
+ }
+
+ ///
+ /// The CustomLinecast method is used to generate a Linecast based on the options defined in the PhysicsCast object.
+ ///
+ /// The world position to start the Linecast from.
+ /// The world position to end the Linecast at.
+ /// The Linecast hit data.
+ /// Returns `true` if the Linecast successfully collides with a valid object.
+ public virtual bool CustomLinecast(Vector3 startPosition, Vector3 endPosition, out RaycastHit hitData)
+ {
+ return Physics.Linecast(startPosition, endPosition, out hitData, ~layersToIgnore, triggerInteraction);
+ }
+
+ ///
+ /// The CustomSphereCast method is used to generate a SphereCast based on the options defined in the PhysicsCast object.
+ ///
+ /// The origin point of the sphere to cast.
+ /// The radius of the sphere.
+ /// The direction into which to sweep the sphere.
+ /// The SphereCast hit data.
+ /// The max length of the sweep.
+ /// Returns `true` if the SphereCast successfully collides with a valid object.
+ public virtual bool CustomSphereCast(Vector3 origin, float radius, Vector3 direction, out RaycastHit hitData, float maxDistance)
+ {
+ return Physics.SphereCast(origin, radius, direction, out hitData, maxDistance, ~layersToIgnore, triggerInteraction);
+ }
+
+ ///
+ /// The CustomCapsuleCast method is used to generate a CapsuleCast based on the options defined in the PhysicsCast object.
+ ///
+ /// The center of the sphere at the start of the capsule.
+ /// The center of the sphere at the end of the capsule.
+ /// The radius of the capsule.
+ /// The direction into which to sweep the capsule.
+ /// The CapsuleCast hit data.
+ /// The max length of the sweep.
+ /// Returns `true` if the CapsuleCast successfully collides with a valid object.
+ public virtual bool CustomCapsuleCast(Vector3 point1, Vector3 point2, float radius, Vector3 direction, out RaycastHit hitData, float maxDistance)
+ {
+ return Physics.CapsuleCast(point1, point2, radius, direction, out hitData, maxDistance, ~layersToIgnore, triggerInteraction);
+ }
+
+ ///
+ /// The CustomBoxCast method is used to generate a BoxCast based on the options defined in the PhysicsCast object.
+ ///
+ /// The center of the box.
+ /// Half the size of the box in each dimension.
+ /// The direction in which to cast the box.
+ /// The BoxCast hit data.
+ /// The rotation of the box.
+ /// The max length of the cast.
+ /// Returns `true` if the box successfully collides with a valid object.
+ public virtual bool CustomBoxCast(Vector3 center, Vector3 halfExtents, Vector3 direction, out RaycastHit hitData, Quaternion orientation, float maxDistance)
+ {
+ return Physics.BoxCast(center, halfExtents, direction, out hitData, orientation, maxDistance, ~layersToIgnore, triggerInteraction);
+ }
+ }
}
\ No newline at end of file
diff --git a/Scripts/Utility/PhysicsCast.cs.meta b/Scripts/Cast/PhysicsCast.cs.meta
similarity index 100%
rename from Scripts/Utility/PhysicsCast.cs.meta
rename to Scripts/Cast/PhysicsCast.cs.meta
diff --git a/Scripts/Cast/PointsCast.cs b/Scripts/Cast/PointsCast.cs
new file mode 100644
index 00000000..06166b31
--- /dev/null
+++ b/Scripts/Cast/PointsCast.cs
@@ -0,0 +1,107 @@
+namespace VRTK.Core.Cast
+{
+ using UnityEngine;
+ using UnityEngine.Events;
+ using System;
+ using System.Collections.Generic;
+ using VRTK.Core.Process;
+ using VRTK.Core.Utility;
+
+ ///
+ /// Contains information about the current state.
+ ///
+ public class PointsCastData
+ {
+ ///
+ /// The result of the most recent cast. when the cast didn't hit anything.
+ ///
+ public RaycastHit? targetHit;
+ ///
+ /// The points along the the most recent cast.
+ ///
+ public IReadOnlyList points;
+ }
+
+ ///
+ /// The base of casting components that result in points along the cast.
+ ///
+ public abstract class PointsCast : MonoBehaviour, IProcessable
+ {
+ ///
+ /// Allows to optionally affect the cast.
+ ///
+ [Tooltip("Allows to optionally affect the cast.")]
+ public PhysicsCast physicsCast;
+ ///
+ /// Allows to optionally determine targets based on the set rules.
+ ///
+ [Tooltip("Allows to optionally determine targets based on the set rules.")]
+ public ExclusionRule targetValidity;
+
+ ///
+ /// Defines the event with the state and sender .
+ ///
+ [Serializable]
+ public class PointsCastUnityEvent : UnityEvent
+ {
+ }
+
+ ///
+ /// Emitted whenever the cast result changes.
+ ///
+ public PointsCastUnityEvent CastResultsChanged = new PointsCastUnityEvent();
+
+ ///
+ /// The result of the most recent cast. when the cast didn't hit anything or an invalid target according to .
+ ///
+ public RaycastHit? TargetHit
+ {
+ get
+ {
+ return targetHit;
+ }
+ protected set
+ {
+ targetHit = value != null && !ExclusionRule.ShouldExclude(value.Value.transform.gameObject, targetValidity) ? value : null;
+ }
+ }
+
+ ///
+ /// The points along the the most recent cast.
+ ///
+ public IReadOnlyList Points => points;
+
+ protected List points = new List();
+
+ private RaycastHit? targetHit;
+
+ ///
+ /// Casts and creates points along the cast.
+ ///
+ public abstract void CastPoints();
+
+ ///
+ public void Process()
+ {
+ CastPoints();
+ }
+
+ ///
+ /// Builds the event payload for the current state of the cast.
+ ///
+ /// The current state of the cast.
+ protected virtual PointsCastData GetPayload()
+ {
+ return new PointsCastData
+ {
+ targetHit = TargetHit,
+ points = Points
+ };
+ }
+
+ protected virtual void OnCastResultsChanged()
+ {
+ CastResultsChanged?.Invoke(GetPayload(), this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Cast/PointsCast.cs.meta b/Scripts/Cast/PointsCast.cs.meta
new file mode 100644
index 00000000..c9728400
--- /dev/null
+++ b/Scripts/Cast/PointsCast.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 8c527ebb1902401b89088eb9c8e627df
+timeCreated: 1526406894
\ No newline at end of file
diff --git a/Scripts/Cast/StraightLineCast.cs b/Scripts/Cast/StraightLineCast.cs
new file mode 100644
index 00000000..ff67d744
--- /dev/null
+++ b/Scripts/Cast/StraightLineCast.cs
@@ -0,0 +1,37 @@
+namespace VRTK.Core.Cast
+{
+ using UnityEngine;
+ using System.Linq;
+
+ ///
+ /// Casts a straight line and creates points at the origin and target.
+ ///
+ public class StraightLineCast : PointsCast
+ {
+ ///
+ /// The maximum length to cast.
+ ///
+ [Tooltip("The maximum length to cast.")]
+ public float maximumLength = 100f;
+
+ protected virtual void Awake()
+ {
+ points.Clear();
+ points.AddRange(Enumerable.Repeat(Vector3.zero, 2));
+ }
+
+ ///
+ public override void CastPoints()
+ {
+ Ray ray = new Ray(transform.position, transform.forward);
+ RaycastHit hitData;
+ bool hasCollided = PhysicsCast.Raycast(physicsCast, ray, out hitData, maximumLength, Physics.IgnoreRaycastLayer);
+ TargetHit = hasCollided ? hitData : (RaycastHit?)null;
+
+ points[0] = transform.position;
+ points[1] = hasCollided ? hitData.point : transform.position + transform.forward * maximumLength;
+
+ OnCastResultsChanged();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Cast/StraightLineCast.cs.meta b/Scripts/Cast/StraightLineCast.cs.meta
new file mode 100644
index 00000000..7fec7946
--- /dev/null
+++ b/Scripts/Cast/StraightLineCast.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 7934e9d7f2f8434c9358d4d4e8a14b6a
+timeCreated: 1526406940
\ No newline at end of file
diff --git a/Scripts/Pointer.meta b/Scripts/Pointer.meta
new file mode 100644
index 00000000..ce99db63
--- /dev/null
+++ b/Scripts/Pointer.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2a8115f519a29a948b2df09b1e703d7a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Pointer/Pointer.cs b/Scripts/Pointer/Pointer.cs
new file mode 100644
index 00000000..f51f71ed
--- /dev/null
+++ b/Scripts/Pointer/Pointer.cs
@@ -0,0 +1,175 @@
+namespace VRTK.Core.Pointer
+{
+ using UnityEngine;
+ using UnityEngine.Events;
+ using System;
+ using VRTK.Core.Cast;
+ using VRTK.Core.Data.Type;
+
+ ///
+ /// Contains information about the current state.
+ ///
+ public class PointerData : SurfaceData
+ {
+ ///
+ /// Whether the pointer is currently hovering over a target.
+ ///
+ public bool isHovering;
+ ///
+ /// The duration that the pointer has been hovering over it's current target.
+ ///
+ public float hoverDuration;
+ ///
+ /// The points cast data generated by the pointer's .
+ ///
+ public PointsCastData pointsCastData;
+ }
+
+ ///
+ /// Allows pointing at objects and notifies when a target is hit, continues to be hit or stops being hit by listening to a .
+ ///
+ public class Pointer : MonoBehaviour
+ {
+ ///
+ /// The source of hit information about the current target.
+ ///
+ public PointsCast pointsCast;
+
+ ///
+ /// Defines the event with the state and sender .
+ ///
+ [Serializable]
+ public class PointerUnityEvent : UnityEvent
+ {
+ }
+
+ ///
+ /// Emitted when the pointer collides with a new target.
+ ///
+ public PointerUnityEvent Entered = new PointerUnityEvent();
+ ///
+ /// Emitted when the pointer stops colliding with an existing target.
+ ///
+ public PointerUnityEvent Exited = new PointerUnityEvent();
+ ///
+ /// Emitted when the pointer changes its hovering position over an existing target.
+ ///
+ public PointerUnityEvent Hovering = new PointerUnityEvent();
+
+ ///
+ /// Reports hover duration of the pointer over the current target.
+ ///
+ public float HoverDuration
+ {
+ get;
+ protected set;
+ }
+
+ ///
+ /// Whether the pointer is currently hovering over a target.
+ ///
+ /// if the pointer is currently hovering over a target, otherwise.
+ public bool IsHovering => HoverDuration > 0f;
+
+ protected PointsCastData previousPointsCastData;
+
+ ///
+ /// The current state of the pointer.
+ ///
+ /// A object if the pointer is currently hitting a target, otherwise.
+ public PointerData GetCurrentState()
+ {
+ return previousPointsCastData == null ? null : GetPayload(previousPointsCastData);
+ }
+
+ protected virtual void OnEnable()
+ {
+ pointsCast.CastResultsChanged.AddListener(CastResultsChanged);
+ }
+
+ protected virtual void OnDisable()
+ {
+ pointsCast.CastResultsChanged.RemoveListener(CastResultsChanged);
+ }
+
+ ///
+ /// Handles the provided data to transition state and emit the pointer events.
+ ///
+ /// The data describing the results of the most recent cast.
+ /// The object from which was retrieved from.
+ protected virtual void CastResultsChanged(PointsCastData data, object sender)
+ {
+ if (data.targetHit != null)
+ {
+ Transform targetTransform = data.targetHit.Value.transform;
+ if (targetTransform != null && targetTransform != previousPointsCastData?.targetHit?.transform)
+ {
+ TryEmitExit(data);
+ OnEntered(data);
+ }
+
+ HoverDuration += Time.deltaTime;
+ OnHovering(data);
+ }
+ else
+ {
+ TryEmitExit(data);
+ }
+
+ previousPointsCastData = data;
+ }
+
+ ///
+ /// Checks to see if the pointer is not currently colliding with a valid target and emits the event.
+ ///
+ /// The current points cast data.
+ protected virtual void TryEmitExit(PointsCastData data)
+ {
+ if (previousPointsCastData?.targetHit?.transform == null)
+ {
+ return;
+ }
+
+ HoverDuration = 0f;
+ OnExited(data);
+ previousPointsCastData = null;
+ }
+
+ ///
+ /// Builds a valid pointer payload to use in the events.
+ ///
+ /// A object of the pointer's current state.
+ protected virtual PointerData GetPayload(PointsCastData data)
+ {
+ Transform targetTransform = pointsCast.TargetHit?.transform;
+ return new PointerData
+ {
+ transform = transform,
+ positionOverride = targetTransform == null ? (Vector3?)null : targetTransform.position,
+ rotationOverride = targetTransform == null ? (Quaternion?)null : targetTransform.localRotation,
+ localScaleOverride = targetTransform == null ? (Vector3?)null : targetTransform.localScale,
+ origin = transform.position,
+ direction = transform.forward,
+ CollisionData = pointsCast.TargetHit ?? default(RaycastHit),
+ isHovering = IsHovering,
+ hoverDuration = HoverDuration,
+ pointsCastData = data
+ };
+ }
+
+ protected virtual void OnEntered(PointsCastData data)
+ {
+ Entered?.Invoke(GetPayload(data), this);
+ }
+
+ protected virtual void OnExited(PointsCastData data)
+ {
+ Exited?.Invoke(GetPayload(data), this);
+ }
+
+ protected virtual void OnHovering(PointsCastData data)
+ {
+ Hovering?.Invoke(GetPayload(data), this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Pointer/Pointer.cs.meta b/Scripts/Pointer/Pointer.cs.meta
new file mode 100644
index 00000000..7250722d
--- /dev/null
+++ b/Scripts/Pointer/Pointer.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 5195ecf61608495884b10998b2c6d117
+timeCreated: 1526406779
\ No newline at end of file
diff --git a/Scripts/Pointer/PointerSelection.cs b/Scripts/Pointer/PointerSelection.cs
new file mode 100644
index 00000000..f1563023
--- /dev/null
+++ b/Scripts/Pointer/PointerSelection.cs
@@ -0,0 +1,26 @@
+namespace VRTK.Core.Pointer
+{
+ using UnityEngine;
+
+ ///
+ /// Adds the concept of selection to a .
+ ///
+ public class PointerSelection : MonoBehaviour
+ {
+ public Pointer pointer;
+
+ ///
+ /// Emitted whenever is called.
+ ///
+ public Pointer.PointerUnityEvent Selected = new Pointer.PointerUnityEvent();
+
+ ///
+ /// Gets the current pointer state and emits it.
+ ///
+ /// The emitted pointer state is in case the pointer isn't hitting any target.
+ public virtual void Select()
+ {
+ Selected?.Invoke(pointer.GetCurrentState(), this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Pointer/PointerSelection.cs.meta b/Scripts/Pointer/PointerSelection.cs.meta
new file mode 100644
index 00000000..69a4e30f
--- /dev/null
+++ b/Scripts/Pointer/PointerSelection.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: db0b134eb27b4e74a71ed8c651efb2b9
+timeCreated: 1526406886
\ No newline at end of file
diff --git a/Scripts/Tracking/SurfaceLocator.cs b/Scripts/Tracking/SurfaceLocator.cs
index 491e92da..3883f2d5 100644
--- a/Scripts/Tracking/SurfaceLocator.cs
+++ b/Scripts/Tracking/SurfaceLocator.cs
@@ -3,6 +3,7 @@
using UnityEngine;
using UnityEngine.Events;
using System;
+ using VRTK.Core.Cast;
using VRTK.Core.Utility;
using VRTK.Core.Data.Type;
using VRTK.Core.Process;
diff --git a/Scripts/Utility/BezierCurveGenerator.cs b/Scripts/Utility/BezierCurveGenerator.cs
new file mode 100644
index 00000000..ea322a63
--- /dev/null
+++ b/Scripts/Utility/BezierCurveGenerator.cs
@@ -0,0 +1,52 @@
+namespace VRTK.Core.Utility
+{
+ using UnityEngine;
+ using System.Collections.Generic;
+
+ ///
+ /// A collection of helper methods generating points on a bezier curve.
+ ///
+ public static class BezierCurveGenerator
+ {
+ ///
+ /// Generates points on a bezier curve.
+ ///
+ /// The number of points to generate.
+ /// Points defining the bezier curve.
+ /// The generated points.
+ public static Vector3[] GeneratePoints(int count, Vector3[] controlPoints)
+ {
+ Vector3[] calculatedPoints = new Vector3[count];
+ float stepSize = count != 1 ? 1f / (count - 1) : count;
+
+ for (int f = 0; f < count; f++)
+ {
+ calculatedPoints[f] = GeneratePoint(controlPoints, f * stepSize);
+ }
+
+ return calculatedPoints;
+ }
+
+ private static Vector3 GeneratePoint(IReadOnlyList controlPoints, float t)
+ {
+ int index;
+ if (t >= 1f)
+ {
+ t = 1f;
+ index = controlPoints.Count - 4;
+ }
+ else
+ {
+ t = Mathf.Clamp01(t) * ((controlPoints.Count - 1) / 3f);
+ index = (int)t;
+ t -= index;
+ index *= 3;
+ }
+
+ float t1 = t;
+ t1 = Mathf.Clamp01(t1);
+ float oneMinusT = 1f - t1;
+ return oneMinusT * oneMinusT * oneMinusT * controlPoints[index] + 3f * oneMinusT * oneMinusT * t1 * controlPoints[index + 1] + 3f * oneMinusT * t1 * t1 * controlPoints[index + 2] + t1 * t1 * t1 * controlPoints[index + 3];
+ }
+ }
+}
diff --git a/Scripts/Utility/BezierCurveGenerator.cs.meta b/Scripts/Utility/BezierCurveGenerator.cs.meta
new file mode 100644
index 00000000..e326d7da
--- /dev/null
+++ b/Scripts/Utility/BezierCurveGenerator.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: a17fd115e2a54dbeb58716bc64cb8c7c
+timeCreated: 1526417342
\ No newline at end of file
diff --git a/Scripts/Utility/TransformExtensions.cs b/Scripts/Utility/TransformExtensions.cs
new file mode 100644
index 00000000..b56c20ca
--- /dev/null
+++ b/Scripts/Utility/TransformExtensions.cs
@@ -0,0 +1,18 @@
+namespace VRTK.Core.Utility
+{
+ using UnityEngine;
+
+ public static class TransformExtensions
+ {
+ ///
+ /// The SetGlobalScale method is used to set a transform scale based on a global scale instead of a local scale.
+ ///
+ /// The reference to the transform to scale.
+ /// A Vector3 of a global scale to apply to the given transform.
+ public static void SetGlobalScale(this Transform transform, Vector3 globalScale)
+ {
+ transform.localScale = Vector3.one;
+ transform.localScale = new Vector3(globalScale.x / transform.lossyScale.x, globalScale.y / transform.lossyScale.y, globalScale.z / transform.lossyScale.z);
+ }
+ }
+}
diff --git a/Scripts/Utility/TransformExtensions.cs.meta b/Scripts/Utility/TransformExtensions.cs.meta
new file mode 100644
index 00000000..4af8e183
--- /dev/null
+++ b/Scripts/Utility/TransformExtensions.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: a54e9605dd1c416e8f50e7e6943e3f9a
+timeCreated: 1526738386
\ No newline at end of file
diff --git a/Scripts/Visual/PointsRenderer.cs b/Scripts/Visual/PointsRenderer.cs
new file mode 100644
index 00000000..46f8d494
--- /dev/null
+++ b/Scripts/Visual/PointsRenderer.cs
@@ -0,0 +1,261 @@
+namespace VRTK.Core.Visual
+{
+ using UnityEngine;
+ using System;
+ using System.Collections.Generic;
+ using VRTK.Core.Cast;
+ using VRTK.Core.Utility;
+
+ public class PointsRenderer : MonoBehaviour
+ {
+ ///
+ /// The visibility of the pointer elements.
+ ///
+ public enum Visibility
+ {
+ ///
+ /// The element will only be visible when the pointer is in the active and enabled state.
+ ///
+ OnWhenActiveAndEnabled,
+ ///
+ /// The element will always be visible regardless of the pointer state.
+ ///
+ AlwaysOn,
+ ///
+ /// The element will never be visible regardless of the pointer state.
+ ///
+ AlwaysOff
+ }
+
+ [Serializable]
+ public class Element
+ {
+ ///
+ /// The to represent the element when the pointer is colliding with a valid object.
+ ///
+ [Tooltip("The GameObject to represent the element when the pointer is colliding with a valid object.")]
+ public GameObject validObject;
+ ///
+ /// The to represent the element when the pointer is colliding with an invalid object or not colliding at all.
+ ///
+ [Tooltip("The GameObject to represent the element when the pointer is colliding with an invalid object or not colliding at all.")]
+ public GameObject invalidObject;
+ ///
+ /// Determines when the element should be seen.
+ ///
+ [Tooltip("Determines when the element GameObject should be seen.")]
+ public Visibility visibility = Visibility.OnWhenActiveAndEnabled;
+ }
+
+ ///
+ /// The from which the points to render will be taken from.
+ ///
+ [Tooltip("The PointsCast from which the points to render will be taken from.")]
+ public PointsCast pointsCast;
+
+ ///
+ /// The direction to scale the s in. Set axes to 0 to disable scaling on that axis.
+ ///
+ [Tooltip("The direction to scale the Elements in. Set axes to 0 to disable scaling on that axis.")]
+ public Vector3 scaleDirection = Vector3.forward;
+
+ ///
+ /// The to represent the pointer's tracer.
+ ///
+ [Tooltip("The Element to represent the pointer's tracer.")]
+ public Element tracer;
+ ///
+ /// The to represent the pointer's optional middle.
+ ///
+ [Tooltip("The Element to represent the pointer's optional middle.")]
+ public Element middle;
+ ///
+ /// The to represent the pointer's cursor.
+ ///
+ [Tooltip("The Element to represent the pointer's cursor.")]
+ public Element cursor;
+
+ protected readonly List validMiddleClones = new List();
+ protected readonly List invalidMiddleClones = new List();
+
+ protected virtual void OnEnable()
+ {
+ pointsCast.CastResultsChanged.AddListener(CastResultsChanged);
+ }
+
+ protected virtual void OnDisable()
+ {
+ pointsCast.CastResultsChanged.RemoveListener(CastResultsChanged);
+
+ foreach (List gameObjects in new[]
+ {
+ validMiddleClones,
+ invalidMiddleClones
+ })
+ {
+ gameObjects.ForEach(Destroy);
+ gameObjects.Clear();
+ }
+
+ foreach (GameObject @object in new[]
+ {
+ tracer.validObject,
+ tracer.invalidObject,
+ cursor.validObject,
+ cursor.invalidObject
+ })
+ {
+ @object.SetActive(false);
+ }
+ }
+
+ protected virtual void CastResultsChanged(PointsCastData data, object sender)
+ {
+ UpdateNumberOfClones(data);
+ UpdateElements();
+ }
+
+ ///
+ /// Ensures the number of cloned elements matches the provided number of points.
+ ///
+ /// The points data to create cloned elements for.
+ protected virtual void UpdateNumberOfClones(PointsCastData data)
+ {
+ int targetCount = data.points.Count - 1;
+ foreach (List gameObjects in new[]
+ {
+ validMiddleClones,
+ invalidMiddleClones
+ })
+ {
+ for (int index = gameObjects.Count - 1; index >= targetCount; index--)
+ {
+ Destroy(gameObjects[index]);
+ gameObjects.RemoveAt(index);
+ }
+ }
+
+ if (middle.validObject != null)
+ {
+ for (int index = validMiddleClones.Count; index < targetCount; index++)
+ {
+ validMiddleClones.Add(Instantiate(middle.validObject, middle.validObject.transform.parent));
+ }
+ }
+
+ if (middle.invalidObject != null)
+ {
+ for (int index = invalidMiddleClones.Count; index < targetCount; index++)
+ {
+ invalidMiddleClones.Add(Instantiate(middle.invalidObject, middle.invalidObject.transform.parent));
+ }
+ }
+ }
+
+ ///
+ /// Updates all elements.
+ ///
+ protected virtual void UpdateElements()
+ {
+ UpdateElement(0, false, tracer.visibility, tracer.validObject, tracer.invalidObject);
+ UpdateElement(pointsCast.Points.Count - 1, false, cursor.visibility, cursor.validObject, cursor.invalidObject);
+
+ if (middle.validObject != null)
+ {
+ middle.validObject.SetActive(false);
+ }
+
+ if (middle.invalidObject != null)
+ {
+ middle.invalidObject.SetActive(false);
+ }
+
+ for (int index = 0; index < validMiddleClones.Count; index++)
+ {
+ UpdateElement(index, true, middle.visibility, validMiddleClones[index], invalidMiddleClones[index]);
+ }
+ }
+
+ ///
+ /// Updates the element for a specific point.
+ ///
+ /// The index of the point that is represented by the element.
+ /// Whether the element is part of the line or alternatively it's representing a point.
+ /// The visibility of the element.
+ /// The valid representation of the element.
+ /// The invalid representation of the element.
+ protected virtual void UpdateElement(int pointsIndex, bool isPartOfLine, Visibility visibility, GameObject validObject, GameObject invalidObject)
+ {
+ IReadOnlyList points = pointsCast.Points;
+ int pointsCount = points.Count;
+ if (0 > pointsIndex || pointsIndex >= pointsCount)
+ {
+ return;
+ }
+
+ bool isPartOfPoint = !isPartOfLine && (pointsIndex == 0 || pointsIndex == pointsCount - 1);
+
+ Vector3 targetPoint = points[pointsIndex];
+ Vector3 otherPoint = pointsIndex + 1 < pointsCount ? points[pointsIndex + 1] : points[pointsIndex - 1];
+ Vector3 direction = otherPoint - targetPoint;
+ Vector3 position = isPartOfPoint ? targetPoint : targetPoint + 0.5f * direction;
+ float scaleTarget = Mathf.Abs(Vector3.Distance(targetPoint, otherPoint)) * (pointsIndex == 0 && isPartOfLine && pointsCount == 2 ? 1f : 0.5f);
+
+ bool isElementActive = IsElementVisible(visibility) && pointsCast.TargetHit != null;
+ validObject.SetActive(isElementActive);
+ invalidObject.SetActive(!isElementActive);
+
+ foreach (GameObject @object in new[]
+ {
+ validObject,
+ invalidObject
+ })
+ {
+ @object.transform.position = position;
+
+ if (isPartOfPoint)
+ {
+ continue;
+ }
+
+ Vector3 scale = @object.transform.lossyScale;
+
+ for (int index = 0; index < 3; index++)
+ {
+ if (Math.Abs(scaleDirection[index]) >= float.Epsilon)
+ {
+ scale[index] = scaleDirection[index] * scaleTarget;
+ }
+ }
+
+ @object.transform.SetGlobalScale(scale);
+ @object.transform.rotation = Quaternion.LookRotation(direction);
+ }
+ }
+
+ ///
+ /// Whether a given is considered visible.
+ ///
+ /// The to check for.
+ /// if is considered visible, otherwise .
+ protected virtual bool IsElementVisible(Visibility visibility)
+ {
+ if (!isActiveAndEnabled)
+ {
+ return false;
+ }
+
+ switch (visibility)
+ {
+ case Visibility.OnWhenActiveAndEnabled:
+ return pointsCast.isActiveAndEnabled;
+ case Visibility.AlwaysOn:
+ return true;
+ case Visibility.AlwaysOff:
+ return false;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(visibility), visibility, null);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Visual/PointsRenderer.cs.meta b/Scripts/Visual/PointsRenderer.cs.meta
new file mode 100644
index 00000000..0945c50e
--- /dev/null
+++ b/Scripts/Visual/PointsRenderer.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 255407672fbf4289a8950abfa476e0b0
+timeCreated: 1526406857
\ No newline at end of file