summaryrefslogtreecommitdiff
path: root/indra/newview/llviewercamera.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llviewercamera.cpp
Print done when done.
Diffstat (limited to 'indra/newview/llviewercamera.cpp')
-rw-r--r--indra/newview/llviewercamera.cpp611
1 files changed, 611 insertions, 0 deletions
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
new file mode 100644
index 0000000000..c4c25b33f4
--- /dev/null
+++ b/indra/newview/llviewercamera.cpp
@@ -0,0 +1,611 @@
+/**
+ * @file llviewercamera.cpp
+ * @brief LLViewerCamera class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <iomanip> // for setprecision
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewercamera.h"
+
+#include "llquaternion.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvovolume.h"
+#include "llworld.h"
+
+BOOL LLViewerCamera::sDisableCameraConstraints = FALSE;
+
+LLViewerCamera *gCamera = NULL;
+
+LLViewerCamera::LLViewerCamera() : LLCamera()
+{
+ calcProjection(getFar());
+ S32 i;
+ for (i = 0; i < 16; i++)
+ {
+ mGLProjectionMatrix[i] = 0.f;
+ }
+ mCameraFOVDefault = DEFAULT_FIELD_OF_VIEW;
+ mPixelMeterRatio = 0.f;
+ mScreenPixelArea = 0;
+ mZoomFactor = 1.f;
+ mZoomSubregion = 1;
+}
+
+void LLViewerCamera::updateCameraLocation(const LLVector3 &center,
+ const LLVector3 &up_direction,
+ const LLVector3 &point_of_interest)
+{
+ LLVector3 last_position;
+ LLVector3 last_axis;
+ last_position = getOrigin();
+ last_axis = getAtAxis();
+
+ mLastPointOfInterest = point_of_interest;
+
+ // constrain to max distance from avatar
+ LLVector3 camera_offset = center - gAgent.getPositionAgent();
+
+ setOriginAndLookAt(center, up_direction, point_of_interest);
+
+ F32 dpos = (center - last_position).magVec();
+ LLQuaternion rotation;
+ rotation.shortestArc(last_axis, getAtAxis());
+
+ F32 x, y, z;
+ F32 drot;
+ rotation.getAngleAxis(&drot, &x, &y, &z);
+ mVelocityStat.addValue(dpos);
+ mAngularVelocityStat.addValue(drot);
+ // update pixel meter ratio using default fov, not modified one
+ mPixelMeterRatio = mViewHeightInPixels / (2.f*tanf(mCameraFOVDefault*0.5));
+ // update screen pixel area
+ mScreenPixelArea =(S32)((F32)mViewHeightInPixels * ((F32)mViewHeightInPixels * mAspect));
+}
+
+// Handy copies of last good GL matrices
+F64 gGLModelView[16];
+S32 gGLViewport[4];
+
+const LLMatrix4 &LLViewerCamera::getProjection() const
+{
+ calcProjection(getFar());
+ return mProjectionMatrix;
+
+}
+
+const LLMatrix4 &LLViewerCamera::getModelview() const
+{
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ getMatrixToLocal(mModelviewMatrix);
+ mModelviewMatrix *= cfr;
+ return mModelviewMatrix;
+}
+
+void LLViewerCamera::calcProjection(const F32 far_distance) const
+{
+ F32 fov_y, z_far, z_near, aspect, f;
+ fov_y = getView();
+ z_far = far_distance;
+ z_near = getNear();
+ aspect = getAspect();
+
+ f = 1/tan(fov_y*0.5f);
+
+ mProjectionMatrix.zero();
+ mProjectionMatrix.mMatrix[0][0] = f/aspect;
+ mProjectionMatrix.mMatrix[1][1] = f;
+ mProjectionMatrix.mMatrix[2][2] = (z_far + z_near)/(z_near - z_far);
+ mProjectionMatrix.mMatrix[3][2] = (2*z_far*z_near)/(z_near - z_far);
+ mProjectionMatrix.mMatrix[2][3] = -1;
+}
+
+// Sets up opengl state for 3D drawing. If for selection, also
+// sets up a pick matrix. x and y are ignored if for_selection is false.
+// The picking region is centered on x,y and has the specified width and
+// height.
+
+LLMatrix4 gProjectionMat;
+
+void LLViewerCamera::updateFrustumPlanes()
+{
+ GLint viewport[4];
+ GLdouble model[16];
+ GLdouble proj[16];
+ GLdouble objX,objY,objZ;
+
+ LLVector3 frust[8];
+
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ glGetDoublev(GL_MODELVIEW_MATRIX, model);
+ glGetDoublev(GL_PROJECTION_MATRIX,proj);
+
+ gluUnProject(viewport[0],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[0].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[1].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[2].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[3].setVec((F32)objX,(F32)objY,(F32)objZ);
+ /*gluUnProject(viewport[0],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[4].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[5].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[6].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[7].setVec((F32)objX,(F32)objY,(F32)objZ);*/
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ LLVector3 vec = frust[i] - getOrigin();
+ vec.normVec();
+ frust[i+4] = getOrigin() + vec*getFar()*2.0;
+ }
+
+ calcAgentFrustumPlanes(frust);
+}
+
+void LLViewerCamera::setPerspective(BOOL for_selection,
+ S32 x, S32 y_from_bot, S32 width, S32 height,
+ BOOL limit_select_distance,
+ F32 z_near, F32 z_far)
+{
+ F32 fov_y, aspect;
+ fov_y = RAD_TO_DEG * getView();
+ BOOL z_default_near, z_default_far = FALSE;
+ if (z_far <= 0)
+ {
+ z_default_far = TRUE;
+ z_far = getFar();
+ }
+ if (z_near <= 0)
+ {
+ z_default_near = TRUE;
+ z_near = getNear();
+ }
+ aspect = getAspect();
+
+ // Load camera view matrix
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+
+ if (for_selection)
+ {
+ // make a tiny little viewport
+ // anything drawn into this viewport will be "selected"
+ const U8 VIEWPORT_VECTOR_LEN = 4;
+ GLint viewport[VIEWPORT_VECTOR_LEN];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ gluPickMatrix(x + width / 2, y_from_bot + height / 2, width, height, viewport);
+
+ if (limit_select_distance)
+ {
+ // ...select distance from control
+ z_far = gSavedSettings.getF32("MaxSelectDistance");
+ }
+ else
+ {
+ z_far = gAgent.mDrawDistance;
+ }
+ }
+ else
+ {
+ // Only override the far clip if it's not passed in explicitly.
+ if (z_default_far)
+ {
+ z_far = MAX_FAR_CLIP;
+ }
+ glViewport(x, y_from_bot, width, height);
+ }
+
+ if (mZoomFactor > 1.f)
+ {
+ float offset = mZoomFactor - 1.f;
+ int pos_y = mZoomSubregion / llceil(mZoomFactor);
+ int pos_x = mZoomSubregion - (pos_y*llceil(mZoomFactor));
+ glTranslatef(offset - (F32)pos_x * 2.f, offset - (F32)pos_y * 2.f, 0.f);
+ glScalef(mZoomFactor, mZoomFactor, 1.f);
+ }
+
+ calcProjection(z_far); // Update the projection matrix cache
+
+ gluPerspective(fov_y,
+ aspect,
+ z_near,
+ z_far);
+ glGetFloatv(GL_PROJECTION_MATRIX, (float*)&gProjectionMat);
+
+ glMatrixMode( GL_MODELVIEW );
+
+ glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
+
+ GLfloat ogl_matrix[16];
+ getOpenGLTransform(ogl_matrix);
+ glMultMatrixf(ogl_matrix);
+
+ if (for_selection && (width > 1 || height > 1))
+ {
+ calculateFrustumPlanesFromWindow((F32)(x - width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
+ (F32)(y_from_bot - height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f,
+ (F32)(x + width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
+ (F32)(y_from_bot + height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f);
+
+ }
+
+ // if not picking and not doing a snapshot, cache various GL matrices
+ if (!for_selection && mZoomFactor == 1.f)
+ {
+ // Save GL matrices for access elsewhere in code, especially project_world_to_screen
+ glGetDoublev(GL_PROJECTION_MATRIX, mGLProjectionMatrix);
+ glGetDoublev(GL_MODELVIEW_MATRIX, gGLModelView);
+ glGetIntegerv(GL_VIEWPORT, (GLint*)gGLViewport);
+ }
+
+ updateFrustumPlanes();
+
+ if (gSavedSettings.getBOOL("CameraOffset"))
+ {
+ glMatrixMode(GL_PROJECTION);
+ glTranslatef(0,0,-50);
+ glRotatef(20.0,1,0,0);
+ glMatrixMode(GL_MODELVIEW);
+ }
+}
+
+
+// Uses the last GL matrices set in set_perspective to project a point from
+// screen coordinates to the agent's region.
+void LLViewerCamera::projectScreenToPosAgent(const S32 screen_x, const S32 screen_y, LLVector3* pos_agent) const
+{
+
+ GLdouble x, y, z;
+ gluUnProject(
+ GLdouble(screen_x), GLdouble(screen_y), 0.0,
+ gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
+ &x,
+ &y,
+ &z );
+ pos_agent->setVec( (F32)x, (F32)y, (F32)z );
+}
+
+// Uses the last GL matrices set in set_perspective to project a point from
+// the agent's region space to screen coordinates. Returns TRUE if point in within
+// the current window.
+BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp) const
+{
+ BOOL in_front = TRUE;
+ GLdouble x, y, z; // object's window coords, GL-style
+
+ LLVector3 dir_to_point = pos_agent - getOrigin();
+ dir_to_point /= dir_to_point.magVec();
+
+ if (dir_to_point * getAtAxis() < 0.f)
+ {
+ if (clamp)
+ {
+ return FALSE;
+ }
+ else
+ {
+ in_front = FALSE;
+ }
+ }
+
+ if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ],
+ gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
+ &x, &y, &z))
+ {
+ // convert screen coordinates to virtual UI coordinates
+ x /= gViewerWindow->getDisplayScale().mV[VX];
+ y /= gViewerWindow->getDisplayScale().mV[VY];
+
+ // should now have the x,y coords of grab_point in screen space
+ const LLRect& window_rect = gViewerWindow->getWindowRect();
+
+ // ...sanity check
+ S32 int_x = lltrunc(x);
+ S32 int_y = lltrunc(y);
+
+ BOOL valid = TRUE;
+
+ if (clamp)
+ {
+ if (int_x < window_rect.mLeft)
+ {
+ out_point.mX = window_rect.mLeft;
+ valid = FALSE;
+ }
+ else if (int_x > window_rect.mRight)
+ {
+ out_point.mX = window_rect.mRight;
+ valid = FALSE;
+ }
+ else
+ {
+ out_point.mX = int_x;
+ }
+
+ if (int_y < window_rect.mBottom)
+ {
+ out_point.mY = window_rect.mBottom;
+ valid = FALSE;
+ }
+ else if (int_y > window_rect.mTop)
+ {
+ out_point.mY = window_rect.mTop;
+ valid = FALSE;
+ }
+ else
+ {
+ out_point.mY = int_y;
+ }
+ return valid;
+ }
+ else
+ {
+ out_point.mX = int_x;
+ out_point.mY = int_y;
+
+ if (int_x < window_rect.mLeft)
+ {
+ valid = FALSE;
+ }
+ else if (int_x > window_rect.mRight)
+ {
+ valid = FALSE;
+ }
+ if (int_y < window_rect.mBottom)
+ {
+ valid = FALSE;
+ }
+ else if (int_y > window_rect.mTop)
+ {
+ valid = FALSE;
+ }
+
+ return in_front && valid;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// Uses the last GL matrices set in set_perspective to project a point from
+// the agent's region space to the nearest edge in screen coordinates.
+// Returns TRUE if projection succeeds.
+BOOL LLViewerCamera::projectPosAgentToScreenEdge(const LLVector3 &pos_agent,
+ LLCoordGL &out_point) const
+{
+ LLVector3 dir_to_point = pos_agent - getOrigin();
+ dir_to_point /= dir_to_point.magVec();
+
+ BOOL in_front = TRUE;
+ if (dir_to_point * getAtAxis() < 0.f)
+ {
+ in_front = FALSE;
+ }
+
+ GLdouble x, y, z; // object's window coords, GL-style
+ if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY],
+ pos_agent.mV[VZ], gGLModelView,
+ mGLProjectionMatrix, (GLint*)gGLViewport,
+ &x, &y, &z))
+ {
+ x /= gViewerWindow->getDisplayScale().mV[VX];
+ y /= gViewerWindow->getDisplayScale().mV[VY];
+ // should now have the x,y coords of grab_point in screen space
+ const LLRect& window_rect = gViewerWindow->getVirtualWindowRect();
+
+ // ...sanity check
+ S32 int_x = lltrunc(x);
+ S32 int_y = lltrunc(y);
+
+ // find the center
+ GLdouble center_x = (GLdouble)(0.5f * (window_rect.mLeft + window_rect.mRight));
+ GLdouble center_y = (GLdouble)(0.5f * (window_rect.mBottom + window_rect.mTop));
+
+ if (x == center_x && y == center_y)
+ {
+ // can't project to edge from exact center
+ return FALSE;
+ }
+
+ // find the line from center to local
+ GLdouble line_x = x - center_x;
+ GLdouble line_y = y - center_y;
+
+ int_x = lltrunc(center_x);
+ int_y = lltrunc(center_y);
+
+
+ if (0.f == line_x)
+ {
+ // the slope of the line is undefined
+ if (line_y > 0.f)
+ {
+ int_y = window_rect.mTop;
+ }
+ else
+ {
+ int_y = window_rect.mBottom;
+ }
+ }
+ else if (0 == window_rect.getWidth())
+ {
+ // the diagonal slope of the view is undefined
+ if (y < window_rect.mBottom)
+ {
+ int_y = window_rect.mBottom;
+ }
+ else if ( y > window_rect.mTop)
+ {
+ int_y = window_rect.mTop;
+ }
+ }
+ else
+ {
+ F32 line_slope = (F32)(line_y / line_x);
+ F32 rect_slope = ((F32)window_rect.getHeight()) / ((F32)window_rect.getWidth());
+
+ if (fabs(line_slope) > rect_slope)
+ {
+ if (line_y < 0.f)
+ {
+ // bottom
+ int_y = window_rect.mBottom;
+ }
+ else
+ {
+ // top
+ int_y = window_rect.mTop;
+ }
+ int_x = lltrunc(((GLdouble)int_y - center_y) / line_slope + center_x);
+ }
+ else if (fabs(line_slope) < rect_slope)
+ {
+ if (line_x < 0.f)
+ {
+ // left
+ int_x = window_rect.mLeft;
+ }
+ else
+ {
+ // right
+ int_x = window_rect.mRight;
+ }
+ int_y = lltrunc(((GLdouble)int_x - center_x) * line_slope + center_y);
+ }
+ else
+ {
+ // exactly parallel ==> push to the corners
+ if (line_x > 0.f)
+ {
+ int_x = window_rect.mRight;
+ }
+ else
+ {
+ int_x = window_rect.mLeft;
+ }
+ if (line_y > 0.0f)
+ {
+ int_y = window_rect.mTop;
+ }
+ else
+ {
+ int_y = window_rect.mBottom;
+ }
+ }
+ }
+ if (!in_front)
+ {
+ int_x = window_rect.mLeft + window_rect.mRight - int_x;
+ int_y = window_rect.mBottom + window_rect.mTop - int_y;
+ }
+ out_point.mX = int_x;
+ out_point.mY = int_y;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void LLViewerCamera::getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right)
+{
+ LLVector3 to_vec = pos_agent - getOrigin();
+
+ F32 at_dist = to_vec * getAtAxis();
+
+ F32 height_meters = at_dist* (F32)tan(getView()/2.f);
+ F32 height_pixels = getViewHeightInPixels()/2.f;
+
+ F32 pixel_aspect = gViewerWindow->getWindow()->getPixelAspectRatio();
+
+ F32 meters_per_pixel = height_meters / height_pixels;
+ up = getUpAxis() * meters_per_pixel * gViewerWindow->getDisplayScale().mV[VY];
+ right = -1.f * pixel_aspect * meters_per_pixel * getLeftAxis() * gViewerWindow->getDisplayScale().mV[VX];
+}
+
+LLVector3 LLViewerCamera::roundToPixel(const LLVector3 &pos_agent)
+{
+ F32 dist = (pos_agent - getOrigin()).magVec();
+ // Convert to screen space and back, preserving the depth.
+ LLCoordGL screen_point;
+ if (!projectPosAgentToScreen(pos_agent, screen_point, FALSE))
+ {
+ // Off the screen, just return the original position.
+ return pos_agent;
+ }
+
+ LLVector3 ray_dir;
+
+ projectScreenToPosAgent(screen_point.mX, screen_point.mY, &ray_dir);
+ ray_dir -= getOrigin();
+ ray_dir.normVec();
+
+ LLVector3 pos_agent_rounded = getOrigin() + ray_dir*dist;
+
+ /*
+ LLVector3 pixel_x, pixel_y;
+ getPixelVectors(pos_agent_rounded, pixel_y, pixel_x);
+ pos_agent_rounded += 0.5f*pixel_x, 0.5f*pixel_y;
+ */
+ return pos_agent_rounded;
+}
+
+BOOL LLViewerCamera::cameraUnderWater() const
+{
+ return getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight();
+}
+
+BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts)
+{
+ S32 i, num_faces;
+ LLDrawable* drawablep = volumep->mDrawable;
+
+ if (!drawablep)
+ {
+ return FALSE;
+ }
+
+ num_faces = drawablep->getNumFaces();
+ for (i = 0; i < num_faces; i++)
+ {
+ LLStrider<LLVector3> vertices;
+ LLFace* face = drawablep->getFace(i);
+ face->getVertices(vertices);
+
+ for (S32 v = 0; v < (S32)drawablep->getFace(i)->getGeomCount(); v++)
+ {
+ LLVector3 vec = vertices[v];
+ if (!face->isState(LLFace::GLOBAL))
+ {
+ vec = vec*face->getRenderMatrix();
+ }
+
+ BOOL in_frustum = pointInFrustum(vec) > 0;
+
+ if ( !in_frustum && all_verts ||
+ in_frustum && !all_verts)
+ {
+ return !all_verts;
+ }
+ }
+ }
+ return all_verts;
+}