Files
SingularityViewer/indra/newview/llmaniptranslate.cpp
Shyotl e756140e1d Innitial commit of experimental v2 texture system port work. Compiles and runs on windows, at least. Fixing bugs as they come.
Need to test:
localassetbrowser
preview related floaters
hgfloatertexteditor
maps
media textures! Currently very hacky
web browser
alpha masks on avatars
bumpmaps
Are all sky components appearing?
LLViewerDynamicTexture (texture baking, browser, animated textures, anim previews, etc)
Snapshot related features
Customize avatar
vfs floater
UI textures in general
Texture priority issues
2011-03-31 03:22:01 -05:00

2286 lines
72 KiB
C++

/**
* @file llmaniptranslate.cpp
* @brief LLManipTranslate class implementation
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
/**
* Positioning tool
*/
#include "llviewerprecompiledheaders.h"
#include "llmaniptranslate.h"
#include "llgl.h"
#include "llrender.h"
#include "llagent.h"
#include "llbbox.h"
#include "llbox.h"
#include "llviewercontrol.h"
#include "llcriticaldamp.h"
#include "llcylinder.h"
#include "lldrawable.h"
#include "llfloatertools.h"
#include "llfontgl.h"
#include "llglheaders.h"
#include "llhudrender.h"
#include "llresmgr.h"
#include "llselectmgr.h"
#include "llrendersphere.h"
#include "llstatusbar.h"
#include "lltoolmgr.h"
#include "llviewercamera.h"
#include "llviewerjoint.h"
#include "llviewerobject.h"
#include "llviewerwindow.h"
#include "llvoavatar.h"
#include "llworld.h"
#include "llui.h"
#include "pipeline.h"
// [RLVa:KB]
#include "rlvhandler.h"
// [/RLVa:KB]
const S32 NUM_AXES = 3;
const S32 MOUSE_DRAG_SLOP = 2; // pixels
const F32 HANDLE_HIDE_ANGLE = 0.15f; // radians
const F32 SELECTED_ARROW_SCALE = 1.3f;
const F32 MANIPULATOR_HOTSPOT_START = 0.2f;
const F32 MANIPULATOR_HOTSPOT_END = 1.2f;
const F32 SNAP_GUIDE_SCREEN_SIZE = 0.7f;
const F32 MIN_PLANE_MANIP_DOT_PRODUCT = 0.25f;
const F32 PLANE_TICK_SIZE = 0.4f;
const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
const F32 SNAP_ARROW_SCALE = 0.7f;
static LLPointer<LLViewerTexture> sGridTex = NULL ;
const LLManip::EManipPart MANIPULATOR_IDS[9] =
{
LLManip::LL_X_ARROW,
LLManip::LL_Y_ARROW,
LLManip::LL_Z_ARROW,
LLManip::LL_X_ARROW,
LLManip::LL_Y_ARROW,
LLManip::LL_Z_ARROW,
LLManip::LL_YZ_PLANE,
LLManip::LL_XZ_PLANE,
LLManip::LL_XY_PLANE
};
const U32 ARROW_TO_AXIS[4] =
{
VX,
VX,
VY,
VZ
};
LLManipTranslate::LLManipTranslate( LLToolComposite* composite )
: LLManip( std::string("Move"), composite ),
mLastHoverMouseX(-1),
mLastHoverMouseY(-1),
mSendUpdateOnMouseUp(FALSE),
mMouseOutsideSlop(FALSE),
mCopyMadeThisDrag(FALSE),
mMouseDownX(-1),
mMouseDownY(-1),
mAxisArrowLength(50),
mConeSize(0),
mArrowLengthMeters(0.f),
mPlaneManipOffsetMeters(0.f),
mUpdateTimer(),
mSnapOffsetMeters(0.f),
mArrowScales(1.f, 1.f, 1.f),
mPlaneScales(1.f, 1.f, 1.f),
mPlaneManipPositions(1.f, 1.f, 1.f, 1.f)
{
if (sGridTex.isNull())
{
restoreGL();
}
}
//static
U32 LLManipTranslate::getGridTexName()
{
if(sGridTex.isNull())
{
restoreGL() ;
}
return sGridTex.isNull() ? 0 : sGridTex->getTexName() ;
}
//static
void LLManipTranslate::destroyGL()
{
if (sGridTex)
{
sGridTex = NULL ;
}
}
//static
void LLManipTranslate::restoreGL()
{
//generate grid texture
U32 rez = 512;
U32 mip = 0;
destroyGL() ;
sGridTex = LLViewerTextureManager::getLocalTexture() ;
if(!sGridTex->createGLTexture())
{
sGridTex = NULL ;
return ;
}
GLuint* d = new GLuint[rez*rez];
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, sGridTex->getTexName(), true);
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR);
while (rez >= 1)
{
for (U32 i = 0; i < rez*rez; i++)
{
d[i] = 0x00FFFFFF;
}
U32 subcol = 0xFFFFFFFF;
if (rez >= 4)
{ //large grain grid
for (U32 i = 0; i < rez; i++)
{
if (rez <= 16)
{
if (rez == 16)
{
subcol = 0xA0FFFFFF;
}
else if (rez == 8)
{
subcol = 0x80FFFFFF;
}
else
{
subcol = 0x40FFFFFF;
}
}
else
{
subcol = 0xFFFFFFFF;
}
d[i *rez+ 0 ] = subcol;
d[0 *rez+ i ] = subcol;
if (rez >= 32)
{
d[i *rez+ (rez-1)] = subcol;
d[(rez-1) *rez+ i ] = subcol;
}
if (rez >= 64)
{
subcol = 0xFFFFFFFF;
if (i > 0 && i < (rez-1))
{
d[i *rez+ 1 ] = subcol;
d[i *rez+ (rez-2)] = subcol;
d[1 *rez+ i ] = subcol;
d[(rez-2) *rez+ i ] = subcol;
}
}
}
}
subcol = 0x50A0A0A0;
if (rez >= 128)
{ //small grain grid
for (U32 i = 8; i < rez; i+=8)
{
for (U32 j = 2; j < rez-2; j++)
{
d[i *rez+ j] = subcol;
d[j *rez+ i] = subcol;
}
}
}
if (rez >= 64)
{ //medium grain grid
if (rez == 64)
{
subcol = 0x50A0A0A0;
}
else
{
subcol = 0xA0D0D0D0;
}
for (U32 i = 32; i < rez; i+=32)
{
U32 pi = i-1;
for (U32 j = 2; j < rez-2; j++)
{
d[i *rez+ j] = subcol;
d[j *rez+ i] = subcol;
if (rez > 128)
{
d[pi *rez+ j] = subcol;
d[j *rez+ pi] = subcol;
}
}
}
}
#ifdef LL_WINDOWS
LLImageGL::setManualImage(GL_TEXTURE_2D, mip, GL_RGBA, rez, rez, GL_RGBA, GL_UNSIGNED_BYTE, d);
#else
LLImageGL::setManualImage(GL_TEXTURE_2D, mip, GL_RGBA, rez, rez, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, d);
#endif
rez = rez >> 1;
mip++;
}
delete [] d;
}
LLManipTranslate::~LLManipTranslate()
{
for_each(mProjectedManipulators.begin(), mProjectedManipulators.end(), DeletePointer());
}
void LLManipTranslate::handleSelect()
{
LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
gFloaterTools->setStatusText("move");
LLManip::handleSelect();
}
BOOL LLManipTranslate::handleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
// didn't click in any UI object, so must have clicked in the world
if( (mHighlightedPart == LL_X_ARROW ||
mHighlightedPart == LL_Y_ARROW ||
mHighlightedPart == LL_Z_ARROW ||
mHighlightedPart == LL_YZ_PLANE ||
mHighlightedPart == LL_XZ_PLANE ||
mHighlightedPart == LL_XY_PLANE ) )
{
handled = handleMouseDownOnPart( x, y, mask );
}
return handled;
}
// Assumes that one of the arrows on an object was hit.
BOOL LLManipTranslate::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
{
BOOL can_move = canAffectSelection();
if (!can_move)
{
return FALSE;
}
highlightManipulators(x, y);
S32 hit_part = mHighlightedPart;
if( (hit_part != LL_X_ARROW) &&
(hit_part != LL_Y_ARROW) &&
(hit_part != LL_Z_ARROW) &&
(hit_part != LL_YZ_PLANE) &&
(hit_part != LL_XZ_PLANE) &&
(hit_part != LL_XY_PLANE) )
{
return TRUE;
}
mHelpTextTimer.reset();
sNumTimesHelpTextShown++;
LLSelectMgr::getInstance()->getGrid(mGridOrigin, mGridRotation, mGridScale);
LLSelectMgr::getInstance()->enableSilhouette(FALSE);
// we just started a drag, so save initial object positions
LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_MOVE);
mManipPart = (EManipPart)hit_part;
mMouseDownX = x;
mMouseDownY = y;
mMouseOutsideSlop = FALSE;
LLVector3 axis;
LLSelectNode *selectNode = mObjectSelection->getFirstMoveableNode(TRUE);
if (!selectNode)
{
// didn't find the object in our selection...oh well
llwarns << "Trying to translate an unselected object" << llendl;
return TRUE;
}
LLViewerObject *selected_object = selectNode->getObject();
if (!selected_object)
{
// somehow we lost the object!
llwarns << "Translate manip lost the object, no selected object" << llendl;
gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
return TRUE;
}
// Compute unit vectors for arrow hit and a plane through that vector
BOOL axis_exists = getManipAxis(selected_object, mManipPart, axis);
getManipNormal(selected_object, mManipPart, mManipNormal);
//LLVector3 select_center_agent = gAgent.getPosAgentFromGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal());
// TomY: The above should (?) be identical to the below
LLVector3 select_center_agent = getPivotPoint();
mSubdivisions = llclamp(getSubdivisionLevel(select_center_agent, axis_exists ? axis : LLVector3::z_axis, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
// if we clicked on a planar manipulator, recenter mouse cursor
if (mManipPart >= LL_YZ_PLANE && mManipPart <= LL_XY_PLANE)
{
LLCoordGL mouse_pos;
if (!LLViewerCamera::getInstance()->projectPosAgentToScreen(select_center_agent, mouse_pos))
{
// mouse_pos may be nonsense
llwarns << "Failed to project object center to screen" << llendl;
}
else if (gSavedSettings.getBOOL("SnapToMouseCursor"))
{
LLUI::setCursorPositionScreen(mouse_pos.mX, mouse_pos.mY);
x = mouse_pos.mX;
y = mouse_pos.mY;
}
}
LLSelectMgr::getInstance()->updateSelectionCenter();
LLVector3d object_start_global = gAgent.getPosGlobalFromAgent(getPivotPoint());
getMousePointOnPlaneGlobal(mDragCursorStartGlobal, x, y, object_start_global, mManipNormal);
mDragSelectionStartGlobal = object_start_global;
mCopyMadeThisDrag = FALSE;
// Route future Mouse messages here preemptively. (Release on mouse up.)
setMouseCapture( TRUE );
return TRUE;
}
BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
{
// Translation tool only works if mouse button is down.
// Bail out if mouse not down.
if( !hasMouseCapture() )
{
lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (inactive)" << llendl;
// Always show cursor
// gViewerWindow->setCursor(UI_CURSOR_ARROW);
gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
highlightManipulators(x, y);
return TRUE;
}
// Handle auto-rotation if necessary.
const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
const S32 ROTATE_H_MARGIN = gViewerWindow->getWindowWidth() / 20;
const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
BOOL rotated = FALSE;
// ...build mode moves camera about focus point
if (mObjectSelection->getSelectType() != SELECT_TYPE_HUD)
{
if (x < ROTATE_H_MARGIN)
{
gAgent.cameraOrbitAround(rotate_angle);
rotated = TRUE;
}
else if (x > gViewerWindow->getWindowWidth() - ROTATE_H_MARGIN)
{
gAgent.cameraOrbitAround(-rotate_angle);
rotated = TRUE;
}
}
// Suppress processing if mouse hasn't actually moved.
// This may cause problems if the camera moves outside of the
// rotation above.
if( x == mLastHoverMouseX && y == mLastHoverMouseY && !rotated)
{
lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (mouse unmoved)" << llendl;
gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
return TRUE;
}
mLastHoverMouseX = x;
mLastHoverMouseY = y;
// Suppress if mouse hasn't moved past the initial slop region
// Reset once we start moving
if( !mMouseOutsideSlop )
{
if (abs(mMouseDownX - x) < MOUSE_DRAG_SLOP && abs(mMouseDownY - y) < MOUSE_DRAG_SLOP )
{
lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (mouse inside slop)" << llendl;
gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
return TRUE;
}
else
{
// ...just went outside the slop region
mMouseOutsideSlop = TRUE;
// If holding down shift, leave behind a copy.
if (mask == MASK_COPY)
{
// ...we're trying to make a copy
// LLSelectMgr::getInstance()->selectDuplicate(LLVector3::zero, FALSE);
// mCopyMadeThisDrag = TRUE;
// [RLVa:KB] - Checked: 2009-07-05 (RLVa-1.0.0b)
if (!gRlvHandler.hasBehaviour(RLV_BHVR_REZ))
{
LLSelectMgr::getInstance()->selectDuplicate(LLVector3::zero, FALSE);
mCopyMadeThisDrag = TRUE;
}
// [/RLVa:KB]
// When we make the copy, we don't want to do any other processing.
// If so, the object will also be moved, and the copy will be offset.
lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (made copy)" << llendl;
gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
}
}
}
// Throttle updates to 10 per second.
BOOL send_update = FALSE;
LLVector3 axis_f;
LLVector3d axis_d;
// pick the first object to constrain to grid w/ common origin
// this is so we don't screw up groups
LLSelectNode* selectNode = mObjectSelection->getFirstMoveableNode(TRUE);
if (!selectNode)
{
// somehow we lost the object!
llwarns << "Translate manip lost the object, no selectNode" << llendl;
gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
return TRUE;
}
LLViewerObject* object = selectNode->getObject();
if (!object)
{
// somehow we lost the object!
llwarns << "Translate manip lost the object, no object in selectNode" << llendl;
gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
return TRUE;
}
// Compute unit vectors for arrow hit and a plane through that vector
BOOL axis_exists = getManipAxis(object, mManipPart, axis_f); // TODO: move this
axis_d.setVec(axis_f);
LLSelectMgr::getInstance()->updateSelectionCenter();
LLVector3d current_pos_global = gAgent.getPosGlobalFromAgent(getPivotPoint());
mSubdivisions = llclamp(getSubdivisionLevel(getPivotPoint(), axis_f, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
// Project the cursor onto that plane
LLVector3d relative_move;
getMousePointOnPlaneGlobal(relative_move, x, y, current_pos_global, mManipNormal);\
relative_move -= mDragCursorStartGlobal;
// You can't move more than some distance from your original mousedown point.
if (gSavedSettings.getBOOL("LimitDragDistance"))
{
F32 max_drag_distance = gSavedSettings.getF32("MaxDragDistance");
if (relative_move.magVecSquared() > max_drag_distance * max_drag_distance)
{
lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (too far)" << llendl;
gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
return TRUE;
}
}
F64 axis_magnitude = relative_move * axis_d; // dot product
LLVector3d cursor_point_snap_line;
F64 off_axis_magnitude;
getMousePointOnPlaneGlobal(cursor_point_snap_line, x, y, current_pos_global, mSnapOffsetAxis % axis_f);
off_axis_magnitude = axis_exists ? llabs((cursor_point_snap_line - current_pos_global) * LLVector3d(mSnapOffsetAxis)) : 0.f;
if (gSavedSettings.getBOOL("SnapEnabled"))
{
if (off_axis_magnitude > mSnapOffsetMeters)
{
mInSnapRegime = TRUE;
LLVector3 mouse_down_offset(mDragCursorStartGlobal - mDragSelectionStartGlobal);
LLVector3 cursor_snap_agent = gAgent.getPosAgentFromGlobal(cursor_point_snap_line);
if (!gSavedSettings.getBOOL("SnapToMouseCursor"))
{
cursor_snap_agent -= mouse_down_offset;
}
F32 cursor_grid_dist = (cursor_snap_agent - mGridOrigin) * axis_f;
F32 snap_dist = getMinGridScale() / (2.f * mSubdivisions);
F32 relative_snap_dist = fmodf(llabs(cursor_grid_dist) + snap_dist, getMinGridScale() / mSubdivisions);
if (relative_snap_dist < snap_dist * 2.f)
{
if (cursor_grid_dist > 0.f)
{
cursor_grid_dist -= relative_snap_dist - snap_dist;
}
else
{
cursor_grid_dist += relative_snap_dist - snap_dist;
}
}
F32 object_start_on_axis = (gAgent.getPosAgentFromGlobal(mDragSelectionStartGlobal) - mGridOrigin) * axis_f;
axis_magnitude = cursor_grid_dist - object_start_on_axis;
}
else if (mManipPart >= LL_YZ_PLANE && mManipPart <= LL_XY_PLANE)
{
// subtract offset from object center
LLVector3d cursor_point_global;
getMousePointOnPlaneGlobal( cursor_point_global, x, y, current_pos_global, mManipNormal );
cursor_point_global -= (mDragCursorStartGlobal - mDragSelectionStartGlobal);
// snap to planar grid
LLVector3 cursor_point_agent = gAgent.getPosAgentFromGlobal(cursor_point_global);
LLVector3 camera_plane_projection = LLViewerCamera::getInstance()->getAtAxis();
camera_plane_projection -= projected_vec(camera_plane_projection, mManipNormal);
camera_plane_projection.normVec();
LLVector3 camera_projected_dir = camera_plane_projection;
camera_plane_projection.rotVec(~mGridRotation);
camera_plane_projection.scaleVec(mGridScale);
camera_plane_projection.abs();
F32 max_grid_scale;
if (camera_plane_projection.mV[VX] > camera_plane_projection.mV[VY] &&
camera_plane_projection.mV[VX] > camera_plane_projection.mV[VZ])
{
max_grid_scale = mGridScale.mV[VX];
}
else if (camera_plane_projection.mV[VY] > camera_plane_projection.mV[VZ])
{
max_grid_scale = mGridScale.mV[VY];
}
else
{
max_grid_scale = mGridScale.mV[VZ];
}
F32 num_subdivisions = llclamp(getSubdivisionLevel(getPivotPoint(), camera_projected_dir, max_grid_scale), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
F32 grid_scale_a;
F32 grid_scale_b;
LLVector3 cursor_point_grid = (cursor_point_agent - mGridOrigin) * ~mGridRotation;
switch (mManipPart)
{
case LL_YZ_PLANE:
grid_scale_a = mGridScale.mV[VY] / num_subdivisions;
grid_scale_b = mGridScale.mV[VZ] / num_subdivisions;
cursor_point_grid.mV[VY] -= fmod(cursor_point_grid.mV[VY] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
cursor_point_grid.mV[VZ] -= fmod(cursor_point_grid.mV[VZ] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
break;
case LL_XZ_PLANE:
grid_scale_a = mGridScale.mV[VX] / num_subdivisions;
grid_scale_b = mGridScale.mV[VZ] / num_subdivisions;
cursor_point_grid.mV[VX] -= fmod(cursor_point_grid.mV[VX] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
cursor_point_grid.mV[VZ] -= fmod(cursor_point_grid.mV[VZ] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
break;
case LL_XY_PLANE:
grid_scale_a = mGridScale.mV[VX] / num_subdivisions;
grid_scale_b = mGridScale.mV[VY] / num_subdivisions;
cursor_point_grid.mV[VX] -= fmod(cursor_point_grid.mV[VX] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
cursor_point_grid.mV[VY] -= fmod(cursor_point_grid.mV[VY] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
break;
default:
break;
}
cursor_point_agent = (cursor_point_grid * mGridRotation) + mGridOrigin;
relative_move.setVec(cursor_point_agent - gAgent.getPosAgentFromGlobal(mDragSelectionStartGlobal));
mInSnapRegime = TRUE;
}
else
{
mInSnapRegime = FALSE;
}
}
else
{
mInSnapRegime = FALSE;
}
// Clamp to arrow direction
// *FIX: does this apply anymore?
if (!axis_exists)
{
axis_magnitude = relative_move.normVec();
axis_d.setVec(relative_move);
axis_d.normVec();
axis_f.setVec(axis_d);
}
LLVector3d clamped_relative_move = axis_magnitude * axis_d; // scalar multiply
LLVector3 clamped_relative_move_f = (F32)axis_magnitude * axis_f; // scalar multiply
for (LLObjectSelection::iterator iter = mObjectSelection->begin();
iter != mObjectSelection->end(); iter++)
{
LLSelectNode* selectNode = *iter;
LLViewerObject* object = selectNode->getObject();
// Only apply motion to root objects and objects selected
// as "individual".
if (!object->isRootEdit() && !selectNode->mIndividualSelection)
{
continue;
}
if (!object->isRootEdit())
{
// child objects should not update if parent is selected
LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
if (editable_root->isSelected())
{
// we will be moved properly by our parent, so skip
continue;
}
}
if (object->permMove())
{
// handle attachments in local space
if (object->isAttachment() && object->mDrawable.notNull())
{
// calculate local version of relative move
LLQuaternion objWorldRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
objWorldRotation.transQuat();
LLVector3 old_position_local = object->getPosition();
LLVector3 new_position_local = selectNode->mSavedPositionLocal + (clamped_relative_move_f * objWorldRotation);
// move and clamp root object first, before adjusting children
if (new_position_local != old_position_local)
{
send_update = TRUE;
}
//RN: I forget, but we need to do this because of snapping which doesn't often result
// in position changes even when the mouse moves
object->setPosition(new_position_local);
rebuild(object);
gAgent.getAvatarObject()->clampAttachmentPositions();
new_position_local = object->getPosition();
if (selectNode->mIndividualSelection)
{
send_update = FALSE;
// counter-translate child objects if we are moving the root as an individual
object->resetChildrenPosition(old_position_local - new_position_local, TRUE) ;
}
}
else
{
// compute new position to send to simulators, but don't set it yet.
// We need the old position to know which simulator to send the move message to.
LLVector3d new_position_global = selectNode->mSavedPositionGlobal + clamped_relative_move;
// Don't let object centers go too far underground
F64 min_height = LLWorld::getInstance()->getMinAllowedZ(object);
if (new_position_global.mdV[VZ] < min_height)
{
new_position_global.mdV[VZ] = min_height;
}
// For safety, cap heights where objects can be dragged
if (new_position_global.mdV[VZ] > MAX_OBJECT_Z)
{
new_position_global.mdV[VZ] = MAX_OBJECT_Z;
}
// Grass is always drawn on the ground, so clamp its position to the ground
if (object->getPCode() == LL_PCODE_LEGACY_GRASS)
{
new_position_global.mdV[VZ] = LLWorld::getInstance()->resolveLandHeightGlobal(new_position_global) + 1.f;
}
if (object->isRootEdit())
{
new_position_global = LLWorld::getInstance()->clipToVisibleRegions(object->getPositionGlobal(), new_position_global);
}
// PR: Only update if changed
LLVector3d old_position_global = object->getPositionGlobal();
LLVector3 old_position_agent = object->getPositionAgent();
LLVector3 new_position_agent = gAgent.getPosAgentFromGlobal(new_position_global);
if (object->isRootEdit())
{
// finally, move parent object after children have calculated new offsets
object->setPositionAgent(new_position_agent);
rebuild(object);
}
else
{
LLViewerObject* root_object = object->getRootEdit();
new_position_agent -= root_object->getPositionAgent();
new_position_agent = new_position_agent * ~root_object->getRotation();
object->setPositionParent(new_position_agent, FALSE);
rebuild(object);
}
if (selectNode->mIndividualSelection)
{
// counter-translate child objects if we are moving the root as an individual
object->resetChildrenPosition(old_position_agent - new_position_agent, TRUE) ;
send_update = FALSE;
}
else if (old_position_global != new_position_global)
{
send_update = TRUE;
}
}
selectNode->mLastPositionLocal = object->getPosition();
}
}
LLSelectMgr::getInstance()->updateSelectionCenter();
gAgent.clearFocusObject();
dialog_refresh_all(); // ??? is this necessary?
lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (active)" << llendl;
gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
return TRUE;
}
void LLManipTranslate::highlightManipulators(S32 x, S32 y)
{
mHighlightedPart = LL_NO_PART;
if (!mObjectSelection->getObjectCount())
{
return;
}
//LLBBox bbox = LLSelectMgr::getInstance()->getBBoxOfSelection();
LLMatrix4 projMatrix = LLViewerCamera::getInstance()->getProjection();
LLMatrix4 modelView = LLViewerCamera::getInstance()->getModelview();
LLVector3 object_position = getPivotPoint();
LLVector3 grid_origin;
LLVector3 grid_scale;
LLQuaternion grid_rotation;
LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
LLVector3 relative_camera_dir;
LLMatrix4 transform;
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
relative_camera_dir = LLVector3(1.f, 0.f, 0.f) * ~grid_rotation;
LLVector4 translation(object_position);
transform.initRotTrans(grid_rotation, translation);
LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
transform *= cfr;
LLMatrix4 window_scale;
F32 zoom_level = 2.f * gAgent.mHUDCurZoom;
window_scale.initAll(LLVector3(zoom_level / LLViewerCamera::getInstance()->getAspect(), zoom_level, 0.f),
LLQuaternion::DEFAULT,
LLVector3::zero);
transform *= window_scale;
}
else
{
relative_camera_dir = (object_position - LLViewerCamera::getInstance()->getOrigin()) * ~grid_rotation;
relative_camera_dir.normVec();
transform.initRotTrans(grid_rotation, LLVector4(object_position));
transform *= modelView;
transform *= projMatrix;
}
S32 numManips = 0;
// edges
mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 0.f, 0.f, 1.f);
mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 0.f, 0.f, 1.f);
mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 0.f, 1.f);
mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 0.f, 1.f);
mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 1.f);
mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 1.f);
mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 0.f, 0.f, 1.f);
mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 0.f, 0.f, 1.f);
mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 0.f, 1.f);
mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 0.f, 1.f);
mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 1.f);
mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 1.f);
S32 num_arrow_manips = numManips;
// planar manipulators
BOOL planar_manip_yz_visible = FALSE;
BOOL planar_manip_xz_visible = FALSE;
BOOL planar_manip_xy_visible = FALSE;
mManipulatorVertices[numManips] = LLVector4(0.f, mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 1.f);
mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
mManipulatorVertices[numManips] = LLVector4(0.f, mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 1.f);
mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
if (llabs(relative_camera_dir.mV[VX]) > MIN_PLANE_MANIP_DOT_PRODUCT)
{
planar_manip_yz_visible = TRUE;
}
mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 0.f, mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 1.f);
mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 0.f, mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 1.f);
mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
if (llabs(relative_camera_dir.mV[VY]) > MIN_PLANE_MANIP_DOT_PRODUCT)
{
planar_manip_xz_visible = TRUE;
}
mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 0.f, 1.f);
mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 0.f, 1.f);
mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
if (llabs(relative_camera_dir.mV[VZ]) > MIN_PLANE_MANIP_DOT_PRODUCT)
{
planar_manip_xy_visible = TRUE;
}
for_each(mProjectedManipulators.begin(), mProjectedManipulators.end(), DeletePointer());
mProjectedManipulators.clear();
for (S32 i = 0; i < num_arrow_manips; i+= 2)
{
LLVector4 projected_start = mManipulatorVertices[i] * transform;
projected_start = projected_start * (1.f / projected_start.mV[VW]);
LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
projected_end = projected_end * (1.f / projected_end.mV[VW]);
ManipulatorHandle* projManipulator =
new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
MANIPULATOR_IDS[i / 2],
10.f); // 10 pixel hotspot for arrows
mProjectedManipulators.insert(projManipulator);
}
if (planar_manip_yz_visible)
{
S32 i = num_arrow_manips;
LLVector4 projected_start = mManipulatorVertices[i] * transform;
projected_start = projected_start * (1.f / projected_start.mV[VW]);
LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
projected_end = projected_end * (1.f / projected_end.mV[VW]);
ManipulatorHandle* projManipulator =
new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
MANIPULATOR_IDS[i / 2],
20.f); // 20 pixels for planar manipulators
mProjectedManipulators.insert(projManipulator);
}
if (planar_manip_xz_visible)
{
S32 i = num_arrow_manips + 2;
LLVector4 projected_start = mManipulatorVertices[i] * transform;
projected_start = projected_start * (1.f / projected_start.mV[VW]);
LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
projected_end = projected_end * (1.f / projected_end.mV[VW]);
ManipulatorHandle* projManipulator =
new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
MANIPULATOR_IDS[i / 2],
20.f); // 20 pixels for planar manipulators
mProjectedManipulators.insert(projManipulator);
}
if (planar_manip_xy_visible)
{
S32 i = num_arrow_manips + 4;
LLVector4 projected_start = mManipulatorVertices[i] * transform;
projected_start = projected_start * (1.f / projected_start.mV[VW]);
LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
projected_end = projected_end * (1.f / projected_end.mV[VW]);
ManipulatorHandle* projManipulator =
new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
MANIPULATOR_IDS[i / 2],
20.f); // 20 pixels for planar manipulators
mProjectedManipulators.insert(projManipulator);
}
LLVector2 manip_start_2d;
LLVector2 manip_end_2d;
LLVector2 manip_dir;
F32 half_width = gViewerWindow->getWindowWidth() / 2.f;
F32 half_height = gViewerWindow->getWindowHeight() / 2.f;
LLVector2 mousePos((F32)x - half_width, (F32)y - half_height);
LLVector2 mouse_delta;
for (minpulator_list_t::iterator iter = mProjectedManipulators.begin();
iter != mProjectedManipulators.end(); ++iter)
{
ManipulatorHandle* manipulator = *iter;
{
manip_start_2d.setVec(manipulator->mStartPosition.mV[VX] * half_width, manipulator->mStartPosition.mV[VY] * half_height);
manip_end_2d.setVec(manipulator->mEndPosition.mV[VX] * half_width, manipulator->mEndPosition.mV[VY] * half_height);
manip_dir = manip_end_2d - manip_start_2d;
mouse_delta = mousePos - manip_start_2d;
F32 manip_length = manip_dir.normVec();
F32 mouse_pos_manip = mouse_delta * manip_dir;
F32 mouse_dist_manip_squared = mouse_delta.magVecSquared() - (mouse_pos_manip * mouse_pos_manip);
if (mouse_pos_manip > 0.f &&
mouse_pos_manip < manip_length &&
mouse_dist_manip_squared < manipulator->mHotSpotRadius * manipulator->mHotSpotRadius)
{
mHighlightedPart = manipulator->mManipID;
break;
}
}
}
}
F32 LLManipTranslate::getMinGridScale()
{
F32 scale;
switch (mManipPart)
{
case LL_NO_PART:
default:
scale = 1.f;
break;
case LL_X_ARROW:
scale = mGridScale.mV[VX];
break;
case LL_Y_ARROW:
scale = mGridScale.mV[VY];
break;
case LL_Z_ARROW:
scale = mGridScale.mV[VZ];
break;
case LL_YZ_PLANE:
scale = llmin(mGridScale.mV[VY], mGridScale.mV[VZ]);
break;
case LL_XZ_PLANE:
scale = llmin(mGridScale.mV[VX], mGridScale.mV[VZ]);
break;
case LL_XY_PLANE:
scale = llmin(mGridScale.mV[VX], mGridScale.mV[VY]);
break;
}
return scale;
}
BOOL LLManipTranslate::handleMouseUp(S32 x, S32 y, MASK mask)
{
// first, perform normal processing in case this was a quick-click
handleHover(x, y, mask);
if(hasMouseCapture())
{
// make sure arrow colors go back to normal
mManipPart = LL_NO_PART;
LLSelectMgr::getInstance()->enableSilhouette(TRUE);
// Might have missed last update due to UPDATE_DELAY timing.
LLSelectMgr::getInstance()->sendMultipleUpdate( UPD_POSITION );
mInSnapRegime = FALSE;
LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
//gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
}
return LLManip::handleMouseUp(x, y, mask);
}
void LLManipTranslate::render()
{
glMatrixMode(GL_MODELVIEW);
gGL.pushMatrix();
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
F32 zoom = gAgent.mHUDCurZoom;
glScalef(zoom, zoom, zoom);
}
{
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
renderGuidelines();
}
{
renderTranslationHandles();
renderSnapGuides();
}
gGL.popMatrix();
renderText();
}
void LLManipTranslate::renderSnapGuides()
{
static const LLCachedControl<bool> snap_enabled("SnapEnabled",false);
if (!snap_enabled)
{
return;
}
F32 max_subdivisions = sGridMaxSubdivisionLevel;//(F32)gSavedSettings.getS32("GridSubdivision");
static const LLCachedControl<F32> line_alpha("GridOpacity",1.0);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
LLGLDepthTest gls_depth(GL_TRUE);
LLGLDisable gls_cull(GL_CULL_FACE);
LLVector3 translate_axis;
if (mManipPart == LL_NO_PART)
{
return;
}
LLSelectNode *first_node = mObjectSelection->getFirstMoveableNode(TRUE);
if (!first_node)
{
return;
}
updateGridSettings();
F32 smallest_grid_unit_scale = getMinGridScale() / max_subdivisions;
LLVector3 grid_origin;
LLVector3 grid_scale;
LLQuaternion grid_rotation;
LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
LLVector3 saved_selection_center = getSavedPivotPoint(); //LLSelectMgr::getInstance()->getSavedBBoxOfSelection().getCenterAgent();
LLVector3 selection_center = getPivotPoint();
LLViewerObject *first_object = first_node->getObject();
//pick appropriate projection plane for snap rulers according to relative camera position
if (mManipPart >= LL_X_ARROW && mManipPart <= LL_Z_ARROW)
{
LLVector3 normal;
LLColor4 inner_color;
LLManip::EManipPart temp_manip = mManipPart;
switch (mManipPart)
{
case LL_X_ARROW:
normal.setVec(1,0,0);
inner_color.setVec(0,1,1,line_alpha);
mManipPart = LL_YZ_PLANE;
break;
case LL_Y_ARROW:
normal.setVec(0,1,0);
inner_color.setVec(1,0,1,line_alpha);
mManipPart = LL_XZ_PLANE;
break;
case LL_Z_ARROW:
normal.setVec(0,0,1);
inner_color.setVec(1,1,0,line_alpha);
mManipPart = LL_XY_PLANE;
break;
default:
break;
}
highlightIntersection(normal, selection_center, grid_rotation, inner_color);
mManipPart = temp_manip;
getManipAxis(first_object, mManipPart, translate_axis);
LLVector3 at_axis_abs;
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
at_axis_abs = LLVector3::x_axis * ~grid_rotation;
}
else
{
at_axis_abs = saved_selection_center - LLViewerCamera::getInstance()->getOrigin();
at_axis_abs.normVec();
at_axis_abs = at_axis_abs * ~grid_rotation;
}
at_axis_abs.abs();
if (at_axis_abs.mV[VX] > at_axis_abs.mV[VY] && at_axis_abs.mV[VX] > at_axis_abs.mV[VZ])
{
if (mManipPart == LL_Y_ARROW)
{
mSnapOffsetAxis = LLVector3::z_axis;
}
else if (mManipPart == LL_Z_ARROW)
{
mSnapOffsetAxis = LLVector3::y_axis;
}
else if (at_axis_abs.mV[VY] > at_axis_abs.mV[VZ])
{
mSnapOffsetAxis = LLVector3::z_axis;
}
else
{
mSnapOffsetAxis = LLVector3::y_axis;
}
}
else if (at_axis_abs.mV[VY] > at_axis_abs.mV[VZ])
{
if (mManipPart == LL_X_ARROW)
{
mSnapOffsetAxis = LLVector3::z_axis;
}
else if (mManipPart == LL_Z_ARROW)
{
mSnapOffsetAxis = LLVector3::x_axis;
}
else if (at_axis_abs.mV[VX] > at_axis_abs.mV[VZ])
{
mSnapOffsetAxis = LLVector3::z_axis;
}
else
{
mSnapOffsetAxis = LLVector3::x_axis;
}
}
else
{
if (mManipPart == LL_X_ARROW)
{
mSnapOffsetAxis = LLVector3::y_axis;
}
else if (mManipPart == LL_Y_ARROW)
{
mSnapOffsetAxis = LLVector3::x_axis;
}
else if (at_axis_abs.mV[VX] > at_axis_abs.mV[VY])
{
mSnapOffsetAxis = LLVector3::y_axis;
}
else
{
mSnapOffsetAxis = LLVector3::x_axis;
}
}
mSnapOffsetAxis = mSnapOffsetAxis * grid_rotation;
F32 guide_size_meters;
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
guide_size_meters = 1.f / gAgent.mHUDCurZoom;
mSnapOffsetMeters = mArrowLengthMeters * 1.5f;
}
else
{
LLVector3 cam_to_selection = getPivotPoint() - LLViewerCamera::getInstance()->getOrigin();
F32 current_range = cam_to_selection.normVec();
guide_size_meters = SNAP_GUIDE_SCREEN_SIZE * gViewerWindow->getWindowHeight() * current_range / LLViewerCamera::getInstance()->getPixelMeterRatio();
F32 fraction_of_fov = mAxisArrowLength / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
F32 apparent_angle = fraction_of_fov * LLViewerCamera::getInstance()->getView(); // radians
F32 offset_at_camera = tan(apparent_angle) * 1.5f;
F32 range = dist_vec(gAgent.getPosAgentFromGlobal(first_node->mSavedPositionGlobal), LLViewerCamera::getInstance()->getOrigin());
mSnapOffsetMeters = range * offset_at_camera;
}
LLVector3 tick_start;
LLVector3 tick_end;
// how far away from grid origin is the selection along the axis of translation?
F32 dist_grid_axis = (selection_center - mGridOrigin) * translate_axis;
// find distance to nearest smallest grid unit
F32 offset_nearest_grid_unit = fmodf(dist_grid_axis, smallest_grid_unit_scale);
// how many smallest grid units are we away from largest grid scale?
S32 sub_div_offset = llround(fmod(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() / sGridMinSubdivisionLevel) / smallest_grid_unit_scale);
S32 num_ticks_per_side = llmax(1, llfloor(0.5f * guide_size_meters / smallest_grid_unit_scale));
LLGLDepthTest gls_depth(GL_FALSE);
for (S32 pass = 0; pass < 3; pass++)
{
LLColor4 line_color = setupSnapGuideRenderPass(pass);
gGL.begin(LLRender::LINES);
{
LLVector3 line_start = selection_center + (mSnapOffsetMeters * mSnapOffsetAxis) + (translate_axis * (guide_size_meters * 0.5f + offset_nearest_grid_unit));
LLVector3 line_end = selection_center + (mSnapOffsetMeters * mSnapOffsetAxis) - (translate_axis * (guide_size_meters * 0.5f + offset_nearest_grid_unit));
LLVector3 line_mid = (line_start + line_end) * 0.5f;
gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
gGL.vertex3fv(line_start.mV);
gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
gGL.vertex3fv(line_mid.mV);
gGL.vertex3fv(line_mid.mV);
gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
gGL.vertex3fv(line_end.mV);
line_start.setVec(selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) + (translate_axis * guide_size_meters * 0.5f));
line_end.setVec(selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) - (translate_axis * guide_size_meters * 0.5f));
line_mid = (line_start + line_end) * 0.5f;
gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
gGL.vertex3fv(line_start.mV);
gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
gGL.vertex3fv(line_mid.mV);
gGL.vertex3fv(line_mid.mV);
gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
gGL.vertex3fv(line_end.mV);
for (S32 i = -num_ticks_per_side; i <= num_ticks_per_side; i++)
{
tick_start = selection_center + (translate_axis * (smallest_grid_unit_scale * (F32)i - offset_nearest_grid_unit));
F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_start, translate_axis, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / cur_subdivisions)) != 0.f)
{
continue;
}
// add in off-axis offset
tick_start += (mSnapOffsetAxis * mSnapOffsetMeters);
BOOL is_sub_tick = FALSE;
F32 tick_scale = 1.f;
for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
{
if (fmodf((F32)(i + sub_div_offset), division_level) == 0.f)
{
break;
}
tick_scale *= 0.7f;
is_sub_tick = TRUE;
}
// S32 num_ticks_to_fade = is_sub_tick ? num_ticks_per_side / 2 : num_ticks_per_side;
// F32 alpha = line_alpha * (1.f - (0.8f * ((F32)llabs(i) / (F32)num_ticks_to_fade)));
tick_end = tick_start + (mSnapOffsetAxis * mSnapOffsetMeters * tick_scale);
gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
gGL.vertex3fv(tick_start.mV);
gGL.vertex3fv(tick_end.mV);
tick_start = selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) +
(translate_axis * (getMinGridScale() / (F32)(max_subdivisions) * (F32)i - offset_nearest_grid_unit));
tick_end = tick_start - (mSnapOffsetAxis * mSnapOffsetMeters * tick_scale);
gGL.vertex3fv(tick_start.mV);
gGL.vertex3fv(tick_end.mV);
}
}
gGL.end();
if (mInSnapRegime)
{
LLVector3 line_start = selection_center - mSnapOffsetAxis * mSnapOffsetMeters;
LLVector3 line_end = selection_center + mSnapOffsetAxis * mSnapOffsetMeters;
gGL.begin(LLRender::LINES);
{
gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
gGL.vertex3fv(line_start.mV);
gGL.vertex3fv(line_end.mV);
}
gGL.end();
// draw snap guide arrow
gGL.begin(LLRender::TRIANGLES);
{
gGL.color4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
LLVector3 arrow_dir;
LLVector3 arrow_span = translate_axis;
arrow_dir = -mSnapOffsetAxis;
gGL.vertex3fv((line_start + arrow_dir * mConeSize * SNAP_ARROW_SCALE).mV);
gGL.vertex3fv((line_start + arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
gGL.vertex3fv((line_start - arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
arrow_dir = mSnapOffsetAxis;
gGL.vertex3fv((line_end + arrow_dir * mConeSize * SNAP_ARROW_SCALE).mV);
gGL.vertex3fv((line_end + arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
gGL.vertex3fv((line_end - arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
}
gGL.end();
}
}
sub_div_offset = llround(fmod(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() * 32.f) / smallest_grid_unit_scale);
LLVector2 screen_translate_axis(llabs(translate_axis * LLViewerCamera::getInstance()->getLeftAxis()), llabs(translate_axis * LLViewerCamera::getInstance()->getUpAxis()));
screen_translate_axis.normVec();
S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing);
// render tickmark values
for (S32 i = -num_ticks_per_side; i <= num_ticks_per_side; i++)
{
LLVector3 tick_pos = selection_center + (translate_axis * ((smallest_grid_unit_scale * (F32)i) - offset_nearest_grid_unit));
F32 alpha = line_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side)));
F32 tick_scale = 1.f;
for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
{
if (fmodf((F32)(i + sub_div_offset), division_level) == 0.f)
{
break;
}
tick_scale *= 0.7f;
}
if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / llmin(sGridMaxSubdivisionLevel, getSubdivisionLevel(tick_pos, translate_axis, getMinGridScale(), tick_label_spacing)))) == 0.f)
{
F32 snap_offset_meters;
if (mSnapOffsetAxis * LLViewerCamera::getInstance()->getUpAxis() > 0.f)
{
snap_offset_meters = mSnapOffsetMeters;
}
else
{
snap_offset_meters = -mSnapOffsetMeters;
}
LLVector3 text_origin = selection_center +
(translate_axis * ((smallest_grid_unit_scale * (F32)i) - offset_nearest_grid_unit)) +
(mSnapOffsetAxis * snap_offset_meters * (1.f + tick_scale));
LLVector3 tick_offset = (tick_pos - mGridOrigin) * ~mGridRotation;
F32 offset_val = 0.5f * tick_offset.mV[ARROW_TO_AXIS[mManipPart]] / getMinGridScale();
EGridMode grid_mode = LLSelectMgr::getInstance()->getGridMode();
F32 text_highlight = 0.8f;
if(i - llround(offset_nearest_grid_unit / smallest_grid_unit_scale) == 0 && mInSnapRegime)
{
text_highlight = 1.f;
}
if (grid_mode == GRID_MODE_WORLD)
{
// rescale units to meters from multiple of grid scale
offset_val *= 2.f * grid_scale[ARROW_TO_AXIS[mManipPart]];
renderTickValue(text_origin, offset_val, std::string("m"), LLColor4(text_highlight, text_highlight, text_highlight, alpha));
}
else
{
renderTickValue(text_origin, offset_val, std::string("x"), LLColor4(text_highlight, text_highlight, text_highlight, alpha));
}
}
}
if (mObjectSelection->getSelectType() != SELECT_TYPE_HUD)
{
// render helpful text
if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText)
{
F32 snap_offset_meters_up;
if (mSnapOffsetAxis * LLViewerCamera::getInstance()->getUpAxis() > 0.f)
{
snap_offset_meters_up = mSnapOffsetMeters;
}
else
{
snap_offset_meters_up = -mSnapOffsetMeters;
}
LLVector3 selection_center_start = getSavedPivotPoint();//LLSelectMgr::getInstance()->getSavedBBoxOfSelection().getCenterAgent();
LLVector3 help_text_pos = selection_center_start + (snap_offset_meters_up * 3.f * mSnapOffsetAxis);
const LLFontGL* big_fontp = LLFontGL::getFontSansSerif();
std::string help_text = "Move mouse cursor over ruler to snap";
LLColor4 help_text_color = LLColor4::white;
help_text_color.mV[VALPHA] = clamp_rescale(mHelpTextTimer.getElapsedTimeF32(), sHelpTextVisibleTime, sHelpTextVisibleTime + sHelpTextFadeTime, line_alpha, 0.f);
hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, mObjectSelection->getSelectType() == SELECT_TYPE_HUD);
help_text = "to snap to grid";
help_text_pos -= LLViewerCamera::getInstance()->getUpAxis() * mSnapOffsetMeters * 0.2f;
hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, mObjectSelection->getSelectType() == SELECT_TYPE_HUD);
}
}
}
else
{
// render gridlines for planar snapping
F32 u = 0, v = 0;
LLColor4 inner_color;
LLVector3 normal;
LLVector3 grid_center = selection_center - grid_origin;
F32 usc = 1;
F32 vsc = 1;
grid_center *= ~grid_rotation;
switch (mManipPart)
{
case LL_YZ_PLANE:
u = grid_center.mV[VY];
v = grid_center.mV[VZ];
usc = grid_scale.mV[VY];
vsc = grid_scale.mV[VZ];
inner_color.setVec(0,1,1,line_alpha);
normal.setVec(1,0,0);
break;
case LL_XZ_PLANE:
u = grid_center.mV[VX];
v = grid_center.mV[VZ];
usc = grid_scale.mV[VX];
vsc = grid_scale.mV[VZ];
inner_color.setVec(1,0,1,line_alpha);
normal.setVec(0,1,0);
break;
case LL_XY_PLANE:
u = grid_center.mV[VX];
v = grid_center.mV[VY];
usc = grid_scale.mV[VX];
vsc = grid_scale.mV[VY];
inner_color.setVec(1,1,0,line_alpha);
normal.setVec(0,0,1);
break;
default:
break;
}
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
highlightIntersection(normal, selection_center, grid_rotation, inner_color);
gGL.pushMatrix();
F32 x,y,z,angle_radians;
grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
gGL.translatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
F32 sz = mGridSizeMeters;
F32 tiles = sz;
glMatrixMode(GL_TEXTURE);
gGL.pushMatrix();
usc = 1.0f/usc;
vsc = 1.0f/vsc;
while (usc > vsc*4.0f)
{
usc *= 0.5f;
}
while (vsc > usc * 4.0f)
{
vsc *= 0.5f;
}
glScalef(usc, vsc, 1.0f);
gGL.translatef(u, v, 0);
float a = line_alpha;
LLColor4 col = gColors.getColor("SilhouetteChildColor");
{
//draw grid behind objects
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
{
LLGLDisable stencil(GL_STENCIL_TEST);
{
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GREATER);
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, getGridTexName());
gGL.flush();
gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
renderGrid(u,v,tiles,0.9f, 0.9f, 0.9f,a*0.15f);
gGL.flush();
gGL.setSceneBlendType(LLRender::BT_ALPHA);
}
{
LLGLDisable alpha_test(GL_ALPHA_TEST);
//draw black overlay
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
renderGrid(u,v,tiles,0.0f, 0.0f, 0.0f,a*0.16f);
//draw grid top
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, getGridTexName());
renderGrid(u,v,tiles,1,1,1,a);
gGL.popMatrix();
glMatrixMode(GL_MODELVIEW);
gGL.popMatrix();
}
{
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
renderGuidelines();
}
{
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GREATER);
LLGLEnable stipple(GL_LINE_STIPPLE);
gGL.flush();
glLineStipple(1, 0x3333);
switch (mManipPart)
{
case LL_YZ_PLANE:
renderGuidelines(FALSE, TRUE, TRUE);
break;
case LL_XZ_PLANE:
renderGuidelines(TRUE, FALSE, TRUE);
break;
case LL_XY_PLANE:
renderGuidelines(TRUE, TRUE, FALSE);
break;
default:
break;
}
gGL.flush();
}
}
}
}
}
void LLManipTranslate::renderGrid(F32 x, F32 y, F32 size, F32 r, F32 g, F32 b, F32 a)
{
F32 d = size*0.5f;
for (F32 xx = -size-d; xx < size+d; xx += d)
{
gGL.begin(LLRender::TRIANGLE_STRIP);
for (F32 yy = -size-d; yy < size+d; yy += d)
{
float dx, dy, da;
dx = xx; dy = yy;
da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
gGL.texCoord2f(dx, dy);
renderGridVert(dx,dy,r,g,b,da);
dx = xx+d; dy = yy;
da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
gGL.texCoord2f(dx, dy);
renderGridVert(dx,dy,r,g,b,da);
dx = xx; dy = yy+d;
da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
gGL.texCoord2f(dx, dy);
renderGridVert(dx,dy,r,g,b,da);
dx = xx+d; dy = yy+d;
da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
gGL.texCoord2f(dx, dy);
renderGridVert(dx,dy,r,g,b,da);
}
gGL.end();
}
}
void LLManipTranslate::highlightIntersection(LLVector3 normal,
LLVector3 selection_center,
LLQuaternion grid_rotation,
LLColor4 inner_color)
{
if (!gSavedSettings.getBOOL("GridCrossSections"))
{
return;
}
U32 types[] = { LLRenderPass::PASS_SIMPLE, LLRenderPass::PASS_ALPHA, LLRenderPass::PASS_FULLBRIGHT, LLRenderPass::PASS_SHINY };
U32 num_types = LL_ARRAY_SIZE(types);
GLuint stencil_mask = 0xFFFFFFFF;
//stencil in volumes
gGL.flush();
{
glStencilMask(stencil_mask);
glClearStencil(1);
glClear(GL_STENCIL_BUFFER_BIT);
LLGLEnable cull_face(GL_CULL_FACE);
LLGLEnable stencil(GL_STENCIL_TEST);
LLGLDepthTest depth (GL_TRUE, GL_FALSE, GL_ALWAYS);
glStencilFunc(GL_ALWAYS, 0, stencil_mask);
gGL.setColorMask(false, false);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
glColor4f(1,1,1,1);
//setup clip plane
normal = normal * grid_rotation;
if (normal * (LLViewerCamera::getInstance()->getOrigin()-selection_center) < 0)
{
normal = -normal;
}
F32 d = -(selection_center * normal);
F64 plane[] = { normal.mV[0], normal.mV[1], normal.mV[2], d };
LLGLEnable clip(GL_CLIP_PLANE0);
glClipPlane(GL_CLIP_PLANE0, plane);
BOOL particles = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
BOOL clouds = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLASSIC_CLOUDS);
if (particles)
{
LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
}
if (clouds)
{
LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_CLASSIC_CLOUDS);
}
//stencil in volumes
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
glCullFace(GL_FRONT);
for (U32 i = 0; i < num_types; i++)
{
gPipeline.renderObjects(types[i], LLVertexBuffer::MAP_VERTEX, FALSE);
}
glStencilOp(GL_DECR, GL_DECR, GL_DECR);
glCullFace(GL_BACK);
for (U32 i = 0; i < num_types; i++)
{
gPipeline.renderObjects(types[i], LLVertexBuffer::MAP_VERTEX, FALSE);
}
if (particles)
{
LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
}
if (clouds)
{
LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_CLASSIC_CLOUDS);
}
gGL.setColorMask(true, false);
}
gGL.color4f(1,1,1,1);
gGL.pushMatrix();
F32 x,y,z,angle_radians;
grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
gGL.translatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
F32 sz = mGridSizeMeters;
F32 tiles = sz;
//draw volume/plane intersections
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
LLGLDepthTest depth(GL_FALSE);
LLGLEnable stencil(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, 0, stencil_mask);
renderGrid(0,0,tiles,inner_color.mV[0], inner_color.mV[1], inner_color.mV[2], 0.25f);
}
glStencilFunc(GL_ALWAYS, 255, 0xFFFFFFFF);
glStencilMask(0xFFFFFFFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
gGL.popMatrix();
}
void LLManipTranslate::renderText()
{
if (mObjectSelection->getRootObjectCount() && !mObjectSelection->isAttachment())
{
LLVector3 pos = getPivotPoint();
renderXYZ(pos);
}
else
{
const BOOL children_ok = TRUE;
LLViewerObject* objectp = mObjectSelection->getFirstRootObject(children_ok);
if (objectp)
{
renderXYZ(objectp->getPositionEdit());
}
}
}
void LLManipTranslate::renderTranslationHandles()
{
LLVector3 grid_origin;
LLVector3 grid_scale;
LLQuaternion grid_rotation;
LLGLDepthTest gls_depth(GL_FALSE);
LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
LLVector3 at_axis;
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
at_axis = LLVector3::x_axis * ~grid_rotation;
}
else
{
at_axis = LLViewerCamera::getInstance()->getAtAxis() * ~grid_rotation;
}
if (at_axis.mV[VX] > 0.f)
{
mPlaneManipPositions.mV[VX] = 1.f;
}
else
{
mPlaneManipPositions.mV[VX] = -1.f;
}
if (at_axis.mV[VY] > 0.f)
{
mPlaneManipPositions.mV[VY] = 1.f;
}
else
{
mPlaneManipPositions.mV[VY] = -1.f;
}
if (at_axis.mV[VZ] > 0.f)
{
mPlaneManipPositions.mV[VZ] = 1.f;
}
else
{
mPlaneManipPositions.mV[VZ] = -1.f;
}
LLViewerObject *first_object = mObjectSelection->getFirstMoveableObject(TRUE);
if (!first_object) return;
LLVector3 selection_center = getPivotPoint();
// Drag handles
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
mArrowLengthMeters = mAxisArrowLength / gViewerWindow->getWindowHeight();
mArrowLengthMeters /= gAgent.mHUDCurZoom;
}
else
{
LLVector3 camera_pos_agent = gAgent.getCameraPositionAgent();
F32 range = dist_vec(camera_pos_agent, selection_center);
F32 range_from_agent = dist_vec(gAgent.getPositionAgent(), selection_center);
// Don't draw handles if you're too far away
if (gSavedSettings.getBOOL("LimitSelectDistance"))
{
if (range_from_agent > gSavedSettings.getF32("MaxSelectDistance"))
{
return;
}
}
if (range > 0.001f)
{
// range != zero
F32 fraction_of_fov = mAxisArrowLength / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
F32 apparent_angle = fraction_of_fov * LLViewerCamera::getInstance()->getView(); // radians
mArrowLengthMeters = range * tan(apparent_angle);
}
else
{
// range == zero
mArrowLengthMeters = 1.0f;
}
}
mPlaneManipOffsetMeters = mArrowLengthMeters * 1.8f;
mGridSizeMeters = gSavedSettings.getF32("GridDrawSize");
mConeSize = mArrowLengthMeters / 4.f;
glMatrixMode(GL_MODELVIEW);
gGL.pushMatrix();
{
gGL.translatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
F32 angle_radians, x, y, z;
grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
LLQuaternion invRotation = grid_rotation;
invRotation.conjQuat();
LLVector3 relative_camera_dir;
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
relative_camera_dir = LLVector3::x_axis * invRotation;
}
else
{
relative_camera_dir = (selection_center - LLViewerCamera::getInstance()->getOrigin()) * invRotation;
}
relative_camera_dir.normVec();
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
LLGLDisable cull_face(GL_CULL_FACE);
LLColor4 color1;
LLColor4 color2;
// update manipulator sizes
for (S32 index = 0; index < 3; index++)
{
if (index == mManipPart - LL_X_ARROW || index == mHighlightedPart - LL_X_ARROW)
{
mArrowScales.mV[index] = lerp(mArrowScales.mV[index], SELECTED_ARROW_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
mPlaneScales.mV[index] = lerp(mPlaneScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
}
else if (index == mManipPart - LL_YZ_PLANE || index == mHighlightedPart - LL_YZ_PLANE)
{
mArrowScales.mV[index] = lerp(mArrowScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
mPlaneScales.mV[index] = lerp(mPlaneScales.mV[index], SELECTED_ARROW_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
}
else
{
mArrowScales.mV[index] = lerp(mArrowScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
mPlaneScales.mV[index] = lerp(mPlaneScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
}
}
if ((mManipPart == LL_NO_PART || mManipPart == LL_YZ_PLANE) && llabs(relative_camera_dir.mV[VX]) > MIN_PLANE_MANIP_DOT_PRODUCT)
{
// render YZ plane manipulator
gGL.pushMatrix();
glScalef(mPlaneManipPositions.mV[VX], mPlaneManipPositions.mV[VY], mPlaneManipPositions.mV[VZ]);
gGL.translatef(0.f, mPlaneManipOffsetMeters, mPlaneManipOffsetMeters);
glScalef(mPlaneScales.mV[VX], mPlaneScales.mV[VX], mPlaneScales.mV[VX]);
if (mHighlightedPart == LL_YZ_PLANE)
{
color1.setVec(0.f, 1.f, 0.f, 1.f);
color2.setVec(0.f, 0.f, 1.f, 1.f);
}
else
{
color1.setVec(0.f, 1.f, 0.f, 0.6f);
color2.setVec(0.f, 0.f, 1.f, 0.6f);
}
gGL.begin(LLRender::TRIANGLES);
{
gGL.color4fv(color1.mV);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f));
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
gGL.color4fv(color2.mV);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
}
gGL.end();
LLUI::setLineWidth(3.0f);
gGL.begin(LLRender::LINES);
{
gGL.color4f(0.f, 0.f, 0.f, 0.3f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
}
gGL.end();
LLUI::setLineWidth(1.0f);
gGL.popMatrix();
}
if ((mManipPart == LL_NO_PART || mManipPart == LL_XZ_PLANE) && llabs(relative_camera_dir.mV[VY]) > MIN_PLANE_MANIP_DOT_PRODUCT)
{
// render XZ plane manipulator
gGL.pushMatrix();
glScalef(mPlaneManipPositions.mV[VX], mPlaneManipPositions.mV[VY], mPlaneManipPositions.mV[VZ]);
gGL.translatef(mPlaneManipOffsetMeters, 0.f, mPlaneManipOffsetMeters);
glScalef(mPlaneScales.mV[VY], mPlaneScales.mV[VY], mPlaneScales.mV[VY]);
if (mHighlightedPart == LL_XZ_PLANE)
{
color1.setVec(0.f, 0.f, 1.f, 1.f);
color2.setVec(1.f, 0.f, 0.f, 1.f);
}
else
{
color1.setVec(0.f, 0.f, 1.f, 0.6f);
color2.setVec(1.f, 0.f, 0.f, 0.6f);
}
gGL.begin(LLRender::TRIANGLES);
{
gGL.color4fv(color1.mV);
gGL.vertex3f(mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
gGL.vertex3f(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), 0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
gGL.vertex3f(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
gGL.color4fv(color2.mV);
gGL.vertex3f(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
gGL.vertex3f(mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f));
gGL.vertex3f(mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
}
gGL.end();
LLUI::setLineWidth(3.0f);
gGL.begin(LLRender::LINES);
{
gGL.color4f(0.f, 0.f, 0.f, 0.3f);
gGL.vertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f);
gGL.vertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f);
gGL.vertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
gGL.vertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
gGL.vertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
}
gGL.end();
LLUI::setLineWidth(1.0f);
gGL.popMatrix();
}
if ((mManipPart == LL_NO_PART || mManipPart == LL_XY_PLANE) && llabs(relative_camera_dir.mV[VZ]) > MIN_PLANE_MANIP_DOT_PRODUCT)
{
// render XY plane manipulator
gGL.pushMatrix();
glScalef(mPlaneManipPositions.mV[VX], mPlaneManipPositions.mV[VY], mPlaneManipPositions.mV[VZ]);
/* Y
^
v1
| \
|<- v0
| /| \
v2__v__v3 > X
*/
LLVector3 v0,v1,v2,v3;
#if 0
// This should theoretically work but looks off; could be tuned later -SJB
gGL.translatef(-mPlaneManipOffsetMeters, -mPlaneManipOffsetMeters, 0.f);
v0 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), 0.f);
v1 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.75f), 0.f);
v2 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f);
v3 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.75f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f);
#else
gGL.translatef(mPlaneManipOffsetMeters, mPlaneManipOffsetMeters, 0.f);
v0 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f);
v1 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), 0.f);
v2 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), 0.f);
v3 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), 0.f);
#endif
glScalef(mPlaneScales.mV[VZ], mPlaneScales.mV[VZ], mPlaneScales.mV[VZ]);
if (mHighlightedPart == LL_XY_PLANE)
{
color1.setVec(1.f, 0.f, 0.f, 1.f);
color2.setVec(0.f, 1.f, 0.f, 1.f);
}
else
{
color1.setVec(0.8f, 0.f, 0.f, 0.6f);
color2.setVec(0.f, 0.8f, 0.f, 0.6f);
}
gGL.begin(LLRender::TRIANGLES);
{
gGL.color4fv(color1.mV);
gGL.vertex3fv(v0.mV);
gGL.vertex3fv(v1.mV);
gGL.vertex3fv(v2.mV);
gGL.color4fv(color2.mV);
gGL.vertex3fv(v2.mV);
gGL.vertex3fv(v3.mV);
gGL.vertex3fv(v0.mV);
}
gGL.end();
LLUI::setLineWidth(3.0f);
gGL.begin(LLRender::LINES);
{
gGL.color4f(0.f, 0.f, 0.f, 0.3f);
LLVector3 v12 = (v1 + v2) * .5f;
gGL.vertex3fv(v0.mV);
gGL.vertex3fv(v12.mV);
gGL.vertex3fv(v12.mV);
gGL.vertex3fv((v12 + (v0-v12)*.3f + (v2-v12)*.3f).mV);
gGL.vertex3fv(v12.mV);
gGL.vertex3fv((v12 + (v0-v12)*.3f + (v1-v12)*.3f).mV);
LLVector3 v23 = (v2 + v3) * .5f;
gGL.vertex3fv(v0.mV);
gGL.vertex3fv(v23.mV);
gGL.vertex3fv(v23.mV);
gGL.vertex3fv((v23 + (v0-v23)*.3f + (v3-v23)*.3f).mV);
gGL.vertex3fv(v23.mV);
gGL.vertex3fv((v23 + (v0-v23)*.3f + (v2-v23)*.3f).mV);
}
gGL.end();
LLUI::setLineWidth(1.0f);
gGL.popMatrix();
}
}
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
// Since we draw handles with depth testing off, we need to draw them in the
// proper depth order.
// Copied from LLDrawable::updateGeometry
LLVector3 pos_agent = first_object->getPositionAgent();
LLVector3 camera_agent = gAgent.getCameraPositionAgent();
LLVector3 headPos = pos_agent - camera_agent;
LLVector3 orientWRTHead = headPos * invRotation;
// Find nearest vertex
U32 nearest = (orientWRTHead.mV[0] < 0.0f ? 1 : 0) +
(orientWRTHead.mV[1] < 0.0f ? 2 : 0) +
(orientWRTHead.mV[2] < 0.0f ? 4 : 0);
// opposite faces on Linden cubes:
// 0 & 5
// 1 & 3
// 2 & 4
// Table of order to draw faces, based on nearest vertex
static U32 face_list[8][NUM_AXES*2] = {
{ 2,0,1, 4,5,3 }, // v6 F201 F453
{ 2,0,3, 4,5,1 }, // v7 F203 F451
{ 4,0,1, 2,5,3 }, // v5 F401 F253
{ 4,0,3, 2,5,1 }, // v4 F403 F251
{ 2,5,1, 4,0,3 }, // v2 F251 F403
{ 2,5,3, 4,0,1 }, // v3 F253 F401
{ 4,5,1, 2,0,3 }, // v1 F451 F203
{ 4,5,3, 2,0,1 }, // v0 F453 F201
};
static const EManipPart which_arrow[6] = {
LL_Z_ARROW,
LL_X_ARROW,
LL_Y_ARROW,
LL_X_ARROW,
LL_Y_ARROW,
LL_Z_ARROW};
// draw arrows for deeper faces first, closer faces last
LLVector3 camera_axis;
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
camera_axis = LLVector3::x_axis;
}
else
{
camera_axis.setVec(gAgent.getCameraPositionAgent() - first_object->getPositionAgent());
}
for (U32 i = 0; i < NUM_AXES*2; i++)
{
U32 face = face_list[nearest][i];
LLVector3 arrow_axis;
getManipAxis(first_object, which_arrow[face], arrow_axis);
renderArrow(which_arrow[face],
mManipPart,
(face >= 3) ? -mConeSize : mConeSize,
(face >= 3) ? -mArrowLengthMeters : mArrowLengthMeters,
mConeSize,
FALSE);
}
}
}
gGL.popMatrix();
}
void LLManipTranslate::renderArrow(S32 which_arrow, S32 selected_arrow, F32 box_size, F32 arrow_size, F32 handle_size, BOOL reverse_direction)
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
LLGLEnable gls_blend(GL_BLEND);
LLGLEnable gls_color_material(GL_COLOR_MATERIAL);
for (S32 pass = 1; pass <= 2; pass++)
{
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, pass == 1 ? GL_LEQUAL : GL_GREATER);
gGL.pushMatrix();
S32 index = 0;
index = ARROW_TO_AXIS[which_arrow];
// assign a color for this arrow
LLColor4 color; // black
if (which_arrow == selected_arrow || which_arrow == mHighlightedPart)
{
color.mV[index] = (pass == 1) ? 1.f : 0.5f;
}
else if (selected_arrow != LL_NO_PART)
{
color.mV[VALPHA] = 0.f;
}
else
{
color.mV[index] = pass == 1 ? .8f : .35f ; // red, green, or blue
color.mV[VALPHA] = 0.6f;
}
gGL.color4fv( color.mV );
LLVector3 vec;
{
LLUI::setLineWidth(2.0f);
gGL.begin(LLRender::LINES);
vec.mV[index] = box_size;
gGL.vertex3f(vec.mV[0], vec.mV[1], vec.mV[2]);
vec.mV[index] = arrow_size;
gGL.vertex3f(vec.mV[0], vec.mV[1], vec.mV[2]);
gGL.end();
LLUI::setLineWidth(1.0f);
}
gGL.translatef(vec.mV[0], vec.mV[1], vec.mV[2]);
glScalef(handle_size, handle_size, handle_size);
F32 rot = 0.0f;
LLVector3 axis;
switch(which_arrow)
{
case LL_X_ARROW:
rot = reverse_direction ? -90.0f : 90.0f;
axis.mV[1] = 1.0f;
break;
case LL_Y_ARROW:
rot = reverse_direction ? 90.0f : -90.0f;
axis.mV[0] = 1.0f;
break;
case LL_Z_ARROW:
rot = reverse_direction ? 180.0f : 0.0f;
axis.mV[0] = 1.0f;
break;
default:
llerrs << "renderArrow called with bad arrow " << which_arrow << llendl;
break;
}
glColor4fv(color.mV);
glRotatef(rot, axis.mV[0], axis.mV[1], axis.mV[2]);
glScalef(mArrowScales.mV[index], mArrowScales.mV[index], mArrowScales.mV[index] * 1.5f);
gCone.render(CONE_LOD_HIGHEST);
gGL.popMatrix();
}
}
void LLManipTranslate::renderGridVert(F32 x_trans, F32 y_trans, F32 r, F32 g, F32 b, F32 alpha)
{
gGL.color4f(r, g, b, alpha);
switch (mManipPart)
{
case LL_YZ_PLANE:
gGL.vertex3f(0, x_trans, y_trans);
break;
case LL_XZ_PLANE:
gGL.vertex3f(x_trans, 0, y_trans);
break;
case LL_XY_PLANE:
gGL.vertex3f(x_trans, y_trans, 0);
break;
default:
gGL.vertex3f(0,0,0);
break;
}
}
// virtual
BOOL LLManipTranslate::canAffectSelection()
{
BOOL can_move = mObjectSelection->getObjectCount() != 0;
if (can_move)
{
struct f : public LLSelectedObjectFunctor
{
virtual bool apply(LLViewerObject* objectp)
{
return objectp->permMove() && (objectp->permModify() || !gSavedSettings.getBOOL("EditLinkedParts"));
}
} func;
can_move = mObjectSelection->applyToObjects(&func);
}
return can_move;
}