PageRenderTime 74ms CodeModel.GetById 2ms app.highlight 64ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llviewercamera.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 892 lines | 670 code | 138 blank | 84 comment | 82 complexity | b82f18bd8a342b1700cd0ba4b2385adb MD5 | raw file
  1/** 
  2 * @file llviewercamera.cpp
  3 * @brief LLViewerCamera class implementation
  4 *
  5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#define LLVIEWERCAMERA_CPP
 30#include "llviewercamera.h"
 31
 32// Viewer includes
 33#include "llagent.h"
 34#include "llagentcamera.h"
 35#include "llmatrix4a.h"
 36#include "llviewercontrol.h"
 37#include "llviewerobjectlist.h"
 38#include "llviewerregion.h"
 39#include "llviewerwindow.h"
 40#include "llvovolume.h"
 41#include "llworld.h"
 42#include "lltoolmgr.h"
 43#include "llviewerjoystick.h"
 44
 45// Linden library includes
 46#include "lldrawable.h"
 47#include "llface.h"
 48#include "llgl.h"
 49#include "llglheaders.h"
 50#include "llquaternion.h"
 51#include "llwindow.h"			// getPixelAspectRatio()
 52
 53// System includes
 54#include <iomanip> // for setprecision
 55
 56U32 LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD;
 57
 58//glu pick matrix implementation borrowed from Mesa3D
 59glh::matrix4f gl_pick_matrix(GLfloat x, GLfloat y, GLfloat width, GLfloat height, GLint* viewport)
 60{
 61	GLfloat m[16];
 62	GLfloat sx, sy;
 63	GLfloat tx, ty;
 64
 65	sx = viewport[2] / width;
 66	sy = viewport[3] / height;
 67	tx = (viewport[2] + 2.f * (viewport[0] - x)) / width;
 68	ty = (viewport[3] + 2.f * (viewport[1] - y)) / height;
 69
 70	#define M(row,col) m[col*4+row]
 71	M(0,0) = sx; M(0,1) = 0.f; M(0,2) = 0.f; M(0,3) = tx;
 72	M(1,0) = 0.f; M(1,1) = sy; M(1,2) = 0.f; M(1,3) = ty;
 73	M(2,0) = 0.f; M(2,1) = 0.f; M(2,2) = 1.f; M(2,3) = 0.f;
 74	M(3,0) = 0.f; M(3,1) = 0.f; M(3,2) = 0.f; M(3,3) = 1.f;
 75	#undef M
 76
 77	return glh::matrix4f(m);
 78}
 79
 80glh::matrix4f gl_perspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
 81{
 82	GLfloat f = 1.f/tanf(DEG_TO_RAD*fovy/2.f);
 83
 84	return glh::matrix4f(f/aspect, 0, 0, 0,
 85						 0, f, 0, 0,
 86						 0, 0, (zFar+zNear)/(zNear-zFar), (2.f*zFar*zNear)/(zNear-zFar),
 87						 0, 0, -1.f, 0);
 88}
 89
 90glh::matrix4f gl_lookat(LLVector3 eye, LLVector3 center, LLVector3 up)
 91{
 92	LLVector3 f = center-eye;
 93	f.normVec();
 94	up.normVec();
 95	LLVector3 s = f % up;
 96	LLVector3 u = s % f;
 97
 98	return glh::matrix4f(s[0], s[1], s[2], 0,
 99					  u[0], u[1], u[2], 0,
100					  -f[0], -f[1], -f[2], 0,
101					  0, 0, 0, 1);
102	
103}
104
105// Build time optimization, generate this once in .cpp file
106template class LLViewerCamera* LLSingleton<class LLViewerCamera>::getInstance();
107
108LLViewerCamera::LLViewerCamera() : LLCamera()
109{
110	calcProjection(getFar());
111	mCameraFOVDefault = DEFAULT_FIELD_OF_VIEW;
112	mCosHalfCameraFOV = cosf(mCameraFOVDefault * 0.5f);
113	mPixelMeterRatio = 0.f;
114	mScreenPixelArea = 0;
115	mZoomFactor = 1.f;
116	mZoomSubregion = 1;
117	mAverageSpeed = 0.f;
118	mAverageAngularSpeed = 0.f;
119	gSavedSettings.getControl("CameraAngle")->getCommitSignal()->connect(boost::bind(&LLViewerCamera::updateCameraAngle, this, _2));
120}
121
122void LLViewerCamera::updateCameraLocation(const LLVector3 &center,
123											const LLVector3 &up_direction,
124											const LLVector3 &point_of_interest)
125{
126	// do not update if avatar didn't move
127	if (!LLViewerJoystick::getInstance()->getCameraNeedsUpdate())
128	{
129		return;
130	}
131
132	LLVector3 last_position;
133	LLVector3 last_axis;
134	last_position = getOrigin();
135	last_axis = getAtAxis();
136
137	mLastPointOfInterest = point_of_interest;
138
139	// constrain to max distance from avatar
140	LLVector3 camera_offset = center - gAgent.getPositionAgent();
141
142	LLViewerRegion * regp = gAgent.getRegion();
143	F32 water_height = (NULL != regp) ? regp->getWaterHeight() : 0.f;
144
145	LLVector3 origin = center;
146	if (origin.mV[2] > water_height)
147	{
148		origin.mV[2] = llmax(origin.mV[2], water_height+0.20f);
149	}
150	else
151	{
152		origin.mV[2] = llmin(origin.mV[2], water_height-0.20f);
153	}
154
155	setOriginAndLookAt(origin, up_direction, point_of_interest);
156
157	mVelocityDir = center - last_position ; 
158	F32 dpos = mVelocityDir.normVec() ;
159	LLQuaternion rotation;
160	rotation.shortestArc(last_axis, getAtAxis());
161
162	F32 x, y, z;
163	F32 drot;
164	rotation.getAngleAxis(&drot, &x, &y, &z);
165
166	mVelocityStat.addValue(dpos);
167	mAngularVelocityStat.addValue(drot);
168	
169	mAverageSpeed = mVelocityStat.getMeanPerSec() ;
170	mAverageAngularSpeed = mAngularVelocityStat.getMeanPerSec() ;
171	mCosHalfCameraFOV = cosf(0.5f * getView() * llmax(1.0f, getAspect()));
172
173	// update pixel meter ratio using default fov, not modified one
174	mPixelMeterRatio = getViewHeightInPixels()/ (2.f*tanf(mCameraFOVDefault*0.5));
175	// update screen pixel area
176	mScreenPixelArea =(S32)((F32)getViewHeightInPixels() * ((F32)getViewHeightInPixels() * getAspect()));
177}
178
179const LLMatrix4 &LLViewerCamera::getProjection() const
180{
181	calcProjection(getFar());
182	return mProjectionMatrix;
183
184}
185
186const LLMatrix4 &LLViewerCamera::getModelview() const
187{
188	LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
189	getMatrixToLocal(mModelviewMatrix);
190	mModelviewMatrix *= cfr;
191	return mModelviewMatrix;
192}
193
194void LLViewerCamera::calcProjection(const F32 far_distance) const
195{
196	F32 fov_y, z_far, z_near, aspect, f;
197	fov_y = getView();
198	z_far = far_distance;
199	z_near = getNear();
200	aspect = getAspect();
201
202	f = 1/tan(fov_y*0.5f);
203
204	mProjectionMatrix.setZero();
205	mProjectionMatrix.mMatrix[0][0] = f/aspect;
206	mProjectionMatrix.mMatrix[1][1] = f;
207	mProjectionMatrix.mMatrix[2][2] = (z_far + z_near)/(z_near - z_far);
208	mProjectionMatrix.mMatrix[3][2] = (2*z_far*z_near)/(z_near - z_far);
209	mProjectionMatrix.mMatrix[2][3] = -1;
210}
211
212// Sets up opengl state for 3D drawing.  If for selection, also
213// sets up a pick matrix.  x and y are ignored if for_selection is false.
214// The picking region is centered on x,y and has the specified width and
215// height.
216
217//static
218void LLViewerCamera::updateFrustumPlanes(LLCamera& camera, BOOL ortho, BOOL zflip, BOOL no_hacks)
219{
220	GLint* viewport = (GLint*) gGLViewport;
221	F64 model[16];
222	F64 proj[16];
223
224	for (U32 i = 0; i < 16; i++)
225	{
226		model[i] = (F64) gGLModelView[i];
227		proj[i] = (F64) gGLProjection[i];
228	}
229
230	GLdouble objX,objY,objZ;
231
232	LLVector3 frust[8];
233
234	if (no_hacks)
235	{
236		gluUnProject(viewport[0],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
237		frust[0].setVec((F32)objX,(F32)objY,(F32)objZ);
238		gluUnProject(viewport[0]+viewport[2],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
239		frust[1].setVec((F32)objX,(F32)objY,(F32)objZ);
240		gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
241		frust[2].setVec((F32)objX,(F32)objY,(F32)objZ);
242		gluUnProject(viewport[0],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
243		frust[3].setVec((F32)objX,(F32)objY,(F32)objZ);
244
245		gluUnProject(viewport[0],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
246		frust[4].setVec((F32)objX,(F32)objY,(F32)objZ);
247		gluUnProject(viewport[0]+viewport[2],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
248		frust[5].setVec((F32)objX,(F32)objY,(F32)objZ);
249		gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
250		frust[6].setVec((F32)objX,(F32)objY,(F32)objZ);
251		gluUnProject(viewport[0],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
252		frust[7].setVec((F32)objX,(F32)objY,(F32)objZ);
253	}
254	else if (zflip)
255	{
256		gluUnProject(viewport[0],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
257		frust[0].setVec((F32)objX,(F32)objY,(F32)objZ);
258		gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
259		frust[1].setVec((F32)objX,(F32)objY,(F32)objZ);
260		gluUnProject(viewport[0]+viewport[2],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
261		frust[2].setVec((F32)objX,(F32)objY,(F32)objZ);
262		gluUnProject(viewport[0],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
263		frust[3].setVec((F32)objX,(F32)objY,(F32)objZ);
264
265		gluUnProject(viewport[0],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
266		frust[4].setVec((F32)objX,(F32)objY,(F32)objZ);
267		gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
268		frust[5].setVec((F32)objX,(F32)objY,(F32)objZ);
269		gluUnProject(viewport[0]+viewport[2],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
270		frust[6].setVec((F32)objX,(F32)objY,(F32)objZ);
271		gluUnProject(viewport[0],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
272		frust[7].setVec((F32)objX,(F32)objY,(F32)objZ);
273
274		for (U32 i = 0; i < 4; i++)
275		{
276			frust[i+4] = frust[i+4]-frust[i];
277			frust[i+4].normVec();
278			frust[i+4] = frust[i] + frust[i+4]*camera.getFar();
279		}
280	}
281	else
282	{
283		gluUnProject(viewport[0],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
284		frust[0].setVec((F32)objX,(F32)objY,(F32)objZ);
285		gluUnProject(viewport[0]+viewport[2],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
286		frust[1].setVec((F32)objX,(F32)objY,(F32)objZ);
287		gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
288		frust[2].setVec((F32)objX,(F32)objY,(F32)objZ);
289		gluUnProject(viewport[0],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
290		frust[3].setVec((F32)objX,(F32)objY,(F32)objZ);
291		
292		if (ortho)
293		{
294			LLVector3 far_shift = camera.getAtAxis()*camera.getFar()*2.f; 
295			for (U32 i = 0; i < 4; i++)
296			{
297				frust[i+4] = frust[i] + far_shift;
298			}
299		}
300		else
301		{
302			for (U32 i = 0; i < 4; i++)
303			{
304				LLVector3 vec = frust[i] - camera.getOrigin();
305				vec.normVec();
306				frust[i+4] = camera.getOrigin() + vec*camera.getFar();
307			}
308		}
309	}
310
311	camera.calcAgentFrustumPlanes(frust);
312}
313
314void LLViewerCamera::setPerspective(BOOL for_selection,
315									S32 x, S32 y_from_bot, S32 width, S32 height,
316									BOOL limit_select_distance,
317									F32 z_near, F32 z_far)
318{
319	F32 fov_y, aspect;
320	fov_y = RAD_TO_DEG * getView();
321	BOOL z_default_near, z_default_far = FALSE;
322	if (z_far <= 0)
323	{
324		z_default_far = TRUE;
325		z_far = getFar();
326	}
327	if (z_near <= 0)
328	{
329		z_default_near = TRUE;
330		z_near = getNear();
331	}
332	aspect = getAspect();
333
334	// Load camera view matrix
335	gGL.matrixMode(LLRender::MM_PROJECTION);
336	gGL.loadIdentity();
337
338	glh::matrix4f proj_mat;
339
340	if (for_selection)
341	{
342		// make a tiny little viewport
343		// anything drawn into this viewport will be "selected"
344
345		GLint viewport[4];
346		viewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft;
347		viewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom;
348		viewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth();
349		viewport[3] = gViewerWindow->getWorldViewRectRaw().getHeight();
350		
351		proj_mat = gl_pick_matrix(x+width/2.f, y_from_bot+height/2.f, (GLfloat) width, (GLfloat) height, viewport);
352
353		if (limit_select_distance)
354		{
355			// ...select distance from control
356			z_far = gSavedSettings.getF32("MaxSelectDistance");
357		}
358		else
359		{
360			z_far = gAgentCamera.mDrawDistance;
361		}
362	}
363	else
364	{
365		// Only override the far clip if it's not passed in explicitly.
366		if (z_default_far)
367		{
368			z_far = MAX_FAR_CLIP;
369		}
370		glViewport(x, y_from_bot, width, height);
371		gGLViewport[0] = x;
372		gGLViewport[1] = y_from_bot;
373		gGLViewport[2] = width;
374		gGLViewport[3] = height;
375	}
376	
377	if (mZoomFactor > 1.f)
378	{
379		float offset = mZoomFactor - 1.f;
380		int pos_y = mZoomSubregion / llceil(mZoomFactor);
381		int pos_x = mZoomSubregion - (pos_y*llceil(mZoomFactor));
382		glh::matrix4f translate;
383		translate.set_translate(glh::vec3f(offset - (F32)pos_x * 2.f, offset - (F32)pos_y * 2.f, 0.f));
384		glh::matrix4f scale;
385		scale.set_scale(glh::vec3f(mZoomFactor, mZoomFactor, 1.f));
386
387		proj_mat = scale*proj_mat;
388		proj_mat = translate*proj_mat;
389	}
390
391	calcProjection(z_far); // Update the projection matrix cache
392
393	proj_mat *= gl_perspective(fov_y,aspect,z_near,z_far);
394
395	gGL.loadMatrix(proj_mat.m);
396
397	for (U32 i = 0; i < 16; i++)
398	{
399		gGLProjection[i] = proj_mat.m[i];
400	}
401
402	gGL.matrixMode(LLRender::MM_MODELVIEW);
403
404	glh::matrix4f modelview((GLfloat*) OGL_TO_CFR_ROTATION);
405
406	GLfloat			ogl_matrix[16];
407
408	getOpenGLTransform(ogl_matrix);
409
410	modelview *= glh::matrix4f(ogl_matrix);
411	
412	gGL.loadMatrix(modelview.m);
413	
414	if (for_selection && (width > 1 || height > 1))
415	{
416		// NB: as of this writing, i believe the code below is broken (doesn't take into account the world view, assumes entire window)
417		// however, it is also unused (the GL matricies are used for selection, (see LLCamera::sphereInFrustum())) and so i'm not
418		// comfortable hacking on it.
419		calculateFrustumPlanesFromWindow((F32)(x - width / 2) / (F32)gViewerWindow->getWindowWidthScaled() - 0.5f,
420								(F32)(y_from_bot - height / 2) / (F32)gViewerWindow->getWindowHeightScaled() - 0.5f,
421								(F32)(x + width / 2) / (F32)gViewerWindow->getWindowWidthScaled() - 0.5f,
422								(F32)(y_from_bot + height / 2) / (F32)gViewerWindow->getWindowHeightScaled() - 0.5f);
423
424	}
425
426	// if not picking and not doing a snapshot, cache various GL matrices
427	if (!for_selection && mZoomFactor == 1.f)
428	{
429		// Save GL matrices for access elsewhere in code, especially project_world_to_screen
430		for (U32 i = 0; i < 16; i++)
431		{
432			gGLModelView[i] = modelview.m[i];
433		}
434	}
435
436	updateFrustumPlanes(*this);
437}
438
439
440// Uses the last GL matrices set in set_perspective to project a point from
441// screen coordinates to the agent's region.
442void LLViewerCamera::projectScreenToPosAgent(const S32 screen_x, const S32 screen_y, LLVector3* pos_agent) const
443{
444	GLdouble x, y, z;
445
446	F64 mdlv[16];
447	F64 proj[16];
448
449	for (U32 i = 0; i < 16; i++)
450	{
451		mdlv[i] = (F64) gGLModelView[i];
452		proj[i] = (F64) gGLProjection[i];
453	}
454
455	gluUnProject(
456		GLdouble(screen_x), GLdouble(screen_y), 0.0,
457		mdlv, proj, (GLint*)gGLViewport,
458		&x,
459		&y,
460		&z );
461	pos_agent->setVec( (F32)x, (F32)y, (F32)z );
462}
463
464// Uses the last GL matrices set in set_perspective to project a point from
465// the agent's region space to screen coordinates.  Returns TRUE if point in within
466// the current window.
467BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp) const
468{
469	BOOL in_front = TRUE;
470	GLdouble	x, y, z;			// object's window coords, GL-style
471
472	LLVector3 dir_to_point = pos_agent - getOrigin();
473	dir_to_point /= dir_to_point.magVec();
474
475	if (dir_to_point * getAtAxis() < 0.f)
476	{
477		if (clamp)
478		{
479			return FALSE;
480		}
481		else
482		{
483			in_front = FALSE;
484		}
485	}
486
487	LLRect world_view_rect = gViewerWindow->getWorldViewRectRaw();
488	S32	viewport[4];
489	viewport[0] = world_view_rect.mLeft;
490	viewport[1] = world_view_rect.mBottom;
491	viewport[2] = world_view_rect.getWidth();
492	viewport[3] = world_view_rect.getHeight();
493
494	F64 mdlv[16];
495	F64 proj[16];
496
497	for (U32 i = 0; i < 16; i++)
498	{
499		mdlv[i] = (F64) gGLModelView[i];
500		proj[i] = (F64) gGLProjection[i];
501	}
502
503	if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ],
504								mdlv, proj, (GLint*)viewport,
505								&x, &y, &z))
506	{
507		// convert screen coordinates to virtual UI coordinates
508		x /= gViewerWindow->getDisplayScale().mV[VX];
509		y /= gViewerWindow->getDisplayScale().mV[VY];
510
511		// should now have the x,y coords of grab_point in screen space
512		LLRect world_rect = gViewerWindow->getWorldViewRectScaled();
513
514		// convert to pixel coordinates
515		S32 int_x = lltrunc(x);
516		S32 int_y = lltrunc(y);
517
518		BOOL valid = TRUE;
519
520		if (clamp)
521		{
522			if (int_x < world_rect.mLeft)
523			{
524				out_point.mX = world_rect.mLeft;
525				valid = FALSE;
526			}
527			else if (int_x > world_rect.mRight)
528			{
529				out_point.mX = world_rect.mRight;
530				valid = FALSE;
531			}
532			else
533			{
534				out_point.mX = int_x;
535			}
536
537			if (int_y < world_rect.mBottom)
538			{
539				out_point.mY = world_rect.mBottom;
540				valid = FALSE;
541			}
542			else if (int_y > world_rect.mTop)
543			{
544				out_point.mY = world_rect.mTop;
545				valid = FALSE;
546			}
547			else
548			{
549				out_point.mY = int_y;
550			}
551			return valid;
552		}
553		else
554		{
555			out_point.mX = int_x;
556			out_point.mY = int_y;
557
558			if (int_x < world_rect.mLeft)
559			{
560				valid = FALSE;
561			}
562			else if (int_x > world_rect.mRight)
563			{
564				valid = FALSE;
565			}
566			if (int_y < world_rect.mBottom)
567			{
568				valid = FALSE;
569			}
570			else if (int_y > world_rect.mTop)
571			{
572				valid = FALSE;
573			}
574
575			return in_front && valid;
576		}
577	}
578	else
579	{
580		return FALSE;
581	}
582}
583
584// Uses the last GL matrices set in set_perspective to project a point from
585// the agent's region space to the nearest edge in screen coordinates.
586// Returns TRUE if projection succeeds.
587BOOL LLViewerCamera::projectPosAgentToScreenEdge(const LLVector3 &pos_agent,
588												LLCoordGL &out_point) const
589{
590	LLVector3 dir_to_point = pos_agent - getOrigin();
591	dir_to_point /= dir_to_point.magVec();
592
593	BOOL in_front = TRUE;
594	if (dir_to_point * getAtAxis() < 0.f)
595	{
596		in_front = FALSE;
597	}
598
599	LLRect world_view_rect = gViewerWindow->getWorldViewRectRaw();
600	S32	viewport[4];
601	viewport[0] = world_view_rect.mLeft;
602	viewport[1] = world_view_rect.mBottom;
603	viewport[2] = world_view_rect.getWidth();
604	viewport[3] = world_view_rect.getHeight();
605	GLdouble	x, y, z;			// object's window coords, GL-style
606
607	F64 mdlv[16];
608	F64 proj[16];
609
610	for (U32 i = 0; i < 16; i++)
611	{
612		mdlv[i] = (F64) gGLModelView[i];
613		proj[i] = (F64) gGLProjection[i];
614	}
615
616	if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY],
617							  pos_agent.mV[VZ], mdlv,
618							  proj, (GLint*)viewport,
619							  &x, &y, &z))
620	{
621		x /= gViewerWindow->getDisplayScale().mV[VX];
622		y /= gViewerWindow->getDisplayScale().mV[VY];
623		// should now have the x,y coords of grab_point in screen space
624		const LLRect& world_rect = gViewerWindow->getWorldViewRectScaled();
625
626		// ...sanity check
627		S32 int_x = lltrunc(x);
628		S32 int_y = lltrunc(y);
629
630		// find the center
631		GLdouble center_x = (GLdouble)world_rect.getCenterX();
632		GLdouble center_y = (GLdouble)world_rect.getCenterY();
633
634		if (x == center_x  &&  y == center_y)
635		{
636			// can't project to edge from exact center
637			return FALSE;
638		}
639
640		// find the line from center to local
641		GLdouble line_x = x - center_x;
642		GLdouble line_y = y - center_y;
643
644		int_x = lltrunc(center_x);
645		int_y = lltrunc(center_y);
646
647
648		if (0.f == line_x)
649		{
650			// the slope of the line is undefined
651			if (line_y > 0.f)
652			{
653				int_y = world_rect.mTop;
654			}
655			else
656			{
657				int_y = world_rect.mBottom;
658			}
659		}
660		else if (0 == world_rect.getWidth())
661		{
662			// the diagonal slope of the view is undefined
663			if (y < world_rect.mBottom)
664			{
665				int_y = world_rect.mBottom;
666			}
667			else if ( y > world_rect.mTop)
668			{
669				int_y = world_rect.mTop;
670			}
671		}
672		else
673		{
674			F32 line_slope = (F32)(line_y / line_x);
675			F32 rect_slope = ((F32)world_rect.getHeight()) / ((F32)world_rect.getWidth());
676
677			if (fabs(line_slope) > rect_slope)
678			{
679				if (line_y < 0.f)
680				{
681					// bottom
682					int_y = world_rect.mBottom;
683				}
684				else
685				{
686					// top
687					int_y = world_rect.mTop;
688				}
689				int_x = lltrunc(((GLdouble)int_y - center_y) / line_slope + center_x);
690			}
691			else if (fabs(line_slope) < rect_slope)
692			{
693				if (line_x < 0.f)
694				{
695					// left
696					int_x = world_rect.mLeft;
697				}
698				else
699				{
700					// right
701					int_x = world_rect.mRight;
702				}
703				int_y = lltrunc(((GLdouble)int_x - center_x) * line_slope + center_y);
704			}
705			else
706			{
707				// exactly parallel ==> push to the corners
708				if (line_x > 0.f)
709				{
710					int_x = world_rect.mRight;
711				}
712				else
713				{
714					int_x = world_rect.mLeft;
715				}
716				if (line_y > 0.0f)
717				{
718					int_y = world_rect.mTop;
719				}
720				else
721				{
722					int_y = world_rect.mBottom;
723				}
724			}
725		}
726		if (!in_front)
727		{
728			int_x = world_rect.mLeft + world_rect.mRight - int_x;
729			int_y = world_rect.mBottom + world_rect.mTop - int_y;
730		}
731
732		out_point.mX = int_x + world_rect.mLeft;
733		out_point.mY = int_y + world_rect.mBottom;
734		return TRUE;
735	}
736	return FALSE;
737}
738
739
740void LLViewerCamera::getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right)
741{
742	LLVector3 to_vec = pos_agent - getOrigin();
743
744	F32 at_dist = to_vec * getAtAxis();
745
746	F32 height_meters = at_dist* (F32)tan(getView()/2.f);
747	F32 height_pixels = getViewHeightInPixels()/2.f;
748
749	F32 pixel_aspect = gViewerWindow->getWindow()->getPixelAspectRatio();
750
751	F32 meters_per_pixel = height_meters / height_pixels;
752	up = getUpAxis() * meters_per_pixel * gViewerWindow->getDisplayScale().mV[VY];
753	right = -1.f * pixel_aspect * meters_per_pixel * getLeftAxis() * gViewerWindow->getDisplayScale().mV[VX];
754}
755
756LLVector3 LLViewerCamera::roundToPixel(const LLVector3 &pos_agent)
757{
758	F32 dist = (pos_agent - getOrigin()).magVec();
759	// Convert to screen space and back, preserving the depth.
760	LLCoordGL screen_point;
761	if (!projectPosAgentToScreen(pos_agent, screen_point, FALSE))
762	{
763		// Off the screen, just return the original position.
764		return pos_agent;
765	}
766
767	LLVector3 ray_dir;
768
769	projectScreenToPosAgent(screen_point.mX, screen_point.mY, &ray_dir);
770	ray_dir -= getOrigin();
771	ray_dir.normVec();
772
773	LLVector3 pos_agent_rounded = getOrigin() + ray_dir*dist;
774
775	/*
776	LLVector3 pixel_x, pixel_y;
777	getPixelVectors(pos_agent_rounded, pixel_y, pixel_x);
778	pos_agent_rounded += 0.5f*pixel_x, 0.5f*pixel_y;
779	*/
780	return pos_agent_rounded;
781}
782
783BOOL LLViewerCamera::cameraUnderWater() const
784{
785	if(!gAgent.getRegion())
786	{
787		return FALSE ;
788	}
789	return getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight();
790}
791
792BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts)
793{
794	S32 i, num_faces;
795	LLDrawable* drawablep = volumep->mDrawable;
796
797	if (!drawablep)
798	{
799		return FALSE;
800	}
801
802	LLVolume* volume = volumep->getVolume();
803	if (!volume)
804	{
805		return FALSE;
806	}
807
808	LLVOVolume* vo_volume = (LLVOVolume*) volumep;
809
810	vo_volume->updateRelativeXform();
811	LLMatrix4 mat = vo_volume->getRelativeXform();
812	
813	LLMatrix4 render_mat(vo_volume->getRenderRotation(), LLVector4(vo_volume->getRenderPosition()));
814
815	LLMatrix4a render_mata;
816	render_mata.loadu(render_mat);
817	LLMatrix4a mata;
818	mata.loadu(mat);
819
820	num_faces = volume->getNumVolumeFaces();
821	for (i = 0; i < num_faces; i++)
822	{
823		const LLVolumeFace& face = volume->getVolumeFace(i);
824				
825		for (U32 v = 0; v < face.mNumVertices; v++)
826		{
827			const LLVector4a& src_vec = face.mPositions[v];
828			LLVector4a vec;
829			mata.affineTransform(src_vec, vec);
830
831			if (drawablep->isActive())
832			{
833				LLVector4a t = vec;
834				render_mata.affineTransform(t, vec);
835			}
836
837			BOOL in_frustum = pointInFrustum(LLVector3(vec.getF32ptr())) > 0;
838
839			if (( !in_frustum && all_verts) ||
840				 (in_frustum && !all_verts))
841			{
842				return !all_verts;
843			}
844		}
845	}
846	return all_verts;
847}
848
849// changes local camera and broadcasts change
850/* virtual */ void LLViewerCamera::setView(F32 vertical_fov_rads)
851{
852	F32 old_fov = LLViewerCamera::getInstance()->getView();
853
854	// cap the FoV
855	vertical_fov_rads = llclamp(vertical_fov_rads, getMinView(), getMaxView());
856
857	if (vertical_fov_rads == old_fov) return;
858
859	// send the new value to the simulator
860	LLMessageSystem* msg = gMessageSystem;
861	msg->newMessageFast(_PREHASH_AgentFOV);
862	msg->nextBlockFast(_PREHASH_AgentData);
863	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
864	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
865	msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode);
866
867	msg->nextBlockFast(_PREHASH_FOVBlock);
868	msg->addU32Fast(_PREHASH_GenCounter, 0);
869	msg->addF32Fast(_PREHASH_VerticalAngle, vertical_fov_rads);
870
871	gAgent.sendReliableMessage();
872
873	// sync the camera with the new value
874	LLCamera::setView(vertical_fov_rads); // call base implementation
875}
876
877void LLViewerCamera::setDefaultFOV(F32 vertical_fov_rads) 
878{
879	vertical_fov_rads = llclamp(vertical_fov_rads, getMinView(), getMaxView());
880	setView(vertical_fov_rads);
881	mCameraFOVDefault = vertical_fov_rads; 
882	mCosHalfCameraFOV = cosf(mCameraFOVDefault * 0.5f);
883}
884
885
886// static
887void LLViewerCamera::updateCameraAngle( void* user_data, const LLSD& value)
888{
889	LLViewerCamera* self=(LLViewerCamera*)user_data;
890	self->setDefaultFOV(value.asReal());	
891}
892