GvrLaserVisual.cs
12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
//-----------------------------------------------------------------------
// <copyright file="GvrLaserVisual.cs" company="Google Inc.">
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
using System;
using UnityEngine;
using UnityEngine.Assertions;
/// <summary>Visualizes a laser and a reticle using a LineRenderer and a Quad.</summary>
/// <remarks>
/// Provides functions for settings the end point of the laser, and clamps the laser and reticle
/// based on max distances.
/// </remarks>
[RequireComponent(typeof(LineRenderer))]
[HelpURL("https://developers.google.com/vr/reference/unity/class/GvrLaserVisual")]
public class GvrLaserVisual : MonoBehaviour, IGvrArmModelReceiver
{
/// <summary>Used to position the reticle at the current position.</summary>
[Tooltip("Used to position the reticle at the current position.")]
public GvrControllerReticleVisual reticle;
/// <summary>The controller visual's transform.</summary>
/// <remarks>
/// The end point of the visual will not necessarily be along the forward direction of the
/// laser. This is particularly true in both Camera and Hybrid Raycast Modes. In that case, both
/// the laser and the controller are rotated to face the end point. This reference is used to
/// control the rotation of the controller.
/// </remarks>
[Tooltip("Used to rotate the controller to face the current position.")]
public Transform controller;
/// <summary>Color of the laser pointer including alpha transparency.</summary>
[Tooltip("Start color of the laser pointer including alpha transparency.")]
public Color laserColor = new Color(1.0f, 1.0f, 1.0f, 0.25f);
/// <summary>Color of the laser pointer including alpha transparency.</summary>
[Tooltip("End color of the laser pointer including alpha transparency.")]
public Color laserColorEnd = new Color(1.0f, 1.0f, 1.0f, 0.0f);
/// <summary>Maximum distance of the laser (meters).</summary>
[Tooltip("Maximum distance of the laser(meters).")]
[Range(0.0f, 20.0f)]
public float maxLaserDistance = 1.0f;
/// <summary>The rate that the current position moves towards the target position.</summary>
[Tooltip("The rate that the current position moves towards the target position.")]
public float lerpSpeed = 20.0f;
/// <summary>The threashold at which position change becomes immediate.</summary>
/// <remarks>
/// If the targetPosition is greater than this threshold, then the position changes immediately
/// instead of lerping.
/// </remarks>
[Tooltip("If the target position is greater than this threshold, then the position changes " +
"immediately instead of lerping.")]
public float lerpThreshold = 1.0f;
/// <summary>
/// This is primarily used for Hybrid Raycast mode (details in _GvrBasePointer_) to prevent
/// mismatches between the laser and the reticle when the "camera" component of the ray is used.
/// </summary>
[Tooltip("Determines if the laser will shrink when it isn't facing in the forward direction " +
"of the transform.")]
public bool shrinkLaser = true;
/// <summary>Amount to shrink the laser when it is fully shrunk.</summary>
[Range(0.0f, 1.0f)]
[Tooltip("Amount to shrink the laser when it is fully shrunk.")]
public float shrunkScale = 0.2f;
/// <summary>
/// Begin shrinking the laser when the angle between `transform.forward` and the reticle is
/// greater than this value.
/// </summary>
[Range(0.0f, 15.0f)]
[Tooltip("Begin shrinking the laser when the angle between transform.forward and the reticle " +
"is greater than this value.")]
public float beginShrinkAngleDegrees = 0.0f;
/// <summary>
/// Finish shrinking the laser when the angle between `transform.forward` and the reticle is
/// greater than this value.
/// </summary>
[Range(0.0f, 15.0f)]
[Tooltip("Finish shrinking the laser when the angle between transform.forward and the reticle " +
"is greater than this value.")]
public float endShrinkAngleDegrees = 2.0f;
/// <summary>Ratio to shrink the visual by.</summary>
protected float shrinkRatio;
/// <summary>Distance to the target object.</summary>
protected float targetDistance;
/// <summary>Current distance to the visual.</summary>
protected float currentDistance;
/// <summary>Current world position of the visual.</summary>
protected Vector3 currentPosition;
/// <summary>Current local position of the visual.</summary>
protected Vector3 currentLocalPosition;
/// <summary>Current local rotation of the visual.</summary>
protected Quaternion currentLocalRotation;
private const float LERP_CLAMP_THRESHOLD = 0.02f;
/// <summary>
/// Optional delegate for customizing how the currentPosition is calculated based on the
/// distance.
/// </summary>
/// <remarks>
/// If not set, the `currentPosition` is determined based on the distance multiplied by the
/// forward direction of the transform added to the position of the transform.
/// </remarks>
/// <returns>
/// Default: The distance mutliplied by the forwrad direction of the transform added to the
/// position of the transform.
/// Overridden: An implementation for calculating `currentPosition` from distance.
/// </returns>
/// <param name="distance">The distance to use in calculating the `currentPosition`.</param>
public delegate Vector3 GetPointForDistanceDelegate(float distance);
/// <summary>Gets or sets the arm model used to control the visual.</summary>
/// <value>The arm model used to control the visual.</value>
public GvrBaseArmModel ArmModel { get; set; }
/// <summary>Gets a reference to the laser's line renderer.</summary>
/// <value>The laser's line renderer.</value>
public LineRenderer Laser { get; private set; }
/// <summary>Gets or sets the function to use for determining the point at a distance.</summary>
/// <value>The function to use for determining the point at a distance.</value>
public GetPointForDistanceDelegate GetPointForDistanceFunction { get; set; }
/// <summary>Gets the current distance to the visual.</summary>
/// <value>The current distance to the visual.</value>
public float CurrentDistance
{
get { return currentDistance; }
}
/// <summary>Set the distance of the laser.</summary>
/// <remarks>Clamps the distance of the laser and reticle.</remarks>
/// <param name="distance">Target distance from the pointer to draw the visual at.</param>
/// <param name="immediate">
/// If `true`, the distance is changed immediately. Otherwise, it will lerp.
/// </param>
public virtual void SetDistance(float distance, bool immediate = false)
{
targetDistance = distance;
if (immediate)
{
currentDistance = targetDistance;
}
if (targetDistance > lerpThreshold)
{
currentDistance = targetDistance;
}
}
/// @cond
/// <summary>The MonoBehavior's Awake method.</summary>
protected virtual void Awake()
{
Laser = GetComponent<LineRenderer>();
}
/// @endcond
/// @cond
/// <summary>The MonoBehavior's Awake method.</summary>
protected virtual void LateUpdate()
{
UpdateCurrentPosition();
UpdateControllerOrientation();
UpdateReticlePosition();
UpdateLaserEndPoint();
UpdateLaserAlpha();
}
/// @endcond
/// <summary>Updates the current position of the visual.</summary>
protected virtual void UpdateCurrentPosition()
{
if (currentDistance != targetDistance)
{
float speed = GetSpeed();
currentDistance = Mathf.Lerp(currentDistance, targetDistance, speed);
float diff = Mathf.Abs(targetDistance - currentDistance);
if (diff < LERP_CLAMP_THRESHOLD)
{
currentDistance = targetDistance;
}
}
if (GetPointForDistanceFunction != null)
{
currentPosition = GetPointForDistanceFunction(currentDistance);
}
else
{
Vector3 origin = transform.position;
currentPosition = origin + (transform.forward * currentDistance);
}
currentLocalPosition = transform.InverseTransformPoint(currentPosition);
currentLocalRotation = Quaternion.FromToRotation(Vector3.forward, currentLocalPosition);
}
/// <summary>
/// Updates the rotation of the controller based on the current local rotation.
/// </summary>
protected virtual void UpdateControllerOrientation()
{
if (controller == null)
{
return;
}
controller.localRotation = currentLocalRotation;
}
/// <summary> Updates the position of the reticle to the current position.</summary>
protected virtual void UpdateReticlePosition()
{
if (reticle == null)
{
return;
}
reticle.transform.position = currentPosition;
}
/// <summary>Updates the endpoint of the laser based on max distance.</summary>
protected virtual void UpdateLaserEndPoint()
{
if (Laser == null)
{
return;
}
Vector3 laserStartPoint = Vector3.zero;
Vector3 laserEndPoint;
if (controller != null)
{
Vector3 worldPosition = transform.position;
Vector3 rotatedPosition = controller.InverseTransformPoint(worldPosition);
rotatedPosition = currentLocalRotation * rotatedPosition;
laserStartPoint = controller.TransformPoint(rotatedPosition);
laserStartPoint = transform.InverseTransformPoint(laserStartPoint);
}
laserEndPoint = Vector3.ClampMagnitude(currentLocalPosition, maxLaserDistance);
if (shrinkLaser)
{
// Calculate the angle of rotation in degrees.
float angle = Vector3.Angle(Vector3.forward, currentLocalPosition);
// Calculate the shrink ratio based on the angle.
float shrinkAngleDelta = endShrinkAngleDegrees - beginShrinkAngleDegrees;
float clampedAngle =
Mathf.Clamp(angle - beginShrinkAngleDegrees, 0.0f, shrinkAngleDelta);
shrinkRatio = clampedAngle / shrinkAngleDelta;
// Calculate the shrink coeff.
float shrinkCoeff = GvrMathHelpers.EaseOutCubic(shrunkScale, 1.0f, 1.0f - shrinkRatio);
// Calculate the final distance of the laser.
Vector3 diff = laserStartPoint - currentLocalPosition;
Vector3 dir = diff.normalized;
float dist = Mathf.Min(diff.magnitude, maxLaserDistance) * shrinkCoeff;
// Update the laser start and end points.
laserEndPoint = currentLocalPosition;
laserStartPoint = laserEndPoint + (dir * dist);
}
Laser.useWorldSpace = false;
Laser.SetPosition(0, laserStartPoint);
Laser.SetPosition(1, laserEndPoint);
}
/// <summary>Updates the alpha of the laser beam.</summary>
protected virtual void UpdateLaserAlpha()
{
float alpha = ArmModel != null ? ArmModel.PreferredAlpha : 1.0f;
Color finalStartColor = Color.Lerp(Color.clear, laserColor, alpha);
Color finalEndColor = laserColorEnd;
// If shrinking the laser, the colors are inversed based on the shrink ratio.
// This is to ensure that the feathering of the laser goes in the right direction.
if (shrinkLaser)
{
float colorRatio = GvrMathHelpers.EaseOutCubic(0.0f, 1.0f, shrinkRatio);
finalEndColor = Color.Lerp(finalEndColor, finalStartColor, colorRatio);
finalStartColor = Color.Lerp(finalStartColor, laserColorEnd, colorRatio);
}
Laser.startColor = finalStartColor;
Laser.endColor = finalEndColor;
}
/// <summary>Gets the speed of the moving pointer visual.</summary>
/// <returns>The lerp speed of the moving pointer visual.</returns>
protected virtual float GetSpeed()
{
return lerpSpeed > 0.0f ? lerpSpeed * Time.unscaledDeltaTime : 1.0f;
}
}