diff --git a/Assets/VRTK/SDK/OculusVR/SDK_OculusVRBoundaries.cs b/Assets/VRTK/SDK/OculusVR/SDK_OculusVRBoundaries.cs index 166f6037d..0f4d1cc71 100644 --- a/Assets/VRTK/SDK/OculusVR/SDK_OculusVRBoundaries.cs +++ b/Assets/VRTK/SDK/OculusVR/SDK_OculusVRBoundaries.cs @@ -102,8 +102,8 @@ public virtual OvrAvatar GetAvatar() avatarContainer = FindObjectOfType(); if (avatarContainer) { - var objectFollow = avatarContainer.gameObject.AddComponent(); - objectFollow.objectToFollow = GetPlayArea(); + var objectFollow = avatarContainer.gameObject.AddComponent(); + objectFollow.gameObjectToFollow = GetPlayArea().gameObject; } } return avatarContainer; diff --git a/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_BasePointerRenderer.cs b/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_BasePointerRenderer.cs index 2838befd0..d1b38fdf5 100644 --- a/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_BasePointerRenderer.cs +++ b/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_BasePointerRenderer.cs @@ -2,6 +2,7 @@ namespace VRTK { using UnityEngine; + using System; using System.Collections.Generic; /// @@ -25,12 +26,31 @@ public enum VisibilityStates AlwaysOff } + /// + /// Specifies the smoothing to be applied to the pointer. + /// + [Serializable] + public sealed class PointerOriginSmoothingSettings + { + [Tooltip("Whether or not to smooth the position of the pointer origin when positioning the pointer tip.")] + public bool smoothsPosition; + [Tooltip("The maximum allowed distance between the unsmoothed pointer origin and the smoothed pointer origin per frame to use for smoothing.")] + public float maxAllowedPerFrameDistanceDifference = 0.003f; + + [Tooltip("Whether or not to smooth the rotation of the pointer origin when positioning the pointer tip.")] + public bool smoothsRotation; + [Tooltip("The maximum allowed angle between the unsmoothed pointer origin and the smoothed pointer origin per frame to use for smoothing.")] + public float maxAllowedPerFrameAngleDifference = 1.5f; + } + [Header("General Renderer Settings")] [Tooltip("An optional Play Area Cursor generator to add to the destination position of the pointer tip.")] public VRTK_PlayAreaCursor playareaCursor; [Tooltip("The layers for the pointer's raycasts to ignore.")] public LayerMask layersToIgnore = Physics.IgnoreRaycastLayer; + [Tooltip("Specifies the smoothing to be applied to the pointer origin when positioning the pointer tip.")] + public PointerOriginSmoothingSettings pointerOriginSmoothingSettings = new PointerOriginSmoothingSettings(); [Header("General Appearance Settings")] @@ -57,6 +77,8 @@ public enum VisibilityStates protected GameObject objectInteractor; protected GameObject objectInteractorAttachPoint; + protected GameObject pointerOriginTransformFollowGameObject; + protected VRTK_TransformFollow pointerOriginTransformFollow; protected VRTK_InteractGrab controllerGrabScript; protected Rigidbody savedAttachPoint; protected bool attachedToInteractorAttachPoint = false; @@ -99,6 +121,17 @@ public virtual void Toggle(bool pointerState, bool actualState) ToggleObjectInteraction(pointerState); TogglePlayArea(pointerState, actualState); ToggleRenderer(pointerState, actualState); + + if (pointerOriginTransformFollowGameObject != null && pointerOriginTransformFollowGameObject.activeSelf != pointerState) + { + UpdatePointerOriginTransformFollow(); + pointerOriginTransformFollowGameObject.SetActive(pointerState); + pointerOriginTransformFollow.enabled = pointerState; + if (pointerState) + { + pointerOriginTransformFollow.Follow(); + } + } } /// @@ -139,6 +172,7 @@ protected virtual void OnEnable() defaultMaterial = Resources.Load("WorldPointer") as Material; makeRendererVisible = new List(); CreatePointerObjects(); + CreatePointerOriginTransformFollow(); } protected virtual void OnDisable() @@ -149,6 +183,13 @@ protected virtual void OnDisable() Destroy(objectInteractor); } controllerGrabScript = null; + Destroy(pointerOriginTransformFollowGameObject); + } + + protected virtual void OnValidate() + { + pointerOriginSmoothingSettings.maxAllowedPerFrameDistanceDifference = Mathf.Max(0.0001f, pointerOriginSmoothingSettings.maxAllowedPerFrameDistanceDifference); + pointerOriginSmoothingSettings.maxAllowedPerFrameAngleDifference = Mathf.Max(0.0001f, pointerOriginSmoothingSettings.maxAllowedPerFrameAngleDifference); } protected virtual void FixedUpdate() @@ -157,6 +198,11 @@ protected virtual void FixedUpdate() { UpdateObjectInteractor(); } + + if (controllingPointer && pointerOriginTransformFollow.isActiveAndEnabled) + { + UpdatePointerOriginTransformFollow(); + } } protected virtual void ToggleObjectInteraction(bool state) @@ -194,29 +240,20 @@ protected virtual void UpdateObjectInteractor() objectInteractor.transform.position = destinationHit.point; } - protected virtual Vector3 GetOriginPosition() - { - return (controllingPointer.customOrigin ? controllingPointer.customOrigin.position : controllingPointer.transform.position); - } - - protected virtual Vector3 GetOriginLocalPosition() - { - return (controllingPointer.customOrigin ? controllingPointer.customOrigin.localPosition : Vector3.zero); - } - - protected virtual Vector3 GetOriginForward() - { - return (controllingPointer.customOrigin ? controllingPointer.customOrigin.forward : controllingPointer.transform.forward); - } - - protected virtual Quaternion GetOriginRotation() + protected virtual void UpdatePointerOriginTransformFollow() { - return (controllingPointer.customOrigin ? controllingPointer.customOrigin.rotation : controllingPointer.transform.rotation); + pointerOriginTransformFollow.gameObjectToFollow = (controllingPointer.customOrigin == null ? transform : controllingPointer.customOrigin).gameObject; + pointerOriginTransformFollow.smoothsPosition = pointerOriginSmoothingSettings.smoothsPosition; + pointerOriginTransformFollow.maxAllowedPerFrameDistanceDifference = pointerOriginSmoothingSettings.maxAllowedPerFrameDistanceDifference; + pointerOriginTransformFollow.smoothsRotation = pointerOriginSmoothingSettings.smoothsRotation; + pointerOriginTransformFollow.maxAllowedPerFrameAngleDifference = pointerOriginSmoothingSettings.maxAllowedPerFrameAngleDifference; } - protected virtual Quaternion GetOriginLocalRotation() + protected Transform GetOrigin(bool smoothed = true) { - return (controllingPointer.customOrigin ? controllingPointer.customOrigin.localRotation : Quaternion.identity); + return smoothed + ? pointerOriginTransformFollow.gameObjectToChange.transform + : (controllingPointer.customOrigin == null ? transform : controllingPointer.customOrigin); } protected virtual void PointerEnter(RaycastHit givenHit) @@ -416,6 +453,16 @@ protected virtual void ScaleObjectInteractor(Vector3 scaleAmount) } } + protected virtual void CreatePointerOriginTransformFollow() + { + pointerOriginTransformFollowGameObject = new GameObject(string.Format("[{0}]BasePointerRenderer_Origin_Smoothed", gameObject.name)); + pointerOriginTransformFollowGameObject.SetActive(false); + pointerOriginTransformFollow = pointerOriginTransformFollowGameObject.AddComponent(); + pointerOriginTransformFollow.enabled = false; + pointerOriginTransformFollow.gameObjectToChange = pointerOriginTransformFollowGameObject; + pointerOriginTransformFollow.followsScale = false; + } + protected virtual float OverrideBeamLength(float currentLength) { if (!controllerGrabScript || !controllerGrabScript.GetGrabbedObject()) diff --git a/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_BezierPointerRenderer.cs b/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_BezierPointerRenderer.cs index c8f97402a..5a8d466f6 100644 --- a/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_BezierPointerRenderer.cs +++ b/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_BezierPointerRenderer.cs @@ -184,9 +184,10 @@ protected virtual void CreateCursor() protected virtual Vector3 ProjectForwardBeam() { - float attachedRotation = Vector3.Dot(Vector3.up, transform.forward.normalized); + Transform origin = GetOrigin(); + float attachedRotation = Vector3.Dot(Vector3.up, origin.forward.normalized); float calculatedLength = maximumLength; - Vector3 useForward = GetOriginForward(); + Vector3 useForward = origin.forward; if ((attachedRotation * 100f) > heightLimitAngle) { useForward = new Vector3(useForward.x, fixedForwardBeamForward.y, useForward.z); @@ -195,11 +196,11 @@ protected virtual Vector3 ProjectForwardBeam() } else { - fixedForwardBeamForward = GetOriginForward(); + fixedForwardBeamForward = origin.forward; } var actualLength = calculatedLength; - Ray pointerRaycast = new Ray(GetOriginPosition(), useForward); + Ray pointerRaycast = new Ray(origin.position, useForward); RaycastHit collidedWith; var hasRayHit = Physics.Raycast(pointerRaycast, out collidedWith, calculatedLength, ~layersToIgnore); @@ -264,7 +265,7 @@ protected virtual void AdjustForEarlyCollisions(Vector3 jointPosition, Vector3 d collisionCheckFrequency = Mathf.Clamp(collisionCheckFrequency, 0, tracerDensity); Vector3[] beamPoints = new Vector3[] { - GetOriginPosition(), + GetOrigin().position, jointPosition + new Vector3(0f, curveOffset, 0f), downPosition, downPosition, @@ -308,7 +309,7 @@ protected virtual void DisplayCurvedBeam(Vector3 jointPosition, Vector3 downPosi { Vector3[] beamPoints = new Vector3[] { - GetOriginPosition(), + GetOrigin(false).position, jointPosition + new Vector3(0f, curveOffset, 0f), downPosition, downPosition, diff --git a/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_StraightPointerRenderer.cs b/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_StraightPointerRenderer.cs index e2c849ab0..af35c13c3 100644 --- a/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_StraightPointerRenderer.cs +++ b/Assets/VRTK/Scripts/Pointers/PointerRenderers/VRTK_StraightPointerRenderer.cs @@ -174,7 +174,8 @@ protected virtual void CheckRayHit(bool rayHit, RaycastHit pointerCollidedWith) protected virtual float CastRayForward() { - Ray pointerRaycast = new Ray(GetOriginPosition(), GetOriginForward()); + Transform origin = GetOrigin(); + Ray pointerRaycast = new Ray(origin.position, origin.forward); RaycastHit pointerCollidedWith; var rayHit = Physics.Raycast(pointerRaycast, out pointerCollidedWith, maximumLength, ~layersToIgnore); @@ -202,8 +203,9 @@ protected virtual void SetPointerAppearance(float tracerLength) actualCursor.transform.localScale = Vector3.one * (scaleFactor * cursorScaleMultiplier); actualCursor.transform.localPosition = new Vector3(0f, 0f, tracerLength); - actualContainer.transform.position = GetOriginPosition(); - actualContainer.transform.rotation = GetOriginRotation(); + Transform origin = GetOrigin(); + actualContainer.transform.position = origin.position; + actualContainer.transform.rotation = origin.rotation; ScaleObjectInteractor(actualCursor.transform.localScale * 1.05f); @@ -215,7 +217,7 @@ protected virtual void SetPointerAppearance(float tracerLength) } if (cursorDistanceRescale) { - float collisionDistance = Vector3.Distance(destinationHit.point, GetOriginPosition()); + float collisionDistance = Vector3.Distance(destinationHit.point, origin.position); actualCursor.transform.localScale = cursorOriginalScale * collisionDistance; } } @@ -223,7 +225,7 @@ protected virtual void SetPointerAppearance(float tracerLength) { if (cursorMatchTargetRotation) { - actualCursor.transform.forward = GetOriginForward(); + actualCursor.transform.forward = origin.forward; } if (cursorDistanceRescale) { diff --git a/Assets/VRTK/Scripts/Pointers/VRTK_BasePointer.cs b/Assets/VRTK/Scripts/Pointers/VRTK_BasePointer.cs index fa2368fea..49bf26890 100644 --- a/Assets/VRTK/Scripts/Pointers/VRTK_BasePointer.cs +++ b/Assets/VRTK/Scripts/Pointers/VRTK_BasePointer.cs @@ -6,6 +6,7 @@ namespace VRTK #if UNITY_5_5_OR_NEWER using UnityEngine.AI; #endif + using System; /// /// This abstract class provides any game pointer the ability to know the state of the implemented pointer. @@ -31,12 +32,31 @@ public enum pointerVisibilityStates Always_Off } + /// + /// Specifies the smoothing to be applied to the pointer. + /// + [Serializable] + public sealed class PointerOriginSmoothingSettings + { + [Tooltip("Whether or not to smooth the position of the pointer origin when positioning the pointer tip.")] + public bool smoothsPosition; + [Tooltip("The maximum allowed distance between the unsmoothed pointer origin and the smoothed pointer origin per frame to use for smoothing.")] + public float maxAllowedPerFrameDistanceDifference = 0.003f; + + [Tooltip("Whether or not to smooth the rotation of the pointer origin when positioning the pointer tip.")] + public bool smoothsRotation; + [Tooltip("The maximum allowed angle between the unsmoothed pointer origin and the smoothed pointer origin per frame to use for smoothing.")] + public float maxAllowedPerFrameAngleDifference = 1.5f; + } + [Header("Base Pointer Settings", order = 2)] [Tooltip("The controller that will be used to toggle the pointer. If the script is being applied onto a controller then this parameter can be left blank as it will be auto populated by the controller the script is on at runtime.")] public VRTK_ControllerEvents controller = null; [Tooltip("A custom transform to use as the origin of the pointer. If no pointer origin transform is provided then the transform the script is attached to is used.")] public Transform pointerOriginTransform = null; + [Tooltip("Specifies the smoothing to be applied to the pointer origin when positioning the pointer tip.")] + public PointerOriginSmoothingSettings pointerOriginSmoothingSettings = new PointerOriginSmoothingSettings(); [Tooltip("The material to use on the rendered version of the pointer. If no material is selected then the default `WorldPointer` material will be used.")] public Material pointerMaterial; [Tooltip("The colour of the beam when it is colliding with a valid target. It can be set to a different colour for each controller.")] @@ -75,6 +95,8 @@ public enum pointerVisibilityStates private bool attachedToInteractorAttachPoint = false; private float savedBeamLength = 0f; private VRTK_InteractGrab controllerGrabScript; + private GameObject pointerOriginTransformFollowGameObject; + private VRTK_TransformFollow pointerOriginTransformFollow; /// /// The IsActive method is used to determine if the pointer currently active. @@ -123,6 +145,7 @@ protected override void OnEnable() pointerOriginTransform = (pointerOriginTransform == null ? VRTK_SDK_Bridge.GenerateControllerPointerOrigin(gameObject) : pointerOriginTransform); AttemptSetController(); + CreatePointerOriginTransformFollow(); var tmpMaterial = Resources.Load("WorldPointer") as Material; if (pointerMaterial != null) @@ -149,6 +172,7 @@ protected override void OnDisable() AliasRegistration(false); controllerGrabScript = null; + Destroy(pointerOriginTransformFollowGameObject); } protected virtual void Start() @@ -166,6 +190,11 @@ protected virtual void FixedUpdate() { UpdateObjectInteractor(); } + + if (pointerOriginTransformFollow.isActiveAndEnabled) + { + UpdatePointerOriginTransformFollow(); + } } protected virtual void AliasRegistration(bool state) @@ -187,34 +216,29 @@ protected virtual void AliasRegistration(bool state) } } - protected virtual Vector3 GetOriginPosition() + protected virtual void OnValidate() { - return (pointerOriginTransform ? pointerOriginTransform.position : transform.position); + pointerOriginSmoothingSettings.maxAllowedPerFrameDistanceDifference = Mathf.Max(0.0001f, pointerOriginSmoothingSettings.maxAllowedPerFrameDistanceDifference); + pointerOriginSmoothingSettings.maxAllowedPerFrameAngleDifference = Mathf.Max(0.0001f, pointerOriginSmoothingSettings.maxAllowedPerFrameAngleDifference); } - protected virtual Vector3 GetOriginLocalPosition() + protected Transform GetOrigin(bool smoothed = true) { - return (pointerOriginTransform ? pointerOriginTransform.localPosition : Vector3.zero); + return smoothed && isActive ? pointerOriginTransformFollow.gameObjectToChange.transform : (pointerOriginTransform == null ? transform : pointerOriginTransform); } - protected virtual Vector3 GetOriginForward() - { - return (pointerOriginTransform ? pointerOriginTransform.forward : transform.forward); - } - - protected virtual Quaternion GetOriginRotation() - { - return (pointerOriginTransform ? pointerOriginTransform.rotation : transform.rotation); - } - - protected virtual Quaternion GetOriginLocalRotation() + protected virtual void UpdateObjectInteractor() { - return (pointerOriginTransform ? pointerOriginTransform.localRotation : Quaternion.identity); + objectInteractor.transform.position = destinationPosition; } - protected virtual void UpdateObjectInteractor() + protected virtual void UpdatePointerOriginTransformFollow() { - objectInteractor.transform.position = destinationPosition; + pointerOriginTransformFollow.gameObjectToFollow = (pointerOriginTransform == null ? transform : pointerOriginTransform).gameObject; + pointerOriginTransformFollow.smoothsPosition = pointerOriginSmoothingSettings.smoothsPosition; + pointerOriginTransformFollow.maxAllowedPerFrameDistanceDifference = pointerOriginSmoothingSettings.maxAllowedPerFrameDistanceDifference; + pointerOriginTransformFollow.smoothsRotation = pointerOriginSmoothingSettings.smoothsRotation; + pointerOriginTransformFollow.maxAllowedPerFrameAngleDifference = pointerOriginSmoothingSettings.maxAllowedPerFrameAngleDifference; } protected virtual void InitPointer() @@ -435,6 +459,16 @@ protected virtual void CreateObjectInteractor() objectInteractor.SetActive(false); } + protected virtual void CreatePointerOriginTransformFollow() + { + pointerOriginTransformFollowGameObject = new GameObject(string.Format("[{0}]BasePointer_Origin_Smoothed", gameObject.name)); + pointerOriginTransformFollowGameObject.SetActive(false); + pointerOriginTransformFollow = pointerOriginTransformFollowGameObject.AddComponent(); + pointerOriginTransformFollow.gameObjectToChange = pointerOriginTransformFollowGameObject; + pointerOriginTransformFollow.followsScale = false; + UpdatePointerOriginTransformFollow(); + } + protected virtual float OverrideBeamLength(float currentLength) { if (!controllerGrabScript || !controllerGrabScript.GetGrabbedObject()) @@ -524,6 +558,12 @@ private void TurnOnBeam(uint index) TogglePointer(true); isActive = true; destinationSetActive = true; + + if (pointerOriginTransformFollowGameObject != null) + { + pointerOriginTransformFollowGameObject.SetActive(true); + pointerOriginTransformFollow.Follow(); + } } } @@ -541,6 +581,11 @@ private void DisableBeam() TogglePointer(false); isActive = false; beamEnabledState = 0; + + if (pointerOriginTransformFollowGameObject != null) + { + pointerOriginTransformFollowGameObject.SetActive(false); + } } } } \ No newline at end of file diff --git a/Assets/VRTK/Scripts/Pointers/VRTK_BezierPointer.cs b/Assets/VRTK/Scripts/Pointers/VRTK_BezierPointer.cs index 6af7cc6d1..c05bc81e5 100644 --- a/Assets/VRTK/Scripts/Pointers/VRTK_BezierPointer.cs +++ b/Assets/VRTK/Scripts/Pointers/VRTK_BezierPointer.cs @@ -180,9 +180,10 @@ private void TogglePointerCursor(bool state) private Vector3 ProjectForwardBeam() { - var attachedRotation = Vector3.Dot(Vector3.up, transform.forward.normalized); + var origin = GetOrigin(); + var attachedRotation = Vector3.Dot(Vector3.up, origin.forward.normalized); var calculatedLength = pointerLength; - var useForward = GetOriginForward(); + var useForward = origin.forward; if ((attachedRotation * 100f) > beamHeightLimitAngle) { useForward = new Vector3(useForward.x, fixedForwardBeamForward.y, useForward.z); @@ -191,11 +192,11 @@ private Vector3 ProjectForwardBeam() } else { - fixedForwardBeamForward = GetOriginForward(); + fixedForwardBeamForward = origin.forward; } var actualLength = calculatedLength; - Ray pointerRaycast = new Ray(GetOriginPosition(), useForward); + Ray pointerRaycast = new Ray(origin.position, useForward); RaycastHit collidedWith; var hasRayHit = Physics.Raycast(pointerRaycast, out collidedWith, calculatedLength, ~layersToIgnore); @@ -289,7 +290,7 @@ private void AdjustForEarlyCollisions(Vector3 jointPosition, Vector3 downPositio collisionCheckFrequency = Mathf.Clamp(collisionCheckFrequency, 0, pointerDensity); Vector3[] beamPoints = new Vector3[] { - GetOriginPosition(), + GetOrigin().position, jointPosition + new Vector3(0f, beamCurveOffset, 0f), downPosition, downPosition, @@ -332,7 +333,7 @@ private void DisplayCurvedBeam(Vector3 jointPosition, Vector3 downPosition) { Vector3[] beamPoints = new Vector3[] { - GetOriginPosition(), + GetOrigin(false).position, jointPosition + new Vector3(0f, beamCurveOffset, 0f), downPosition, downPosition, diff --git a/Assets/VRTK/Scripts/Pointers/VRTK_SimplePointer.cs b/Assets/VRTK/Scripts/Pointers/VRTK_SimplePointer.cs index b9a0ae5f3..bd689bf65 100644 --- a/Assets/VRTK/Scripts/Pointers/VRTK_SimplePointer.cs +++ b/Assets/VRTK/Scripts/Pointers/VRTK_SimplePointer.cs @@ -63,7 +63,8 @@ protected override void Update() base.Update(); if (pointerBeam && pointerBeam.activeSelf) { - Ray pointerRaycast = new Ray(GetOriginPosition(), GetOriginForward()); + var origin = GetOrigin(); + Ray pointerRaycast = new Ray(origin.position, origin.forward); RaycastHit pointerCollidedWith; var rayHit = Physics.Raycast(pointerRaycast, out pointerCollidedWith, pointerLength, ~layersToIgnore); var pointerBeamLength = GetPointerBeamLength(rayHit, pointerCollidedWith); @@ -76,7 +77,7 @@ protected override void Update() } if (pointerCursorRescaledAlongDistance) { - float collisionDistance = Vector3.Distance(pointerCollidedWith.point, GetOriginPosition()); + float collisionDistance = Vector3.Distance(pointerCollidedWith.point, origin.position); pointerTip.transform.localScale = pointerCursorOriginalScale * collisionDistance; } } @@ -84,7 +85,7 @@ protected override void Update() { if (pointerCursorMatchTargetNormal) { - pointerTip.transform.forward = GetOriginForward(); + pointerTip.transform.forward = origin.forward; } if (pointerCursorRescaledAlongDistance) { @@ -216,13 +217,15 @@ private void SetPointerTransform(float setLength, float setThicknes) { //if the additional decimal isn't added then the beam position glitches var beamPosition = setLength / (2 + 0.00001f); + Transform smoothedOrigin = GetOrigin(); pointerBeam.transform.localScale = new Vector3(setThicknes, setThicknes, setLength); pointerBeam.transform.localPosition = new Vector3(0f, 0f, beamPosition); - pointerTip.transform.localPosition = new Vector3(0f, 0f, setLength - (pointerTip.transform.localScale.z / 2)); - pointerHolder.transform.position = GetOriginPosition(); - pointerHolder.transform.rotation = GetOriginRotation(); + pointerHolder.transform.position = GetOrigin(false).position; + pointerTip.transform.position = smoothedOrigin.position + smoothedOrigin.forward * (setLength - (pointerTip.transform.localScale.z / 2)); + pointerHolder.transform.LookAt(pointerTip.transform); + base.UpdateDependencies(pointerTip.transform.position); } diff --git a/Assets/VRTK/Scripts/Utilities/ObjectFollow.meta b/Assets/VRTK/Scripts/Utilities/ObjectFollow.meta new file mode 100644 index 000000000..44ade1cb0 --- /dev/null +++ b/Assets/VRTK/Scripts/Utilities/ObjectFollow.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: d1b0e6e2a6639f64b92f23dce1cee7aa +folderAsset: yes +timeCreated: 1484498299 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_ObjectFollow.cs b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_ObjectFollow.cs new file mode 100644 index 000000000..63190c212 --- /dev/null +++ b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_ObjectFollow.cs @@ -0,0 +1,164 @@ +// Object Follow|Utilities|90060 +namespace VRTK +{ + using UnityEngine; + + /// + /// Abstract class that allows to change one game object's properties to follow another game object. + /// + public abstract class VRTK_ObjectFollow : MonoBehaviour + { + [Tooltip("The game object to follow. The followed property values will be taken from this one.")] + public GameObject gameObjectToFollow; + [Tooltip("The game object to change the property values of. If left empty no game object will be changed.")] + public GameObject gameObjectToChange; + + [Tooltip("Whether to follow the position of the given game object.")] + public bool followsPosition = true; + [Tooltip("Whether to smooth the position when following `gameObjectToFollow`.")] + public bool smoothsPosition; + [Tooltip("The maximum allowed distance between the unsmoothed source and the smoothed target per frame to use for smoothing.")] + public float maxAllowedPerFrameDistanceDifference = 0.003f; + /// + /// The position that results by following `gameObjectToFollow`. + /// + public Vector3 targetPosition { get; private set; } + + [Tooltip("Whether to follow the rotation of the given game object.")] + public bool followsRotation = true; + [Tooltip("Whether to smooth the rotation when following `gameObjectToFollow`.")] + public bool smoothsRotation; + [Tooltip("The maximum allowed angle between the unsmoothed source and the smoothed target per frame to use for smoothing.")] + public float maxAllowedPerFrameAngleDifference = 1.5f; + /// + /// The rotation that results by following `gameObjectToFollow`. + /// + public Quaternion targetRotation { get; private set; } + + [Tooltip("Whether to follow the scale of the given game object.")] + public bool followsScale = true; + [Tooltip("Whether to smooth the scale when following `gameObjectToFollow`.")] + public bool smoothsScale; + [Tooltip("The maximum allowed size between the unsmoothed source and the smoothed target per frame to use for smoothing.")] + public float maxAllowedPerFrameSizeDifference = 0.003f; + /// + /// The scale that results by following `gameObjectToFollow`. + /// + public Vector3 targetScale { get; private set; } + + /// + /// Follow `gameObjectToFollow` using the current settings. + /// + public void Follow() + { + if (followsPosition) + { + FollowPosition(); + } + + if (followsRotation) + { + FollowRotation(); + } + + if (followsScale) + { + FollowScale(); + } + } + + protected virtual void OnValidate() + { + maxAllowedPerFrameDistanceDifference = Mathf.Max(0.0001f, maxAllowedPerFrameDistanceDifference); + maxAllowedPerFrameAngleDifference = Mathf.Max(0.0001f, maxAllowedPerFrameAngleDifference); + maxAllowedPerFrameSizeDifference = Mathf.Max(0.0001f, maxAllowedPerFrameSizeDifference); + } + + protected abstract Vector3 GetPositionToFollow(); + + protected abstract void SetPositionOnGameObject(Vector3 newPosition); + + protected abstract Quaternion GetRotationToFollow(); + + protected abstract void SetRotationOnGameObject(Quaternion newRotation); + + protected virtual Vector3 GetScaleToFollow() + { + return gameObjectToFollow.transform.localScale; + } + + protected virtual void SetScaleOnGameObject(Vector3 newScale) + { + gameObjectToChange.transform.localScale = newScale; + } + + private void FollowPosition() + { + var positionToFollow = GetPositionToFollow(); + Vector3 newPosition; + + if (smoothsPosition) + { + var alpha = Mathf.Clamp01(Vector3.Distance(targetPosition, positionToFollow) / maxAllowedPerFrameDistanceDifference); + newPosition = Vector3.Lerp(targetPosition, positionToFollow, alpha); + } + else + { + newPosition = positionToFollow; + } + + targetPosition = newPosition; + + if (gameObjectToChange) + { + SetPositionOnGameObject(newPosition); + } + } + + private void FollowRotation() + { + var rotationToFollow = GetRotationToFollow(); + Quaternion newRotation; + + if (smoothsRotation) + { + var alpha = Mathf.Clamp01(Quaternion.Angle(targetRotation, rotationToFollow) / maxAllowedPerFrameAngleDifference); + newRotation = Quaternion.Lerp(targetRotation, rotationToFollow, alpha); + } + else + { + newRotation = rotationToFollow; + } + + targetRotation = newRotation; + + if (gameObjectToChange) + { + SetRotationOnGameObject(newRotation); + } + } + + private void FollowScale() + { + var scaleToFollow = GetScaleToFollow(); + Vector3 newScale; + + if (smoothsScale) + { + var alpha = Mathf.Clamp01(Vector3.Distance(targetScale, scaleToFollow) / maxAllowedPerFrameSizeDifference); + newScale = Vector3.Lerp(targetScale, scaleToFollow, alpha); + } + else + { + newScale = scaleToFollow; + } + + targetScale = newScale; + + if (gameObjectToChange) + { + SetScaleOnGameObject(newScale); + } + } + } +} \ No newline at end of file diff --git a/Assets/VRTK/Scripts/Utilities/VRTK_ObjectFollow.cs.meta b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_ObjectFollow.cs.meta similarity index 85% rename from Assets/VRTK/Scripts/Utilities/VRTK_ObjectFollow.cs.meta rename to Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_ObjectFollow.cs.meta index c5d57deb8..30aa4b8a3 100644 --- a/Assets/VRTK/Scripts/Utilities/VRTK_ObjectFollow.cs.meta +++ b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_ObjectFollow.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0b3f12b92f9bfb84e99e6e4c57fa51d8 +guid: 462e39a39b2c75e4993aae0e503ee3c9 timeCreated: 1483022501 licenseType: Pro MonoImporter: diff --git a/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_RigidbodyFollow.cs b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_RigidbodyFollow.cs new file mode 100644 index 000000000..0dd120869 --- /dev/null +++ b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_RigidbodyFollow.cs @@ -0,0 +1,104 @@ +namespace VRTK +{ + using UnityEngine; + + /// + /// Changes one game object's rigidbody to follow another game object's rigidbody. + /// + public class VRTK_RigidbodyFollow : VRTK_ObjectFollow + { + /// + /// Specifies how to position and rotate the rigidbody. + /// + /// Use and . + /// Use and . + /// Use and . + public enum MovementOption + { + Set, + Move, + Add + } + + [Tooltip("Specifies how to position and rotate the rigidbody.")] + public MovementOption movementOption = MovementOption.Set; + + private Rigidbody rigidbodyToFollow; + private Rigidbody rigidbodyToChange; + + protected virtual void OnEnable() + { + if (gameObjectToFollow == null) + { + return; + } + + rigidbodyToFollow = gameObjectToFollow.GetComponent(); + + if (gameObjectToChange) + { + rigidbodyToChange = gameObjectToChange.GetComponent(); + } + } + + protected virtual void OnDisable() + { + rigidbodyToFollow = null; + rigidbodyToChange = null; + } + + protected virtual void FixedUpdate() + { + Follow(); + } + + protected override Vector3 GetPositionToFollow() + { + return rigidbodyToFollow.position; + } + + protected override void SetPositionOnGameObject(Vector3 newPosition) + { + switch (movementOption) + { + case MovementOption.Set: + rigidbodyToChange.position = newPosition; + break; + case MovementOption.Move: + rigidbodyToChange.MovePosition(newPosition); + break; + case MovementOption.Add: + // TODO: Test if this is correct + rigidbodyToChange.AddForce(newPosition - rigidbodyToChange.position); + break; + } + } + + protected override Quaternion GetRotationToFollow() + { + return rigidbodyToFollow.rotation; + } + + protected override void SetRotationOnGameObject(Quaternion newRotation) + { + switch (movementOption) + { + case MovementOption.Set: + rigidbodyToChange.rotation = newRotation; + break; + case MovementOption.Move: + rigidbodyToChange.MoveRotation(newRotation); + break; + case MovementOption.Add: + // TODO: Test if this is correct + rigidbodyToChange.AddTorque(newRotation * Quaternion.Inverse(rigidbodyToChange.rotation).eulerAngles); + break; + } + } + + protected override Vector3 GetScaleToFollow() + { + return rigidbodyToFollow.transform.localScale; + } + } +} \ No newline at end of file diff --git a/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_RigidbodyFollow.cs.meta b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_RigidbodyFollow.cs.meta new file mode 100644 index 000000000..a6b1a6b0f --- /dev/null +++ b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_RigidbodyFollow.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 607e1d8cf2e965d458f4985e30c63645 +timeCreated: 1484498299 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_TransformFollow.cs b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_TransformFollow.cs new file mode 100644 index 000000000..2ed179305 --- /dev/null +++ b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_TransformFollow.cs @@ -0,0 +1,109 @@ +namespace VRTK +{ + using UnityEngine; + + /// + /// Changes one game object's transform to follow another game object's transform. + /// + public class VRTK_TransformFollow : VRTK_ObjectFollow + { + /// + /// The moment at which to follow. + /// + /// Follow in the Update method. + /// Follow in the LateUpdate method. + /// Follow in the OnPreRender method. (This script doesn't have to be attached to a camera.) + public enum FollowMoment + { + OnUpdate, + OnLateUpdate, + OnPreRender + } + + [Tooltip("The moment at which to follow.")] + public FollowMoment moment = FollowMoment.OnPreRender; + + private Transform transformToFollow; + private Transform transformToChange; + private bool isListeningToOnPreRender; + + + protected virtual void OnEnable() + { + if (gameObjectToFollow == null) + { + return; + } + + transformToFollow = gameObjectToFollow.transform; + + if (gameObjectToChange) + { + transformToChange = gameObjectToChange.transform; + } + + if (moment == FollowMoment.OnPreRender) + { + Camera.onPreRender += OnCamPreRender; + isListeningToOnPreRender = true; + } + } + + protected virtual void OnDisable() + { + transformToFollow = null; + transformToChange = null; + + if (isListeningToOnPreRender) + { + Camera.onPreRender -= OnCamPreRender; + isListeningToOnPreRender = false; + } + } + + protected virtual void Update() + { + if (moment == FollowMoment.OnUpdate) + { + Follow(); + } + } + + protected virtual void LateUpdate() + { + if (moment == FollowMoment.OnLateUpdate) + { + Follow(); + } + } + + //this method shouldn't be called `OnPreRender` to prevent a name clash with `MonoBehaviour.OnPreRender` since that is used when this script is attached to a camera + protected virtual void OnCamPreRender(Camera cam) + { + if (cam.gameObject.transform == VRTK_SDK_Bridge.GetHeadsetCamera()) + { + Follow(); + } + } + + protected override Vector3 GetPositionToFollow() + { + return transformToFollow.position; + } + + protected override void SetPositionOnGameObject(Vector3 newPosition) + { + transformToChange.position = newPosition; + } + + protected override Quaternion GetRotationToFollow() + { + return transformToFollow.rotation; + } + + protected override void SetRotationOnGameObject(Quaternion newRotation) + { + transformToChange.rotation = newRotation; + } + } +} \ No newline at end of file diff --git a/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_TransformFollow.cs.meta b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_TransformFollow.cs.meta new file mode 100644 index 000000000..21ddd88f8 --- /dev/null +++ b/Assets/VRTK/Scripts/Utilities/ObjectFollow/VRTK_TransformFollow.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fe5040c2eaa9ff147b80ded004191c67 +timeCreated: 1484498299 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRTK/Scripts/Utilities/VRTK_ObjectFollow.cs b/Assets/VRTK/Scripts/Utilities/VRTK_ObjectFollow.cs deleted file mode 100644 index 77a4f5100..000000000 --- a/Assets/VRTK/Scripts/Utilities/VRTK_ObjectFollow.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Object Transform Follow|Utilities|90060 -namespace VRTK -{ - using UnityEngine; - - /// - /// A simple script that when attached to a GameObject will follow the position, scale and rotation of the given Transform. - /// - public class VRTK_ObjectFollow : MonoBehaviour - { - [Tooltip("A transform of an object to follow the position, scale and rotation of.")] - public Transform objectToFollow; - [Tooltip("Follow the position of the given object.")] - public bool followPosition = true; - [Tooltip("Follow the rotation of the given object.")] - public bool followRotation = true; - [Tooltip("Follow the scale of the given object.")] - public bool followScale = true; - - protected virtual void Update() - { - if (objectToFollow != null) - { - if (followScale) - { - transform.localScale = objectToFollow.localScale; - } - - if (followRotation) - { - transform.rotation = objectToFollow.rotation; - } - - if (followPosition) - { - transform.position = objectToFollow.position; - } - } - else - { - Debug.LogError("No Object To Follow defined!"); - } - } - } -} \ No newline at end of file