PageRenderTime 226ms CodeModel.GetById 14ms app.highlight 192ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llmaniptranslate.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2007 lines | 1604 code | 278 blank | 125 comment | 239 complexity | e78d1541749553c330ad50f9c1a0bab2 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file llmaniptranslate.cpp
   3 * @brief LLManipTranslate 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/**
  28 * Positioning tool
  29 */
  30
  31#include "llviewerprecompiledheaders.h"
  32
  33#include "llmaniptranslate.h"
  34
  35#include "llgl.h"
  36#include "llrender.h"
  37
  38#include "llagent.h"
  39#include "llagentcamera.h"
  40#include "llbbox.h"
  41#include "llbox.h"
  42#include "llviewercontrol.h"
  43#include "llcriticaldamp.h"
  44#include "llcylinder.h"
  45#include "lldrawable.h"
  46#include "llfloatertools.h"
  47#include "llfontgl.h"
  48#include "llglheaders.h"
  49#include "llhudrender.h"
  50#include "llresmgr.h"
  51#include "llselectmgr.h"
  52#include "llrendersphere.h"
  53#include "llstatusbar.h"
  54#include "lltoolmgr.h"
  55#include "llviewercamera.h"
  56#include "llviewerjoint.h"
  57#include "llviewerobject.h"
  58#include "llviewerwindow.h"
  59#include "llvoavatarself.h"
  60#include "llworld.h"
  61#include "llui.h"
  62#include "pipeline.h"
  63
  64const S32 NUM_AXES = 3;
  65const S32 MOUSE_DRAG_SLOP = 2;       // pixels
  66const F32 HANDLE_HIDE_ANGLE = 0.15f; // radians
  67const F32 SELECTED_ARROW_SCALE = 1.3f;
  68const F32 MANIPULATOR_HOTSPOT_START = 0.2f;
  69const F32 MANIPULATOR_HOTSPOT_END = 1.2f;
  70const F32 SNAP_GUIDE_SCREEN_SIZE = 0.7f;
  71const F32 MIN_PLANE_MANIP_DOT_PRODUCT = 0.25f;
  72const F32 PLANE_TICK_SIZE = 0.4f;
  73const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
  74const F32 SNAP_ARROW_SCALE = 0.7f;
  75
  76static LLPointer<LLViewerTexture> sGridTex = NULL ;
  77
  78const LLManip::EManipPart MANIPULATOR_IDS[9] = 
  79{
  80	LLManip::LL_X_ARROW,
  81	LLManip::LL_Y_ARROW,
  82	LLManip::LL_Z_ARROW,
  83	LLManip::LL_X_ARROW,
  84	LLManip::LL_Y_ARROW,
  85	LLManip::LL_Z_ARROW,
  86	LLManip::LL_YZ_PLANE,
  87	LLManip::LL_XZ_PLANE,
  88	LLManip::LL_XY_PLANE
  89};
  90
  91const U32 ARROW_TO_AXIS[4] = 
  92{
  93	VX,
  94	VX,
  95	VY,
  96	VZ
  97};
  98
  99// Sort manipulator handles by their screen-space projection
 100struct ClosestToCamera
 101{
 102	bool operator()(const LLManipTranslate::ManipulatorHandle& a,
 103					const LLManipTranslate::ManipulatorHandle& b) const
 104	{
 105		return a.mEndPosition.mV[VZ] < b.mEndPosition.mV[VZ];
 106	}
 107};
 108
 109LLManipTranslate::LLManipTranslate( LLToolComposite* composite )
 110:	LLManip( std::string("Move"), composite ),
 111	mLastHoverMouseX(-1),
 112	mLastHoverMouseY(-1),
 113	mSendUpdateOnMouseUp(FALSE),
 114	mMouseOutsideSlop(FALSE),
 115	mCopyMadeThisDrag(FALSE),
 116	mMouseDownX(-1),
 117	mMouseDownY(-1),
 118	mAxisArrowLength(50),
 119	mConeSize(0),
 120	mArrowLengthMeters(0.f),
 121	mGridSizeMeters(1.f),
 122	mPlaneManipOffsetMeters(0.f),
 123	mUpdateTimer(),
 124	mSnapOffsetMeters(0.f),
 125	mSubdivisions(10.f),
 126	mInSnapRegime(FALSE),
 127	mSnapped(FALSE),
 128	mArrowScales(1.f, 1.f, 1.f),
 129	mPlaneScales(1.f, 1.f, 1.f),
 130	mPlaneManipPositions(1.f, 1.f, 1.f, 1.f)
 131{ 
 132	if (sGridTex.isNull())
 133	{ 
 134		restoreGL();
 135	}
 136}
 137
 138//static
 139U32 LLManipTranslate::getGridTexName()
 140{
 141	if(sGridTex.isNull())
 142	{
 143		restoreGL() ;
 144	}
 145
 146	return sGridTex.isNull() ? 0 : sGridTex->getTexName() ;
 147}
 148
 149//static
 150void LLManipTranslate::destroyGL()
 151{
 152	if (sGridTex)
 153	{
 154		sGridTex = NULL ;
 155	}
 156}
 157
 158//static
 159void LLManipTranslate::restoreGL()
 160{
 161	//generate grid texture
 162	U32 rez = 512;
 163	U32 mip = 0;
 164
 165	destroyGL() ;
 166	sGridTex = LLViewerTextureManager::getLocalTexture() ;
 167	if(!sGridTex->createGLTexture())
 168	{
 169		sGridTex = NULL ;
 170		return ;
 171	}
 172
 173	GLuint* d = new GLuint[rez*rez];	
 174
 175	gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, sGridTex->getTexName(), true);
 176	gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR);
 177
 178	while (rez >= 1)
 179	{
 180		for (U32 i = 0; i < rez*rez; i++)
 181		{
 182			d[i] = 0x00FFFFFF;
 183		}
 184		
 185		U32 subcol = 0xFFFFFFFF;
 186		if (rez >= 4)
 187		{	//large grain grid
 188			for (U32 i = 0; i < rez; i++)
 189			{
 190				if (rez <= 16)
 191				{
 192					if (rez == 16)
 193					{
 194						subcol = 0xA0FFFFFF;
 195					}
 196					else if (rez == 8)
 197					{
 198						subcol = 0x80FFFFFF;
 199					}
 200					else
 201					{
 202						subcol = 0x40FFFFFF;
 203					}
 204				}
 205				else
 206				{
 207					subcol = 0xFFFFFFFF;	
 208				}
 209				d[i			*rez+ 0		 ] = subcol;
 210				d[0			*rez+ i		 ] = subcol;				
 211				if (rez >= 32)
 212				{
 213					d[i			*rez+ (rez-1)] = subcol;
 214					d[(rez-1)	*rez+ i		 ] = subcol;
 215				}
 216
 217				if (rez >= 64)
 218				{
 219					subcol = 0xFFFFFFFF;
 220					
 221					if (i > 0 && i < (rez-1))
 222					{
 223						d[i			*rez+ 1		 ] = subcol;
 224						d[i			*rez+ (rez-2)] = subcol;
 225						d[1			*rez+ i		 ] = subcol;
 226						d[(rez-2)	*rez+ i		 ] = subcol;
 227					}
 228				}
 229			}
 230		}
 231
 232		subcol = 0x50A0A0A0;
 233		if (rez >= 128)
 234		{ //small grain grid
 235			for (U32 i = 8; i < rez; i+=8)
 236			{
 237				for (U32 j = 2; j < rez-2; j++)
 238				{
 239					d[i	*rez+ j] = subcol;
 240					d[j	*rez+ i] = subcol;			
 241				}
 242			}
 243		}
 244		if (rez >= 64)
 245		{ //medium grain grid
 246			if (rez == 64)
 247			{
 248				subcol = 0x50A0A0A0;
 249			}
 250			else
 251			{
 252				subcol = 0xA0D0D0D0;
 253			}
 254
 255			for (U32 i = 32; i < rez; i+=32)
 256			{
 257				U32 pi = i-1;
 258				for (U32 j = 2; j < rez-2; j++)
 259				{
 260					d[i		*rez+ j] = subcol;
 261					d[j		*rez+ i] = subcol;
 262
 263					if (rez > 128)
 264					{
 265						d[pi	*rez+ j] = subcol;
 266						d[j		*rez+ pi] = subcol;			
 267					}
 268				}
 269			}
 270		}
 271#ifdef LL_WINDOWS
 272		LLImageGL::setManualImage(GL_TEXTURE_2D, mip, GL_RGBA, rez, rez, GL_RGBA, GL_UNSIGNED_BYTE, d);
 273#else
 274		LLImageGL::setManualImage(GL_TEXTURE_2D, mip, GL_RGBA, rez, rez, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, d);
 275#endif
 276		rez = rez >> 1;
 277		mip++;
 278	}
 279	delete [] d;
 280}
 281
 282
 283LLManipTranslate::~LLManipTranslate()
 284{
 285}
 286
 287
 288void LLManipTranslate::handleSelect()
 289{
 290	LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
 291	gFloaterTools->setStatusText("move");
 292	LLManip::handleSelect();
 293}
 294
 295BOOL LLManipTranslate::handleMouseDown(S32 x, S32 y, MASK mask)
 296{
 297	BOOL	handled = FALSE;
 298
 299	// didn't click in any UI object, so must have clicked in the world
 300	if( (mHighlightedPart == LL_X_ARROW ||
 301		 mHighlightedPart == LL_Y_ARROW ||
 302		 mHighlightedPart == LL_Z_ARROW ||
 303		 mHighlightedPart == LL_YZ_PLANE ||
 304		 mHighlightedPart == LL_XZ_PLANE ||
 305		 mHighlightedPart == LL_XY_PLANE ) )
 306	{
 307		handled = handleMouseDownOnPart( x, y, mask );
 308	}
 309
 310	return handled;
 311}
 312
 313// Assumes that one of the arrows on an object was hit.
 314BOOL LLManipTranslate::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
 315{
 316	BOOL can_move = canAffectSelection();
 317	if (!can_move)
 318	{
 319		return FALSE;
 320	}
 321
 322	highlightManipulators(x, y);
 323	S32 hit_part = mHighlightedPart;
 324
 325	if( (hit_part != LL_X_ARROW) && 
 326		(hit_part != LL_Y_ARROW) &&
 327		(hit_part != LL_Z_ARROW) &&
 328		(hit_part != LL_YZ_PLANE) &&
 329		(hit_part != LL_XZ_PLANE) &&
 330		(hit_part != LL_XY_PLANE) )
 331	{
 332		return TRUE;
 333	}
 334
 335	mHelpTextTimer.reset();
 336	sNumTimesHelpTextShown++;
 337
 338	LLSelectMgr::getInstance()->getGrid(mGridOrigin, mGridRotation, mGridScale);
 339
 340	LLSelectMgr::getInstance()->enableSilhouette(FALSE);
 341
 342	// we just started a drag, so save initial object positions
 343	LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_MOVE);
 344
 345	mManipPart = (EManipPart)hit_part;
 346	mMouseDownX = x;
 347	mMouseDownY = y;
 348	mMouseOutsideSlop = FALSE;
 349
 350	LLVector3		axis;
 351
 352	LLSelectNode *selectNode = mObjectSelection->getFirstMoveableNode(TRUE);
 353
 354	if (!selectNode)
 355	{
 356		// didn't find the object in our selection...oh well
 357		llwarns << "Trying to translate an unselected object" << llendl;
 358		return TRUE;
 359	}
 360
 361	LLViewerObject *selected_object = selectNode->getObject();
 362	if (!selected_object)
 363	{
 364		// somehow we lost the object!
 365		llwarns << "Translate manip lost the object, no selected object" << llendl;
 366		gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
 367		return TRUE;
 368	}
 369
 370	// Compute unit vectors for arrow hit and a plane through that vector
 371	BOOL axis_exists = getManipAxis(selected_object, mManipPart, axis);
 372	getManipNormal(selected_object, mManipPart, mManipNormal);
 373
 374	//LLVector3 select_center_agent = gAgent.getPosAgentFromGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal());
 375	// TomY: The above should (?) be identical to the below
 376	LLVector3 select_center_agent = getPivotPoint();
 377	mSubdivisions = llclamp(getSubdivisionLevel(select_center_agent, axis_exists ? axis : LLVector3::z_axis, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
 378
 379	// if we clicked on a planar manipulator, recenter mouse cursor
 380	if (mManipPart >= LL_YZ_PLANE && mManipPart <= LL_XY_PLANE)
 381	{
 382		LLCoordGL mouse_pos;
 383		if (!LLViewerCamera::getInstance()->projectPosAgentToScreen(select_center_agent, mouse_pos))
 384		{
 385			// mouse_pos may be nonsense
 386			llwarns << "Failed to project object center to screen" << llendl;
 387		}
 388		else if (gSavedSettings.getBOOL("SnapToMouseCursor"))
 389		{
 390			LLUI::setMousePositionScreen(mouse_pos.mX, mouse_pos.mY);
 391			x = mouse_pos.mX;
 392			y = mouse_pos.mY;
 393		}
 394	}
 395
 396	LLSelectMgr::getInstance()->updateSelectionCenter();
 397	LLVector3d object_start_global = gAgent.getPosGlobalFromAgent(getPivotPoint());
 398	getMousePointOnPlaneGlobal(mDragCursorStartGlobal, x, y, object_start_global, mManipNormal);
 399	mDragSelectionStartGlobal = object_start_global;
 400	mCopyMadeThisDrag = FALSE;
 401
 402	// Route future Mouse messages here preemptively.  (Release on mouse up.)
 403	setMouseCapture( TRUE );
 404
 405	return TRUE;
 406}
 407
 408BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
 409{
 410	// Translation tool only works if mouse button is down.
 411	// Bail out if mouse not down.
 412	if( !hasMouseCapture() )
 413	{
 414		lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (inactive)" << llendl;		
 415		// Always show cursor
 416		// gViewerWindow->setCursor(UI_CURSOR_ARROW);
 417		gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
 418
 419		highlightManipulators(x, y);
 420		return TRUE;
 421	}
 422	
 423	// Handle auto-rotation if necessary.
 424	LLRect world_rect = gViewerWindow->getWorldViewRectScaled();
 425	const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
 426	const S32 ROTATE_H_MARGIN = world_rect.getWidth() / 20;
 427	const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
 428	BOOL rotated = FALSE;
 429
 430	// ...build mode moves camera about focus point
 431	if (mObjectSelection->getSelectType() != SELECT_TYPE_HUD)
 432	{
 433		if (x < ROTATE_H_MARGIN)
 434		{
 435			gAgentCamera.cameraOrbitAround(rotate_angle);
 436			rotated = TRUE;
 437		}
 438		else if (x > world_rect.getWidth() - ROTATE_H_MARGIN)
 439		{
 440			gAgentCamera.cameraOrbitAround(-rotate_angle);
 441			rotated = TRUE;
 442		}
 443	}
 444
 445	// Suppress processing if mouse hasn't actually moved.
 446	// This may cause problems if the camera moves outside of the
 447	// rotation above.
 448	if( x == mLastHoverMouseX && y == mLastHoverMouseY && !rotated)
 449	{
 450		lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (mouse unmoved)" << llendl;
 451		gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
 452		return TRUE;
 453	}
 454	mLastHoverMouseX = x;
 455	mLastHoverMouseY = y;
 456
 457	// Suppress if mouse hasn't moved past the initial slop region
 458	// Reset once we start moving
 459	if( !mMouseOutsideSlop )
 460	{
 461		if (abs(mMouseDownX - x) < MOUSE_DRAG_SLOP && abs(mMouseDownY - y) < MOUSE_DRAG_SLOP )
 462		{
 463			lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (mouse inside slop)" << llendl;
 464			gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
 465			return TRUE;
 466		}
 467		else
 468		{
 469			// ...just went outside the slop region
 470			mMouseOutsideSlop = TRUE;
 471			// If holding down shift, leave behind a copy.
 472			if (mask == MASK_COPY)
 473			{
 474				// ...we're trying to make a copy
 475				LLSelectMgr::getInstance()->selectDuplicate(LLVector3::zero, FALSE);
 476				mCopyMadeThisDrag = TRUE;
 477
 478				// When we make the copy, we don't want to do any other processing.
 479				// If so, the object will also be moved, and the copy will be offset.
 480				lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (made copy)" << llendl;
 481				gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
 482			}
 483		}
 484	}
 485
 486	// Throttle updates to 10 per second.
 487	BOOL send_update = FALSE;
 488
 489	LLVector3		axis_f;
 490	LLVector3d		axis_d;
 491
 492	// pick the first object to constrain to grid w/ common origin
 493	// this is so we don't screw up groups
 494	LLSelectNode* selectNode = mObjectSelection->getFirstMoveableNode(TRUE);
 495	if (!selectNode)
 496	{
 497		// somehow we lost the object!
 498		llwarns << "Translate manip lost the object, no selectNode" << llendl;
 499		gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
 500		return TRUE;
 501	}
 502
 503	LLViewerObject* object = selectNode->getObject();
 504	if (!object)
 505	{
 506		// somehow we lost the object!
 507		llwarns << "Translate manip lost the object, no object in selectNode" << llendl;
 508		gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
 509		return TRUE;
 510	}
 511
 512	// Compute unit vectors for arrow hit and a plane through that vector
 513	BOOL axis_exists = getManipAxis(object, mManipPart, axis_f);		// TODO: move this
 514
 515	axis_d.setVec(axis_f);
 516
 517	LLSelectMgr::getInstance()->updateSelectionCenter();
 518	LLVector3d current_pos_global = gAgent.getPosGlobalFromAgent(getPivotPoint());
 519
 520	mSubdivisions = llclamp(getSubdivisionLevel(getPivotPoint(), axis_f, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
 521
 522	// Project the cursor onto that plane
 523	LLVector3d relative_move;
 524	getMousePointOnPlaneGlobal(relative_move, x, y, current_pos_global, mManipNormal);\
 525	relative_move -= mDragCursorStartGlobal;
 526
 527	// You can't move more than some distance from your original mousedown point.
 528	if (gSavedSettings.getBOOL("LimitDragDistance"))
 529	{
 530		F32 max_drag_distance = gSavedSettings.getF32("MaxDragDistance");
 531
 532		if (relative_move.magVecSquared() > max_drag_distance * max_drag_distance)
 533		{
 534			lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (too far)" << llendl;
 535			gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
 536			return TRUE;
 537		}
 538	}
 539
 540	F64 axis_magnitude = relative_move * axis_d;					// dot product
 541	LLVector3d cursor_point_snap_line;
 542	
 543	F64 off_axis_magnitude;
 544
 545	getMousePointOnPlaneGlobal(cursor_point_snap_line, x, y, current_pos_global, mSnapOffsetAxis % axis_f);
 546	off_axis_magnitude = axis_exists ? llabs((cursor_point_snap_line - current_pos_global) * LLVector3d(mSnapOffsetAxis)) : 0.f;
 547
 548	if (gSavedSettings.getBOOL("SnapEnabled"))
 549	{
 550		if (off_axis_magnitude > mSnapOffsetMeters)
 551		{
 552			mInSnapRegime = TRUE;
 553			LLVector3 mouse_down_offset(mDragCursorStartGlobal - mDragSelectionStartGlobal);
 554			LLVector3 cursor_snap_agent = gAgent.getPosAgentFromGlobal(cursor_point_snap_line);
 555			if (!gSavedSettings.getBOOL("SnapToMouseCursor"))
 556			{
 557				cursor_snap_agent -= mouse_down_offset;
 558			}
 559
 560			F32 cursor_grid_dist = (cursor_snap_agent - mGridOrigin) * axis_f;
 561			
 562			F32 snap_dist = getMinGridScale() / (2.f * mSubdivisions);
 563			F32 relative_snap_dist = fmodf(llabs(cursor_grid_dist) + snap_dist, getMinGridScale() / mSubdivisions);
 564			if (relative_snap_dist < snap_dist * 2.f)
 565			{
 566				if (cursor_grid_dist > 0.f)
 567				{
 568					cursor_grid_dist -= relative_snap_dist - snap_dist;
 569				}
 570				else
 571				{
 572					cursor_grid_dist += relative_snap_dist - snap_dist;
 573				}
 574			}
 575
 576			F32 object_start_on_axis = (gAgent.getPosAgentFromGlobal(mDragSelectionStartGlobal) - mGridOrigin) * axis_f;
 577			axis_magnitude = cursor_grid_dist - object_start_on_axis;
 578		}
 579		else if (mManipPart >= LL_YZ_PLANE && mManipPart <= LL_XY_PLANE)
 580		{
 581			// subtract offset from object center
 582			LLVector3d cursor_point_global;
 583			getMousePointOnPlaneGlobal( cursor_point_global, x, y, current_pos_global, mManipNormal );
 584			cursor_point_global -= (mDragCursorStartGlobal - mDragSelectionStartGlobal);
 585
 586			// snap to planar grid
 587			LLVector3 cursor_point_agent = gAgent.getPosAgentFromGlobal(cursor_point_global);
 588			LLVector3 camera_plane_projection = LLViewerCamera::getInstance()->getAtAxis();
 589			camera_plane_projection -= projected_vec(camera_plane_projection, mManipNormal);
 590			camera_plane_projection.normVec();
 591			LLVector3 camera_projected_dir = camera_plane_projection;
 592			camera_plane_projection.rotVec(~mGridRotation);
 593			camera_plane_projection.scaleVec(mGridScale);
 594			camera_plane_projection.abs();
 595			F32 max_grid_scale;
 596			if (camera_plane_projection.mV[VX] > camera_plane_projection.mV[VY] &&
 597				camera_plane_projection.mV[VX] > camera_plane_projection.mV[VZ])
 598			{
 599				max_grid_scale = mGridScale.mV[VX];
 600			}
 601			else if (camera_plane_projection.mV[VY] > camera_plane_projection.mV[VZ])
 602			{
 603				max_grid_scale = mGridScale.mV[VY];
 604			}
 605			else
 606			{
 607				max_grid_scale = mGridScale.mV[VZ];
 608			}
 609
 610			F32 num_subdivisions = llclamp(getSubdivisionLevel(getPivotPoint(), camera_projected_dir, max_grid_scale), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
 611
 612			F32 grid_scale_a;
 613			F32 grid_scale_b;
 614			LLVector3 cursor_point_grid = (cursor_point_agent - mGridOrigin) * ~mGridRotation;
 615
 616			switch (mManipPart)
 617			{
 618			case LL_YZ_PLANE:
 619				grid_scale_a = mGridScale.mV[VY] / num_subdivisions;
 620				grid_scale_b = mGridScale.mV[VZ] / num_subdivisions;
 621				cursor_point_grid.mV[VY] -= fmod(cursor_point_grid.mV[VY] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
 622				cursor_point_grid.mV[VZ] -= fmod(cursor_point_grid.mV[VZ] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
 623				break;
 624			case LL_XZ_PLANE:
 625				grid_scale_a = mGridScale.mV[VX] / num_subdivisions;
 626				grid_scale_b = mGridScale.mV[VZ] / num_subdivisions;
 627				cursor_point_grid.mV[VX] -= fmod(cursor_point_grid.mV[VX] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
 628				cursor_point_grid.mV[VZ] -= fmod(cursor_point_grid.mV[VZ] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
 629				break;
 630			case LL_XY_PLANE:
 631				grid_scale_a = mGridScale.mV[VX] / num_subdivisions;
 632				grid_scale_b = mGridScale.mV[VY] / num_subdivisions;
 633				cursor_point_grid.mV[VX] -= fmod(cursor_point_grid.mV[VX] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
 634				cursor_point_grid.mV[VY] -= fmod(cursor_point_grid.mV[VY] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
 635				break;
 636			default:
 637				break;
 638			}
 639			cursor_point_agent = (cursor_point_grid * mGridRotation) + mGridOrigin;
 640			relative_move.setVec(cursor_point_agent - gAgent.getPosAgentFromGlobal(mDragSelectionStartGlobal));
 641			mInSnapRegime = TRUE;
 642		}
 643		else
 644		{
 645			mInSnapRegime = FALSE;
 646		}
 647	}
 648	else
 649	{
 650		mInSnapRegime = FALSE;
 651	}
 652
 653	// Clamp to arrow direction
 654	// *FIX: does this apply anymore?
 655	if (!axis_exists)
 656	{
 657		axis_magnitude = relative_move.normVec();
 658		axis_d.setVec(relative_move);
 659		axis_d.normVec();
 660		axis_f.setVec(axis_d);
 661	}
 662
 663	LLVector3d clamped_relative_move = axis_magnitude * axis_d;	// scalar multiply
 664	LLVector3 clamped_relative_move_f = (F32)axis_magnitude * axis_f; // scalar multiply
 665	
 666	for (LLObjectSelection::iterator iter = mObjectSelection->begin();
 667		 iter != mObjectSelection->end(); iter++)
 668	{
 669		LLSelectNode* selectNode = *iter;
 670		LLViewerObject* object = selectNode->getObject();
 671		
 672		// Only apply motion to root objects and objects selected
 673		// as "individual".
 674		if (!object->isRootEdit() && !selectNode->mIndividualSelection)
 675		{
 676			continue;
 677		}
 678
 679		if (!object->isRootEdit())
 680		{
 681			// child objects should not update if parent is selected
 682			LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
 683			if (editable_root->isSelected())
 684			{
 685				// we will be moved properly by our parent, so skip
 686				continue;
 687			}
 688		}
 689
 690		if (object->permMove())
 691		{
 692			// handle attachments in local space
 693			if (object->isAttachment() && object->mDrawable.notNull())
 694			{
 695				// calculate local version of relative move
 696				LLQuaternion objWorldRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
 697				objWorldRotation.transQuat();
 698
 699				LLVector3 old_position_local = object->getPosition();
 700				LLVector3 new_position_local = selectNode->mSavedPositionLocal + (clamped_relative_move_f * objWorldRotation);
 701
 702				// move and clamp root object first, before adjusting children
 703				if (new_position_local != old_position_local)
 704				{
 705					send_update = TRUE;
 706				}
 707				//RN: I forget, but we need to do this because of snapping which doesn't often result
 708				// in position changes even when the mouse moves
 709				object->setPosition(new_position_local);
 710				rebuild(object);
 711				gAgentAvatarp->clampAttachmentPositions();
 712				new_position_local = object->getPosition();
 713
 714				if (selectNode->mIndividualSelection)
 715				{
 716					send_update = FALSE;
 717		
 718					// counter-translate child objects if we are moving the root as an individual
 719					object->resetChildrenPosition(old_position_local - new_position_local, TRUE) ;					
 720				}
 721			}
 722			else
 723			{
 724				// compute new position to send to simulators, but don't set it yet.
 725				// We need the old position to know which simulator to send the move message to.
 726				LLVector3d new_position_global = selectNode->mSavedPositionGlobal + clamped_relative_move;
 727
 728				// Don't let object centers go too far underground
 729				F64 min_height = LLWorld::getInstance()->getMinAllowedZ(object, object->getPositionGlobal());
 730				if (new_position_global.mdV[VZ] < min_height)
 731				{
 732					new_position_global.mdV[VZ] = min_height;
 733				}
 734
 735				// For safety, cap heights where objects can be dragged
 736				if (new_position_global.mdV[VZ] > MAX_OBJECT_Z)
 737				{
 738					new_position_global.mdV[VZ] = MAX_OBJECT_Z;
 739				}
 740
 741				// Grass is always drawn on the ground, so clamp its position to the ground
 742				if (object->getPCode() == LL_PCODE_LEGACY_GRASS)
 743				{
 744					new_position_global.mdV[VZ] = LLWorld::getInstance()->resolveLandHeightGlobal(new_position_global) + 1.f;
 745				}
 746				
 747				if (object->isRootEdit())
 748				{
 749					new_position_global = LLWorld::getInstance()->clipToVisibleRegions(object->getPositionGlobal(), new_position_global);
 750				}
 751
 752				// PR: Only update if changed
 753				LLVector3d old_position_global = object->getPositionGlobal();
 754				LLVector3 old_position_agent = object->getPositionAgent();
 755				LLVector3 new_position_agent = gAgent.getPosAgentFromGlobal(new_position_global);
 756				if (object->isRootEdit())
 757				{
 758					// finally, move parent object after children have calculated new offsets
 759					object->setPositionAgent(new_position_agent);
 760					rebuild(object);
 761				}
 762				else
 763				{
 764					LLViewerObject* root_object = object->getRootEdit();
 765					new_position_agent -= root_object->getPositionAgent();
 766					new_position_agent = new_position_agent * ~root_object->getRotation();
 767					object->setPositionParent(new_position_agent, FALSE);
 768					rebuild(object);
 769				}
 770
 771				if (selectNode->mIndividualSelection)
 772				{
 773					// counter-translate child objects if we are moving the root as an individual
 774					object->resetChildrenPosition(old_position_agent - new_position_agent, TRUE) ;					
 775					send_update = FALSE;
 776				}
 777				else if (old_position_global != new_position_global)
 778				{
 779					send_update = TRUE;
 780				}
 781			}
 782			selectNode->mLastPositionLocal  = object->getPosition();
 783		}
 784	}
 785
 786	LLSelectMgr::getInstance()->updateSelectionCenter();
 787	gAgentCamera.clearFocusObject();
 788	dialog_refresh_all();		// ??? is this necessary?
 789
 790	lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (active)" << llendl;
 791	gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
 792	return TRUE;
 793}
 794
 795void LLManipTranslate::highlightManipulators(S32 x, S32 y)
 796{
 797	mHighlightedPart = LL_NO_PART;
 798
 799	if (!mObjectSelection->getObjectCount())
 800	{
 801		return;
 802	}
 803	
 804	//LLBBox bbox = LLSelectMgr::getInstance()->getBBoxOfSelection();
 805	LLMatrix4 projMatrix = LLViewerCamera::getInstance()->getProjection();
 806	LLMatrix4 modelView = LLViewerCamera::getInstance()->getModelview();
 807
 808	LLVector3 object_position = getPivotPoint();
 809	
 810	LLVector3 grid_origin;
 811	LLVector3 grid_scale;
 812	LLQuaternion grid_rotation;
 813
 814	LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
 815
 816	LLVector3 relative_camera_dir;
 817
 818	LLMatrix4 transform;
 819
 820	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 821	{
 822		relative_camera_dir = LLVector3(1.f, 0.f, 0.f) * ~grid_rotation;
 823		LLVector4 translation(object_position);
 824		transform.initRotTrans(grid_rotation, translation);
 825		LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
 826		transform *= cfr;
 827		LLMatrix4 window_scale;
 828		F32 zoom_level = 2.f * gAgentCamera.mHUDCurZoom;
 829		window_scale.initAll(LLVector3(zoom_level / LLViewerCamera::getInstance()->getAspect(), zoom_level, 0.f),
 830			LLQuaternion::DEFAULT,
 831			LLVector3::zero);
 832		transform *= window_scale;
 833	}
 834	else
 835	{
 836		relative_camera_dir = (object_position - LLViewerCamera::getInstance()->getOrigin()) * ~grid_rotation;
 837		relative_camera_dir.normVec();
 838
 839		transform.initRotTrans(grid_rotation, LLVector4(object_position));
 840		transform *= modelView;
 841		transform *= projMatrix;
 842	}
 843		
 844	S32 numManips = 0;
 845
 846	// edges
 847	mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 0.f, 0.f, 1.f);
 848	mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 0.f, 0.f, 1.f);
 849
 850	mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 0.f, 1.f);
 851	mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 0.f, 1.f);
 852
 853	mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 1.f);
 854	mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 1.f);
 855
 856	mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 0.f, 0.f, 1.f);
 857	mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 0.f, 0.f, 1.f);
 858
 859	mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 0.f, 1.f);
 860	mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 0.f, 1.f);
 861
 862	mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 1.f);
 863	mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 1.f);
 864
 865	S32 num_arrow_manips = numManips;
 866
 867	// planar manipulators
 868	BOOL planar_manip_yz_visible = FALSE;
 869	BOOL planar_manip_xz_visible = FALSE;
 870	BOOL planar_manip_xy_visible = FALSE;
 871
 872	mManipulatorVertices[numManips] = LLVector4(0.f, mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 1.f);
 873	mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
 874	mManipulatorVertices[numManips] = LLVector4(0.f, mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 1.f);
 875	mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
 876	if (llabs(relative_camera_dir.mV[VX]) > MIN_PLANE_MANIP_DOT_PRODUCT)
 877	{
 878		planar_manip_yz_visible = TRUE;
 879	}
 880
 881	mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 0.f, mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 1.f);
 882	mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
 883	mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 0.f, mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 1.f);
 884	mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
 885	if (llabs(relative_camera_dir.mV[VY]) > MIN_PLANE_MANIP_DOT_PRODUCT)
 886	{
 887		planar_manip_xz_visible = TRUE;
 888	}
 889
 890	mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 0.f, 1.f);
 891	mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
 892	mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 0.f, 1.f);
 893	mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
 894	if (llabs(relative_camera_dir.mV[VZ]) > MIN_PLANE_MANIP_DOT_PRODUCT)
 895	{
 896		planar_manip_xy_visible = TRUE;
 897	}
 898
 899	// Project up to 9 manipulators to screen space 2*X, 2*Y, 2*Z, 3*planes
 900	std::vector<ManipulatorHandle> projected_manipulators;
 901	projected_manipulators.reserve(9);
 902	
 903	for (S32 i = 0; i < num_arrow_manips; i+= 2)
 904	{
 905		LLVector4 projected_start = mManipulatorVertices[i] * transform;
 906		projected_start = projected_start * (1.f / projected_start.mV[VW]);
 907
 908		LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
 909		projected_end = projected_end * (1.f / projected_end.mV[VW]);
 910
 911		ManipulatorHandle projected_manip(
 912				LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]), 
 913				LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]), 
 914				MANIPULATOR_IDS[i / 2],
 915				10.f); // 10 pixel hotspot for arrows
 916		projected_manipulators.push_back(projected_manip);
 917	}
 918
 919	if (planar_manip_yz_visible)
 920	{
 921		S32 i = num_arrow_manips;
 922		LLVector4 projected_start = mManipulatorVertices[i] * transform;
 923		projected_start = projected_start * (1.f / projected_start.mV[VW]);
 924
 925		LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
 926		projected_end = projected_end * (1.f / projected_end.mV[VW]);
 927
 928		ManipulatorHandle projected_manip(
 929				LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]), 
 930				LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]), 
 931				MANIPULATOR_IDS[i / 2],
 932				20.f); // 20 pixels for planar manipulators
 933		projected_manipulators.push_back(projected_manip);
 934	}
 935
 936	if (planar_manip_xz_visible)
 937	{
 938		S32 i = num_arrow_manips + 2;
 939		LLVector4 projected_start = mManipulatorVertices[i] * transform;
 940		projected_start = projected_start * (1.f / projected_start.mV[VW]);
 941
 942		LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
 943		projected_end = projected_end * (1.f / projected_end.mV[VW]);
 944
 945		ManipulatorHandle projected_manip(
 946				LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]), 
 947				LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]), 
 948				MANIPULATOR_IDS[i / 2],
 949				20.f); // 20 pixels for planar manipulators
 950		projected_manipulators.push_back(projected_manip);
 951	}
 952
 953	if (planar_manip_xy_visible)
 954	{
 955		S32 i = num_arrow_manips + 4;
 956		LLVector4 projected_start = mManipulatorVertices[i] * transform;
 957		projected_start = projected_start * (1.f / projected_start.mV[VW]);
 958
 959		LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
 960		projected_end = projected_end * (1.f / projected_end.mV[VW]);
 961
 962		ManipulatorHandle projected_manip(
 963				LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]), 
 964				LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]), 
 965				MANIPULATOR_IDS[i / 2],
 966				20.f); // 20 pixels for planar manipulators
 967		projected_manipulators.push_back(projected_manip);
 968	}
 969
 970	LLVector2 manip_start_2d;
 971	LLVector2 manip_end_2d;
 972	LLVector2 manip_dir;
 973	LLRect world_view_rect = gViewerWindow->getWorldViewRectScaled();
 974	F32 half_width = (F32)world_view_rect.getWidth() / 2.f;
 975	F32 half_height = (F32)world_view_rect.getHeight() / 2.f;
 976	LLVector2 mousePos((F32)x - half_width, (F32)y - half_height);
 977	LLVector2 mouse_delta;
 978
 979	// Keep order consistent with insertion via stable_sort
 980	std::stable_sort( projected_manipulators.begin(),
 981		projected_manipulators.end(),
 982		ClosestToCamera() );
 983
 984	std::vector<ManipulatorHandle>::iterator it = projected_manipulators.begin();
 985	for ( ; it != projected_manipulators.end(); ++it)
 986	{
 987		ManipulatorHandle& manipulator = *it;
 988		{
 989			manip_start_2d.setVec(manipulator.mStartPosition.mV[VX] * half_width, manipulator.mStartPosition.mV[VY] * half_height);
 990			manip_end_2d.setVec(manipulator.mEndPosition.mV[VX] * half_width, manipulator.mEndPosition.mV[VY] * half_height);
 991			manip_dir = manip_end_2d - manip_start_2d;
 992
 993			mouse_delta = mousePos - manip_start_2d;
 994
 995			F32 manip_length = manip_dir.normVec();
 996
 997			F32 mouse_pos_manip = mouse_delta * manip_dir;
 998			F32 mouse_dist_manip_squared = mouse_delta.magVecSquared() - (mouse_pos_manip * mouse_pos_manip);
 999
1000			if (mouse_pos_manip > 0.f &&
1001				mouse_pos_manip < manip_length &&
1002				mouse_dist_manip_squared < manipulator.mHotSpotRadius * manipulator.mHotSpotRadius)
1003			{
1004				mHighlightedPart = manipulator.mManipID;
1005				break;
1006			}
1007		}
1008	}
1009}
1010
1011F32 LLManipTranslate::getMinGridScale()
1012{
1013	F32 scale;
1014	switch (mManipPart)
1015	{
1016	case LL_NO_PART:
1017	default:
1018		scale = 1.f;
1019		break;
1020	case LL_X_ARROW:
1021		scale = mGridScale.mV[VX];
1022		break;
1023	case LL_Y_ARROW:
1024		scale = mGridScale.mV[VY];
1025		break;
1026	case LL_Z_ARROW:
1027		scale = mGridScale.mV[VZ];
1028		break;
1029	case LL_YZ_PLANE:
1030		scale = llmin(mGridScale.mV[VY], mGridScale.mV[VZ]);
1031		break;
1032	case LL_XZ_PLANE:
1033		scale = llmin(mGridScale.mV[VX], mGridScale.mV[VZ]);
1034		break;
1035	case LL_XY_PLANE:
1036		scale = llmin(mGridScale.mV[VX], mGridScale.mV[VY]);
1037		break;
1038	}
1039
1040	return scale;
1041}
1042
1043
1044BOOL LLManipTranslate::handleMouseUp(S32 x, S32 y, MASK mask)
1045{
1046	// first, perform normal processing in case this was a quick-click
1047	handleHover(x, y, mask);
1048
1049	if(hasMouseCapture())
1050	{
1051		// make sure arrow colors go back to normal
1052		mManipPart = LL_NO_PART;
1053		LLSelectMgr::getInstance()->enableSilhouette(TRUE);
1054
1055		// Might have missed last update due to UPDATE_DELAY timing.
1056		LLSelectMgr::getInstance()->sendMultipleUpdate( UPD_POSITION );
1057		
1058		mInSnapRegime = FALSE;
1059		LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
1060		//gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
1061	}
1062
1063	return LLManip::handleMouseUp(x, y, mask);
1064}
1065
1066
1067void LLManipTranslate::render()
1068{
1069	gGL.matrixMode(LLRender::MM_MODELVIEW);
1070	gGL.pushMatrix();
1071	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
1072	{
1073		F32 zoom = gAgentCamera.mHUDCurZoom;
1074		gGL.scalef(zoom, zoom, zoom);
1075	}
1076	{
1077		LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
1078		renderGuidelines();
1079	}
1080	{
1081		renderTranslationHandles();
1082		renderSnapGuides();
1083	}
1084	gGL.popMatrix();
1085
1086	renderText();
1087}
1088
1089void LLManipTranslate::renderSnapGuides()
1090{
1091	if (!gSavedSettings.getBOOL("SnapEnabled"))
1092	{
1093		return;
1094	}
1095
1096	F32 max_subdivisions = sGridMaxSubdivisionLevel;//(F32)gSavedSettings.getS32("GridSubdivision");
1097	F32 line_alpha = gSavedSettings.getF32("GridOpacity");
1098
1099	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
1100	LLGLDepthTest gls_depth(GL_TRUE);
1101	LLGLDisable gls_cull(GL_CULL_FACE);
1102	LLVector3 translate_axis;
1103
1104	if (mManipPart == LL_NO_PART)
1105	{
1106		return;
1107	}
1108
1109	LLSelectNode *first_node = mObjectSelection->getFirstMoveableNode(TRUE);
1110	if (!first_node)
1111	{
1112		return;
1113	}
1114	
1115	updateGridSettings();
1116
1117	F32 smallest_grid_unit_scale = getMinGridScale() / max_subdivisions;
1118	LLVector3 grid_origin;
1119	LLVector3 grid_scale;
1120	LLQuaternion grid_rotation;
1121
1122	LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
1123	LLVector3 saved_selection_center = getSavedPivotPoint(); //LLSelectMgr::getInstance()->getSavedBBoxOfSelection().getCenterAgent();
1124	LLVector3 selection_center = getPivotPoint();
1125
1126	LLViewerObject *first_object = first_node->getObject();
1127
1128	//pick appropriate projection plane for snap rulers according to relative camera position
1129	if (mManipPart >= LL_X_ARROW && mManipPart <= LL_Z_ARROW)
1130	{
1131		LLVector3 normal;
1132		LLColor4 inner_color;
1133		LLManip::EManipPart temp_manip = mManipPart;
1134		switch (mManipPart)
1135		{
1136		case LL_X_ARROW:
1137			normal.setVec(1,0,0);
1138			inner_color.setVec(0,1,1,line_alpha);
1139			mManipPart = LL_YZ_PLANE;
1140			break;
1141		case LL_Y_ARROW:
1142			normal.setVec(0,1,0);
1143			inner_color.setVec(1,0,1,line_alpha);
1144			mManipPart = LL_XZ_PLANE;
1145			break;
1146		case LL_Z_ARROW:
1147			normal.setVec(0,0,1);
1148			inner_color.setVec(1,1,0,line_alpha);
1149			mManipPart = LL_XY_PLANE;
1150			break;
1151		default:
1152			break;
1153		}
1154
1155		highlightIntersection(normal, selection_center, grid_rotation, inner_color);
1156		mManipPart = temp_manip;
1157		getManipAxis(first_object, mManipPart, translate_axis);
1158
1159		LLVector3 at_axis_abs;
1160		if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
1161		{
1162			at_axis_abs = LLVector3::x_axis * ~grid_rotation;
1163		}
1164		else
1165		{
1166			at_axis_abs = saved_selection_center - LLViewerCamera::getInstance()->getOrigin();
1167			at_axis_abs.normVec();
1168
1169			at_axis_abs = at_axis_abs * ~grid_rotation;
1170		}
1171		at_axis_abs.abs();
1172
1173		if (at_axis_abs.mV[VX] > at_axis_abs.mV[VY] && at_axis_abs.mV[VX] > at_axis_abs.mV[VZ])
1174		{
1175			if (mManipPart == LL_Y_ARROW)
1176			{
1177				mSnapOffsetAxis = LLVector3::z_axis;
1178			}
1179			else if (mManipPart == LL_Z_ARROW)
1180			{
1181				mSnapOffsetAxis = LLVector3::y_axis;
1182			}
1183			else if (at_axis_abs.mV[VY] > at_axis_abs.mV[VZ])
1184			{
1185				mSnapOffsetAxis = LLVector3::z_axis;
1186			}
1187			else
1188			{
1189				mSnapOffsetAxis = LLVector3::y_axis;
1190			}
1191		}
1192		else if (at_axis_abs.mV[VY] > at_axis_abs.mV[VZ])
1193		{
1194			if (mManipPart == LL_X_ARROW)
1195			{
1196				mSnapOffsetAxis = LLVector3::z_axis;
1197			}
1198			else if (mManipPart == LL_Z_ARROW)
1199			{
1200				mSnapOffsetAxis = LLVector3::x_axis;
1201			}
1202			else if (at_axis_abs.mV[VX] > at_axis_abs.mV[VZ])
1203			{
1204				mSnapOffsetAxis = LLVector3::z_axis;
1205			}
1206			else
1207			{
1208				mSnapOffsetAxis = LLVector3::x_axis;
1209			}
1210		}
1211		else
1212		{
1213			if (mManipPart == LL_X_ARROW)
1214			{
1215				mSnapOffsetAxis = LLVector3::y_axis;
1216			}
1217			else if (mManipPart == LL_Y_ARROW)
1218			{
1219				mSnapOffsetAxis = LLVector3::x_axis;
1220			}
1221			else if (at_axis_abs.mV[VX] > at_axis_abs.mV[VY])
1222			{
1223				mSnapOffsetAxis = LLVector3::y_axis;
1224			}
1225			else
1226			{
1227				mSnapOffsetAxis = LLVector3::x_axis;
1228			}
1229		}
1230
1231		mSnapOffsetAxis = mSnapOffsetAxis * grid_rotation;
1232
1233		F32 guide_size_meters;
1234
1235		if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
1236		{
1237			guide_size_meters = 1.f / gAgentCamera.mHUDCurZoom;
1238			mSnapOffsetMeters = mArrowLengthMeters * 1.5f;
1239		}
1240		else
1241		{
1242			LLVector3 cam_to_selection = getPivotPoint() - LLViewerCamera::getInstance()->getOrigin();
1243			F32 current_range = cam_to_selection.normVec();
1244			guide_size_meters = SNAP_GUIDE_SCREEN_SIZE * gViewerWindow->getWorldViewHeightRaw() * current_range / LLViewerCamera::getInstance()->getPixelMeterRatio();
1245	
1246			F32 fraction_of_fov = mAxisArrowLength / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
1247			F32 apparent_angle = fraction_of_fov * LLViewerCamera::getInstance()->getView();  // radians
1248			F32 offset_at_camera = tan(apparent_angle) * 1.5f;
1249			F32 range = dist_vec(gAgent.getPosAgentFromGlobal(first_node->mSavedPositionGlobal), LLViewerCamera::getInstance()->getOrigin());
1250			mSnapOffsetMeters = range * offset_at_camera;
1251		}
1252
1253		LLVector3 tick_start;
1254		LLVector3 tick_end;
1255
1256		// how far away from grid origin is the selection along the axis of translation?
1257		F32 dist_grid_axis = (selection_center - mGridOrigin) * translate_axis;
1258		// find distance to nearest smallest grid unit
1259		F32 offset_nearest_grid_unit = fmodf(dist_grid_axis, smallest_grid_unit_scale);
1260		// how many smallest grid units are we away from largest grid scale?
1261		S32 sub_div_offset = llround(fmod(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() / sGridMinSubdivisionLevel) / smallest_grid_unit_scale);
1262		S32 num_ticks_per_side = llmax(1, llfloor(0.5f * guide_size_meters / smallest_grid_unit_scale));
1263
1264		LLGLDepthTest gls_depth(GL_FALSE);
1265
1266		for (S32 pass = 0; pass < 3; pass++)
1267		{
1268			LLColor4 line_color = setupSnapGuideRenderPass(pass);
1269
1270			gGL.begin(LLRender::LINES);
1271			{
1272				LLVector3 line_start = selection_center + (mSnapOffsetMeters * mSnapOffsetAxis) + (translate_axis * (guide_size_meters * 0.5f + offset_nearest_grid_unit));
1273				LLVector3 line_end = selection_center + (mSnapOffsetMeters * mSnapOffsetAxis) - (translate_axis * (guide_size_meters * 0.5f + offset_nearest_grid_unit));
1274				LLVector3 line_mid = (line_start + line_end) * 0.5f;
1275
1276				gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
1277				gGL.vertex3fv(line_start.mV);
1278				gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
1279				gGL.vertex3fv(line_mid.mV);
1280				gGL.vertex3fv(line_mid.mV);
1281				gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
1282				gGL.vertex3fv(line_end.mV);
1283
1284				line_start.setVec(selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) + (translate_axis * guide_size_meters * 0.5f));
1285				line_end.setVec(selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) - (translate_axis * guide_size_meters * 0.5f));
1286				line_mid = (line_start + line_end) * 0.5f;
1287
1288				gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
1289				gGL.vertex3fv(line_start.mV);
1290				gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
1291				gGL.vertex3fv(line_mid.mV);
1292				gGL.vertex3fv(line_mid.mV);
1293				gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
1294				gGL.vertex3fv(line_end.mV);
1295
1296				for (S32 i = -num_ticks_per_side; i <= num_ticks_per_side; i++)
1297				{
1298					tick_start = selection_center + (translate_axis * (smallest_grid_unit_scale * (F32)i - offset_nearest_grid_unit));
1299
1300					F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_start, translate_axis, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
1301
1302					if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / cur_subdivisions)) != 0.f)
1303					{
1304						continue;
1305					}
1306
1307					// add in off-axis offset
1308					tick_start += (mSnapOffsetAxis * mSnapOffsetMeters);
1309
1310					BOOL is_sub_tick = FALSE;
1311					F32 tick_scale = 1.f;
1312					for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
1313					{
1314						if (fmodf((F32)(i + sub_div_offset), division_level) == 0.f)
1315						{
1316							break;
1317						}
1318						tick_scale *= 0.7f;
1319						is_sub_tick = TRUE;
1320					}
1321
1322// 					S32 num_ticks_to_fade = is_sub_tick ? num_ticks_per_side / 2 : num_ticks_per_side;
1323// 					F32 alpha = line_alpha * (1.f - (0.8f *  ((F32)llabs(i) / (F32)num_ticks_to_fade)));
1324
1325					tick_end = tick_start + (mSnapOffsetAxis * mSnapOffsetMeters * tick_scale);
1326
1327					gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
1328					gGL.vertex3fv(tick_start.mV);
1329					gGL.vertex3fv(tick_end.mV);
1330
1331					tick_start = selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) +
1332						(translate_axis * (getMinGridScale() / (F32)(max_subdivisions) * (F32)i - offset_nearest_grid_unit));
1333					tick_end = tick_start - (mSnapOffsetAxis * mSnapOffsetMeters * tick_scale);
1334
1335					gGL.vertex3fv(tick_start.mV);
1336					gGL.vertex3fv(tick_end.mV);
1337				}
1338			}
1339			gGL.end();
1340
1341			if (mInSnapRegime)
1342			{
1343				LLVector3 line_start = selection_center - mSnapOffsetAxis * mSnapOffsetMeters;
1344				LLVector3 line_end = selection_center + mSnapOffsetAxis * mSnapOffsetMeters;
1345
1346				gGL.begin(LLRender::LINES);
1347				{
1348					gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
1349
1350					gGL.vertex3fv(line_start.mV);
1351					gGL.vertex3fv(line_end.mV);
1352				}
1353				gGL.end();
1354
1355				// draw snap guide arrow
1356				gGL.begin(LLRender::TRIANGLES);
1357				{
1358					gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
1359
1360					LLVector3 arrow_dir;
1361					LLVector3 arrow_span = translate_axis;
1362
1363					arrow_dir = -mSnapOffsetAxis;
1364					gGL.vertex3fv((line_start + arrow_dir * mConeSize * SNAP_ARROW_SCALE).mV);
1365					gGL.vertex3fv((line_start + arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
1366					gGL.vertex3fv((line_start - arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
1367
1368					arrow_dir = mSnapOffsetAxis;
1369					gGL.vertex3fv((line_end + arrow_dir * mConeSize * SNAP_ARROW_SCALE).mV);
1370					gGL.vertex3fv((line_end + arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
1371					gGL.vertex3fv((line_end - arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
1372				}
1373				gGL.end();
1374			}
1375		}
1376
1377		sub_div_offset = llround(fmod(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() * 32.f) / smallest_grid_unit_scale);
1378
1379		LLVector2 screen_translate_axis(llabs(translate_axis * LLViewerCamera::getInstance()->getLeftAxis()), llabs(translate_axis * LLViewerCamera::getInstance()->getUpAxis()));
1380		screen_translate_axis.normVec();
1381
1382		S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing);
1383        
1384		// render tickmark values
1385		for (S32 i = -num_ticks_per_side; i <= num_ticks_per_side; i++)
1386		{
1387			LLVector3 tick_pos = selection_center + (translate_axis * ((smallest_grid_unit_scale * (F32)i) - offset_nearest_grid_unit));
1388			F32 alpha = line_alpha * (1.f - (0.5f *  ((F32)llabs(i) / (F32)num_ticks_per_side)));
1389
1390			F32 tick_scale = 1.f;
1391			for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
1392			{
1393				if (fmodf((F32)(i + sub_div_offset), division_level) == 0.f)
1394				{
1395					break;
1396				}
1397				tick_scale *= 0.7f;
1398			}
1399
1400			if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / llmin(sGridMaxSubdivisionLevel, getSubdivisionLevel(tick_pos, translate_axis, getMinGridScale(), tick_label_spacing)))) == 0.f)
1401			{
1402				F32 snap_offset_meters;
1403
1404				if (mSnapOffsetAxis * LLViewerCamera::getInstance()->getUpAxis() > 0.f)
1405				{
1406					snap_offset_meters = mSnapOffsetMeters;			
1407				}
1408				else
1409				{
1410					snap_offset_meters = -mSnapOffsetMeters;
1411				}
1412				LLVector3 text_origin = selection_center + 
1413						(translate_axis * ((smallest_grid_unit_scale * (F32)i) - offset_nearest_grid_unit)) + 
1414							(mSnapOffsetAxis * snap_offset_meters * (1.f + tick_scale));
1415				
1416				LLVector3 tick_offset = (tick_pos - mGridOrigin) * ~mGridRotation;
1417				F32 offset_val = 0.5f * tick_offset.mV[ARROW_TO_AXIS[mManipPart]] / getMinGridScale();
1418				EGridMode grid_mode = LLSelectMgr::getInstance()->getGridMode();
1419				F32 text_highlight = 0.8f;
1420				if(i - llround(offset_nearest_grid_unit / smallest_grid_unit_scale) == 0 && mInSnapRegime)
1421				{
1422					text_highlight = 1.f;
1423				}
1424				
1425				if (grid_mode == GRID_MODE_WORLD)
1426				{
1427					// rescale units to meters from multiple of grid scale
1428					offset_val *= 2.f * grid_scale[ARROW_TO_AXIS[mManipPart]];
1429					renderTickValue(text_origin, offset_val, std::string("m"), LLColor4(text_highlight, text_highlight, text_highlight, alpha));
1430				}
1431				else
1432				{
1433					renderTickValue(text_origin, offset_val, std::string("x"), LLColor4(text_highlight, text_highlight, text_highlight, alpha));
1434				}
1435			}
1436		}
1437		if (mObjectSelection->getSelectType() != SELECT_TYPE_HUD)
1438		{
1439			// render helpful text
1440			if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText)
1441			{
1442				F32 snap_offset_meters_up;
1443				if (mSnapOffsetAxis * LLViewerCamera::getInstance()->getUpAxis() > 0.f)
1444				{
1445					snap_offset_meters_up = mSnapOffsetMeters;			
1446				}
1447				else
1448				{
1449					snap_offset_meters_up = -mSnapOffsetMeters;
1450				}
1451
1452				LLVector3 selection_center_start = getSavedPivotPoint();//LLSelectMgr::getInstance()->getSavedBBoxOfSelection().getCenterAgent();
1453
1454				LLVector3 help_text_pos = selection_center_start + (snap_offset_meters_up * 3.f * mSnapOffsetAxis);
1455				const LLFontGL* big_fontp = LLFontGL::getFontSansSerif();
1456
1457				std::string help_text = "Move mouse cursor over ruler";
1458				LLColor4 help_text_color = LLColor4::white;
1459				help_text_color.mV[VALPHA] = clamp_rescale(mHelpTextTimer.getElapsedTimeF32(), sHelpTextVisibleTime, sHelpTextVisibleTime + sHelpTextFadeTime, line_alpha, 0.f);
1460				hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, mObjectSelection->getSelectType() == SELECT_TYPE_HUD);
1461				help_text = "to snap to grid";
1462				help_text_pos -= LLViewerCamera::getInstance()->getUpAxis() * mSnapOffsetMeters * 0.2f;
1463				hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, mObjectSelection->getSelectType() == SELECT_TYPE_HUD);
1464			}
1465		}
1466	}
1467	else
1468	{
1469		// render gridlines for planar snapping
1470
1471		F32 u = 0, v = 0;
1472        LLColor4 inner_color;
1473		LLVector3 normal;
1474		LLVector3 grid_center = selection_center - grid_origin;
1475		F32 usc = 1;
1476		F32 vsc = 1;
1477		
1478		grid_center *= ~grid_rotation;
1479
1480		switch (mManipPart)
1481		{
1482		case LL_YZ_PLANE:
1483			u = grid_center.mV[VY];
1484			v = grid_center.mV[VZ];
1485			usc = grid_scale.mV[VY];
1486			vsc = grid_scale.mV[VZ];
1487			inner_color.setVec(0,1,1,line_alpha);
1488			normal.setVec(1,0,0);
1489			break;
1490		case LL_XZ_PLANE:
1491			u = grid_center.mV[VX];
1492			v = grid_center.mV[VZ];
1493			usc = grid_scale.mV[VX];
1494			vsc = grid_scale.mV[VZ];
1495			inner_color.setVec(1,0,1,line_alpha);
1496			normal.setVec(0,1,0);
1497			break;
1498		case LL_XY_PLANE:
1499			u = grid_center.mV[VX];
1500			v = grid_center.mV[VY];
1501			usc = grid_scale.mV[VX];
1502			vsc = grid_scale.mV[VY];
1503			inner_color.setVec(1,1,0,line_alpha);
1504			normal.setVec(0,0,1);
1505			break;
1506		default:
1507			break;
1508		}
1509
1510		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
1511		highlightIntersection(normal, selection_center, grid_rotation, inner_color);
1512
1513		gGL.pushMatrix();
1514
1515		F32 x,y,z,angle_radians;
1516		grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
1517		gGL.translatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
1518		gGL.rotatef(angle_radians * RAD_TO_DEG, x, y, z);
1519		
1520		F32 sz = mGridSizeMeters;
1521		F32 tiles = sz;
1522
1523		gGL.matrixMode(LLRender::MM_TEXTURE);
1524		gGL.pushMatrix();
1525		usc = 1.0f/usc;
1526		vsc = 1.0f/vsc;
1527		
1528		while (usc > vsc*4.0f)
1529		{
1530			usc *= 0.5f;
1531		}
1532		while (vsc > usc * 4.0f)
1533		{
1534			vsc *= 0.5f;
1535		}
1536
1537		gGL.scalef(usc, vsc, 1.0f);
1538		gGL.translatef(u, v, 0);
1539		
1540		float a = line_alpha;
1541
1542		LLColor4 col = LLUIColorTable::instance().getColor("SilhouetteChildColor");
1543		{
1544			//draw grid behind objects
1545			LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
1546
1547			{
1548				LLGLDisable stencil(GL_STENCIL_TEST);
1549				{
1550					LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GREATER);
1551					gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, getGridTexName());
1552					gGL.flush();
1553					gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE_MINUS_SOURCE_AL

Large files files are truncated, but you can click here to view the full file