Skip to content

Commit

Permalink
fix(Pointer): prevent bezier pointer clipping
Browse files Browse the repository at this point in the history
The Bezier Pointer beam can occasionally clip through the floor due
to the way the curve is calculated based on 1 forward point and 1
downward point.

This fix casts additional rays along the curve to check for early
collisions and if a collision is found then the target location
is updated to prevent clipping.
  • Loading branch information
thestonefox committed Dec 29, 2016
1 parent 7e4cd63 commit 1c6b243
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 13 deletions.
39 changes: 33 additions & 6 deletions Assets/VRTK/Scripts/Internal/VRTK_CurveGenerator.cs
Expand Up @@ -90,20 +90,47 @@ public void Create(int setFrequency, float radius, GameObject tracer, bool resca

public void SetPoints(Vector3[] controlPoints, Material material, Color color)
{
points = controlPoints;
modes = new BezierControlPointMode[]
{
BezierControlPointMode.Free,
BezierControlPointMode.Free
};
PointsInit(controlPoints);
SetObjects(material, color);
}

public Vector3[] GetPoints(Vector3[] controlPoints)
{
PointsInit(controlPoints);

Vector3[] calculatedPoints = new Vector3[frequency];
float stepSize = frequency * 1;
if (Loop || stepSize == 1)
{
stepSize = 1f / stepSize;
}
else
{
stepSize = 1f / (stepSize - 1);
}

for (int f = 0; f < frequency; f++)
{
calculatedPoints[f] = GetPoint(f * stepSize);
}
return calculatedPoints;
}

public void TogglePoints(bool state)
{
gameObject.SetActive(state);
}

private void PointsInit(Vector3[] controlPoints)
{
points = controlPoints;
modes = new BezierControlPointMode[]
{
BezierControlPointMode.Free,
BezierControlPointMode.Free
};
}

private GameObject CreateSphere()
{
customTracer = false;
Expand Down
69 changes: 62 additions & 7 deletions Assets/VRTK/Scripts/Pointers/VRTK_BezierPointer.cs
Expand Up @@ -26,6 +26,8 @@ public class VRTK_BezierPointer : VRTK_BasePointer
public float pointerLength = 10f;
[Tooltip("The number of items to render in the beam bezier curve. A high number here will most likely have a negative impact of game performance due to large number of rendered objects.")]
public int pointerDensity = 10;
[Tooltip("The number of points along the bezier curve to check for an early beam collision. Useful if the bezier curve is appearing to clip through teleport locations. 0 won't make any checks and it will be capped at `Pointer Density`. The higher the number, the more CPU intensive the checks become.")]
public int collisionCheckFrequency = 0;
[Tooltip("The amount of height offset to apply to the projected beam to generate a smoother curve even when the beam is pointing straight.")]
public float beamCurveOffset = 1f;
[Tooltip("The maximum angle in degrees of the origin before the beam curve height is restricted. A lower angle setting will prevent the beam being projected high into the sky and curving back down.")]
Expand All @@ -39,7 +41,9 @@ public class VRTK_BezierPointer : VRTK_BasePointer
public float pointerCursorRadius = 0.5f;
[Tooltip("The pointer cursor will be rotated to match the angle of the target surface if this is true, if it is false then the pointer cursor will always be horizontal.")]
public bool pointerCursorMatchTargetRotation = false;

[Header("Custom Appearance Settings", order = 4)]

[Tooltip("A custom Game Object can be applied here to use instead of the default sphere for the beam tracer. The custom Game Object will match the rotation of the object attached to.")]
public GameObject customPointerTracer;
[Tooltip("A custom Game Object can be applied here to use instead of the default flat cylinder for the pointer cursor.")]
Expand Down Expand Up @@ -89,8 +93,7 @@ protected override void Update()
{
var jointPosition = ProjectForwardBeam();
var downPosition = ProjectDownBeam(jointPosition);
DisplayCurvedBeam(jointPosition, downPosition);
SetPointerCursor(downPosition);
AdjustForEarlyCollisions(jointPosition, downPosition);
}
}

Expand Down Expand Up @@ -207,6 +210,7 @@ private Vector3 ProjectForwardBeam()

private Vector3 ProjectDownBeam(Vector3 jointPosition)
{
Vector3 downPosition = Vector3.zero;
Ray projectedBeamDownRaycast = new Ray(jointPosition, Vector3.down);
RaycastHit collidedWith;

Expand All @@ -221,26 +225,28 @@ private Vector3 ProjectDownBeam(Vector3 jointPosition)
pointerContactTarget = null;
pointerContactRaycastHit = new RaycastHit();
contactNormal = Vector3.zero;
destinationPosition = projectedBeamDownRaycast.GetPoint(0f);
downPosition = projectedBeamDownRaycast.GetPoint(0f);
}

if (downRayHit)
{
pointerContactTarget = collidedWith.transform;
pointerContactRaycastHit = collidedWith;
contactNormal = collidedWith.normal;
destinationPosition = projectedBeamDownRaycast.GetPoint(collidedWith.distance);
downPosition = projectedBeamDownRaycast.GetPoint(collidedWith.distance);
base.PointerIn();
}
return destinationPosition;
return downPosition;
}

private void SetPointerCursor(Vector3 downPosition)
private void SetPointerCursor(Vector3 cursorPosition)
{
destinationPosition = cursorPosition;

if (pointerContactTarget != null)
{
TogglePointerCursor(true);
pointerCursor.transform.position = downPosition;
pointerCursor.transform.position = cursorPosition;
if (pointerCursorMatchTargetRotation)
{
pointerCursor.transform.rotation = Quaternion.FromToRotation(Vector3.up, contactNormal);
Expand All @@ -259,6 +265,55 @@ private void SetPointerCursor(Vector3 downPosition)
}
}

private void AdjustForEarlyCollisions(Vector3 jointPosition, Vector3 downPosition)
{
Vector3 newDownPosition = downPosition;
Vector3 newJointPosition = jointPosition;

if (collisionCheckFrequency > 0)
{
collisionCheckFrequency = Mathf.Clamp(collisionCheckFrequency, 0, pointerDensity);
Vector3[] beamPoints = new Vector3[]
{
GetOriginPosition(),
jointPosition + new Vector3(0f, beamCurveOffset, 0f),
downPosition,
downPosition,
};

Vector3[] checkPoints = curvedBeam.GetPoints(beamPoints);
int checkFrequency = pointerDensity / collisionCheckFrequency;

for (int i = 0; i < pointerDensity - checkFrequency; i += checkFrequency)
{
var currentPoint = checkPoints[i];
var nextPoint = (i + checkFrequency < checkPoints.Length ? checkPoints[i + checkFrequency] : checkPoints[checkPoints.Length - 1]);
var nextPointDirection = (nextPoint - currentPoint).normalized;
var nextPointDistance = Vector3.Distance(currentPoint, nextPoint);

Ray checkCollisionRay = new Ray(currentPoint, nextPointDirection);
RaycastHit checkCollisionHit;

if (Physics.Raycast(checkCollisionRay, out checkCollisionHit, nextPointDistance, ~layersToIgnore))
{
var collisionPoint = checkCollisionRay.GetPoint(checkCollisionHit.distance);
Ray downwardCheckRay = new Ray(collisionPoint + (Vector3.up * 0.01f), Vector3.down);
RaycastHit downwardCheckHit;

if (Physics.Raycast(downwardCheckRay, out downwardCheckHit, float.PositiveInfinity, ~layersToIgnore))
{
newDownPosition = downwardCheckRay.GetPoint(downwardCheckHit.distance); ;
newJointPosition = (newDownPosition.y < jointPosition.y ? new Vector3(newDownPosition.x, jointPosition.y, newDownPosition.z) : jointPosition);
break;
}
}
}
}

DisplayCurvedBeam(newJointPosition, newDownPosition);
SetPointerCursor(newDownPosition);
}

private void DisplayCurvedBeam(Vector3 jointPosition, Vector3 downPosition)
{
Vector3[] beamPoints = new Vector3[]
Expand Down
1 change: 1 addition & 0 deletions DOCUMENTATION.md
Expand Up @@ -652,6 +652,7 @@ The laser beam is activated by default by pressing the `Touchpad` on the control

* **Pointer Length:** The length of the projected forward pointer beam, this is basically the distance able to point from the origin position.
* **Pointer Density:** The number of items to render in the beam bezier curve. A high number here will most likely have a negative impact of game performance due to large number of rendered objects.
* **Collision Check Frequency:** The number of points along the bezier curve to check for an early beam collision. Useful if the bezier curve is appearing to clip through teleport locations. 0 won't make any checks and it will be capped at `Pointer Density`. The higher the number, the more CPU intensive the checks become.
* **Beam Curve Offset:** The amount of height offset to apply to the projected beam to generate a smoother curve even when the beam is pointing straight.
* **Beam Height Limit Angle:** The maximum angle in degrees of the origin before the beam curve height is restricted. A lower angle setting will prevent the beam being projected high into the sky and curving back down.
* **Rescale Pointer Tracer:** Rescale each pointer tracer element according to the length of the Bezier curve.
Expand Down

0 comments on commit 1c6b243

Please sign in to comment.