Skip to content

Commit

Permalink
Merge pull request #1332 from Railboy/Pointers-rebased
Browse files Browse the repository at this point in the history
MRDL->MRTK: Multi-step pointer sources
  • Loading branch information
StephenHodgson authored Nov 11, 2017
2 parents 842c4a1 + 00e448a commit 29ff159
Show file tree
Hide file tree
Showing 13 changed files with 355 additions and 87 deletions.
5 changes: 3 additions & 2 deletions Assets/HoloToolkit/Input/Scripts/Cursor/Cursor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ protected virtual void UpdateCursorTransform()
GameObject newTargetedObject = focusDetails.Object;

// Get the forward vector looking back along the pointing ray.
Vector3 lookForward = -Pointer.Ray.direction;
RayStep lastStep = Pointer.Rays[Pointer.Rays.Length - 1];
Vector3 lookForward = -lastStep.direction;

// Normalize scale on before update
targetScale = Vector3.one;
Expand All @@ -300,7 +301,7 @@ protected virtual void UpdateCursorTransform()
{
TargetedObject = null;
TargetedCursorModifier = null;
targetPosition = Pointer.Ray.origin + Pointer.Ray.direction * DefaultCursorDistance;
targetPosition = lastStep.terminus;
targetRotation = lookForward.magnitude > 0 ? Quaternion.LookRotation(lookForward, Vector3.up) : transform.rotation;
}
else
Expand Down
3 changes: 2 additions & 1 deletion Assets/HoloToolkit/Input/Scripts/Cursor/CursorModifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ public Quaternion GetModifiedRotation(ICursor cursor)
{
Quaternion rotation;

Vector3 forward = UseGazeBasedNormal ? -cursor.Pointer.Ray.direction : HostTransform.rotation * CursorNormal;
RayStep lastStep = cursor.Pointer.Rays[cursor.Pointer.Rays.Length - 1];
Vector3 forward = UseGazeBasedNormal ? -lastStep.direction : HostTransform.rotation * CursorNormal;

// Determine the cursor forward
if (forward.magnitude > 0)
Expand Down
2 changes: 2 additions & 0 deletions Assets/HoloToolkit/Input/Scripts/Focus/FocusDetails.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using UnityEngine;

namespace HoloToolkit.Unity.InputModule
Expand All @@ -9,6 +10,7 @@ namespace HoloToolkit.Unity.InputModule
/// FocusDetails struct contains information about which game object has the focus currently.
/// Also contains information about the normal of that point.
/// </summary>
[Serializable]
public struct FocusDetails
{
public Vector3 Point;
Expand Down
180 changes: 128 additions & 52 deletions Assets/HoloToolkit/Input/Scripts/Focus/FocusManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ private void OnValidate()

#region Data

private class PointerData
private class PointerData : PointerResult
{
public readonly IPointingSource PointingSource;

Expand All @@ -146,25 +146,24 @@ public PointerInputEventData UnityUIPointerData
}
}

public Vector3 StartPoint { get; private set; }

public FocusDetails End { get; private set; }

public GameObject PreviousEndObject { get; private set; }

public RaycastHit LastRaycastHit { get; private set; }

public PointerData(IPointingSource pointingSource)
{
PointingSource = pointingSource;
}

[Obsolete("Use UpdateHit(RaycastHit hit, RayStep sourceRay, int rayStepIndex) or UpdateHit (float extent)")]
public void UpdateHit(RaycastHit hit)
{
throw new NotImplementedException();
}

public void UpdateHit(RaycastHit hit, RayStep sourceRay, int rayStepIndex)
{
LastRaycastHit = hit;
PreviousEndObject = End.Object;
RayStepIndex = rayStepIndex;

StartPoint = PointingSource.Ray.origin;
StartPoint = sourceRay.origin;
End = new FocusDetails
{
Point = hit.point,
Expand All @@ -173,12 +172,13 @@ public void UpdateHit(RaycastHit hit)
};
}

public void UpdateHit(RaycastResult result, RaycastHit hit)
public void UpdateHit(RaycastResult result, RaycastHit hit, RayStep sourceRay, int rayStepIndex)
{
// We do not update the PreviousEndObject here because
// it's already been updated in the first physics raycast.
StartPoint = PointingSource.Ray.origin;

RayStepIndex = rayStepIndex;
StartPoint = sourceRay.origin;
End = new FocusDetails
{
Point = hit.point,
Expand All @@ -191,18 +191,26 @@ public void UpdateHit(float extent)
{
PreviousEndObject = End.Object;

StartPoint = PointingSource.Ray.origin;
RayStep firstStep = PointingSource.Rays[0];
RayStep finalStep = PointingSource.Rays[PointingSource.Rays.Length - 1];
RayStepIndex = 0;

StartPoint = firstStep.origin;
End = new FocusDetails
{
Point = (StartPoint + (extent * PointingSource.Ray.direction)),
Normal = (-PointingSource.Ray.direction),
Point = finalStep.terminus,
Normal = (-finalStep.direction),
Object = null
};
}

public void ResetFocusedObjects()
public void ResetFocusedObjects(bool clearPreviousObject = true)
{
PreviousEndObject = null;
if (clearPreviousObject)
{
PreviousEndObject = null;
}

End = new FocusDetails
{
Point = End.Point,
Expand Down Expand Up @@ -489,7 +497,20 @@ private void UpdatePointers()

private void UpdatePointer(PointerData pointer)
{
pointer.PointingSource.UpdatePointer();
// Call the pointer's OnPreRaycast function
// This will give it a chance to prepare itself for raycasts
// eg, by building its Rays array
pointer.PointingSource.OnPreRaycast();

// If pointer interaction isn't enabled, clear its result object and return
if (!pointer.PointingSource.InteractionEnabled)
{
// Don't clear the previous focused object since we still want to trigger FocusExit events
pointer.ResetFocusedObjects(false);
return;
}

// Otherwise, continue
var prioritizedLayerMasks = (pointer.PointingSource.PrioritizedLayerMasksOverride ?? pointingRaycastLayerMasks);

// Perform raycast to determine focused object
Expand All @@ -501,26 +522,64 @@ private void UpdatePointer(PointerData pointer)
// NOTE: We need to do this AFTER RaycastPhysics so we use the current hit point to perform the correct 2D UI Raycast.
RaycastUnityUI(pointer, prioritizedLayerMasks);
}

// Set the pointer's result last
pointer.PointingSource.Result = pointer;

// Call the pointer's OnPostRaycast function
// This will give it a chance to respond to raycast results
// eg by updating its appearance
pointer.PointingSource.OnPostRaycast();
}

/// <summary>
/// Perform a Unity physics Raycast to determine which scene objects with a collider is currently being gazed at, if any.
/// </summary>
private void RaycastPhysics(PointerData pointer, LayerMask[] prioritizedLayerMasks)
{
bool isHit;
RaycastHit physicsHit = default(RaycastHit);
float extent = GetPointingExtent(pointer);
RayStep rayStep = default(RayStep);
bool isHit = false;
int rayStepIndex = 0;

// Check raycast for each step in the pointing source
for (int i = 0; i < pointer.PointingSource.Rays.Length; i++)
{
if (RaycastPhysicsStep(pointer.PointingSource.Rays[i], prioritizedLayerMasks, out physicsHit))
{
// Set the pointer source's origin ray to this step
isHit = true;
rayStep = pointer.PointingSource.Rays[i];
rayStepIndex = i;
// No need to continue once we've hit something
break;
}
}

if (isHit)
{
pointer.UpdateHit(physicsHit, rayStep, rayStepIndex);
}
else
{
pointer.UpdateHit(GetPointingExtent(pointer));
}
}

private bool RaycastPhysicsStep(RayStep step, LayerMask[] prioritizedLayerMasks, out RaycastHit physicsHit)
{
bool isHit = false;
physicsHit = default(RaycastHit);

// If there is only one priority, don't prioritize
if (prioritizedLayerMasks.Length == 1)
{
isHit = Physics.Raycast(pointer.PointingSource.Ray, out physicsHit, extent, prioritizedLayerMasks[0]);
isHit = Physics.Raycast(step.origin, step.direction, out physicsHit, step.length, prioritizedLayerMasks[0]);
}
else
{
// Raycast across all layers and prioritize
RaycastHit? hit = PrioritizeHits(Physics.RaycastAll(pointer.PointingSource.Ray, extent, /*All layers*/ -1), prioritizedLayerMasks);
RaycastHit? hit = PrioritizeHits(Physics.RaycastAll(step.origin, step.direction, step.length, Physics.AllLayers), prioritizedLayerMasks);
isHit = hit.HasValue;

if (isHit)
Expand All @@ -529,36 +588,67 @@ private void RaycastPhysics(PointerData pointer, LayerMask[] prioritizedLayerMas
}
}

if (isHit)
{
pointer.UpdateHit(physicsHit);
}
else
{
pointer.UpdateHit(GetPointingExtent(pointer));
}
return isHit;
}

private void RaycastUnityUI(PointerData pointer, LayerMask[] prioritizedLayerMasks)
{
Debug.Assert(pointer.End.Point != Vector3.zero, string.Format("No pointer {0} end point found to raycast against!", pointer.PointingSource.GetType()));
Debug.Assert(UIRaycastCamera != null, "You must assign a UIRaycastCamera on the FocusManager before you can process uGUI raycasting.");

RaycastResult uiRaycastResult = default(RaycastResult);
bool overridePhysicsRaycast = false;
RayStep rayStep = default(RayStep);
int rayStepIndex = 0;

// Cast rays for every step until we score a hit
for (int i = 0; i < pointer.PointingSource.Rays.Length; i++)
{
if (RaycastUnityUIStep(pointer, pointer.PointingSource.Rays[i], prioritizedLayerMasks, out overridePhysicsRaycast, out uiRaycastResult))
{
rayStepIndex = i;
rayStep = pointer.PointingSource.Rays[i];
break;
}
}

// Check if we need to overwrite the physics raycast info
if ((pointer.End.Object == null || overridePhysicsRaycast) && uiRaycastResult.module.eventCamera == UIRaycastCamera)
{
newUiRaycastPosition.x = uiRaycastResult.screenPosition.x;
newUiRaycastPosition.y = uiRaycastResult.screenPosition.y;
newUiRaycastPosition.z = uiRaycastResult.distance;

Vector3 worldPos = UIRaycastCamera.ScreenToWorldPoint(newUiRaycastPosition);

var hitInfo = new RaycastHit
{
point = worldPos,
normal = -uiRaycastResult.gameObject.transform.forward
};

pointer.UpdateHit(uiRaycastResult, hitInfo, rayStep, rayStepIndex);
}
}

private bool RaycastUnityUIStep(PointerData pointer, RayStep step, LayerMask[] prioritizedLayerMasks, out bool overridePhysicsRaycast, out RaycastResult uiRaycastResult)
{
// Move the uiRaycast camera to the the current pointer's position.
UIRaycastCamera.transform.position = pointer.PointingSource.Ray.origin;
UIRaycastCamera.transform.forward = pointer.PointingSource.Ray.direction;
UIRaycastCamera.transform.position = step.origin;
UIRaycastCamera.transform.forward = step.direction;

// We always raycast from the center of the camera.
pointer.UnityUIPointerData.position = new Vector2(UIRaycastCamera.pixelWidth * 0.5f, UIRaycastCamera.pixelHeight * 0.5f);

// Graphics raycast
RaycastResult uiRaycastResult = EventSystem.current.Raycast(pointer.UnityUIPointerData, prioritizedLayerMasks);
uiRaycastResult = EventSystem.current.Raycast(pointer.UnityUIPointerData, prioritizedLayerMasks);
pointer.UnityUIPointerData.pointerCurrentRaycast = uiRaycastResult;

overridePhysicsRaycast = false;

// If we have a raycast result, check if we need to overwrite the physics raycast info
if (uiRaycastResult.gameObject != null)
{
bool overridePhysicsRaycast = false;
if (pointer.End.Object != null)
{
// Check layer prioritization
Expand Down Expand Up @@ -588,25 +678,11 @@ private void RaycastUnityUI(PointerData pointer, LayerMask[] prioritizedLayerMas
}
}
}

// Check if we need to overwrite the physics raycast info
if ((pointer.End.Object == null || overridePhysicsRaycast) && uiRaycastResult.module.eventCamera == UIRaycastCamera)
{
newUiRaycastPosition.x = uiRaycastResult.screenPosition.x;
newUiRaycastPosition.y = uiRaycastResult.screenPosition.y;
newUiRaycastPosition.z = uiRaycastResult.distance;

Vector3 worldPos = UIRaycastCamera.ScreenToWorldPoint(newUiRaycastPosition);

var hitInfo = new RaycastHit
{
point = worldPos,
normal = -uiRaycastResult.gameObject.transform.forward
};

pointer.UpdateHit(uiRaycastResult, hitInfo);
}
// If we've hit somthing, no need to go further
return true;
}
// If we haven't hit something, keep going
return false;
}

private void UpdateFocusedObjects()
Expand Down
15 changes: 14 additions & 1 deletion Assets/HoloToolkit/Input/Scripts/Focus/IPointingSource.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using UnityEngine;
using UnityEngine.EventSystems;

Expand All @@ -11,14 +12,26 @@ namespace HoloToolkit.Unity.InputModule
/// </summary>
public interface IPointingSource
{
Ray Ray { get; }
bool InteractionEnabled { get; }

float? ExtentOverride { get; }

[Obsolete("Will be removed in a later version. For equivalent behavior have Rays return a RayStep array with a single element.")]
Ray Ray { get; }

RayStep[] Rays { get; }

LayerMask[] PrioritizedLayerMasksOverride { get; }

PointerResult Result { get; set; }

[Obsolete("Will be removed in a later version. Use OnPreRaycast / OnPostRaycast instead.")]
void UpdatePointer();

void OnPreRaycast();

void OnPostRaycast();

bool OwnsInput(BaseEventData eventData);
}
}
Loading

0 comments on commit 29ff159

Please sign in to comment.