aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmath/llcamera.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llmath/llcamera.cpp')
-rw-r--r--linden/indra/llmath/llcamera.cpp610
1 files changed, 610 insertions, 0 deletions
diff --git a/linden/indra/llmath/llcamera.cpp b/linden/indra/llmath/llcamera.cpp
new file mode 100644
index 0000000..13666ba
--- /dev/null
+++ b/linden/indra/llmath/llcamera.cpp
@@ -0,0 +1,610 @@
1/**
2 * @file llcamera.cpp
3 * @brief Implementation of the LLCamera class.
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include "llmath.h"
31#include "llcamera.h"
32
33// ---------------- Constructors and destructors ----------------
34
35LLCamera::LLCamera() :
36 LLCoordFrame(),
37 mView(DEFAULT_FIELD_OF_VIEW),
38 mAspect(DEFAULT_ASPECT_RATIO),
39 mViewHeightInPixels( -1 ), // invalid height
40 mNearPlane(DEFAULT_NEAR_PLANE),
41 mFarPlane(DEFAULT_FAR_PLANE),
42 mFixedDistance(-1.f)
43{
44 calculateFrustumPlanes();
45}
46
47
48LLCamera::LLCamera(F32 z_field_of_view, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane) :
49 LLCoordFrame(),
50 mView(z_field_of_view),
51 mAspect(aspect_ratio),
52 mViewHeightInPixels(view_height_in_pixels),
53 mNearPlane(near_plane),
54 mFarPlane(far_plane),
55 mFixedDistance(-1.f)
56{
57 if (mView < MIN_FIELD_OF_VIEW) { mView = MIN_FIELD_OF_VIEW; }
58 else if (mView > MAX_FIELD_OF_VIEW) { mView = MAX_FIELD_OF_VIEW; }
59
60 if (mAspect < MIN_ASPECT_RATIO) { mAspect = MIN_ASPECT_RATIO; }
61 else if (mAspect > MAX_ASPECT_RATIO) { mAspect = MAX_ASPECT_RATIO; }
62
63 if (mNearPlane < MIN_NEAR_PLANE) { mNearPlane = MIN_NEAR_PLANE; }
64 else if (mNearPlane > MAX_NEAR_PLANE) { mNearPlane = MAX_NEAR_PLANE; }
65
66 if (mFarPlane < 0) { mFarPlane = DEFAULT_FAR_PLANE; }
67 else if (mFarPlane < MIN_FAR_PLANE) { mFarPlane = MIN_FAR_PLANE; }
68 else if (mFarPlane > MAX_FAR_PLANE) { mFarPlane = MAX_FAR_PLANE; }
69
70 calculateFrustumPlanes();
71}
72
73
74
75// ---------------- LLCamera::setFoo() member functions ----------------
76
77void LLCamera::setView(F32 field_of_view)
78{
79 mView = field_of_view;
80 if (mView < MIN_FIELD_OF_VIEW) { mView = MIN_FIELD_OF_VIEW; }
81 else if (mView > MAX_FIELD_OF_VIEW) { mView = MAX_FIELD_OF_VIEW; }
82 calculateFrustumPlanes();
83}
84
85void LLCamera::setViewHeightInPixels(S32 height)
86{
87 mViewHeightInPixels = height;
88
89 // Don't really need to do this, but update the pixel meter ratio with it.
90 calculateFrustumPlanes();
91}
92
93void LLCamera::setAspect(F32 aspect_ratio)
94{
95 mAspect = aspect_ratio;
96 if (mAspect < MIN_ASPECT_RATIO) { mAspect = MIN_ASPECT_RATIO; }
97 else if (mAspect > MAX_ASPECT_RATIO) { mAspect = MAX_ASPECT_RATIO; }
98 calculateFrustumPlanes();
99}
100
101
102void LLCamera::setNear(F32 near_plane)
103{
104 mNearPlane = near_plane;
105 if (mNearPlane < MIN_NEAR_PLANE) { mNearPlane = MIN_NEAR_PLANE; }
106 else if (mNearPlane > MAX_NEAR_PLANE) { mNearPlane = MAX_NEAR_PLANE; }
107 calculateFrustumPlanes();
108}
109
110
111void LLCamera::setFar(F32 far_plane)
112{
113 mFarPlane = far_plane;
114 if (mFarPlane < MIN_FAR_PLANE) { mFarPlane = MIN_FAR_PLANE; }
115 else if (mFarPlane > MAX_FAR_PLANE) { mFarPlane = MAX_FAR_PLANE; }
116 calculateFrustumPlanes();
117}
118
119
120// ---------------- read/write to buffer ----------------
121
122size_t LLCamera::writeFrustumToBuffer(char *buffer) const
123{
124 memcpy(buffer, &mView, sizeof(F32));
125 buffer += sizeof(F32);
126 memcpy(buffer, &mAspect, sizeof(F32));
127 buffer += sizeof(F32);
128 memcpy(buffer, &mNearPlane, sizeof(F32));
129 buffer += sizeof(F32);
130 memcpy(buffer, &mFarPlane, sizeof(F32));
131 return 4*sizeof(F32);
132}
133
134size_t LLCamera::readFrustumFromBuffer(const char *buffer)
135{
136 memcpy(&mView, buffer, sizeof(F32));
137 buffer += sizeof(F32);
138 memcpy(&mAspect, buffer, sizeof(F32));
139 buffer += sizeof(F32);
140 memcpy(&mNearPlane, buffer, sizeof(F32));
141 buffer += sizeof(F32);
142 memcpy(&mFarPlane, buffer, sizeof(F32));
143 return 4*sizeof(F32);
144}
145
146
147// ---------------- test methods ----------------
148
149int LLCamera::AABBInFrustum(const LLVector3 &center, const LLVector3& radius)
150{
151 static const LLVector3 scaler[] = {
152 LLVector3(-1,-1,-1),
153 LLVector3( 1,-1,-1),
154 LLVector3(-1, 1,-1),
155 LLVector3( 1, 1,-1),
156 LLVector3(-1,-1, 1),
157 LLVector3( 1,-1, 1),
158 LLVector3(-1, 1, 1),
159 LLVector3( 1, 1, 1)
160 };
161
162 U8 mask = 0;
163 S32 result = 2;
164
165 for (int i = 0; i < 6; i++)
166 {
167 mask = mAgentPlaneMask[i];
168 LLPlane p = mAgentPlanes[i];
169 LLVector3 n = LLVector3(p);
170 float d = p.mV[3];
171 LLVector3 rscale = radius.scaledVec(scaler[mask]);
172
173 LLVector3 minp = center - rscale;
174 LLVector3 maxp = center + rscale;
175
176 if (n * minp > -d)
177 {
178 return 0;
179 }
180
181 if (n * maxp > -d)
182 {
183 result = 1;
184 }
185 }
186
187 return result;
188}
189
190int LLCamera::sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius)
191{
192 LLVector3 dist = sphere_center-mFrustCenter;
193 float dsq = dist * dist;
194 float rsq = mFarPlane*0.5f + radius;
195 rsq *= rsq;
196
197 if (dsq < rsq)
198 {
199 return 1;
200 }
201
202 return 0;
203}
204
205// HACK: This version is still around because the version below doesn't work
206// unless the agent planes are initialized.
207// Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
208// NOTE: 'center' is in absolute frame.
209int LLCamera::sphereInFrustumOld(const LLVector3 &sphere_center, const F32 radius) const
210{
211 // Returns 1 if sphere is in frustum, 0 if not.
212 // modified so that default view frust is along X with Z vertical
213 F32 x, y, z, rightDist, leftDist, topDist, bottomDist;
214
215 // Subtract the view position
216 //LLVector3 relative_center;
217 //relative_center = sphere_center - getOrigin();
218 LLVector3 rel_center(sphere_center);
219 rel_center -= mOrigin;
220
221 bool all_in = TRUE;
222
223 // Transform relative_center.x to camera frame
224 x = mXAxis * rel_center;
225 if (x < MIN_NEAR_PLANE - radius)
226 {
227 return 0;
228 }
229 else if (x < MIN_NEAR_PLANE + radius)
230 {
231 all_in = FALSE;
232 }
233
234 if (x > mFarPlane + radius)
235 {
236 return 0;
237 }
238 else if (x > mFarPlane - radius)
239 {
240 all_in = FALSE;
241 }
242
243 // Transform relative_center.y to camera frame
244 y = mYAxis * rel_center;
245
246 // distance to plane is the dot product of (x, y, 0) * plane_normal
247 rightDist = x * mLocalPlanes[PLANE_RIGHT][VX] + y * mLocalPlanes[PLANE_RIGHT][VY];
248 if (rightDist < -radius)
249 {
250 return 0;
251 }
252 else if (rightDist < radius)
253 {
254 all_in = FALSE;
255 }
256
257 leftDist = x * mLocalPlanes[PLANE_LEFT][VX] + y * mLocalPlanes[PLANE_LEFT][VY];
258 if (leftDist < -radius)
259 {
260 return 0;
261 }
262 else if (leftDist < radius)
263 {
264 all_in = FALSE;
265 }
266
267 // Transform relative_center.y to camera frame
268 z = mZAxis * rel_center;
269
270 topDist = x * mLocalPlanes[PLANE_TOP][VX] + z * mLocalPlanes[PLANE_TOP][VZ];
271 if (topDist < -radius)
272 {
273 return 0;
274 }
275 else if (topDist < radius)
276 {
277 all_in = FALSE;
278 }
279
280 bottomDist = x * mLocalPlanes[PLANE_BOTTOM][VX] + z * mLocalPlanes[PLANE_BOTTOM][VZ];
281 if (bottomDist < -radius)
282 {
283 return 0;
284 }
285 else if (bottomDist < radius)
286 {
287 all_in = FALSE;
288 }
289
290 if (all_in)
291 {
292 return 2;
293 }
294
295 return 1;
296}
297
298
299// HACK: This (presumably faster) version only currently works if you set up the
300// frustum planes using GL. At some point we should get those planes through another
301// mechanism, and then we can get rid of the "old" version above.
302
303// Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
304// NOTE: 'center' is in absolute frame.
305int LLCamera::sphereInFrustum(const LLVector3 &sphere_center, const F32 radius) const
306{
307 // Returns 1 if sphere is in frustum, 0 if not.
308 int res = 2;
309 for (int i = 0; i < 6; i++)
310 {
311 float d = mAgentPlanes[i].dist(sphere_center);
312
313 if (d > radius)
314 {
315 return 0;
316 }
317
318 if (d > -radius)
319 {
320 res = 1;
321 }
322 }
323
324 return res;
325}
326
327
328// return height of a sphere of given radius, located at center, in pixels
329F32 LLCamera::heightInPixels(const LLVector3 &center, F32 radius ) const
330{
331 if (radius == 0.f) return 0.f;
332
333 // If height initialized
334 if (mViewHeightInPixels > -1)
335 {
336 // Convert sphere to coord system with 0,0,0 at camera
337 LLVector3 vec = center - mOrigin;
338
339 // Compute distance to sphere
340 F32 dist = vec.magVec();
341
342 // Calculate angle of whole object
343 F32 angle = 2.0f * (F32) atan2(radius, dist);
344
345 // Calculate fraction of field of view
346 F32 fraction_of_fov = angle / mView;
347
348 // Compute number of pixels tall, based on vertical field of view
349 return (fraction_of_fov * mViewHeightInPixels);
350 }
351 else
352 {
353 // return invalid height
354 return -1.0f;
355 }
356}
357
358// If pos is visible, return the distance from pos to the camera.
359// Use fudge distance to scale rad against top/bot/left/right planes
360// Otherwise, return -distance
361F32 LLCamera::visibleDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
362{
363 if (mFixedDistance > 0)
364 {
365 return mFixedDistance;
366 }
367 LLVector3 dvec = pos - mOrigin;
368 // Check visibility
369 F32 dist = dvec.magVec();
370 if (dist > rad)
371 {
372 F32 dp,tdist;
373 dp = dvec * mXAxis;
374 if (dp < -rad)
375 return -dist;
376
377 rad *= fudgedist;
378 LLVector3 tvec(pos);
379 for (int p=0; p<PLANE_NUM; p++)
380 {
381 if (!(planemask & (1<<p)))
382 continue;
383 tdist = -(mWorldPlanes[p].dist(tvec));
384 if (tdist > rad)
385 return -dist;
386 }
387 }
388 return dist;
389}
390
391// Like visibleDistance, except uses mHorizPlanes[], which are left and right
392// planes perpindicular to (0,0,1) in world space
393F32 LLCamera::visibleHorizDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
394{
395 if (mFixedDistance > 0)
396 {
397 return mFixedDistance;
398 }
399 LLVector3 dvec = pos - mOrigin;
400 // Check visibility
401 F32 dist = dvec.magVec();
402 if (dist > rad)
403 {
404 rad *= fudgedist;
405 LLVector3 tvec(pos);
406 for (int p=0; p<HORIZ_PLANE_NUM; p++)
407 {
408 if (!(planemask & (1<<p)))
409 continue;
410 F32 tdist = -(mHorizPlanes[p].dist(tvec));
411 if (tdist > rad)
412 return -dist;
413 }
414 }
415 return dist;
416}
417
418// ---------------- friends and operators ----------------
419
420std::ostream& operator<<(std::ostream &s, const LLCamera &C)
421{
422 s << "{ \n";
423 s << " Center = " << C.getOrigin() << "\n";
424 s << " AtAxis = " << C.getXAxis() << "\n";
425 s << " LeftAxis = " << C.getYAxis() << "\n";
426 s << " UpAxis = " << C.getZAxis() << "\n";
427 s << " View = " << C.getView() << "\n";
428 s << " Aspect = " << C.getAspect() << "\n";
429 s << " NearPlane = " << C.mNearPlane << "\n";
430 s << " FarPlane = " << C.mFarPlane << "\n";
431 s << " TopPlane = " << C.mLocalPlanes[LLCamera::PLANE_TOP][VX] << " "
432 << C.mLocalPlanes[LLCamera::PLANE_TOP][VY] << " "
433 << C.mLocalPlanes[LLCamera::PLANE_TOP][VZ] << "\n";
434 s << " BottomPlane = " << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VX] << " "
435 << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VY] << " "
436 << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VZ] << "\n";
437 s << " LeftPlane = " << C.mLocalPlanes[LLCamera::PLANE_LEFT][VX] << " "
438 << C.mLocalPlanes[LLCamera::PLANE_LEFT][VY] << " "
439 << C.mLocalPlanes[LLCamera::PLANE_LEFT][VZ] << "\n";
440 s << " RightPlane = " << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VX] << " "
441 << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VY] << " "
442 << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VZ] << "\n";
443 s << "}";
444 return s;
445}
446
447
448
449// ---------------- private member functions ----------------
450
451void LLCamera::calculateFrustumPlanes()
452{
453 // The planes only change when any of the frustum descriptions change.
454 // They are not affected by changes of the position of the Frustum
455 // because they are known in the view frame and the position merely
456 // provides information on how to get from the absolute frame to the
457 // view frame.
458
459 F32 left,right,top,bottom;
460 top = mFarPlane * (F32)tanf(0.5f * mView);
461 bottom = -top;
462 left = top * mAspect;
463 right = -left;
464
465 calculateFrustumPlanes(left, right, top, bottom);
466}
467
468LLPlane planeFromPoints(LLVector3 p1, LLVector3 p2, LLVector3 p3)
469{
470 LLVector3 n = ((p2-p1)%(p3-p1));
471 n.normVec();
472
473 return LLPlane(p1, n);
474}
475
476
477void LLCamera::calcAgentFrustumPlanes(LLVector3* frust)
478{
479
480 for (int i = 0; i < 8; i++)
481 {
482 mAgentFrustum[i] = frust[i];
483 }
484
485 //frust contains the 8 points of the frustum, calculate 6 planes
486
487 //order of planes is important, keep most likely to fail in the front of the list
488
489 //near - frust[0], frust[1], frust[2]
490 mAgentPlanes[2] = planeFromPoints(frust[0], frust[1], frust[2]);
491
492 //far
493 mAgentPlanes[5] = planeFromPoints(frust[5], frust[4], frust[6]);
494
495 //left
496 mAgentPlanes[0] = planeFromPoints(frust[4], frust[0], frust[7]);
497
498 //right
499 mAgentPlanes[1] = planeFromPoints(frust[1], frust[5], frust[6]);
500
501 //top
502 mAgentPlanes[4] = planeFromPoints(frust[3], frust[2], frust[6]);
503
504 //bottom
505 mAgentPlanes[3] = planeFromPoints(frust[1], frust[0], frust[4]);
506
507 //cache plane octant facing mask for use in AABBInFrustum
508 for (int i = 0; i < 8; i++)
509 {
510 U8 mask = 0;
511 LLPlane p = mAgentPlanes[i];
512 LLVector3 n = LLVector3(p);
513
514 if (n.mV[0] >= 0)
515 {
516 mask |= 1;
517 }
518 if (n.mV[1] >= 0)
519 {
520 mask |= 2;
521 }
522 if (n.mV[2] >= 0)
523 {
524 mask |= 4;
525 }
526 mAgentPlaneMask[i] = mask;
527 }
528}
529
530void LLCamera::calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom)
531{
532 LLVector3 a, b, c;
533
534 // For each plane we need to define 3 points (LLVector3's) in camera view space.
535 // The order in which we pass the points to planeFromPoints() matters, because the
536 // plane normal has a degeneracy of 2; we want it pointing _into_ the frustum.
537
538 a.setVec(0.0f, 0.0f, 0.0f);
539 b.setVec(mFarPlane, right, top);
540 c.setVec(mFarPlane, right, bottom);
541 mLocalPlanes[PLANE_RIGHT].setVec(a, b, c);
542
543 c.setVec(mFarPlane, left, top);
544 mLocalPlanes[PLANE_TOP].setVec(a, c, b);
545
546 b.setVec(mFarPlane, left, bottom);
547 mLocalPlanes[PLANE_LEFT].setVec(a, b, c);
548
549 c.setVec(mFarPlane, right, bottom);
550 mLocalPlanes[PLANE_BOTTOM].setVec( a, c, b);
551
552 //calculate center and radius squared of frustum in world absolute coordinates
553 mFrustCenter = X_AXIS*mFarPlane*0.5f;
554 mFrustCenter = transformToAbsolute(mFrustCenter);
555 mFrustRadiusSquared = mFarPlane*0.5f;
556 mFrustRadiusSquared *= mFrustRadiusSquared * 1.05f; //pad radius squared by 5%
557}
558
559// x and y are in WINDOW space, so x = Y-Axis (left/right), y= Z-Axis(Up/Down)
560void LLCamera::calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2)
561{
562 F32 bottom, top, left, right;
563 F32 view_height = (F32)tanf(0.5f * mView) * mFarPlane;
564 F32 view_width = view_height * mAspect;
565
566 left = x1 * -2.f * view_width;
567 right = x2 * -2.f * view_width;
568 bottom = y1 * 2.f * view_height;
569 top = y2 * 2.f * view_height;
570
571 calculateFrustumPlanes(left, right, top, bottom);
572}
573
574void LLCamera::calculateWorldFrustumPlanes()
575{
576 F32 d;
577 LLVector3 center = mOrigin - mXAxis*mNearPlane;
578 mWorldPlanePos = center;
579 for (int p=0; p<4; p++)
580 {
581 LLVector3 pnorm = LLVector3(mLocalPlanes[p]);
582 LLVector3 norm = rotateToAbsolute(pnorm);
583 norm.normVec();
584 d = -(center * norm);
585 mWorldPlanes[p] = LLPlane(norm, d);
586 }
587 // horizontal planes, perpindicular to (0,0,1);
588 LLVector3 zaxis(0, 0, 1.0f);
589 F32 yaw = getYaw();
590 {
591 LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_LEFT]);
592 tnorm.rotVec(yaw, zaxis);
593 d = -(mOrigin * tnorm);
594 mHorizPlanes[HORIZ_PLANE_LEFT] = LLPlane(tnorm, d);
595 }
596 {
597 LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_RIGHT]);
598 tnorm.rotVec(yaw, zaxis);
599 d = -(mOrigin * tnorm);
600 mHorizPlanes[HORIZ_PLANE_RIGHT] = LLPlane(tnorm, d);
601 }
602}
603
604// NOTE: this is the OpenGL matrix that will transform the default OpenGL view
605// (-Z=at, Y=up) to the default view of the LLCamera class (X=at, Z=up):
606//
607// F32 cfr_transform = { 0.f, 0.f, -1.f, 0.f, // -Z becomes X
608// -1.f, 0.f, 0.f, 0.f, // -X becomes Y
609// 0.f, 1.f, 0.f, 0.f, // Y becomes Z
610// 0.f, 0.f, 0.f, 1.f };