llcamera.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033 
00034 #include "llmath.h"
00035 #include "llcamera.h"
00036 
00037 // ---------------- Constructors and destructors ----------------
00038 
00039 LLCamera::LLCamera() :
00040         LLCoordFrame(),
00041         mView(DEFAULT_FIELD_OF_VIEW),
00042         mAspect(DEFAULT_ASPECT_RATIO),
00043         mViewHeightInPixels( -1 ),                      // invalid height
00044         mNearPlane(DEFAULT_NEAR_PLANE),
00045         mFarPlane(DEFAULT_FAR_PLANE),
00046         mFixedDistance(-1.f)
00047 {
00048         calculateFrustumPlanes();
00049 } 
00050 
00051 
00052 LLCamera::LLCamera(F32 z_field_of_view, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane) :
00053         LLCoordFrame(),
00054         mView(z_field_of_view),
00055         mAspect(aspect_ratio),
00056         mViewHeightInPixels(view_height_in_pixels),
00057         mNearPlane(near_plane),
00058         mFarPlane(far_plane),
00059         mFixedDistance(-1.f)
00060 {
00061         if (mView < MIN_FIELD_OF_VIEW)                  { mView = MIN_FIELD_OF_VIEW; }
00062         else if (mView > MAX_FIELD_OF_VIEW)             { mView = MAX_FIELD_OF_VIEW; }
00063 
00064         if (mAspect < MIN_ASPECT_RATIO)                 { mAspect = MIN_ASPECT_RATIO; }
00065         else if (mAspect > MAX_ASPECT_RATIO)    { mAspect = MAX_ASPECT_RATIO; }
00066 
00067         if (mNearPlane < MIN_NEAR_PLANE)                { mNearPlane = MIN_NEAR_PLANE; }
00068         else if (mNearPlane > MAX_NEAR_PLANE)   { mNearPlane = MAX_NEAR_PLANE; }
00069 
00070         if (mFarPlane < 0)                                              { mFarPlane = DEFAULT_FAR_PLANE; }
00071         else if (mFarPlane < MIN_FAR_PLANE)             { mFarPlane = MIN_FAR_PLANE; }
00072         else if (mFarPlane > MAX_FAR_PLANE)             { mFarPlane = MAX_FAR_PLANE; }
00073 
00074         calculateFrustumPlanes();
00075 } 
00076 
00077 
00078 
00079 // ---------------- LLCamera::setFoo() member functions ----------------
00080 
00081 void LLCamera::setView(F32 field_of_view) 
00082 {
00083         mView = field_of_view;
00084         if (mView < MIN_FIELD_OF_VIEW)                  { mView = MIN_FIELD_OF_VIEW; }
00085         else if (mView > MAX_FIELD_OF_VIEW)             { mView = MAX_FIELD_OF_VIEW; }
00086         calculateFrustumPlanes();
00087 }
00088 
00089 void LLCamera::setViewHeightInPixels(S32 height)
00090 {
00091         mViewHeightInPixels = height;
00092 
00093         // Don't really need to do this, but update the pixel meter ratio with it.
00094         calculateFrustumPlanes();
00095 }
00096 
00097 void LLCamera::setAspect(F32 aspect_ratio) 
00098 {
00099         mAspect = aspect_ratio;
00100         if (mAspect < MIN_ASPECT_RATIO)                 { mAspect = MIN_ASPECT_RATIO; }
00101         else if (mAspect > MAX_ASPECT_RATIO)    { mAspect = MAX_ASPECT_RATIO; }
00102         calculateFrustumPlanes();
00103 }
00104 
00105 
00106 void LLCamera::setNear(F32 near_plane) 
00107 {
00108         mNearPlane = near_plane;
00109         if (mNearPlane < MIN_NEAR_PLANE)                { mNearPlane = MIN_NEAR_PLANE; }
00110         else if (mNearPlane > MAX_NEAR_PLANE)   { mNearPlane = MAX_NEAR_PLANE; }
00111         calculateFrustumPlanes();
00112 }
00113 
00114 
00115 void LLCamera::setFar(F32 far_plane) 
00116 {
00117         mFarPlane = far_plane;
00118         if (mFarPlane < MIN_FAR_PLANE)                  { mFarPlane = MIN_FAR_PLANE; }
00119         else if (mFarPlane > MAX_FAR_PLANE)             { mFarPlane = MAX_FAR_PLANE; }
00120         calculateFrustumPlanes();
00121 }
00122 
00123 
00124 // ---------------- read/write to buffer ---------------- 
00125 
00126 size_t LLCamera::writeFrustumToBuffer(char *buffer) const
00127 {
00128         memcpy(buffer, &mView, sizeof(F32));            /* Flawfinder: ignore */                
00129         buffer += sizeof(F32);
00130         memcpy(buffer, &mAspect, sizeof(F32));          /* Flawfinder: ignore */
00131         buffer += sizeof(F32);
00132         memcpy(buffer, &mNearPlane, sizeof(F32));       /* Flawfinder: ignore */
00133         buffer += sizeof(F32);
00134         memcpy(buffer, &mFarPlane, sizeof(F32));                /* Flawfinder: ignore */
00135         return 4*sizeof(F32);
00136 }
00137 
00138 size_t LLCamera::readFrustumFromBuffer(const char *buffer)
00139 {
00140         memcpy(&mView, buffer, sizeof(F32));            /* Flawfinder: ignore */
00141         buffer += sizeof(F32);
00142         memcpy(&mAspect, buffer, sizeof(F32));          /* Flawfinder: ignore */
00143         buffer += sizeof(F32);
00144         memcpy(&mNearPlane, buffer, sizeof(F32));       /* Flawfinder: ignore */
00145         buffer += sizeof(F32);
00146         memcpy(&mFarPlane, buffer, sizeof(F32));                /* Flawfinder: ignore */
00147         return 4*sizeof(F32);
00148 }
00149 
00150 
00151 // ---------------- test methods  ---------------- 
00152 
00153 int LLCamera::AABBInFrustum(const LLVector3 &center, const LLVector3& radius) 
00154 {
00155         static const LLVector3 scaler[] = {
00156                 LLVector3(-1,-1,-1),
00157                 LLVector3( 1,-1,-1),
00158                 LLVector3(-1, 1,-1),
00159                 LLVector3( 1, 1,-1),
00160                 LLVector3(-1,-1, 1),
00161                 LLVector3( 1,-1, 1),
00162                 LLVector3(-1, 1, 1),
00163                 LLVector3( 1, 1, 1)
00164         };
00165 
00166         U8 mask = 0;
00167         S32 result = 2;
00168 
00169         for (int i = 0; i < 6; i++)
00170         {
00171                 mask = mAgentPlaneMask[i];
00172                 LLPlane p = mAgentPlanes[i];
00173                 LLVector3 n = LLVector3(p);
00174                 float d = p.mV[3];
00175                 LLVector3 rscale = radius.scaledVec(scaler[mask]);
00176 
00177                 LLVector3 minp = center - rscale;
00178                 LLVector3 maxp = center + rscale;
00179 
00180                 if (n * minp > -d) 
00181                 {
00182                         return 0;
00183                 }
00184         
00185                 if (n * maxp > -d)
00186                 {
00187                         result = 1;
00188                 }
00189         }
00190 
00191         return result;
00192 }
00193 
00194 int LLCamera::sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius) 
00195 {
00196         LLVector3 dist = sphere_center-mFrustCenter;
00197         float dsq = dist * dist;
00198         float rsq = mFarPlane*0.5f + radius;
00199         rsq *= rsq;
00200 
00201         if (dsq < rsq) 
00202         {
00203                 return 1;
00204         }
00205         
00206         return 0;       
00207 }
00208 
00209 // HACK: This version is still around because the version below doesn't work
00210 // unless the agent planes are initialized.
00211 // Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
00212 // NOTE: 'center' is in absolute frame.
00213 int LLCamera::sphereInFrustumOld(const LLVector3 &sphere_center, const F32 radius) const 
00214 {
00215         // Returns 1 if sphere is in frustum, 0 if not.
00216         // modified so that default view frust is along X with Z vertical
00217         F32 x, y, z, rightDist, leftDist, topDist, bottomDist;
00218 
00219         // Subtract the view position 
00220         //LLVector3 relative_center;
00221         //relative_center = sphere_center - getOrigin();
00222         LLVector3 rel_center(sphere_center);
00223         rel_center -= mOrigin;
00224 
00225         bool all_in = TRUE;
00226 
00227         // Transform relative_center.x to camera frame
00228         x = mXAxis * rel_center;
00229         if (x < MIN_NEAR_PLANE - radius)
00230         {
00231                 return 0;
00232         }
00233         else if (x < MIN_NEAR_PLANE + radius)
00234         {
00235                 all_in = FALSE;
00236         }
00237 
00238         if (x > mFarPlane + radius)
00239         {
00240                 return 0;
00241         }
00242         else if (x > mFarPlane - radius)
00243         {
00244                 all_in = FALSE;
00245         }
00246 
00247         // Transform relative_center.y to camera frame
00248         y = mYAxis * rel_center;
00249 
00250         // distance to plane is the dot product of (x, y, 0) * plane_normal
00251         rightDist = x * mLocalPlanes[PLANE_RIGHT][VX] + y * mLocalPlanes[PLANE_RIGHT][VY];
00252         if (rightDist < -radius)
00253         {
00254                 return 0;
00255         }
00256         else if (rightDist < radius)
00257         {
00258                 all_in = FALSE;
00259         }
00260 
00261         leftDist = x * mLocalPlanes[PLANE_LEFT][VX] + y * mLocalPlanes[PLANE_LEFT][VY];
00262         if (leftDist < -radius)
00263         {
00264                 return 0;
00265         }
00266         else if (leftDist < radius)
00267         {
00268                 all_in = FALSE;
00269         }
00270 
00271         // Transform relative_center.y to camera frame
00272         z = mZAxis * rel_center;
00273 
00274         topDist = x * mLocalPlanes[PLANE_TOP][VX] + z * mLocalPlanes[PLANE_TOP][VZ];
00275         if (topDist < -radius)
00276         {
00277                 return 0;
00278         }
00279         else if (topDist < radius)
00280         {
00281                 all_in = FALSE;
00282         }
00283 
00284         bottomDist = x * mLocalPlanes[PLANE_BOTTOM][VX] + z * mLocalPlanes[PLANE_BOTTOM][VZ];
00285         if (bottomDist < -radius)
00286         {
00287                 return 0;
00288         }
00289         else if (bottomDist < radius)
00290         {
00291                 all_in = FALSE;
00292         }
00293 
00294         if (all_in)
00295         {
00296                 return 2;
00297         }
00298 
00299         return 1;
00300 }
00301 
00302 
00303 // HACK: This (presumably faster) version only currently works if you set up the
00304 // frustum planes using GL.  At some point we should get those planes through another
00305 // mechanism, and then we can get rid of the "old" version above.
00306 
00307 // Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
00308 // NOTE: 'center' is in absolute frame.
00309 int LLCamera::sphereInFrustum(const LLVector3 &sphere_center, const F32 radius) const 
00310 {
00311         // Returns 1 if sphere is in frustum, 0 if not.
00312         int res = 2;
00313         for (int i = 0; i < 6; i++)
00314         {
00315                 float d = mAgentPlanes[i].dist(sphere_center);
00316 
00317                 if (d > radius) 
00318                 {
00319                         return 0;
00320                 }
00321 
00322                 if (d > -radius)
00323                 {
00324                         res = 1;
00325                 }
00326         }
00327 
00328         return res;
00329 }
00330 
00331 
00332 // return height of a sphere of given radius, located at center, in pixels
00333 F32 LLCamera::heightInPixels(const LLVector3 &center, F32 radius ) const
00334 {
00335         if (radius == 0.f) return 0.f;
00336 
00337         // If height initialized
00338         if (mViewHeightInPixels > -1)
00339         {
00340                 // Convert sphere to coord system with 0,0,0 at camera
00341                 LLVector3 vec = center - mOrigin;
00342 
00343                 // Compute distance to sphere
00344                 F32 dist = vec.magVec();
00345 
00346                 // Calculate angle of whole object
00347                 F32 angle = 2.0f * (F32) atan2(radius, dist);
00348 
00349                 // Calculate fraction of field of view
00350                 F32 fraction_of_fov = angle / mView;
00351 
00352                 // Compute number of pixels tall, based on vertical field of view
00353                 return (fraction_of_fov * mViewHeightInPixels);
00354         }
00355         else
00356         {
00357                 // return invalid height
00358                 return -1.0f;
00359         }
00360 }
00361 
00362 // If pos is visible, return the distance from pos to the camera.
00363 // Use fudge distance to scale rad against top/bot/left/right planes
00364 // Otherwise, return -distance
00365 F32 LLCamera::visibleDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
00366 {
00367         if (mFixedDistance > 0)
00368         {
00369                 return mFixedDistance;
00370         }
00371         LLVector3 dvec = pos - mOrigin;
00372         // Check visibility
00373         F32 dist = dvec.magVec();
00374         if (dist > rad)
00375         {
00376                 F32 dp,tdist;
00377                 dp = dvec * mXAxis;
00378                 if (dp < -rad)
00379                         return -dist;
00380 
00381                 rad *= fudgedist;
00382                 LLVector3 tvec(pos);
00383                 for (int p=0; p<PLANE_NUM; p++)
00384                 {
00385                         if (!(planemask & (1<<p)))
00386                                 continue;
00387                         tdist = -(mWorldPlanes[p].dist(tvec));
00388                         if (tdist > rad)
00389                                 return -dist;
00390                 }
00391         }
00392         return dist;
00393 }
00394 
00395 // Like visibleDistance, except uses mHorizPlanes[], which are left and right
00396 //  planes perpindicular to (0,0,1) in world space
00397 F32 LLCamera::visibleHorizDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
00398 {
00399         if (mFixedDistance > 0)
00400         {
00401                 return mFixedDistance;
00402         }
00403         LLVector3 dvec = pos - mOrigin;
00404         // Check visibility
00405         F32 dist = dvec.magVec();
00406         if (dist > rad)
00407         {
00408                 rad *= fudgedist;
00409                 LLVector3 tvec(pos);
00410                 for (int p=0; p<HORIZ_PLANE_NUM; p++)
00411                 {
00412                         if (!(planemask & (1<<p)))
00413                                 continue;
00414                         F32 tdist = -(mHorizPlanes[p].dist(tvec));
00415                         if (tdist > rad)
00416                                 return -dist;
00417                 }
00418         }
00419         return dist;
00420 }
00421 
00422 // ---------------- friends and operators ----------------  
00423 
00424 std::ostream& operator<<(std::ostream &s, const LLCamera &C) 
00425 {
00426         s << "{ \n";
00427         s << "  Center = " << C.getOrigin() << "\n";
00428         s << "  AtAxis = " << C.getXAxis() << "\n";
00429         s << "  LeftAxis = " << C.getYAxis() << "\n";
00430         s << "  UpAxis = " << C.getZAxis() << "\n";
00431         s << "  View = " << C.getView() << "\n";
00432         s << "  Aspect = " << C.getAspect() << "\n";
00433         s << "  NearPlane   = " << C.mNearPlane << "\n";
00434         s << "  FarPlane    = " << C.mFarPlane << "\n";
00435         s << "  TopPlane    = " << C.mLocalPlanes[LLCamera::PLANE_TOP][VX] << "  " 
00436                                                         << C.mLocalPlanes[LLCamera::PLANE_TOP][VY] << "  " 
00437                                                         << C.mLocalPlanes[LLCamera::PLANE_TOP][VZ] << "\n";
00438         s << "  BottomPlane = " << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VX] << "  " 
00439                                                         << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VY] << "  " 
00440                                                         << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VZ] << "\n";
00441         s << "  LeftPlane   = " << C.mLocalPlanes[LLCamera::PLANE_LEFT][VX] << "  " 
00442                                                         << C.mLocalPlanes[LLCamera::PLANE_LEFT][VY] << "  " 
00443                                                         << C.mLocalPlanes[LLCamera::PLANE_LEFT][VZ] << "\n";
00444         s << "  RightPlane  = " << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VX] << "  " 
00445                                                         << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VY] << "  " 
00446                                                         << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VZ] << "\n";
00447         s << "}";
00448         return s;
00449 }
00450 
00451 
00452 
00453 // ----------------  private member functions ----------------
00454 
00455 void LLCamera::calculateFrustumPlanes() 
00456 {
00457         // The planes only change when any of the frustum descriptions change.
00458         // They are not affected by changes of the position of the Frustum
00459         // because they are known in the view frame and the position merely
00460         // provides information on how to get from the absolute frame to the 
00461         // view frame.
00462 
00463         F32 left,right,top,bottom;
00464         top = mFarPlane * (F32)tanf(0.5f * mView);
00465         bottom = -top;
00466         left = top * mAspect;
00467         right = -left;
00468 
00469         calculateFrustumPlanes(left, right, top, bottom);
00470 }
00471 
00472 LLPlane planeFromPoints(LLVector3 p1, LLVector3 p2, LLVector3 p3)
00473 {
00474         LLVector3 n = ((p2-p1)%(p3-p1));
00475         n.normVec();
00476 
00477         return LLPlane(p1, n);
00478 }
00479 
00480 
00481 void LLCamera::calcAgentFrustumPlanes(LLVector3* frust)
00482 {
00483 
00484         for (int i = 0; i < 8; i++)
00485         {
00486                 mAgentFrustum[i] = frust[i];
00487         }
00488 
00489         //frust contains the 8 points of the frustum, calculate 6 planes
00490 
00491         //order of planes is important, keep most likely to fail in the front of the list
00492 
00493         //near - frust[0], frust[1], frust[2]
00494         mAgentPlanes[2] = planeFromPoints(frust[0], frust[1], frust[2]);
00495 
00496         //far  
00497         mAgentPlanes[5] = planeFromPoints(frust[5], frust[4], frust[6]);
00498 
00499         //left  
00500         mAgentPlanes[0] = planeFromPoints(frust[4], frust[0], frust[7]);
00501 
00502         //right  
00503         mAgentPlanes[1] = planeFromPoints(frust[1], frust[5], frust[6]);
00504 
00505         //top  
00506         mAgentPlanes[4] = planeFromPoints(frust[3], frust[2], frust[6]);
00507 
00508         //bottom  
00509         mAgentPlanes[3] = planeFromPoints(frust[1], frust[0], frust[4]);
00510 
00511         //cache plane octant facing mask for use in AABBInFrustum
00512         for (int i = 0; i < 6; i++)
00513         {
00514                 U8 mask = 0;
00515                 LLPlane p = mAgentPlanes[i];
00516                 LLVector3 n = LLVector3(p);
00517 
00518                 if (n.mV[0] >= 0)
00519                 {
00520                         mask |= 1;
00521                 }
00522                 if (n.mV[1] >= 0)
00523                 {
00524                         mask |= 2;
00525                 }
00526                 if (n.mV[2] >= 0)
00527                 {
00528                         mask |= 4;
00529                 }
00530                 mAgentPlaneMask[i] = mask;
00531         }
00532 }
00533 
00534 void LLCamera::calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom)
00535 {
00536         LLVector3 a, b, c;
00537 
00538         // For each plane we need to define 3 points (LLVector3's) in camera view space.  
00539         // The order in which we pass the points to planeFromPoints() matters, because the 
00540         // plane normal has a degeneracy of 2; we want it pointing _into_ the frustum. 
00541 
00542         a.setVec(0.0f, 0.0f, 0.0f);
00543         b.setVec(mFarPlane, right, top);
00544         c.setVec(mFarPlane, right, bottom);
00545         mLocalPlanes[PLANE_RIGHT].setVec(a, b, c);
00546 
00547         c.setVec(mFarPlane, left, top);
00548         mLocalPlanes[PLANE_TOP].setVec(a, c, b);
00549 
00550         b.setVec(mFarPlane, left, bottom);
00551         mLocalPlanes[PLANE_LEFT].setVec(a, b, c);
00552 
00553         c.setVec(mFarPlane, right, bottom);
00554         mLocalPlanes[PLANE_BOTTOM].setVec( a, c, b); 
00555 
00556         //calculate center and radius squared of frustum in world absolute coordinates
00557         mFrustCenter = X_AXIS*mFarPlane*0.5f;
00558         mFrustCenter = transformToAbsolute(mFrustCenter);
00559         mFrustRadiusSquared = mFarPlane*0.5f;
00560         mFrustRadiusSquared *= mFrustRadiusSquared * 1.05f; //pad radius squared by 5%
00561 }
00562 
00563 // x and y are in WINDOW space, so x = Y-Axis (left/right), y= Z-Axis(Up/Down)
00564 void LLCamera::calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2)
00565 {
00566         F32 bottom, top, left, right;
00567         F32 view_height = (F32)tanf(0.5f * mView) * mFarPlane;
00568         F32 view_width = view_height * mAspect;
00569         
00570         left =   x1 * -2.f * view_width;
00571         right =  x2 * -2.f * view_width;
00572         bottom = y1 * 2.f * view_height; 
00573         top =    y2 * 2.f * view_height;
00574 
00575         calculateFrustumPlanes(left, right, top, bottom);
00576 }
00577 
00578 void LLCamera::calculateWorldFrustumPlanes() 
00579 {
00580         F32 d;
00581         LLVector3 center = mOrigin - mXAxis*mNearPlane;
00582         mWorldPlanePos = center;
00583         for (int p=0; p<4; p++)
00584         {
00585                 LLVector3 pnorm = LLVector3(mLocalPlanes[p]);
00586                 LLVector3 norm = rotateToAbsolute(pnorm);
00587                 norm.normVec();
00588                 d = -(center * norm);
00589                 mWorldPlanes[p] = LLPlane(norm, d);
00590         }
00591         // horizontal planes, perpindicular to (0,0,1);
00592         LLVector3 zaxis(0, 0, 1.0f);
00593         F32 yaw = getYaw();
00594         {
00595                 LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_LEFT]);
00596                 tnorm.rotVec(yaw, zaxis);
00597                 d = -(mOrigin * tnorm);
00598                 mHorizPlanes[HORIZ_PLANE_LEFT] = LLPlane(tnorm, d);
00599         }
00600         {
00601                 LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_RIGHT]);
00602                 tnorm.rotVec(yaw, zaxis);
00603                 d = -(mOrigin * tnorm);
00604                 mHorizPlanes[HORIZ_PLANE_RIGHT] = LLPlane(tnorm, d);
00605         }
00606 }
00607 
00608 // NOTE: this is the OpenGL matrix that will transform the default OpenGL view 
00609 // (-Z=at, Y=up) to the default view of the LLCamera class (X=at, Z=up):
00610 // 
00611 // F32 cfr_transform =  {  0.f,  0.f, -1.f,  0.f,   // -Z becomes X
00612 //                                                -1.f,  0.f,  0.f,  0.f,   // -X becomes Y
00613 //                                         0.f,  1.f,  0.f,  0.f,   //  Y becomes Z
00614 //                                                 0.f,  0.f,  0.f,  1.f };

Generated on Thu Jul 1 06:08:21 2010 for Second Life Viewer by  doxygen 1.4.7