From e72c5dddfb6dadf621ab55b918233fe3bfa3b2d2 Mon Sep 17 00:00:00 2001 From: Harvey Ball Date: Sun, 13 May 2018 20:43:19 +0100 Subject: [PATCH] feat(Pointer): pointer The pointer concept is the ability to designate an area of the scene with a destination. A pointer consists of multiple independent functionality which is implemented through a component that casts and returns points that define the pointer beam, one that generically renders points via game objects and a third component handles the selection functionality of a pointer. A pointer knows when it is activated, deactivated, when it starts hovering (enters) a new object, when it finishes hovering (exits) an object and knows what coordinates of the object it is hovering over. These are all cast as events with a special pointer payload containing information about the pointer target. The Straight Line Cast implementation casts a straight line from an origin point (e.g. controller) and stops when it collides with an object (or reaches the set maximum length). The same applies to the added Parabolic Line Cast, but it casts a parabolic line instead. Additionally the introduction of more components that raycast means the existing `PhysicsCast` class is moved to the new namespace `Cast`. --- Prefabs/Pointers.meta | 8 + Prefabs/Pointers/SharedResources.meta | 8 + .../Pointers/SharedResources/Materials.meta | 8 + .../Materials/PointerDefaultInvalid.mat | 76 ++ .../Materials/PointerDefaultInvalid.mat.meta | 8 + .../Materials/PointerDefaultValid.mat | 76 ++ .../Materials/PointerDefaultValid.mat.meta | 8 + Prefabs/Pointers/SolidParabolicPointer.prefab | 732 ++++++++++++++++++ .../SolidParabolicPointer.prefab.meta | 8 + Prefabs/Pointers/SolidStraightPointer.prefab | 495 ++++++++++++ .../Pointers/SolidStraightPointer.prefab.meta | 8 + Scripts/Cast.meta | 3 + Scripts/Cast/ParabolicLineCast.cs | 178 +++++ Scripts/Cast/ParabolicLineCast.cs.meta | 3 + Scripts/{Utility => Cast}/PhysicsCast.cs | 400 +++++----- Scripts/{Utility => Cast}/PhysicsCast.cs.meta | 0 Scripts/Cast/PointsCast.cs | 107 +++ Scripts/Cast/PointsCast.cs.meta | 3 + Scripts/Cast/StraightLineCast.cs | 37 + Scripts/Cast/StraightLineCast.cs.meta | 3 + Scripts/Pointer.meta | 8 + Scripts/Pointer/Pointer.cs | 176 +++++ Scripts/Pointer/Pointer.cs.meta | 3 + Scripts/Pointer/PointerSelection.cs | 26 + Scripts/Pointer/PointerSelection.cs.meta | 3 + Scripts/Tracking/SurfaceLocator.cs | 1 + Scripts/Utility/BezierCurveGenerator.cs | 52 ++ Scripts/Utility/BezierCurveGenerator.cs.meta | 3 + Scripts/Utility/TransformExtensions.cs | 18 + Scripts/Utility/TransformExtensions.cs.meta | 3 + Scripts/Visual/PointsRenderer.cs | 261 +++++++ Scripts/Visual/PointsRenderer.cs.meta | 3 + 32 files changed, 2526 insertions(+), 200 deletions(-) create mode 100644 Prefabs/Pointers.meta create mode 100644 Prefabs/Pointers/SharedResources.meta create mode 100644 Prefabs/Pointers/SharedResources/Materials.meta create mode 100644 Prefabs/Pointers/SharedResources/Materials/PointerDefaultInvalid.mat create mode 100644 Prefabs/Pointers/SharedResources/Materials/PointerDefaultInvalid.mat.meta create mode 100644 Prefabs/Pointers/SharedResources/Materials/PointerDefaultValid.mat create mode 100644 Prefabs/Pointers/SharedResources/Materials/PointerDefaultValid.mat.meta create mode 100644 Prefabs/Pointers/SolidParabolicPointer.prefab create mode 100644 Prefabs/Pointers/SolidParabolicPointer.prefab.meta create mode 100644 Prefabs/Pointers/SolidStraightPointer.prefab create mode 100644 Prefabs/Pointers/SolidStraightPointer.prefab.meta create mode 100644 Scripts/Cast.meta create mode 100644 Scripts/Cast/ParabolicLineCast.cs create mode 100644 Scripts/Cast/ParabolicLineCast.cs.meta rename Scripts/{Utility => Cast}/PhysicsCast.cs (98%) rename Scripts/{Utility => Cast}/PhysicsCast.cs.meta (100%) create mode 100644 Scripts/Cast/PointsCast.cs create mode 100644 Scripts/Cast/PointsCast.cs.meta create mode 100644 Scripts/Cast/StraightLineCast.cs create mode 100644 Scripts/Cast/StraightLineCast.cs.meta create mode 100644 Scripts/Pointer.meta create mode 100644 Scripts/Pointer/Pointer.cs create mode 100644 Scripts/Pointer/Pointer.cs.meta create mode 100644 Scripts/Pointer/PointerSelection.cs create mode 100644 Scripts/Pointer/PointerSelection.cs.meta create mode 100644 Scripts/Utility/BezierCurveGenerator.cs create mode 100644 Scripts/Utility/BezierCurveGenerator.cs.meta create mode 100644 Scripts/Utility/TransformExtensions.cs create mode 100644 Scripts/Utility/TransformExtensions.cs.meta create mode 100644 Scripts/Visual/PointsRenderer.cs create mode 100644 Scripts/Visual/PointsRenderer.cs.meta 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..359f2b7a --- /dev/null +++ b/Scripts/Pointer/Pointer.cs @@ -0,0 +1,176 @@ +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