aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llviewercamera.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llviewercamera.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/llviewercamera.cpp630
1 files changed, 630 insertions, 0 deletions
diff --git a/linden/indra/newview/llviewercamera.cpp b/linden/indra/newview/llviewercamera.cpp
new file mode 100644
index 0000000..62fa0ef
--- /dev/null
+++ b/linden/indra/newview/llviewercamera.cpp
@@ -0,0 +1,630 @@
1/**
2 * @file llviewercamera.cpp
3 * @brief LLViewerCamera class implementation
4 *
5 * Copyright (c) 2002-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 <iomanip> // for setprecision
29#include "llviewerprecompiledheaders.h"
30
31#include "llviewercamera.h"
32
33#include "llquaternion.h"
34
35#include "llagent.h"
36#include "llviewercontrol.h"
37#include "lldrawable.h"
38#include "llface.h"
39#include "llgl.h"
40#include "llglheaders.h"
41#include "llviewerobjectlist.h"
42#include "llviewerregion.h"
43#include "llviewerwindow.h"
44#include "llvovolume.h"
45#include "llworld.h"
46
47BOOL LLViewerCamera::sDisableCameraConstraints = FALSE;
48
49LLViewerCamera *gCamera = NULL;
50
51LLViewerCamera::LLViewerCamera() : LLCamera()
52{
53 calcProjection(getFar());
54 S32 i;
55 for (i = 0; i < 16; i++)
56 {
57 mGLProjectionMatrix[i] = 0.f;
58 }
59 mCameraFOVDefault = DEFAULT_FIELD_OF_VIEW;
60 mPixelMeterRatio = 0.f;
61 mScreenPixelArea = 0;
62 mZoomFactor = 1.f;
63 mZoomSubregion = 1;
64}
65
66void LLViewerCamera::updateCameraLocation(const LLVector3 &center,
67 const LLVector3 &up_direction,
68 const LLVector3 &point_of_interest)
69{
70 LLVector3 last_position;
71 LLVector3 last_axis;
72 last_position = getOrigin();
73 last_axis = getAtAxis();
74
75 mLastPointOfInterest = point_of_interest;
76
77 // constrain to max distance from avatar
78 LLVector3 camera_offset = center - gAgent.getPositionAgent();
79
80 setOriginAndLookAt(center, up_direction, point_of_interest);
81
82 F32 dpos = (center - last_position).magVec();
83 LLQuaternion rotation;
84 rotation.shortestArc(last_axis, getAtAxis());
85
86 F32 x, y, z;
87 F32 drot;
88 rotation.getAngleAxis(&drot, &x, &y, &z);
89 mVelocityStat.addValue(dpos);
90 mAngularVelocityStat.addValue(drot);
91 // update pixel meter ratio using default fov, not modified one
92 mPixelMeterRatio = mViewHeightInPixels / (2.f*tanf(mCameraFOVDefault*0.5));
93 // update screen pixel area
94 mScreenPixelArea =(S32)((F32)mViewHeightInPixels * ((F32)mViewHeightInPixels * mAspect));
95}
96
97// Handy copies of last good GL matrices
98F64 gGLModelView[16];
99S32 gGLViewport[4];
100
101const LLMatrix4 &LLViewerCamera::getProjection() const
102{
103 calcProjection(getFar());
104 return mProjectionMatrix;
105
106}
107
108const LLMatrix4 &LLViewerCamera::getModelview() const
109{
110 LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
111 getMatrixToLocal(mModelviewMatrix);
112 mModelviewMatrix *= cfr;
113 return mModelviewMatrix;
114}
115
116void LLViewerCamera::calcProjection(const F32 far_distance) const
117{
118 F32 fov_y, z_far, z_near, aspect, f;
119 fov_y = getView();
120 z_far = far_distance;
121 z_near = getNear();
122 aspect = getAspect();
123
124 f = 1/tan(fov_y*0.5f);
125
126 mProjectionMatrix.zero();
127 mProjectionMatrix.mMatrix[0][0] = f/aspect;
128 mProjectionMatrix.mMatrix[1][1] = f;
129 mProjectionMatrix.mMatrix[2][2] = (z_far + z_near)/(z_near - z_far);
130 mProjectionMatrix.mMatrix[3][2] = (2*z_far*z_near)/(z_near - z_far);
131 mProjectionMatrix.mMatrix[2][3] = -1;
132}
133
134// Sets up opengl state for 3D drawing. If for selection, also
135// sets up a pick matrix. x and y are ignored if for_selection is false.
136// The picking region is centered on x,y and has the specified width and
137// height.
138
139LLMatrix4 gProjectionMat;
140
141void LLViewerCamera::updateFrustumPlanes()
142{
143 GLint viewport[4];
144 GLdouble model[16];
145 GLdouble proj[16];
146 GLdouble objX,objY,objZ;
147
148 LLVector3 frust[8];
149
150 glGetIntegerv(GL_VIEWPORT, viewport);
151 glGetDoublev(GL_MODELVIEW_MATRIX, model);
152 glGetDoublev(GL_PROJECTION_MATRIX,proj);
153
154 gluUnProject(viewport[0],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
155 frust[0].setVec((F32)objX,(F32)objY,(F32)objZ);
156 gluUnProject(viewport[0]+viewport[2],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
157 frust[1].setVec((F32)objX,(F32)objY,(F32)objZ);
158 gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
159 frust[2].setVec((F32)objX,(F32)objY,(F32)objZ);
160 gluUnProject(viewport[0],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
161 frust[3].setVec((F32)objX,(F32)objY,(F32)objZ);
162 /*gluUnProject(viewport[0],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
163 frust[4].setVec((F32)objX,(F32)objY,(F32)objZ);
164 gluUnProject(viewport[0]+viewport[2],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
165 frust[5].setVec((F32)objX,(F32)objY,(F32)objZ);
166 gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
167 frust[6].setVec((F32)objX,(F32)objY,(F32)objZ);
168 gluUnProject(viewport[0],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
169 frust[7].setVec((F32)objX,(F32)objY,(F32)objZ);*/
170
171 for (U32 i = 0; i < 4; i++)
172 {
173 LLVector3 vec = frust[i] - getOrigin();
174 vec.normVec();
175 frust[i+4] = getOrigin() + vec*getFar()*2.0;
176 }
177
178 calcAgentFrustumPlanes(frust);
179}
180
181void LLViewerCamera::setPerspective(BOOL for_selection,
182 S32 x, S32 y_from_bot, S32 width, S32 height,
183 BOOL limit_select_distance,
184 F32 z_near, F32 z_far)
185{
186 F32 fov_y, aspect;
187 fov_y = RAD_TO_DEG * getView();
188 BOOL z_default_near, z_default_far = FALSE;
189 if (z_far <= 0)
190 {
191 z_default_far = TRUE;
192 z_far = getFar();
193 }
194 if (z_near <= 0)
195 {
196 z_default_near = TRUE;
197 z_near = getNear();
198 }
199 aspect = getAspect();
200
201 // Load camera view matrix
202 glMatrixMode( GL_PROJECTION );
203 glLoadIdentity();
204
205 if (for_selection)
206 {
207 // make a tiny little viewport
208 // anything drawn into this viewport will be "selected"
209 const U8 VIEWPORT_VECTOR_LEN = 4;
210 GLint viewport[VIEWPORT_VECTOR_LEN];
211 glGetIntegerv(GL_VIEWPORT, viewport);
212 gluPickMatrix(x + width / 2, y_from_bot + height / 2, width, height, viewport);
213
214 if (limit_select_distance)
215 {
216 // ...select distance from control
217 z_far = gSavedSettings.getF32("MaxSelectDistance");
218 }
219 else
220 {
221 z_far = gAgent.mDrawDistance;
222 }
223 }
224 else
225 {
226 // Only override the far clip if it's not passed in explicitly.
227 if (z_default_far)
228 {
229 z_far = MAX_FAR_CLIP;
230 }
231 glViewport(x, y_from_bot, width, height);
232 }
233
234 if (mZoomFactor > 1.f)
235 {
236 float offset = mZoomFactor - 1.f;
237 int pos_y = mZoomSubregion / llceil(mZoomFactor);
238 int pos_x = mZoomSubregion - (pos_y*llceil(mZoomFactor));
239 glTranslatef(offset - (F32)pos_x * 2.f, offset - (F32)pos_y * 2.f, 0.f);
240 glScalef(mZoomFactor, mZoomFactor, 1.f);
241 }
242
243 calcProjection(z_far); // Update the projection matrix cache
244
245 gluPerspective(fov_y,
246 aspect,
247 z_near,
248 z_far);
249 glGetFloatv(GL_PROJECTION_MATRIX, (float*)&gProjectionMat);
250
251 glMatrixMode( GL_MODELVIEW );
252
253 glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
254
255 GLfloat ogl_matrix[16];
256 getOpenGLTransform(ogl_matrix);
257 glMultMatrixf(ogl_matrix);
258
259 if (for_selection && (width > 1 || height > 1))
260 {
261 calculateFrustumPlanesFromWindow((F32)(x - width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
262 (F32)(y_from_bot - height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f,
263 (F32)(x + width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
264 (F32)(y_from_bot + height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f);
265
266 }
267
268 // if not picking and not doing a snapshot, cache various GL matrices
269 if (!for_selection && mZoomFactor == 1.f)
270 {
271 // Save GL matrices for access elsewhere in code, especially project_world_to_screen
272 glGetDoublev(GL_PROJECTION_MATRIX, mGLProjectionMatrix);
273 glGetDoublev(GL_MODELVIEW_MATRIX, gGLModelView);
274 glGetIntegerv(GL_VIEWPORT, (GLint*)gGLViewport);
275 }
276
277 updateFrustumPlanes();
278
279 if (gSavedSettings.getBOOL("CameraOffset"))
280 {
281 glMatrixMode(GL_PROJECTION);
282 glTranslatef(0,0,-50);
283 glRotatef(20.0,1,0,0);
284 glMatrixMode(GL_MODELVIEW);
285 }
286}
287
288
289// Uses the last GL matrices set in set_perspective to project a point from
290// screen coordinates to the agent's region.
291void LLViewerCamera::projectScreenToPosAgent(const S32 screen_x, const S32 screen_y, LLVector3* pos_agent) const
292{
293
294 GLdouble x, y, z;
295 gluUnProject(
296 GLdouble(screen_x), GLdouble(screen_y), 0.0,
297 gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
298 &x,
299 &y,
300 &z );
301 pos_agent->setVec( (F32)x, (F32)y, (F32)z );
302}
303
304// Uses the last GL matrices set in set_perspective to project a point from
305// the agent's region space to screen coordinates. Returns TRUE if point in within
306// the current window.
307BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp) const
308{
309 BOOL in_front = TRUE;
310 GLdouble x, y, z; // object's window coords, GL-style
311
312 LLVector3 dir_to_point = pos_agent - getOrigin();
313 dir_to_point /= dir_to_point.magVec();
314
315 if (dir_to_point * getAtAxis() < 0.f)
316 {
317 if (clamp)
318 {
319 return FALSE;
320 }
321 else
322 {
323 in_front = FALSE;
324 }
325 }
326
327 if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ],
328 gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
329 &x, &y, &z))
330 {
331 // convert screen coordinates to virtual UI coordinates
332 x /= gViewerWindow->getDisplayScale().mV[VX];
333 y /= gViewerWindow->getDisplayScale().mV[VY];
334
335 // should now have the x,y coords of grab_point in screen space
336 const LLRect& window_rect = gViewerWindow->getWindowRect();
337
338 // ...sanity check
339 S32 int_x = lltrunc(x);
340 S32 int_y = lltrunc(y);
341
342 BOOL valid = TRUE;
343
344 if (clamp)
345 {
346 if (int_x < window_rect.mLeft)
347 {
348 out_point.mX = window_rect.mLeft;
349 valid = FALSE;
350 }
351 else if (int_x > window_rect.mRight)
352 {
353 out_point.mX = window_rect.mRight;
354 valid = FALSE;
355 }
356 else
357 {
358 out_point.mX = int_x;
359 }
360
361 if (int_y < window_rect.mBottom)
362 {
363 out_point.mY = window_rect.mBottom;
364 valid = FALSE;
365 }
366 else if (int_y > window_rect.mTop)
367 {
368 out_point.mY = window_rect.mTop;
369 valid = FALSE;
370 }
371 else
372 {
373 out_point.mY = int_y;
374 }
375 return valid;
376 }
377 else
378 {
379 out_point.mX = int_x;
380 out_point.mY = int_y;
381
382 if (int_x < window_rect.mLeft)
383 {
384 valid = FALSE;
385 }
386 else if (int_x > window_rect.mRight)
387 {
388 valid = FALSE;
389 }
390 if (int_y < window_rect.mBottom)
391 {
392 valid = FALSE;
393 }
394 else if (int_y > window_rect.mTop)
395 {
396 valid = FALSE;
397 }
398
399 return in_front && valid;
400 }
401 }
402 else
403 {
404 return FALSE;
405 }
406}
407
408// Uses the last GL matrices set in set_perspective to project a point from
409// the agent's region space to the nearest edge in screen coordinates.
410// Returns TRUE if projection succeeds.
411BOOL LLViewerCamera::projectPosAgentToScreenEdge(const LLVector3 &pos_agent,
412 LLCoordGL &out_point) const
413{
414 LLVector3 dir_to_point = pos_agent - getOrigin();
415 dir_to_point /= dir_to_point.magVec();
416
417 BOOL in_front = TRUE;
418 if (dir_to_point * getAtAxis() < 0.f)
419 {
420 in_front = FALSE;
421 }
422
423 GLdouble x, y, z; // object's window coords, GL-style
424 if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY],
425 pos_agent.mV[VZ], gGLModelView,
426 mGLProjectionMatrix, (GLint*)gGLViewport,
427 &x, &y, &z))
428 {
429 x /= gViewerWindow->getDisplayScale().mV[VX];
430 y /= gViewerWindow->getDisplayScale().mV[VY];
431 // should now have the x,y coords of grab_point in screen space
432 const LLRect& window_rect = gViewerWindow->getVirtualWindowRect();
433
434 // ...sanity check
435 S32 int_x = lltrunc(x);
436 S32 int_y = lltrunc(y);
437
438 // find the center
439 GLdouble center_x = (GLdouble)(0.5f * (window_rect.mLeft + window_rect.mRight));
440 GLdouble center_y = (GLdouble)(0.5f * (window_rect.mBottom + window_rect.mTop));
441
442 if (x == center_x && y == center_y)
443 {
444 // can't project to edge from exact center
445 return FALSE;
446 }
447
448 // find the line from center to local
449 GLdouble line_x = x - center_x;
450 GLdouble line_y = y - center_y;
451
452 int_x = lltrunc(center_x);
453 int_y = lltrunc(center_y);
454
455
456 if (0.f == line_x)
457 {
458 // the slope of the line is undefined
459 if (line_y > 0.f)
460 {
461 int_y = window_rect.mTop;
462 }
463 else
464 {
465 int_y = window_rect.mBottom;
466 }
467 }
468 else if (0 == window_rect.getWidth())
469 {
470 // the diagonal slope of the view is undefined
471 if (y < window_rect.mBottom)
472 {
473 int_y = window_rect.mBottom;
474 }
475 else if ( y > window_rect.mTop)
476 {
477 int_y = window_rect.mTop;
478 }
479 }
480 else
481 {
482 F32 line_slope = (F32)(line_y / line_x);
483 F32 rect_slope = ((F32)window_rect.getHeight()) / ((F32)window_rect.getWidth());
484
485 if (fabs(line_slope) > rect_slope)
486 {
487 if (line_y < 0.f)
488 {
489 // bottom
490 int_y = window_rect.mBottom;
491 }
492 else
493 {
494 // top
495 int_y = window_rect.mTop;
496 }
497 int_x = lltrunc(((GLdouble)int_y - center_y) / line_slope + center_x);
498 }
499 else if (fabs(line_slope) < rect_slope)
500 {
501 if (line_x < 0.f)
502 {
503 // left
504 int_x = window_rect.mLeft;
505 }
506 else
507 {
508 // right
509 int_x = window_rect.mRight;
510 }
511 int_y = lltrunc(((GLdouble)int_x - center_x) * line_slope + center_y);
512 }
513 else
514 {
515 // exactly parallel ==> push to the corners
516 if (line_x > 0.f)
517 {
518 int_x = window_rect.mRight;
519 }
520 else
521 {
522 int_x = window_rect.mLeft;
523 }
524 if (line_y > 0.0f)
525 {
526 int_y = window_rect.mTop;
527 }
528 else
529 {
530 int_y = window_rect.mBottom;
531 }
532 }
533 }
534 if (!in_front)
535 {
536 int_x = window_rect.mLeft + window_rect.mRight - int_x;
537 int_y = window_rect.mBottom + window_rect.mTop - int_y;
538 }
539 out_point.mX = int_x;
540 out_point.mY = int_y;
541 return TRUE;
542 }
543 return FALSE;
544}
545
546
547void LLViewerCamera::getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right)
548{
549 LLVector3 to_vec = pos_agent - getOrigin();
550
551 F32 at_dist = to_vec * getAtAxis();
552
553 F32 height_meters = at_dist* (F32)tan(getView()/2.f);
554 F32 height_pixels = getViewHeightInPixels()/2.f;
555
556 F32 pixel_aspect = gViewerWindow->getWindow()->getPixelAspectRatio();
557
558 F32 meters_per_pixel = height_meters / height_pixels;
559 up = getUpAxis() * meters_per_pixel * gViewerWindow->getDisplayScale().mV[VY];
560 right = -1.f * pixel_aspect * meters_per_pixel * getLeftAxis() * gViewerWindow->getDisplayScale().mV[VX];
561}
562
563LLVector3 LLViewerCamera::roundToPixel(const LLVector3 &pos_agent)
564{
565 F32 dist = (pos_agent - getOrigin()).magVec();
566 // Convert to screen space and back, preserving the depth.
567 LLCoordGL screen_point;
568 if (!projectPosAgentToScreen(pos_agent, screen_point, FALSE))
569 {
570 // Off the screen, just return the original position.
571 return pos_agent;
572 }
573
574 LLVector3 ray_dir;
575
576 projectScreenToPosAgent(screen_point.mX, screen_point.mY, &ray_dir);
577 ray_dir -= getOrigin();
578 ray_dir.normVec();
579
580 LLVector3 pos_agent_rounded = getOrigin() + ray_dir*dist;
581
582 /*
583 LLVector3 pixel_x, pixel_y;
584 getPixelVectors(pos_agent_rounded, pixel_y, pixel_x);
585 pos_agent_rounded += 0.5f*pixel_x, 0.5f*pixel_y;
586 */
587 return pos_agent_rounded;
588}
589
590BOOL LLViewerCamera::cameraUnderWater() const
591{
592 return getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight();
593}
594
595BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts)
596{
597 S32 i, num_faces;
598 LLDrawable* drawablep = volumep->mDrawable;
599
600 if (!drawablep)
601 {
602 return FALSE;
603 }
604
605 num_faces = drawablep->getNumFaces();
606 for (i = 0; i < num_faces; i++)
607 {
608 LLStrider<LLVector3> vertices;
609 LLFace* face = drawablep->getFace(i);
610 face->getVertices(vertices);
611
612 for (S32 v = 0; v < (S32)drawablep->getFace(i)->getGeomCount(); v++)
613 {
614 LLVector3 vec = vertices[v];
615 if (!face->isState(LLFace::GLOBAL))
616 {
617 vec = vec*face->getRenderMatrix();
618 }
619
620 BOOL in_frustum = pointInFrustum(vec) > 0;
621
622 if ( !in_frustum && all_verts ||
623 in_frustum && !all_verts)
624 {
625 return !all_verts;
626 }
627 }
628 }
629 return all_verts;
630}