00001
00032 #include <iomanip>
00033 #include "llviewerprecompiledheaders.h"
00034
00035 #include "llviewercamera.h"
00036
00037 #include "llquaternion.h"
00038
00039 #include "llagent.h"
00040 #include "llviewercontrol.h"
00041 #include "lldrawable.h"
00042 #include "llface.h"
00043 #include "llgl.h"
00044 #include "llglheaders.h"
00045 #include "llviewerobjectlist.h"
00046 #include "llviewerregion.h"
00047 #include "llviewerwindow.h"
00048 #include "llvovolume.h"
00049 #include "llworld.h"
00050
00051 LLViewerCamera *gCamera = NULL;
00052
00053 LLViewerCamera::LLViewerCamera() : LLCamera()
00054 {
00055 calcProjection(getFar());
00056 S32 i;
00057 for (i = 0; i < 16; i++)
00058 {
00059 mGLProjectionMatrix[i] = 0.f;
00060 }
00061 mCameraFOVDefault = DEFAULT_FIELD_OF_VIEW;
00062 mPixelMeterRatio = 0.f;
00063 mScreenPixelArea = 0;
00064 mZoomFactor = 1.f;
00065 mZoomSubregion = 1;
00066 }
00067
00068 void LLViewerCamera::updateCameraLocation(const LLVector3 ¢er,
00069 const LLVector3 &up_direction,
00070 const LLVector3 &point_of_interest)
00071 {
00072 LLVector3 last_position;
00073 LLVector3 last_axis;
00074 last_position = getOrigin();
00075 last_axis = getAtAxis();
00076
00077 mLastPointOfInterest = point_of_interest;
00078
00079
00080 LLVector3 camera_offset = center - gAgent.getPositionAgent();
00081
00082 setOriginAndLookAt(center, up_direction, point_of_interest);
00083
00084 F32 dpos = (center - last_position).magVec();
00085 LLQuaternion rotation;
00086 rotation.shortestArc(last_axis, getAtAxis());
00087
00088 F32 x, y, z;
00089 F32 drot;
00090 rotation.getAngleAxis(&drot, &x, &y, &z);
00091 mVelocityStat.addValue(dpos);
00092 mAngularVelocityStat.addValue(drot);
00093
00094 mPixelMeterRatio = mViewHeightInPixels / (2.f*tanf(mCameraFOVDefault*0.5));
00095
00096 mScreenPixelArea =(S32)((F32)mViewHeightInPixels * ((F32)mViewHeightInPixels * mAspect));
00097 }
00098
00099
00100 F64 gGLModelView[16];
00101 F64 gGLProjection[16];
00102 S32 gGLViewport[4];
00103
00104 const LLMatrix4 &LLViewerCamera::getProjection() const
00105 {
00106 calcProjection(getFar());
00107 return mProjectionMatrix;
00108
00109 }
00110
00111 const LLMatrix4 &LLViewerCamera::getModelview() const
00112 {
00113 LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
00114 getMatrixToLocal(mModelviewMatrix);
00115 mModelviewMatrix *= cfr;
00116 return mModelviewMatrix;
00117 }
00118
00119 void LLViewerCamera::calcProjection(const F32 far_distance) const
00120 {
00121 F32 fov_y, z_far, z_near, aspect, f;
00122 fov_y = getView();
00123 z_far = far_distance;
00124 z_near = getNear();
00125 aspect = getAspect();
00126
00127 f = 1/tan(fov_y*0.5f);
00128
00129 mProjectionMatrix.zero();
00130 mProjectionMatrix.mMatrix[0][0] = f/aspect;
00131 mProjectionMatrix.mMatrix[1][1] = f;
00132 mProjectionMatrix.mMatrix[2][2] = (z_far + z_near)/(z_near - z_far);
00133 mProjectionMatrix.mMatrix[3][2] = (2*z_far*z_near)/(z_near - z_far);
00134 mProjectionMatrix.mMatrix[2][3] = -1;
00135 }
00136
00137
00138
00139
00140
00141
00142 LLMatrix4 gProjectionMat;
00143
00144
00145 void LLViewerCamera::updateFrustumPlanes(LLCamera& camera, BOOL ortho)
00146 {
00147 GLint viewport[4];
00148 GLdouble model[16];
00149 GLdouble proj[16];
00150 GLdouble objX,objY,objZ;
00151
00152 LLVector3 frust[8];
00153
00154 glGetIntegerv(GL_VIEWPORT, viewport);
00155 glGetDoublev(GL_MODELVIEW_MATRIX, model);
00156 glGetDoublev(GL_PROJECTION_MATRIX,proj);
00157
00158 gluUnProject(viewport[0],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
00159 frust[0].setVec((F32)objX,(F32)objY,(F32)objZ);
00160 gluUnProject(viewport[0]+viewport[2],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
00161 frust[1].setVec((F32)objX,(F32)objY,(F32)objZ);
00162 gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
00163 frust[2].setVec((F32)objX,(F32)objY,(F32)objZ);
00164 gluUnProject(viewport[0],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
00165 frust[3].setVec((F32)objX,(F32)objY,(F32)objZ);
00166
00167 if (ortho)
00168 {
00169 LLVector3 far_shift = LLVector3(camera.getFar()*2.0f,0,0);
00170 for (U32 i = 0; i < 4; i++)
00171 {
00172 frust[i+4] = frust[i] + far_shift;
00173 }
00174 }
00175 else
00176 {
00177 for (U32 i = 0; i < 4; i++)
00178 {
00179 LLVector3 vec = frust[i] - camera.getOrigin();
00180 vec.normVec();
00181 frust[i+4] = camera.getOrigin() + vec*camera.getFar()*2.0f;
00182 }
00183 }
00184
00185 camera.calcAgentFrustumPlanes(frust);
00186 }
00187
00188 void LLViewerCamera::setPerspective(BOOL for_selection,
00189 S32 x, S32 y_from_bot, S32 width, S32 height,
00190 BOOL limit_select_distance,
00191 F32 z_near, F32 z_far)
00192 {
00193
00194 F32 fov_y;
00195
00196 fov_y = RAD_TO_DEG * getView();
00197 BOOL z_default_near, z_default_far = FALSE;
00198 if (z_far <= 0)
00199 {
00200 z_default_far = TRUE;
00201 z_far = getFar();
00202 }
00203 if (z_near <= 0)
00204 {
00205 z_default_near = TRUE;
00206 z_near = getNear();
00207 }
00208
00209
00210
00211
00212
00213 glMatrixMode( GL_PROJECTION );
00214 glLoadIdentity();
00215
00216 if (for_selection)
00217 {
00218
00219
00220 const U8 VIEWPORT_VECTOR_LEN = 4;
00221 GLint viewport[VIEWPORT_VECTOR_LEN];
00222 glGetIntegerv(GL_VIEWPORT, viewport);
00223 gluPickMatrix(x + width / 2, y_from_bot + height / 2, width, height, viewport);
00224
00225 if (limit_select_distance)
00226 {
00227
00228 z_far = gSavedSettings.getF32("MaxSelectDistance");
00229 }
00230 else
00231 {
00232 z_far = gAgent.mDrawDistance;
00233 }
00234 }
00235 else
00236 {
00237
00238 if (z_default_far)
00239 {
00240 z_far = MAX_FAR_CLIP;
00241 }
00242 glViewport(x, y_from_bot, width, height);
00243 }
00244
00245 if (mZoomFactor > 1.f)
00246 {
00247 float offset = mZoomFactor - 1.f;
00248 int pos_y = mZoomSubregion / llceil(mZoomFactor);
00249 int pos_x = mZoomSubregion - (pos_y*llceil(mZoomFactor));
00250 glTranslatef(offset - (F32)pos_x * 2.f, offset - (F32)pos_y * 2.f, 0.f);
00251 glScalef(mZoomFactor, mZoomFactor, 1.f);
00252 }
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264 F32 eye_separation = gSavedSettings.getF32("StereoEyeSeparation");
00265 F32 focal_distance = gSavedSettings.getF32("StereoFocalDistance");
00266
00267 const float aspect = (float)width / (float)height;
00268 const float rads = 0.01745329251994329577f * (fov_y * .5f);
00269 const float wd2 = z_near * tan(rads);
00270 const float ndfl = z_near / focal_distance;
00271
00272
00273 float left = -(aspect * wd2) + (eye_separation/2 * ndfl);
00274 float right = (aspect * wd2) + (eye_separation/2 * ndfl);
00275 float top = wd2;
00276 float bottom = -wd2;
00277
00278 glFrustum(left, right, bottom, top, z_near, z_far);
00279
00280
00281 glGetFloatv(GL_PROJECTION_MATRIX, (float*)&gProjectionMat);
00282 glMatrixMode( GL_MODELVIEW );
00283 glLoadMatrixf(OGL_TO_CFR_ROTATION);
00284
00285 GLfloat ogl_matrix[16];
00286 getOpenGLTransform(ogl_matrix);
00287 glMultMatrixf(ogl_matrix);
00288
00289 if (for_selection && (width > 1 || height > 1))
00290 {
00291 calculateFrustumPlanesFromWindow((F32)(x - width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
00292 (F32)(y_from_bot - height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f,
00293 (F32)(x + width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
00294 (F32)(y_from_bot + height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f);
00295
00296 }
00297
00298
00299 if (!for_selection && mZoomFactor == 1.f)
00300 {
00301
00302 glGetDoublev(GL_PROJECTION_MATRIX, mGLProjectionMatrix);
00303 glGetDoublev(GL_MODELVIEW_MATRIX, gGLModelView);
00304 glGetIntegerv(GL_VIEWPORT, (GLint*)gGLViewport);
00305 }
00306
00307 updateFrustumPlanes(*this);
00308
00309 if (gSavedSettings.getBOOL("CameraOffset"))
00310 {
00311 glMatrixMode(GL_PROJECTION);
00312 glTranslatef(0,0,-50);
00313 glRotatef(20.0,1,0,0);
00314 glMatrixMode(GL_MODELVIEW);
00315 }
00316 }
00317
00318
00319
00320
00321 void LLViewerCamera::projectScreenToPosAgent(const S32 screen_x, const S32 screen_y, LLVector3* pos_agent) const
00322 {
00323
00324 GLdouble x, y, z;
00325 gluUnProject(
00326 GLdouble(screen_x), GLdouble(screen_y), 0.0,
00327 gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
00328 &x,
00329 &y,
00330 &z );
00331 pos_agent->setVec( (F32)x, (F32)y, (F32)z );
00332 }
00333
00334
00335
00336
00337 BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp) const
00338 {
00339 BOOL in_front = TRUE;
00340 GLdouble x, y, z;
00341
00342 LLVector3 dir_to_point = pos_agent - getOrigin();
00343 dir_to_point /= dir_to_point.magVec();
00344
00345 if (dir_to_point * getAtAxis() < 0.f)
00346 {
00347 if (clamp)
00348 {
00349 return FALSE;
00350 }
00351 else
00352 {
00353 in_front = FALSE;
00354 }
00355 }
00356
00357 if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ],
00358 gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
00359 &x, &y, &z))
00360 {
00361
00362 x /= gViewerWindow->getDisplayScale().mV[VX];
00363 y /= gViewerWindow->getDisplayScale().mV[VY];
00364
00365
00366 const LLRect& window_rect = gViewerWindow->getWindowRect();
00367
00368
00369 S32 int_x = lltrunc(x);
00370 S32 int_y = lltrunc(y);
00371
00372 BOOL valid = TRUE;
00373
00374 if (clamp)
00375 {
00376 if (int_x < window_rect.mLeft)
00377 {
00378 out_point.mX = window_rect.mLeft;
00379 valid = FALSE;
00380 }
00381 else if (int_x > window_rect.mRight)
00382 {
00383 out_point.mX = window_rect.mRight;
00384 valid = FALSE;
00385 }
00386 else
00387 {
00388 out_point.mX = int_x;
00389 }
00390
00391 if (int_y < window_rect.mBottom)
00392 {
00393 out_point.mY = window_rect.mBottom;
00394 valid = FALSE;
00395 }
00396 else if (int_y > window_rect.mTop)
00397 {
00398 out_point.mY = window_rect.mTop;
00399 valid = FALSE;
00400 }
00401 else
00402 {
00403 out_point.mY = int_y;
00404 }
00405 return valid;
00406 }
00407 else
00408 {
00409 out_point.mX = int_x;
00410 out_point.mY = int_y;
00411
00412 if (int_x < window_rect.mLeft)
00413 {
00414 valid = FALSE;
00415 }
00416 else if (int_x > window_rect.mRight)
00417 {
00418 valid = FALSE;
00419 }
00420 if (int_y < window_rect.mBottom)
00421 {
00422 valid = FALSE;
00423 }
00424 else if (int_y > window_rect.mTop)
00425 {
00426 valid = FALSE;
00427 }
00428
00429 return in_front && valid;
00430 }
00431 }
00432 else
00433 {
00434 return FALSE;
00435 }
00436 }
00437
00438
00439
00440
00441 BOOL LLViewerCamera::projectPosAgentToScreenEdge(const LLVector3 &pos_agent,
00442 LLCoordGL &out_point) const
00443 {
00444 LLVector3 dir_to_point = pos_agent - getOrigin();
00445 dir_to_point /= dir_to_point.magVec();
00446
00447 BOOL in_front = TRUE;
00448 if (dir_to_point * getAtAxis() < 0.f)
00449 {
00450 in_front = FALSE;
00451 }
00452
00453 GLdouble x, y, z;
00454 if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY],
00455 pos_agent.mV[VZ], gGLModelView,
00456 mGLProjectionMatrix, (GLint*)gGLViewport,
00457 &x, &y, &z))
00458 {
00459 x /= gViewerWindow->getDisplayScale().mV[VX];
00460 y /= gViewerWindow->getDisplayScale().mV[VY];
00461
00462 const LLRect& window_rect = gViewerWindow->getVirtualWindowRect();
00463
00464
00465 S32 int_x = lltrunc(x);
00466 S32 int_y = lltrunc(y);
00467
00468
00469 GLdouble center_x = (GLdouble)(0.5f * (window_rect.mLeft + window_rect.mRight));
00470 GLdouble center_y = (GLdouble)(0.5f * (window_rect.mBottom + window_rect.mTop));
00471
00472 if (x == center_x && y == center_y)
00473 {
00474
00475 return FALSE;
00476 }
00477
00478
00479 GLdouble line_x = x - center_x;
00480 GLdouble line_y = y - center_y;
00481
00482 int_x = lltrunc(center_x);
00483 int_y = lltrunc(center_y);
00484
00485
00486 if (0.f == line_x)
00487 {
00488
00489 if (line_y > 0.f)
00490 {
00491 int_y = window_rect.mTop;
00492 }
00493 else
00494 {
00495 int_y = window_rect.mBottom;
00496 }
00497 }
00498 else if (0 == window_rect.getWidth())
00499 {
00500
00501 if (y < window_rect.mBottom)
00502 {
00503 int_y = window_rect.mBottom;
00504 }
00505 else if ( y > window_rect.mTop)
00506 {
00507 int_y = window_rect.mTop;
00508 }
00509 }
00510 else
00511 {
00512 F32 line_slope = (F32)(line_y / line_x);
00513 F32 rect_slope = ((F32)window_rect.getHeight()) / ((F32)window_rect.getWidth());
00514
00515 if (fabs(line_slope) > rect_slope)
00516 {
00517 if (line_y < 0.f)
00518 {
00519
00520 int_y = window_rect.mBottom;
00521 }
00522 else
00523 {
00524
00525 int_y = window_rect.mTop;
00526 }
00527 int_x = lltrunc(((GLdouble)int_y - center_y) / line_slope + center_x);
00528 }
00529 else if (fabs(line_slope) < rect_slope)
00530 {
00531 if (line_x < 0.f)
00532 {
00533
00534 int_x = window_rect.mLeft;
00535 }
00536 else
00537 {
00538
00539 int_x = window_rect.mRight;
00540 }
00541 int_y = lltrunc(((GLdouble)int_x - center_x) * line_slope + center_y);
00542 }
00543 else
00544 {
00545
00546 if (line_x > 0.f)
00547 {
00548 int_x = window_rect.mRight;
00549 }
00550 else
00551 {
00552 int_x = window_rect.mLeft;
00553 }
00554 if (line_y > 0.0f)
00555 {
00556 int_y = window_rect.mTop;
00557 }
00558 else
00559 {
00560 int_y = window_rect.mBottom;
00561 }
00562 }
00563 }
00564 if (!in_front)
00565 {
00566 int_x = window_rect.mLeft + window_rect.mRight - int_x;
00567 int_y = window_rect.mBottom + window_rect.mTop - int_y;
00568 }
00569 out_point.mX = int_x;
00570 out_point.mY = int_y;
00571 return TRUE;
00572 }
00573 return FALSE;
00574 }
00575
00576
00577 void LLViewerCamera::getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right)
00578 {
00579 LLVector3 to_vec = pos_agent - getOrigin();
00580
00581 F32 at_dist = to_vec * getAtAxis();
00582
00583 F32 height_meters = at_dist* (F32)tan(getView()/2.f);
00584 F32 height_pixels = getViewHeightInPixels()/2.f;
00585
00586 F32 pixel_aspect = gViewerWindow->getWindow()->getPixelAspectRatio();
00587
00588 F32 meters_per_pixel = height_meters / height_pixels;
00589 up = getUpAxis() * meters_per_pixel * gViewerWindow->getDisplayScale().mV[VY];
00590 right = -1.f * pixel_aspect * meters_per_pixel * getLeftAxis() * gViewerWindow->getDisplayScale().mV[VX];
00591 }
00592
00593 LLVector3 LLViewerCamera::roundToPixel(const LLVector3 &pos_agent)
00594 {
00595 F32 dist = (pos_agent - getOrigin()).magVec();
00596
00597 LLCoordGL screen_point;
00598 if (!projectPosAgentToScreen(pos_agent, screen_point, FALSE))
00599 {
00600
00601 return pos_agent;
00602 }
00603
00604 LLVector3 ray_dir;
00605
00606 projectScreenToPosAgent(screen_point.mX, screen_point.mY, &ray_dir);
00607 ray_dir -= getOrigin();
00608 ray_dir.normVec();
00609
00610 LLVector3 pos_agent_rounded = getOrigin() + ray_dir*dist;
00611
00612
00613
00614
00615
00616
00617 return pos_agent_rounded;
00618 }
00619
00620 BOOL LLViewerCamera::cameraUnderWater() const
00621 {
00622 return getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight();
00623 }
00624
00625 BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts)
00626 {
00627 S32 i, num_faces;
00628 LLDrawable* drawablep = volumep->mDrawable;
00629
00630 if (!drawablep)
00631 {
00632 return FALSE;
00633 }
00634
00635 LLVolume* volume = volumep->getVolume();
00636 if (!volume)
00637 {
00638 return FALSE;
00639 }
00640
00641 LLVOVolume* vo_volume = (LLVOVolume*) volumep;
00642
00643 vo_volume->updateRelativeXform();
00644 LLMatrix4 mat = vo_volume->getRelativeXform();
00645
00646 LLMatrix4 render_mat(vo_volume->getRenderRotation(), LLVector4(vo_volume->getRenderPosition()));
00647
00648 num_faces = volume->getNumVolumeFaces();
00649 for (i = 0; i < num_faces; i++)
00650 {
00651 const LLVolumeFace& face = volume->getVolumeFace(i);
00652
00653 for (U32 v = 0; v < face.mVertices.size(); v++)
00654 {
00655 LLVector4 vec = LLVector4(face.mVertices[v].mPosition) * mat;
00656
00657 if (drawablep->isActive())
00658 {
00659 vec = vec * render_mat;
00660 }
00661
00662 BOOL in_frustum = pointInFrustum(LLVector3(vec)) > 0;
00663
00664 if ( !in_frustum && all_verts ||
00665 in_frustum && !all_verts)
00666 {
00667 return !all_verts;
00668 }
00669 }
00670 }
00671 return all_verts;
00672 }
00673
00674
00675 void LLViewerCamera::updateStereoValues()
00676 {
00677
00678 mCameraTempPosition = this->getOrigin();
00679
00680
00681 mStereoLastPOI = mLastPointOfInterest;
00682 }
00683
00684 void LLViewerCamera::rotateToLeftEye()
00685 {
00686
00687 F32 eye_separation = gSavedSettings.getF32("StereoEyeSeparation");
00688 F32 focal_distance = gSavedSettings.getF32("StereoFocalDistance");
00689
00690
00691 LLVector3 new_pos = mCameraTempPosition + eye_separation/2 * (this->getLeftAxis());
00692
00693
00694 LLVector3 dir = mStereoLastPOI - mCameraTempPosition;
00695 dir.normVec();
00696 LLVector3 new_poi = mCameraTempPosition + dir * focal_distance;
00697
00698
00699 this->updateCameraLocation(new_pos, getUpAxis(), new_poi);
00700 }
00701
00702 void LLViewerCamera::rotateToRightEye()
00703 {
00704
00705 F32 eye_separation = gSavedSettings.getF32("StereoEyeSeparation");
00706 F32 focal_distance = gSavedSettings.getF32("StereoFocalDistance");
00707
00708
00709 LLVector3 new_pos = mCameraTempPosition - eye_separation/2 * (this->getLeftAxis());
00710
00711
00712 LLVector3 dir = mStereoLastPOI - mCameraTempPosition;
00713 dir.normVec();
00714 LLVector3 new_poi = mCameraTempPosition + dir * focal_distance;
00715
00716
00717 this->updateCameraLocation(new_pos, getUpAxis(), new_poi);
00718 }
00719
00720
00721