Skip to content

Commit 4152c69

Browse files
authored
Merge pull request #1364 from Railboy/Focus-Locking
MRDL->MRTK: Pointer focus locking
2 parents d804220 + 931095f commit 4152c69

File tree

7 files changed

+165
-28
lines changed

7 files changed

+165
-28
lines changed

Assets/HoloToolkit/Input/Scripts/Cursor/Cursor.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,7 @@ protected virtual void UpdateCursorTransform()
287287
{
288288
FocusDetails focusDetails = FocusManager.Instance.GetFocusDetails(Pointer);
289289
GameObject newTargetedObject = focusDetails.Object;
290-
291-
// Get the forward vector looking back along the pointing ray.
292-
RayStep lastStep = Pointer.Rays[Pointer.Rays.Length - 1];
293-
Vector3 lookForward = -lastStep.direction;
290+
Vector3 lookForward = Vector3.forward;
294291

295292
// Normalize scale on before update
296293
targetScale = Vector3.one;
@@ -300,7 +297,9 @@ protected virtual void UpdateCursorTransform()
300297
{
301298
TargetedObject = null;
302299
TargetedCursorModifier = null;
303-
targetPosition = lastStep.terminus;
300+
301+
targetPosition = RayStep.GetPointByDistance(Pointer.Rays, DefaultCursorDistance);
302+
lookForward = -RayStep.GetDirectionByDistance(Pointer.Rays, DefaultCursorDistance);
304303
targetRotation = lookForward.magnitude > 0 ? Quaternion.LookRotation(lookForward, Vector3.up) : transform.rotation;
305304
}
306305
else
@@ -315,6 +314,10 @@ protected virtual void UpdateCursorTransform()
315314
else
316315
{
317316
// If no modifier is on the target, just use the hit result to set cursor position
317+
// Get the look forward by using distance between pointer origin and target position
318+
// (This may not be strictly accurate for extremely wobbly pointers, but it should produce usable results)
319+
float distanceToTarget = Vector3.Distance(Pointer.Rays[0].origin, focusDetails.Point);
320+
lookForward = -RayStep.GetDirectionByDistance(Pointer.Rays, distanceToTarget);
318321
targetPosition = focusDetails.Point + (lookForward * SurfaceCursorDistance);
319322
Vector3 lookRotation = Vector3.Slerp(focusDetails.Normal, lookForward, LookRotationBlend);
320323
targetRotation = Quaternion.LookRotation(lookRotation == Vector3.zero ? lookForward : lookRotation, Vector3.up);

Assets/HoloToolkit/Input/Scripts/Focus/FocusManager.cs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -509,25 +509,33 @@ private void UpdatePointer(PointerData pointer)
509509
{
510510
// Don't clear the previous focused object since we still want to trigger FocusExit events
511511
pointer.ResetFocusedObjects(false);
512-
return;
513512
}
513+
else
514+
{
515+
// If the pointer is locked
516+
// Keep the focus objects the same
517+
// This will ensure that we execute events on those objects
518+
// even if the pointer isn't pointing at them
519+
if (!pointer.PointingSource.FocusLocked)
520+
{
521+
// Otherwise, continue
522+
var prioritizedLayerMasks = (pointer.PointingSource.PrioritizedLayerMasksOverride ?? pointingRaycastLayerMasks);
514523

515-
// Otherwise, continue
516-
var prioritizedLayerMasks = (pointer.PointingSource.PrioritizedLayerMasksOverride ?? pointingRaycastLayerMasks);
524+
// Perform raycast to determine focused object
525+
RaycastPhysics(pointer, prioritizedLayerMasks);
517526

518-
// Perform raycast to determine focused object
519-
RaycastPhysics(pointer, prioritizedLayerMasks);
527+
// If we have a unity event system, perform graphics raycasts as well to support Unity UI interactions
528+
if (EventSystem.current != null)
529+
{
530+
// NOTE: We need to do this AFTER RaycastPhysics so we use the current hit point to perform the correct 2D UI Raycast.
531+
RaycastUnityUI(pointer, prioritizedLayerMasks);
532+
}
520533

521-
// If we have a unity event system, perform graphics raycasts as well to support Unity UI interactions
522-
if (EventSystem.current != null)
523-
{
524-
// NOTE: We need to do this AFTER RaycastPhysics so we use the current hit point to perform the correct 2D UI Raycast.
525-
RaycastUnityUI(pointer, prioritizedLayerMasks);
534+
// Set the pointer's result last
535+
pointer.PointingSource.Result = pointer;
536+
}
526537
}
527538

528-
// Set the pointer's result last
529-
pointer.PointingSource.Result = pointer;
530-
531539
// Call the pointer's OnPostRaycast function
532540
// This will give it a chance to respond to raycast results
533541
// eg by updating its appearance

Assets/HoloToolkit/Input/Scripts/Focus/IPointingSource.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,7 @@ public interface IPointingSource
3333
void OnPostRaycast();
3434

3535
bool OwnsInput(BaseEventData eventData);
36+
37+
bool FocusLocked { get; set; }
3638
}
3739
}

Assets/HoloToolkit/Input/Scripts/Focus/InputSourcePointer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public bool InteractionEnabled
4646
}
4747
}
4848

49+
public bool FocusLocked { get; set; }
50+
4951
private RayStep[] rays = new RayStep[1] { new RayStep(Vector3.zero, Vector3.forward) };
5052

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

Assets/HoloToolkit/Input/Scripts/Focus/RayStep.cs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,97 @@ public static implicit operator Ray(RayStep r)
4444
{
4545
return new Ray(r.origin, r.direction);
4646
}
47+
48+
#region static utility functions
49+
50+
/// <summary>
51+
/// Returns a point along an array of RaySteps by distance
52+
/// </summary>
53+
/// <param name="steps"></param>
54+
/// <param name="distance"></param>
55+
/// <returns></returns>
56+
public static Vector3 GetPointByDistance(RayStep[] steps, float distance)
57+
{
58+
Debug.Assert(steps != null);
59+
Debug.Assert(steps.Length > 0);
60+
61+
Vector3 point = Vector3.zero;
62+
float remainingDistance = distance;
63+
64+
for (int i = 0; i < steps.Length; i++)
65+
{
66+
if (remainingDistance > steps[i].length)
67+
{
68+
remainingDistance -= steps[i].length;
69+
}
70+
else
71+
{
72+
point = Vector3.Lerp(steps[i].origin, steps[i].terminus, remainingDistance / steps[i].length);
73+
remainingDistance = 0;
74+
break;
75+
}
76+
}
77+
78+
if (remainingDistance > 0)
79+
{
80+
// If we reach the end and still have distance left, set the point to the terminus of the last step
81+
point = steps[steps.Length - 1].terminus;
82+
}
83+
84+
return point;
85+
}
86+
87+
/// <summary>
88+
/// Returns a RayStep along an array of RaySteps by distance
89+
/// </summary>
90+
/// <param name="steps"></param>
91+
/// <param name="distance"></param>
92+
/// <returns></returns>
93+
public static RayStep GetStepByDistance(RayStep[] steps, float distance)
94+
{
95+
Debug.Assert(steps != null);
96+
Debug.Assert(steps.Length > 0);
97+
98+
RayStep step = new RayStep();
99+
float remainingDistance = distance;
100+
101+
for (int i = 0; i < steps.Length; i++)
102+
{
103+
if (remainingDistance > steps[i].length)
104+
{
105+
remainingDistance -= steps[i].length;
106+
}
107+
else
108+
{
109+
step = steps[i];
110+
remainingDistance = 0;
111+
break;
112+
}
113+
}
114+
115+
if (remainingDistance > 0)
116+
{
117+
// If we reach the end and still have distance left, return the last step
118+
step = steps[steps.Length - 1];
119+
}
120+
121+
return step;
122+
}
123+
124+
/// <summary>
125+
/// Returns a direction along an array of RaySteps by distance
126+
/// </summary>
127+
/// <param name="steps"></param>
128+
/// <param name="distance"></param>
129+
/// <returns></returns>
130+
public static Vector3 GetDirectionByDistance(RayStep[] steps, float distance)
131+
{
132+
Debug.Assert(steps != null);
133+
Debug.Assert(steps.Length > 0);
134+
135+
return GetStepByDistance(steps, distance).direction;
136+
}
137+
138+
#endregion
47139
}
48140
}

Assets/HoloToolkit/Input/Scripts/Gaze/GazeManager.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ public bool InteractionEnabled
136136
get { return true; }
137137
}
138138

139+
public bool FocusLocked { get; set; }
140+
139141
private float lastHitDistance = 2.0f;
140142

141143
protected override void Awake()

Assets/HoloToolkit/UX/Scripts/Receivers/InteractionReceiver.cs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace HoloToolkit.Unity.Receivers
1313
/// An interaction receiver is simply a component that attached to a list of interactable objects and does something
1414
/// based on events from those interactable objects. This is the base abstract class to extend from.
1515
/// </summary>
16-
public abstract class InteractionReceiver : MonoBehaviour, IInputHandler, IHoldHandler, IInputClickHandler
16+
public abstract class InteractionReceiver : MonoBehaviour, IInputHandler, IHoldHandler, IInputClickHandler, IManipulationHandler
1717
{
1818
#region Public Members
1919
/// <summary>
@@ -31,11 +31,25 @@ public abstract class InteractionReceiver : MonoBehaviour, IInputHandler, IHoldH
3131
/// <summary>
3232
/// Flag for locking focus while selected
3333
/// </summary>
34-
[Tooltip("If true, this object will remain the prime focus while select is held")]
35-
public bool _LockFocus;
34+
public bool LockFocus
35+
{
36+
get
37+
{
38+
return lockFocus;
39+
}
40+
set
41+
{
42+
lockFocus = value;
43+
CheckLockFocus(_selectingFocuser);
44+
}
45+
}
3646
#endregion
3747

3848
#region Private and Protected Members
49+
[Tooltip("If true, this object will remain the prime focus while select is held")]
50+
[SerializeField]
51+
private bool lockFocus = false;
52+
3953
/// <summary>
4054
/// Protected focuser for the current selecting focuser
4155
/// </summary>
@@ -148,27 +162,39 @@ protected bool Isinteractable(GameObject interactable)
148162

149163
private void CheckLockFocus(IPointingSource focuser)
150164
{
151-
if (_LockFocus)
165+
// If our previous selecting focuser isn't the same
166+
if (_selectingFocuser != null && _selectingFocuser != focuser)
167+
{
168+
// If our focus is currently locked, unlock it before moving on
169+
if (LockFocus)
170+
{
171+
_selectingFocuser.FocusLocked = false;
172+
}
173+
}
174+
175+
// Set to the new focuser
176+
_selectingFocuser = focuser;
177+
if (_selectingFocuser != null)
152178
{
153-
//LockFocus(focuser);
179+
_selectingFocuser.FocusLocked = LockFocus;
154180
}
155181
}
156182

157-
private void LockFocus(IPointingSource focuser)
183+
private void LockFocuser(IPointingSource focuser)
158184
{
159185
if (focuser != null)
160186
{
161-
ReleaseFocus();
187+
ReleaseFocuser();
162188
_selectingFocuser = focuser;
163-
// _selectingFocuser.LockFocus();
189+
_selectingFocuser.FocusLocked = true;
164190
}
165191
}
166192

167-
private void ReleaseFocus()
193+
private void ReleaseFocuser()
168194
{
169195
if (_selectingFocuser != null)
170196
{
171-
// _selectingFocuser.ReleaseFocus();
197+
_selectingFocuser.FocusLocked = false;
172198
_selectingFocuser = null;
173199
}
174200
}
@@ -193,6 +219,8 @@ private void OnPointerSpecificFocusChanged(IPointingSource pointer, GameObject o
193219
{
194220
FocusExit(oldFocusedObject, eventData);
195221
}
222+
223+
CheckLockFocus(pointer);
196224
}
197225

198226
#region Global Listener Callbacks

0 commit comments

Comments
 (0)