Skip to content

Commit 104fc03

Browse files
author
Steven Liss
committed
Cache camera variables and reduce recalculations.
1 parent 0c4427f commit 104fc03

File tree

1 file changed

+68
-37
lines changed

1 file changed

+68
-37
lines changed

Assets/HoloToolkit/Utilities/Scripts/HeadsUpDirectionIndicator.cs

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ private enum FrustumPlanes
4747

4848
private GameObject pointer;
4949

50+
private static int frustumLastUpdated = -1;
51+
52+
private static Plane[] indicatorVolume;
53+
private static Vector3 cameraForward;
54+
private static Vector3 cameraPosition;
55+
private static Vector3 cameraRight;
56+
private static Vector3 cameraUp;
57+
5058
private void Start()
5159
{
5260
Depth = Mathf.Clamp(Depth, Camera.main.nearClipPlane, Camera.main.farClipPlane);
@@ -76,35 +84,31 @@ private void Update()
7684
}
7785
else
7886
{
79-
// The top, bottom and side frustum planes are used to restrict the movement
80-
// of the pointer.
81-
82-
// Here we adjust the Camera's frustum planes to place the cursor in a smaller
83-
// volume, thus creating the effect of a "margin"
84-
Plane[] indicatorVolume = GeometryUtility.CalculateFrustumPlanes(Camera.main);
85-
for (int i = 0; i < 4; ++i)
87+
int currentFrameCount = UnityEngine.Time.frameCount;
88+
if (currentFrameCount != frustumLastUpdated)
8689
{
87-
// We can make the frustum smaller by rotating the walls "in" toward the
88-
// camera's forward vector.
89-
90-
// First find the angle between the Camera's forward and the plane's normal
91-
float angle = Mathf.Acos(Vector3.Dot(indicatorVolume[i].normal.normalized, Camera.main.transform.forward));
90+
// Collect the updated camera information for the current frame
91+
CacheCameraTransform(Camera.main);
9292

93-
// Then we calculate how much we should rotate the plane in based on the
94-
// user's setting. 90 degrees is our maximum as at that point we no longer
95-
// have a valid frustum.
96-
float angleStep = IndicatorMarginPercent * (0.5f * Mathf.PI - angle);
93+
// Use the updated camera information to create the new bounding volume
94+
UpdateIndicatorVolume(Camera.main);
9795

98-
// Because the frustum plane normal's face in must actually rotate away from the forward to vector
99-
// to narrow the frustum.
100-
Vector3 normal = Vector3.RotateTowards(indicatorVolume[i].normal, Camera.main.transform.forward, -angleStep, 0.0f);
101-
indicatorVolume[i].normal = normal.normalized;
96+
frustumLastUpdated = currentFrameCount;
10297
}
10398

10499
UpdatePointerTransform(Camera.main, indicatorVolume, TargetObject.transform.position);
105100
}
106101
}
107102

103+
private void CacheCameraTransform(Camera camera)
104+
{
105+
// Cache the camera transform information for the current frame
106+
cameraForward = camera.transform.forward;
107+
cameraPosition = camera.transform.position;
108+
cameraRight = camera.transform.right;
109+
cameraUp = camera.transform.up;
110+
}
111+
108112
// Assuming the target object is outside the view which of the four "wall" planes should
109113
// the pointer snap to.
110114
private FrustumPlanes GetExitPlane(Vector3 targetPosition, Camera camera)
@@ -125,18 +129,18 @@ private FrustumPlanes GetExitPlane(Vector3 targetPosition, Camera camera)
125129

126130
// Calculate the edges of the frustum as world space offsets from the middle of the
127131
// frustum in world space.
128-
Vector3 nearTop = near * tanFovy * camera.transform.up;
129-
Vector3 nearRight = near * tanFovx * camera.transform.right;
132+
Vector3 nearTop = near * tanFovy * cameraUp;
133+
Vector3 nearRight = near * tanFovx * cameraRight;
130134
Vector3 nearBottom = -nearTop;
131135
Vector3 nearLeft = -nearRight;
132-
Vector3 farTop = far * tanFovy * camera.transform.up;
133-
Vector3 farRight = far * tanFovx * camera.transform.right;
136+
Vector3 farTop = far * tanFovy * cameraUp;
137+
Vector3 farRight = far * tanFovx * cameraRight;
134138
Vector3 farLeft = -farRight;
135139

136140
// Caclulate the center point of the near plane and the far plane as offsets from the
137141
// camera in world space.
138-
Vector3 nearBase = near * camera.transform.forward;
139-
Vector3 farBase = far * camera.transform.forward;
142+
Vector3 nearBase = near * cameraForward;
143+
Vector3 farBase = far * cameraForward;
140144

141145
// Calculate the frustum corners needed to create 'd'
142146
Vector3 nearUpperLeft = nearBase + nearTop + nearLeft;
@@ -215,16 +219,16 @@ private FrustumPlanes GetExitPlane(Vector3 targetPosition, Camera camera)
215219
// given a frustum wall we wish to snap the pointer to, this function returns a ray
216220
// along which the pointer should be placed to appear at the appropiate point along
217221
// the edge of the indicator field.
218-
private bool TryGetIndicatorPosition(Vector3 targetPosition, Camera camera, Plane frustumWall, out Ray r)
222+
private bool TryGetIndicatorPosition(Vector3 targetPosition, Plane frustumWall, out Ray r)
219223
{
220224
// Think of the pointer as pointing the shortest rotation a user must make to see a
221225
// target. The shortest rotation can be obtained by finding the great circle defined
222226
// be the target, the camera position and the center position of the view. The tangent
223227
// vector of the great circle points the direction of the shortest rotation. This
224228
// great circle and thus any of it's tangent vectors are coplanar with the plane
225229
// defined by these same three points.
226-
Vector3 cameraToTarget = targetPosition - camera.transform.position;
227-
Vector3 normal = Vector3.Cross(cameraToTarget.normalized, camera.transform.forward);
230+
Vector3 cameraToTarget = targetPosition - cameraPosition;
231+
Vector3 normal = Vector3.Cross(cameraToTarget.normalized, cameraForward);
228232

229233
// In the case that the three points are colinear we cannot form a plane but we'll
230234
// assume the target is directly behind us and we'll use a prechosen plane.
@@ -264,14 +268,14 @@ private bool TryIntersectPlanes(Plane p, Plane q, out Ray intersection)
264268
private void UpdatePointerTransform(Camera camera, Plane[] planes, Vector3 targetPosition)
265269
{
266270
// Start by assuming the pointer should be placed at the target position.
267-
Vector3 indicatorPosition = camera.transform.position + Depth * (targetPosition - camera.transform.position).normalized;
271+
Vector3 indicatorPosition = cameraPosition + Depth * (targetPosition - cameraPosition).normalized;
268272

269273
// Test the target position with the frustum planes except the "far" plane since
270274
// far away objects should be considered in view.
271275
bool pointNotInsideIndicatorField = false;
272276
for (int i = 0; i < 5; ++i)
273277
{
274-
float dot = Vector3.Dot(planes[i].normal, (targetPosition - camera.transform.position).normalized);
278+
float dot = Vector3.Dot(planes[i].normal, (targetPosition - cameraPosition).normalized);
275279
if (dot <= 0.0f)
276280
{
277281
pointNotInsideIndicatorField = true;
@@ -289,9 +293,9 @@ private void UpdatePointerTransform(Camera camera, Plane[] planes, Vector3 targe
289293
FrustumPlanes exitPlane = GetExitPlane(targetPosition, camera);
290294

291295
Ray r;
292-
if (TryGetIndicatorPosition(targetPosition, camera, planes[(int)exitPlane], out r))
296+
if (TryGetIndicatorPosition(targetPosition, planes[(int)exitPlane], out r))
293297
{
294-
indicatorPosition = camera.transform.position + Depth * r.direction.normalized;
298+
indicatorPosition = cameraPosition + Depth * r.direction.normalized;
295299
}
296300
}
297301

@@ -304,14 +308,41 @@ private void UpdatePointerTransform(Camera camera, Plane[] planes, Vector3 targe
304308
// center position of the view that is on the same plane as the pointer position.
305309
// We do this by projecting the vector from the pointer to the camera onto the
306310
// the camera's forward vector.
307-
Vector3 indicatorFieldOffset = indicatorPosition - camera.transform.position;
308-
indicatorFieldOffset = Vector3.Dot(indicatorFieldOffset, camera.transform.forward) * camera.transform.forward;
311+
Vector3 indicatorFieldOffset = indicatorPosition - cameraPosition;
312+
indicatorFieldOffset = Vector3.Dot(indicatorFieldOffset, cameraForward) * cameraForward;
309313

310-
Vector3 indicatorFieldCenter = camera.transform.position + indicatorFieldOffset;
314+
Vector3 indicatorFieldCenter = cameraPosition + indicatorFieldOffset;
311315
Vector3 pointerDirection = (indicatorPosition - indicatorFieldCenter).normalized;
312316

313317
// allign this object's up vector with the pointerDirection
314-
this.transform.rotation = Quaternion.LookRotation(camera.transform.forward, pointerDirection);
318+
this.transform.rotation = Quaternion.LookRotation(cameraForward, pointerDirection);
319+
}
320+
321+
// Here we adjust the Camera's frustum planes to place the cursor in a smaller
322+
// volume, thus creating the effect of a "margin"
323+
private void UpdateIndicatorVolume(Camera camera)
324+
{
325+
// The top, bottom and side frustum planes are used to restrict the movement
326+
// of the pointer. These reside at indices 0-3;
327+
indicatorVolume = GeometryUtility.CalculateFrustumPlanes(camera);
328+
for (int i = 0; i < 4; ++i)
329+
{
330+
// We can make the frustum smaller by rotating the walls "in" toward the
331+
// camera's forward vector.
332+
333+
// First find the angle between the Camera's forward and the plane's normal
334+
float angle = Mathf.Acos(Vector3.Dot(indicatorVolume[i].normal.normalized, cameraForward));
335+
336+
// Then we calculate how much we should rotate the plane in based on the
337+
// user's setting. 90 degrees is our maximum as at that point we no longer
338+
// have a valid frustum.
339+
float angleStep = IndicatorMarginPercent * (0.5f * Mathf.PI - angle);
340+
341+
// Because the frustum plane normal's face in must actually rotate away from the forward to vector
342+
// to narrow the frustum.
343+
Vector3 normal = Vector3.RotateTowards(indicatorVolume[i].normal, cameraForward, -angleStep, 0.0f);
344+
indicatorVolume[i].normal = normal.normalized;
345+
}
315346
}
316347
}
317348
}

0 commit comments

Comments
 (0)