Skip to content

Commit

Permalink
feat(smoothing): add generic smoothing and pointer smoothing settings
Browse files Browse the repository at this point in the history
Most humans aren't able to hold controllers completely still. To
improve the user experience it is therefore a good idea to smooth the
position and rotation of tracked objects like controllers.

This is enabled by improving on the existing `VRTK_ObjectFollow`. This
change adds two new subclasses to support smoothing any combination of
position, rotation and scale of a game object's transform or rigidbody.

One of the best candidates for smoothing are the various pointers in
VRTK. That's why this change also adds smoothing settings to the base
pointer so both the simple and bezier pointer benefit of a smoothing
option.

Thanks to Nathan Rowe from SculptrVR for the pseudocode!
  • Loading branch information
Christopher - Marcel Böddecker committed Feb 18, 2017
1 parent 892196e commit 23e87e7
Show file tree
Hide file tree
Showing 15 changed files with 572 additions and 108 deletions.
4 changes: 2 additions & 2 deletions Assets/VRTK/SDK/OculusVR/SDK_OculusVRBoundaries.cs
Expand Up @@ -102,8 +102,8 @@ public virtual OvrAvatar GetAvatar()
avatarContainer = FindObjectOfType<OvrAvatar>();
if (avatarContainer)
{
var objectFollow = avatarContainer.gameObject.AddComponent<VRTK_ObjectFollow>();
objectFollow.objectToFollow = GetPlayArea();
var objectFollow = avatarContainer.gameObject.AddComponent<VRTK_TransformFollow>();
objectFollow.gameObjectToFollow = GetPlayArea().gameObject;
}
}
return avatarContainer;
Expand Down
Expand Up @@ -2,6 +2,7 @@
namespace VRTK
{
using UnityEngine;
using System;
using System.Collections.Generic;

/// <summary>
Expand All @@ -25,12 +26,31 @@ public enum VisibilityStates
AlwaysOff
}

/// <summary>
/// Specifies the smoothing to be applied to the pointer.
/// </summary>
[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")]

Expand All @@ -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;
Expand Down Expand Up @@ -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();
}
}
}

/// <summary>
Expand Down Expand Up @@ -139,6 +172,7 @@ protected virtual void OnEnable()
defaultMaterial = Resources.Load("WorldPointer") as Material;
makeRendererVisible = new List<GameObject>();
CreatePointerObjects();
CreatePointerOriginTransformFollow();
}

protected virtual void OnDisable()
Expand All @@ -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()
Expand All @@ -157,6 +198,11 @@ protected virtual void FixedUpdate()
{
UpdateObjectInteractor();
}

if (controllingPointer && pointerOriginTransformFollow.isActiveAndEnabled)
{
UpdatePointerOriginTransformFollow();
}
}

protected virtual void ToggleObjectInteraction(bool state)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<VRTK_TransformFollow>();
pointerOriginTransformFollow.enabled = false;
pointerOriginTransformFollow.gameObjectToChange = pointerOriginTransformFollowGameObject;
pointerOriginTransformFollow.followsScale = false;
}

protected virtual float OverrideBeamLength(float currentLength)
{
if (!controllerGrabScript || !controllerGrabScript.GetGrabbedObject())
Expand Down
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand All @@ -215,15 +217,15 @@ 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;
}
}
else
{
if (cursorMatchTargetRotation)
{
actualCursor.transform.forward = GetOriginForward();
actualCursor.transform.forward = origin.forward;
}
if (cursorDistanceRescale)
{
Expand Down

0 comments on commit 23e87e7

Please sign in to comment.