diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llmanipscale.cpp |
Print done when done.
Diffstat (limited to 'indra/newview/llmanipscale.cpp')
-rw-r--r-- | indra/newview/llmanipscale.cpp | 2024 |
1 files changed, 2024 insertions, 0 deletions
diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp new file mode 100644 index 0000000000..aa3749b539 --- /dev/null +++ b/indra/newview/llmanipscale.cpp @@ -0,0 +1,2024 @@ +/** + * @file llmanipscale.cpp + * @brief LLManipScale class implementation + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llmanipscale.h" + +// library includes +#include "llmath.h" +#include "v3math.h" +#include "llquaternion.h" +#include "llgl.h" +#include "v4color.h" +#include "llprimitive.h" + +// viewer includes +#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 "llglheaders.h" +#include "llselectmgr.h" +#include "llstatusbar.h" +#include "llui.h" +#include "llviewercamera.h" +#include "llviewerobject.h" +#include "llviewerwindow.h" +#include "llhudrender.h" +#include "llworld.h" +#include "v2math.h" +#include "llvoavatar.h" + + +const F32 MAX_MANIP_SELECT_DISTANCE_SQUARED = 11.f * 11.f; +const F32 SNAP_GUIDE_SCREEN_OFFSET = 0.05f; +const F32 SNAP_GUIDE_SCREEN_LENGTH = 0.7f; +const F32 SELECTED_MANIPULATOR_SCALE = 1.2f; +const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f; +const S32 NUM_MANIPULATORS = 14; + +const LLManip::EManipPart MANIPULATOR_IDS[NUM_MANIPULATORS] = +{ + LLManip::LL_CORNER_NNN, + LLManip::LL_CORNER_NNP, + LLManip::LL_CORNER_NPN, + LLManip::LL_CORNER_NPP, + LLManip::LL_CORNER_PNN, + LLManip::LL_CORNER_PNP, + LLManip::LL_CORNER_PPN, + LLManip::LL_CORNER_PPP, + LLManip::LL_FACE_POSZ, + LLManip::LL_FACE_POSX, + LLManip::LL_FACE_POSY, + LLManip::LL_FACE_NEGX, + LLManip::LL_FACE_NEGY, + LLManip::LL_FACE_NEGZ +}; + + + +// static +void LLManipScale::setUniform(BOOL b) +{ + gSavedSettings.setBOOL("ScaleUniform", b); +} + +// static +void LLManipScale::setShowAxes(BOOL b) +{ + gSavedSettings.setBOOL("ScaleShowAxes", b); +} + +// static +void LLManipScale::setStretchTextures(BOOL b) +{ + gSavedSettings.setBOOL("ScaleStretchTextures", b); +} + +// static +BOOL LLManipScale::getUniform() +{ + return gSavedSettings.getBOOL("ScaleUniform"); +} + +// static +BOOL LLManipScale::getShowAxes() +{ + return gSavedSettings.getBOOL("ScaleShowAxes"); +} + +// static +BOOL LLManipScale::getStretchTextures() +{ + return gSavedSettings.getBOOL("ScaleStretchTextures"); +} + +inline void LLManipScale::conditionalHighlight( U32 part, const LLColor4* highlight, const LLColor4* normal ) +{ + LLColor4 default_highlight( 1.f, 1.f, 1.f, 1.f ); + LLColor4 default_normal( 0.7f, 0.7f, 0.7f, 0.6f ); + LLColor4 invisible(0.f, 0.f, 0.f, 0.f); + F32 manipulator_scale = 1.f; + + for (S32 i = 0; i < NUM_MANIPULATORS; i++) + { + if((U32)MANIPULATOR_IDS[i] == part) + { + manipulator_scale = mManipulatorScales[i]; + break; + } + } + + mScaledBoxHandleSize = mBoxHandleSize * manipulator_scale; + if (mManipPart != (S32)LL_NO_PART && mManipPart != (S32)part) + { + glColor4fv( invisible.mV ); + } + else if( mHighlightedPart == (S32)part ) + { + glColor4fv( highlight ? highlight->mV : default_highlight.mV ); + } + else + { + glColor4fv( normal ? normal->mV : default_normal.mV ); + } +} + +void LLManipScale::handleSelect() +{ + LLBBox bbox = gSelectMgr->getBBoxOfSelection(); + updateSnapGuides(bbox); + gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + gFloaterTools->setStatusText("Click and drag to stretch selected side"); +} + +void LLManipScale::handleDeselect() +{ + mHighlightedPart = LL_NO_PART; + mManipPart = LL_NO_PART; + gFloaterTools->setStatusText(""); +} + +BOOL sort_manip_by_z(LLManipScale::ManipulatorHandle *new_manip, LLManipScale::ManipulatorHandle *test_manip) +{ + return ((new_manip->mType < test_manip->mType) || (new_manip->mPosition.mV[VZ] < test_manip->mPosition.mV[VZ])); +} + +LLManipScale::LLManipScale( LLToolComposite* composite ) + : + LLManip( "Scale", composite ), + mBoxHandleSize( 1.f ), + mScaledBoxHandleSize( 1.f ), + mManipPart( LL_NO_PART ), + mHighlightedPart( LL_NO_PART ), + mLastMouseX( -1 ), + mLastMouseY( -1 ), + mSendUpdateOnMouseUp( FALSE ), + mLastUpdateFlags( 0 ), + mScaleSnapUnit1(1.f), + mScaleSnapUnit2(1.f), + mSnapRegimeOffset(0.f), + mSnapGuideLength(0.f), + mScaleSnapValue(0.f) +{ + mProjectedManipulators.setInsertBefore(sort_manip_by_z); + mManipulatorScales = new F32[NUM_MANIPULATORS]; + for (S32 i = 0; i < NUM_MANIPULATORS; i++) + { + mManipulatorScales[i] = 1.f; + } +} + +LLManipScale::~LLManipScale() +{ + delete []mManipulatorScales; +} + +void LLManipScale::render() +{ + LLGLSUIDefault gls_ui; + LLGLSNoTexture gls_no_texture; + LLGLDepthTest gls_depth(GL_TRUE); + LLGLEnable gl_blend(GL_BLEND); + LLGLEnable gls_alpha_test(GL_ALPHA_TEST); + + if( isSelectionScalable() ) + { + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD) + { + F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom; + glScalef(zoom, zoom, zoom); + } + + //////////////////////////////////////////////////////////////////////// + // Calculate size of drag handles + + const F32 BOX_HANDLE_BASE_SIZE = 50.0f; // box size in pixels = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR + const F32 BOX_HANDLE_BASE_FACTOR = 0.2f; + + LLVector3 center_agent = gAgent.getPosAgentFromGlobal(gSelectMgr->getSelectionCenterGlobal()); + + F32 range; + F32 range_from_agent; + if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD) + { + mBoxHandleSize = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR / (F32) gCamera->getViewHeightInPixels(); + mBoxHandleSize /= gAgent.getAvatarObject()->mHUDCurZoom; + } + else + { + range = dist_vec(gAgent.getCameraPositionAgent(), center_agent); + range_from_agent = dist_vec(gAgent.getPositionAgent(), center_agent); + + // Don't draw manip if object too far away + if (gSavedSettings.getBOOL("LimitSelectDistance")) + { + F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance"); + if (range_from_agent > max_select_distance) + { + return; + } + } + + if (range > 0.001f) + { + // range != zero + F32 fraction_of_fov = BOX_HANDLE_BASE_SIZE / (F32) gCamera->getViewHeightInPixels(); + F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians + mBoxHandleSize = range * tan(apparent_angle) * BOX_HANDLE_BASE_FACTOR; + } + else + { + // range == zero + mBoxHandleSize = BOX_HANDLE_BASE_FACTOR; + } + } + + //////////////////////////////////////////////////////////////////////// + // Draw bounding box + + LLBBox bbox = gSelectMgr->getBBoxOfSelection(); + LLVector3 pos_agent = bbox.getPositionAgent(); + LLQuaternion rot = bbox.getRotation(); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + { + glTranslatef(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ]); + + F32 angle_radians, x, y, z; + rot.getAngleAxis(&angle_radians, &x, &y, &z); + glRotatef(angle_radians * RAD_TO_DEG, x, y, z); + + + { + LLGLEnable poly_offset(GL_POLYGON_OFFSET_FILL); + glPolygonOffset( -2.f, -2.f); + + // JC - Band-aid until edge stretch working similar to side stretch + // in non-uniform. + // renderEdges( bbox ); + + renderCorners( bbox ); + renderFaces( bbox ); + + if (mManipPart != LL_NO_PART) + { + renderGuidelinesPart( bbox ); + } + + glPolygonOffset( 0.f, 0.f); + } + } + glPopMatrix(); + + if (mManipPart != LL_NO_PART) + { + renderSnapGuides(bbox); + } + glPopMatrix(); + + renderXYZ(bbox.getExtentLocal()); + } +} + +BOOL LLManipScale::handleMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = FALSE; + + LLViewerObject* hit_obj = gViewerWindow->lastObjectHit(); + if( hit_obj || + (mHighlightedPart != LL_NO_PART) ) + { + handled = handleMouseDownOnPart( x, y, mask ); + } + + return handled; +} + +// Assumes that one of the arrows on an object was hit. +BOOL LLManipScale::handleMouseDownOnPart( S32 x, S32 y, MASK mask ) +{ + BOOL can_scale = gSelectMgr->getObjectCount() != 0; + for (LLViewerObject* objectp = gSelectMgr->getFirstObject(); + objectp; + objectp = gSelectMgr->getNextObject()) + { + can_scale = can_scale && objectp->permModify() && objectp->permMove() && !objectp->isSeat(); + } + + if (!can_scale) + { + return FALSE; + } + + highlightManipulators(x, y); + S32 hit_part = mHighlightedPart; + + gSelectMgr->enableSilhouette(FALSE); + mManipPart = (EManipPart)hit_part; + + LLBBox bbox = gSelectMgr->getBBoxOfSelection(); + LLVector3 box_center_agent = bbox.getCenterAgent(); + LLVector3 box_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ) ); + + updateSnapGuides(bbox); + + mDragStartPointGlobal = gAgent.getPosGlobalFromAgent(box_corner_agent); + mDragStartCenterGlobal = gAgent.getPosGlobalFromAgent(box_center_agent); + LLVector3 far_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox ) ); + mDragFarHitGlobal = gAgent.getPosGlobalFromAgent(far_corner_agent); + mDragPointGlobal = mDragStartPointGlobal; + + // we just started a drag, so save initial object positions, orientations, and scales + gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_SCALE); + // Route future Mouse messages here preemptively. (Release on mouse up.) + setMouseCapture( TRUE ); + + mHelpTextTimer.reset(); + sNumTimesHelpTextShown++; + return TRUE; +} + + +BOOL LLManipScale::handleMouseUp(S32 x, S32 y, MASK mask) +{ + // first, perform normal processing in case this was a quick-click + handleHover(x, y, mask); + + gSelectMgr->enableSilhouette(TRUE); + mManipPart = LL_NO_PART; + + // Might have missed last update due to UPDATE_DELAY timing + if (mSendUpdateOnMouseUp) + { + gSelectMgr->sendMultipleUpdate( mLastUpdateFlags ); + } + + //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject")); + gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + return LLManip::handleMouseUp(x, y, mask); +} + + +BOOL LLManipScale::handleHover(S32 x, S32 y, MASK mask) +{ + if( hasMouseCapture() ) + { + if( gSelectMgr->isEmpty() ) + { + // Somehow the object got deselected while we were dragging it. + setMouseCapture( FALSE ); + } + else + { + drag( x, y ); + } + lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (active)" << llendl; + } + else + { + mInSnapRegime = FALSE; + // not dragging... + highlightManipulators(x, y); + } + + // Patch up textures, if possible. + gSelectMgr->adjustTexturesByScale(TRUE, getStretchTextures()); + + gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLSCALE); + return TRUE; +} + +void LLManipScale::highlightManipulators(S32 x, S32 y) +{ + mHighlightedPart = LL_NO_PART; + + // If we have something selected, try to hit its manipulator handles. + // Don't do this with nothing selected, as it kills the framerate. + LLBBox bbox = gSelectMgr->getBBoxOfSelection(); + + if( isSelectionScalable() ) + { + LLMatrix4 transform; + if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD) + { + LLVector4 translation(bbox.getPositionAgent()); + transform.initRotTrans(bbox.getRotation(), translation); + LLMatrix4 cfr(OGL_TO_CFR_ROTATION); + transform *= cfr; + LLMatrix4 window_scale; + F32 zoom_level = 2.f * gAgent.getAvatarObject()->mHUDCurZoom; + window_scale.initAll(LLVector3(zoom_level / gCamera->getAspect(), zoom_level, 0.f), + LLQuaternion::DEFAULT, + LLVector3::zero); + transform *= window_scale; + } + else + { + LLMatrix4 projMatrix = gCamera->getProjection(); + LLMatrix4 modelView = gCamera->getModelview(); + transform.initAll(LLVector3(1.f, 1.f, 1.f), bbox.getRotation(), bbox.getPositionAgent()); + + transform *= modelView; + transform *= projMatrix; + } + + LLVector3 min = bbox.getMinLocal(); + LLVector3 max = bbox.getMaxLocal(); + LLVector3 ctr = bbox.getCenterLocal(); + + mProjectedManipulators.deleteAllData(); + + S32 numManips = 0; + // corners + mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], min.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], max.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], min.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], max.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], min.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], max.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], min.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], max.mV[VZ], 1.f); + + // 1-D highlights are applicable iff one object is selected + if( gSelectMgr->getObjectCount() == 1 ) + { + // face centers + mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], max.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], max.mV[VY], ctr.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], min.mV[VY], ctr.mV[VZ], 1.f); + mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], min.mV[VZ], 1.f); + } + + for (S32 i = 0; i < numManips; i++) + { + LLVector4 projectedVertex = mManipulatorVertices[i] * transform; + projectedVertex = projectedVertex * (1.f / projectedVertex.mV[VW]); + + ManipulatorHandle* projManipulator = new ManipulatorHandle(LLVector3(projectedVertex.mV[VX], projectedVertex.mV[VY], + projectedVertex.mV[VZ]), MANIPULATOR_IDS[i], (i < 7) ? SCALE_MANIP_CORNER : SCALE_MANIP_FACE); + mProjectedManipulators.addDataSorted(projManipulator); + } + + F32 half_width = (F32)gViewerWindow->getWindowWidth() / 2.f; + F32 half_height = (F32)gViewerWindow->getWindowHeight() / 2.f; + LLVector2 manip2d; + LLVector2 mousePos((F32)x - half_width, (F32)y - half_height); + LLVector2 delta; + + mHighlightedPart = LL_NO_PART; + + for (ManipulatorHandle* manipulator = mProjectedManipulators.getFirstData(); + manipulator; + manipulator = mProjectedManipulators.getNextData()) + { + manip2d.setVec(manipulator->mPosition.mV[VX] * half_width, manipulator->mPosition.mV[VY] * half_height); + + delta = manip2d - mousePos; + if (delta.magVecSquared() < MAX_MANIP_SELECT_DISTANCE_SQUARED) + { + mHighlightedPart = manipulator->mManipID; + + //llinfos << "Tried: " << mHighlightedPart << llendl; + break; + } + } + } + + for (S32 i = 0; i < NUM_MANIPULATORS; i++) + { + if (mHighlightedPart == MANIPULATOR_IDS[i]) + { + mManipulatorScales[i] = lerp(mManipulatorScales[i], SELECTED_MANIPULATOR_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + } + else + { + mManipulatorScales[i] = lerp(mManipulatorScales[i], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + } + } + + lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (inactive)" << llendl; +} + + +void LLManipScale::renderFaces( const LLBBox& bbox ) +{ + // Don't bother to render the drag handles for 1-D scaling if + // more than one object is selected or if it is an attachment + if ( gSelectMgr->getObjectCount() > 1 ) + { + return; + } + + // This is a flattened representation of the box as render here + // . + // (+++) (++-) /|\t + // +------------+ | (texture coordinates) + // | | | + // | 1 | (*) --->s + // | +X | + // | | + // (+++) (+-+)| |(+--) (++-) (+++) + // +------------+------------+------------+------------+ + // |0 3|3 7|7 4|4 0| + // | 0 | 4 | 5 | 2 | + // | +Z | -Y | -Z | +Y | + // | | | | | + // |1 2|2 6|6 5|5 1| + // +------------+------------+------------+------------+ + // (-++) (--+)| |(---) (-+-) (-++) + // | 3 | + // | -X | + // | | + // | | + // +------------+ + // (-++) (-+-) + + LLColor4 highlight_color( 1.f, 1.f, 1.f, 0.5f); + LLColor4 normal_color( 1.f, 1.f, 1.f, 0.3f); + + LLColor4 x_highlight_color( 1.f, 0.2f, 0.2f, 1.0f); + LLColor4 x_normal_color( 0.6f, 0.f, 0.f, 0.4f); + + LLColor4 y_highlight_color( 0.2f, 1.f, 0.2f, 1.0f); + LLColor4 y_normal_color( 0.f, 0.6f, 0.f, 0.4f); + + LLColor4 z_highlight_color( 0.2f, 0.2f, 1.f, 1.0f); + LLColor4 z_normal_color( 0.f, 0.f, 0.6f, 0.4f); + + LLColor4 default_normal_color( 0.7f, 0.7f, 0.7f, 0.15f ); + + const LLVector3& min = bbox.getMinLocal(); + const LLVector3& max = bbox.getMaxLocal(); + LLVector3 ctr = bbox.getCenterLocal(); + + if (mManipPart == LL_NO_PART) + { + glColor4fv( default_normal_color.mV ); + glBegin(GL_QUADS); + { + // Face 0 + glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]); + glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]); + glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]); + glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]); + + // Face 1 + glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]); + glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]); + glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]); + glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]); + + // Face 2 + glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]); + glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]); + glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]); + glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]); + + // Face 3 + glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]); + glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]); + glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]); + glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]); + + // Face 4 + glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]); + glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]); + glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]); + glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]); + + // Face 5 + glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]); + glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]); + glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]); + glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]); + } + glEnd(); + } + + // Find nearest vertex + LLVector3 orientWRTHead = bbox.agentToLocalBasis( bbox.getCenterAgent() - gAgent.getCameraPositionAgent() ); + 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][6] = { + { 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 + }; + + { + LLGLDepthTest gls_depth(GL_FALSE); + + for (S32 i = 0; i < 6; i++) + { + U32 face = face_list[nearest][i]; + switch( face ) + { + case 0: + conditionalHighlight( LL_FACE_POSZ, &z_highlight_color, &z_normal_color ); + renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], max.mV[VZ] ) ); + break; + + case 1: + conditionalHighlight( LL_FACE_POSX, &x_highlight_color, &x_normal_color ); + renderAxisHandle( ctr, LLVector3( max.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) ); + break; + + case 2: + conditionalHighlight( LL_FACE_POSY, &y_highlight_color, &y_normal_color ); + renderAxisHandle( ctr, LLVector3( ctr.mV[VX], max.mV[VY], ctr.mV[VZ] ) ); + break; + + case 3: + conditionalHighlight( LL_FACE_NEGX, &x_highlight_color, &x_normal_color ); + renderAxisHandle( ctr, LLVector3( min.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) ); + break; + + case 4: + conditionalHighlight( LL_FACE_NEGY, &y_highlight_color, &y_normal_color ); + renderAxisHandle( ctr, LLVector3( ctr.mV[VX], min.mV[VY], ctr.mV[VZ] ) ); + break; + + case 5: + conditionalHighlight( LL_FACE_NEGZ, &z_highlight_color, &z_normal_color ); + renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], min.mV[VZ] ) ); + break; + } + } + } +} + +void LLManipScale::renderEdges( const LLBBox& bbox ) +{ + LLVector3 extent = bbox.getExtentLocal(); + F32 edge_width = mBoxHandleSize * .6f; + + for( U32 part = LL_EDGE_MIN; part <= LL_EDGE_MAX; part++ ) + { + LLVector3 direction = edgeToUnitVector( part ); + LLVector3 center_to_edge = unitVectorToLocalBBoxExtent( direction, bbox ); + + glPushMatrix(); + { + glTranslatef( center_to_edge.mV[0], center_to_edge.mV[1], center_to_edge.mV[2] ); + conditionalHighlight( part ); + glScalef( + direction.mV[0] ? edge_width : extent.mV[VX], + direction.mV[1] ? edge_width : extent.mV[VY], + direction.mV[2] ? edge_width : extent.mV[VZ] ); + gBox.render(); + } + glPopMatrix(); + } +} + + +void LLManipScale::renderCorners( const LLBBox& bbox ) +{ + U32 part = LL_CORNER_NNN; + + F32 x_offset = bbox.getMinLocal().mV[VX]; + for( S32 i=0; i < 2; i++ ) + { + F32 y_offset = bbox.getMinLocal().mV[VY]; + for( S32 j=0; j < 2; j++ ) + { + F32 z_offset = bbox.getMinLocal().mV[VZ]; + for( S32 k=0; k < 2; k++ ) + { + conditionalHighlight( part ); + renderBoxHandle( x_offset, y_offset, z_offset ); + part++; + + z_offset = bbox.getMaxLocal().mV[VZ]; + + } + y_offset = bbox.getMaxLocal().mV[VY]; + } + x_offset = bbox.getMaxLocal().mV[VX]; + } +} + + +void LLManipScale::renderBoxHandle( F32 x, F32 y, F32 z ) +{ + LLGLDisable gls_tex(GL_TEXTURE_2D); + + glPushMatrix(); + { + glTranslatef( x, y, z ); + glScalef( mScaledBoxHandleSize, mScaledBoxHandleSize, mScaledBoxHandleSize ); + gBox.render(); + } + glPopMatrix(); +} + + +void LLManipScale::renderAxisHandle( const LLVector3& start, const LLVector3& end ) +{ + if( getShowAxes() ) + { + // Draws a single "jacks" style handle: a long, retangular box from start to end. + LLVector3 offset_start = end - start; + offset_start.normVec(); + offset_start = start + mBoxHandleSize * offset_start; + + LLVector3 delta = end - offset_start; + LLVector3 pos = offset_start + 0.5f * delta; + + glPushMatrix(); + { + glTranslatef( pos.mV[VX], pos.mV[VY], pos.mV[VZ] ); + glScalef( + mBoxHandleSize + llabs(delta.mV[VX]), + mBoxHandleSize + llabs(delta.mV[VY]), + mBoxHandleSize + llabs(delta.mV[VZ])); + gBox.render(); + } + glPopMatrix(); + } + else + { + renderBoxHandle( end.mV[VX], end.mV[VY], end.mV[VZ] ); + } +} + + +void LLManipScale::drag( S32 x, S32 y ) +{ + if( (LL_FACE_MIN <= (S32)mManipPart) + && ((S32)mManipPart <= LL_FACE_MAX) ) + { + dragFace( x, y ); + } + else + if( (LL_CORNER_MIN <= (S32)mManipPart) + && ((S32)mManipPart <= LL_CORNER_MAX) ) + { + dragCorner( x, y ); + } + + //gAgent.setObjectTracking(FALSE); + gAgent.clearFocusObject(); +} + +// Scale around the +void LLManipScale::dragCorner( S32 x, S32 y ) +{ + LLBBox bbox = gSelectMgr->getBBoxOfSelection(); + + // Suppress scale if mouse hasn't moved. + if (x == mLastMouseX && y == mLastMouseY) + { + if (mSendUpdateOnMouseUp) + { + sendUpdates(TRUE,TRUE,TRUE); + } + return; + } + + mLastMouseX = x; + mLastMouseY = y; + + LLVector3d drag_start_point_global = mDragStartPointGlobal; + LLVector3d drag_start_center_global = mDragStartCenterGlobal; + LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global); + LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global); + + LLVector3d drag_start_dir_d; + drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global); + LLVector3 drag_start_dir_f; + drag_start_dir_f.setVec(drag_start_dir_d); + + F32 s = 0; + F32 t = 0; + + nearestPointOnLineFromMouse(x, y, + drag_start_center_agent, + drag_start_point_agent, + s, t ); + + F32 drag_start_dist = dist_vec(drag_start_point_agent, drag_start_center_agent); + + if( s <= 0 ) // we only care about intersections in front of the camera + { + return; + } + + LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d; + + F32 scale_factor = t; + + BOOL uniform = LLManipScale::getUniform(); + + if( !uniform ) + { + scale_factor = 0.5f + (scale_factor * 0.5f); + } + + // check for snapping + LLVector3 drag_center_agent = gAgent.getPosAgentFromGlobal(drag_point_global); + LLVector3 mouse_on_plane1; + getMousePointOnPlaneAgent(mouse_on_plane1, x, y, drag_center_agent, mScalePlaneNormal1); + LLVector3 mouse_on_plane2; + getMousePointOnPlaneAgent(mouse_on_plane2, x, y, drag_center_agent, mScalePlaneNormal2); + LLVector3 mouse_dir_1 = mouse_on_plane1 - mScaleCenter; + LLVector3 mouse_dir_2 = mouse_on_plane2 - mScaleCenter; + LLVector3 mouse_to_scale_line_1 = mouse_dir_1 - projected_vec(mouse_dir_1, mScaleDir); + LLVector3 mouse_to_scale_line_2 = mouse_dir_2 - projected_vec(mouse_dir_2, mScaleDir); + LLVector3 mouse_to_scale_line_dir_1 = mouse_to_scale_line_1; + mouse_to_scale_line_dir_1.normVec(); + if (mouse_to_scale_line_dir_1 * mSnapGuideDir1 < 0.f) + { + // need to keep sign of mouse offset wrt to snap guide direction + mouse_to_scale_line_dir_1 *= -1.f; + } + LLVector3 mouse_to_scale_line_dir_2 = mouse_to_scale_line_2; + mouse_to_scale_line_dir_2.normVec(); + if (mouse_to_scale_line_dir_2 * mSnapGuideDir2 < 0.f) + { + // need to keep sign of mouse offset wrt to snap guide direction + mouse_to_scale_line_dir_2 *= -1.f; + } + + F32 snap_dir_dot_mouse_offset1 = mSnapGuideDir1 * mouse_to_scale_line_dir_1; + F32 snap_dir_dot_mouse_offset2 = mSnapGuideDir2 * mouse_to_scale_line_dir_2; + + F32 dist_from_scale_line_1 = mouse_to_scale_line_1 * mouse_to_scale_line_dir_1; + F32 dist_from_scale_line_2 = mouse_to_scale_line_2 * mouse_to_scale_line_dir_2; + + F32 max_scale = partToMaxScale(mManipPart, bbox); + F32 min_scale = partToMinScale(mManipPart, bbox); + + BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled"); + if (snap_enabled && dist_from_scale_line_1 > mSnapRegimeOffset * snap_dir_dot_mouse_offset1) + { + mInSnapRegime = TRUE; + LLVector3 projected_drag_pos = mouse_on_plane1 - (dist_from_scale_line_1 / snap_dir_dot_mouse_offset1) * mSnapGuideDir1; + F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir; + + F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel); + F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions); + F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions); + + mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale); + + scale_factor = mScaleSnapValue / drag_start_dist; + if( !uniform ) + { + scale_factor *= 0.5f; + } + } + else if (snap_enabled && dist_from_scale_line_2 > mSnapRegimeOffset * snap_dir_dot_mouse_offset2) + { + mInSnapRegime = TRUE; + LLVector3 projected_drag_pos = mouse_on_plane2 - (dist_from_scale_line_2 / snap_dir_dot_mouse_offset2) * mSnapGuideDir2; + F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir; + + F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel); + F32 snap_dist = mScaleSnapUnit2 / (2.f * cur_subdivisions); + F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit2 / cur_subdivisions); + + mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale); + + scale_factor = mScaleSnapValue / drag_start_dist; + if( !uniform ) + { + scale_factor *= 0.5f; + } + } + else + { + mInSnapRegime = FALSE; + } + + F32 max_scale_factor = MAX_OBJECT_SCALE / MIN_OBJECT_SCALE; + F32 min_scale_factor = MIN_OBJECT_SCALE / MAX_OBJECT_SCALE; + + // find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale + LLSelectNode* selectNode; + for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() ) + { + LLViewerObject* cur = selectNode->getObject(); + if( cur->permModify() && cur->permMove() && !cur->isAvatar() ) + { + const LLVector3& scale = selectNode->mSavedScale; + + F32 cur_max_scale_factor = llmin( MAX_OBJECT_SCALE / scale.mV[VX], MAX_OBJECT_SCALE / scale.mV[VY], MAX_OBJECT_SCALE / scale.mV[VZ] ); + max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor ); + + F32 cur_min_scale_factor = llmax( MIN_OBJECT_SCALE / scale.mV[VX], MIN_OBJECT_SCALE / scale.mV[VY], MIN_OBJECT_SCALE / scale.mV[VZ] ); + min_scale_factor = llmax( min_scale_factor, cur_min_scale_factor ); + } + } + + scale_factor = llclamp( scale_factor, min_scale_factor, max_scale_factor ); + + LLVector3d drag_global = uniform ? mDragStartCenterGlobal : mDragFarHitGlobal; + + // do the root objects i.e. (TRUE == cur->isRootEdit()) + for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() ) + { + LLViewerObject* cur = selectNode->getObject(); + if( cur->permModify() && cur->permMove() && !cur->isAvatar() && cur->isRootEdit() ) + { + const LLVector3& scale = selectNode->mSavedScale; + cur->setScale( scale_factor * scale ); + + LLVector3 delta_pos; + LLVector3 original_pos = cur->getPositionEdit(); + LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor; + if (!cur->isAttachment()) + { + new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global); + } + cur->setPositionAbsoluteGlobal( new_pos_global ); + rebuild(cur); + + delta_pos = cur->getPositionEdit() - original_pos; + + if (selectNode->mIndividualSelection) + { + // counter-translate child objects if we are moving the root as an individual + for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++) + { + LLViewerObject* childp = cur->mChildList[child_num]; + + if (cur->isAttachment()) + { + LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit()); + childp->setPosition(child_pos); + } + else + { + LLVector3d child_pos_delta(delta_pos); + // RN: this updates drawable position instantly + childp->setPositionAbsoluteGlobal(childp->getPositionGlobal() - child_pos_delta); + } + rebuild(childp); + } + } + } + } + // do the child objects i.e. (FALSE == cur->isRootEdit()) + for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() ) + { + LLViewerObject*cur = selectNode->getObject(); + if( cur->permModify() && cur->permMove() && !cur->isAvatar() && !cur->isRootEdit() ) + { + const LLVector3& scale = selectNode->mSavedScale; + cur->setScale( scale_factor * scale, FALSE ); + + if (!selectNode->mIndividualSelection) + { + cur->setPosition(selectNode->mSavedPositionLocal * scale_factor); + continue; + } + + LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor; + cur->setPositionAbsoluteGlobal( new_pos_global ); + rebuild(cur); + } + } + + + + mDragPointGlobal = drag_point_global; + + sendUpdates( TRUE, TRUE, TRUE ); +} + + +void LLManipScale::dragFace( S32 x, S32 y ) +{ + // Suppress scale if mouse hasn't moved. + if (x == mLastMouseX && y == mLastMouseY) + { + if (mSendUpdateOnMouseUp) + { + sendUpdates(TRUE,TRUE,FALSE); + } + return; + } + + mLastMouseX = x; + mLastMouseY = y; + + LLVector3d drag_start_point_global = mDragStartPointGlobal; + LLVector3d drag_start_center_global = mDragStartCenterGlobal; + LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global); + LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global); + + LLVector3d drag_start_dir_d; + drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global); + LLVector3 drag_start_dir_f; + drag_start_dir_f.setVec(drag_start_dir_d); + + LLBBox bbox = gSelectMgr->getBBoxOfSelection(); + + F32 s = 0; + F32 t = 0; + + nearestPointOnLineFromMouse(x, + y, + drag_start_center_agent, + drag_start_point_agent, + s, t ); + + if( s <= 0 ) // we only care about intersections in front of the camera + { + return; + } + + LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d; + LLVector3 part_dir_local = partToUnitVector( mManipPart ); + + // check for snapping + LLVector3 mouse_on_plane; + getMousePointOnPlaneAgent(mouse_on_plane, x, y, mScaleCenter, mScalePlaneNormal1 ); + + LLVector3 mouse_on_scale_line = mScaleCenter + projected_vec(mouse_on_plane - mScaleCenter, mScaleDir); + LLVector3 drag_delta(mouse_on_scale_line - drag_start_point_agent); + F32 max_drag_dist = partToMaxScale(mManipPart, bbox); + F32 min_drag_dist = partToMinScale(mManipPart, bbox); + + BOOL uniform = LLManipScale::getUniform(); + if( uniform ) + { + drag_delta *= 2.f; + } + + LLVector3 scale_center_to_mouse = mouse_on_plane - mScaleCenter; + F32 dist_from_scale_line = dist_vec(scale_center_to_mouse, (mouse_on_scale_line - mScaleCenter)); + F32 dist_along_scale_line = scale_center_to_mouse * mScaleDir; + + BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled"); + + if (snap_enabled && dist_from_scale_line > mSnapRegimeOffset) + { + mInSnapRegime = TRUE; + + if (dist_along_scale_line > max_drag_dist) + { + mScaleSnapValue = max_drag_dist; + + LLVector3 clamp_point = mScaleCenter + max_drag_dist * mScaleDir; + drag_delta.setVec(clamp_point - drag_start_point_agent); + } + else if (dist_along_scale_line < min_drag_dist) + { + mScaleSnapValue = min_drag_dist; + + LLVector3 clamp_point = mScaleCenter + min_drag_dist * mScaleDir; + drag_delta.setVec(clamp_point - drag_start_point_agent); + } + else + { + F32 drag_dist = scale_center_to_mouse * mScaleDir; + F32 cur_subdivisions = llclamp(getSubdivisionLevel(mScaleCenter + mScaleDir * drag_dist, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel); + F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions); + F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions); + relative_snap_dist -= snap_dist; + + //make sure that values that the scale is "snapped to" + //do not exceed/go under the applicable max/mins + //this causes the box to shift displacements ever so slightly + //although the "snap value" should go down to 0 + //see Jira 1027 + relative_snap_dist = llclamp(relative_snap_dist, + drag_dist - max_drag_dist, + drag_dist - min_drag_dist); + + mScaleSnapValue = drag_dist - relative_snap_dist; + + if (llabs(relative_snap_dist) < snap_dist) + { + LLVector3 drag_correction = relative_snap_dist * mScaleDir; + if (uniform) + { + drag_correction *= 2.f; + } + + drag_delta -= drag_correction; + } + } + } + else + { + mInSnapRegime = FALSE; + } + + BOOL send_scale_update = FALSE; + BOOL send_position_update = FALSE; + + LLVector3 dir_agent; + if( part_dir_local.mV[VX] ) + { + dir_agent = bbox.localToAgentBasis( LLVector3::x_axis ); + } + else if( part_dir_local.mV[VY] ) + { + dir_agent = bbox.localToAgentBasis( LLVector3::y_axis ); + } + else if( part_dir_local.mV[VZ] ) + { + dir_agent = bbox.localToAgentBasis( LLVector3::z_axis ); + } + stretchFace( + projected_vec(drag_start_dir_f, dir_agent) + drag_start_center_agent, + projected_vec(drag_delta, dir_agent)); + send_position_update = TRUE; + send_scale_update = TRUE; + + mDragPointGlobal = drag_point_global; + + sendUpdates( send_position_update, send_scale_update ); +} + +void LLManipScale::sendUpdates( BOOL send_position_update, BOOL send_scale_update, BOOL corner ) +{ + // Throttle updates to 10 per second. + static LLTimer update_timer; + F32 elapsed_time = update_timer.getElapsedTimeF32(); + const F32 UPDATE_DELAY = 0.1f; // min time between transmitted updates + + if( send_scale_update || send_position_update ) + { + U32 update_flags = UPD_NONE; + if (send_position_update) update_flags |= UPD_POSITION; + if (send_scale_update) update_flags |= UPD_SCALE; + +// BOOL send_type = SEND_INDIVIDUALS; + if (corner) + { + update_flags |= UPD_UNIFORM; + } + // keep this up to date for sendonmouseup + mLastUpdateFlags = update_flags; + + // enforce minimum update delay and don't stream updates on sub-object selections + if( elapsed_time > UPDATE_DELAY && gSavedSettings.getBOOL("SelectLinkedSet") ) + { + gSelectMgr->sendMultipleUpdate( update_flags ); + update_timer.reset(); + mSendUpdateOnMouseUp = FALSE; + } + else + { + mSendUpdateOnMouseUp = TRUE; + } + + gSelectMgr->updateSelectionCenter(); + dialog_refresh_all(); + } +} + +// Rescales in a single dimension. Either uniform (standard) or one-sided (scale plus translation) +// depending on mUniform. Handles multiple selection and objects that are not aligned to the bounding box. +void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVector3& drag_delta_agent ) +{ + LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(mDragStartCenterGlobal); + + LLSelectNode *selectNode; + for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() ) + { + LLViewerObject*cur = selectNode->getObject(); + if( cur->permModify() && cur->permMove() && !cur->isAvatar() ) + { + LLBBox cur_bbox = cur->getBoundingBoxAgent(); + LLVector3 start_local = cur_bbox.agentToLocal( drag_start_agent ); + LLVector3 end_local = cur_bbox.agentToLocal( drag_start_agent + drag_delta_agent); + LLVector3 start_center_local = cur_bbox.agentToLocal( drag_start_center_agent ); + LLVector3 axis = nearestAxis( start_local - start_center_local ); + S32 axis_index = axis.mV[0] ? 0 : (axis.mV[1] ? 1 : 2 ); + + LLVector3 delta_local = end_local - start_local; + F32 delta_local_mag = delta_local.magVec(); + LLVector3 dir_local; + if (delta_local_mag == 0.f) + { + dir_local = axis; + } + else + { + dir_local = delta_local / delta_local_mag; // normalized delta_local + } + + F32 denom = axis * dir_local; + F32 desired_delta_size = is_approx_zero(denom) ? 0.f : (delta_local_mag / denom); // in meters + F32 desired_scale = llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_OBJECT_SCALE, MAX_OBJECT_SCALE); + // propagate scale constraint back to position offset + desired_delta_size = desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position + + LLVector3 scale = cur->getScale(); + scale.mV[axis_index] = desired_scale; + cur->setScale(scale, FALSE); + rebuild(cur); + LLVector3 delta_pos; + if( !getUniform() ) + { + LLVector3 delta_pos_local = axis * (0.5f * desired_delta_size); + LLVector3d delta_pos_global; + delta_pos_global.setVec(cur_bbox.localToAgent( delta_pos_local ) - cur_bbox.getCenterAgent()); + LLVector3 cur_pos = cur->getPositionEdit(); + + if (cur->isRootEdit() && !cur->isAttachment()) + { + LLVector3d new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, selectNode->mSavedPositionGlobal + delta_pos_global); + cur->setPositionGlobal( new_pos_global ); + } + else + { + LLXform* parent_xform = cur->mDrawable->getXform()->getParent(); + LLVector3 new_pos_local; + // this works in attachment point space using world space delta + if (parent_xform) + { + new_pos_local = selectNode->mSavedPositionLocal + (LLVector3(delta_pos_global) * ~parent_xform->getWorldRotation()); + } + else + { + new_pos_local = selectNode->mSavedPositionLocal + LLVector3(delta_pos_global); + } + cur->setPosition(new_pos_local); + } + delta_pos = cur->getPositionEdit() - cur_pos; + } + if (cur->isRootEdit() && selectNode->mIndividualSelection) + { + // counter-translate child objects if we are moving the root as an individual + for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++) + { + LLViewerObject* childp = cur->mChildList[child_num]; + if (!getUniform()) + { + LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit()); + childp->setPosition(child_pos); + rebuild(childp); + } + } + } + } + } +} + + +void LLManipScale::renderGuidelinesPart( const LLBBox& bbox ) +{ + LLVector3 guideline_start = bbox.getCenterLocal(); + + LLVector3 guideline_end = unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ); + + if (!getUniform()) + { + guideline_start = unitVectorToLocalBBoxExtent( -partToUnitVector( mManipPart ), bbox ); + } + + guideline_end -= guideline_start; + guideline_end.normVec(); + guideline_end *= gWorldPointer->getRegionWidthInMeters(); + guideline_end += guideline_start; + + { + LLGLDepthTest gls_depth(GL_TRUE); + gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.5f) ); + } + { + LLGLDepthTest gls_depth(GL_FALSE); + gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.25f) ); + } +} + +void LLManipScale::updateSnapGuides(const LLBBox& bbox) +{ + LLVector3 grid_origin; + LLVector3 grid_scale; + LLQuaternion grid_rotation; + gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale); + + LLVector3 box_corner_agent = bbox.localToAgent(unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox )); + mScaleCenter = getUniform() ? bbox.getCenterAgent() : bbox.localToAgent(unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox )); + mScaleDir = box_corner_agent - mScaleCenter; + mScaleDir.normVec(); + + if(gSelectMgr->getSelectType() == SELECT_TYPE_HUD) + { + mSnapRegimeOffset = SNAP_GUIDE_SCREEN_OFFSET / gAgent.getAvatarObject()->mHUDCurZoom; + + } + else + { + F32 object_distance = dist_vec(mScaleCenter, gCamera->getOrigin()); + mSnapRegimeOffset = (SNAP_GUIDE_SCREEN_OFFSET * gViewerWindow->getWindowWidth() * object_distance) / gCamera->getPixelMeterRatio(); + } + LLVector3 cam_at_axis; + F32 snap_guide_length; + if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD) + { + cam_at_axis.setVec(1.f, 0.f, 0.f); + snap_guide_length = SNAP_GUIDE_SCREEN_LENGTH / gAgent.getAvatarObject()->mHUDCurZoom; + } + else + { + cam_at_axis = gCamera->getAtAxis(); + F32 manipulator_distance = dist_vec(box_corner_agent, gCamera->getOrigin()); + snap_guide_length = (SNAP_GUIDE_SCREEN_LENGTH * gViewerWindow->getWindowWidth() * manipulator_distance) / gCamera->getPixelMeterRatio(); + } + + mSnapGuideLength = snap_guide_length / llmax(0.1f, (llmin(mSnapGuideDir1 * cam_at_axis, mSnapGuideDir2 * cam_at_axis))); + + LLVector3 off_axis_dir = mScaleDir % cam_at_axis; + off_axis_dir.normVec(); + + if( (LL_FACE_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_FACE_MAX) ) + { + LLVector3 object_scale = bbox.getMaxLocal(); + object_scale.scaleVec(off_axis_dir * ~bbox.getRotation()); + object_scale.abs(); + if (object_scale.mV[VX] > object_scale.mV[VY] && object_scale.mV[VX] > object_scale.mV[VZ]) + { + mSnapGuideDir1 = LLVector3::x_axis * bbox.getRotation(); + } + else if (object_scale.mV[VY] > object_scale.mV[VZ]) + { + mSnapGuideDir1 = LLVector3::y_axis * bbox.getRotation(); + } + else + { + mSnapGuideDir1 = LLVector3::z_axis * bbox.getRotation(); + } + + LLVector3 scale_snap = grid_scale; + mScaleSnapUnit1 = scale_snap.scaleVec(partToUnitVector( mManipPart )).magVec(); + mScaleSnapUnit2 = mScaleSnapUnit1; + mSnapGuideDir1 *= mSnapGuideDir1 * gCamera->getUpAxis() > 0.f ? 1.f : -1.f; + mSnapGuideDir2 = mSnapGuideDir1 * -1.f; + mSnapDir1 = mScaleDir; + mSnapDir2 = mScaleDir; + } + else if( (LL_CORNER_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_CORNER_MAX) ) + { + LLVector3 local_scale_dir = partToUnitVector( mManipPart ); + LLVector3 local_camera_dir; + if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD) + { + local_camera_dir = LLVector3(-1.f, 0.f, 0.f) * ~bbox.getRotation(); + } + else + { + local_camera_dir = (gCamera->getOrigin() - bbox.getCenterAgent()) * ~bbox.getRotation(); + local_camera_dir.normVec(); + } + local_scale_dir -= projected_vec(local_scale_dir, local_camera_dir); + local_scale_dir.normVec(); + LLVector3 x_axis_proj_camera = LLVector3::x_axis - projected_vec(LLVector3::x_axis, local_camera_dir); + x_axis_proj_camera.normVec(); + LLVector3 y_axis_proj_camera = LLVector3::y_axis - projected_vec(LLVector3::y_axis, local_camera_dir); + y_axis_proj_camera.normVec(); + LLVector3 z_axis_proj_camera = LLVector3::z_axis - projected_vec(LLVector3::z_axis, local_camera_dir); + z_axis_proj_camera.normVec(); + F32 x_axis_proj = llabs(local_scale_dir * x_axis_proj_camera); + F32 y_axis_proj = llabs(local_scale_dir * y_axis_proj_camera); + F32 z_axis_proj = llabs(local_scale_dir * z_axis_proj_camera); + + if (x_axis_proj > y_axis_proj && x_axis_proj > z_axis_proj) + { + mSnapGuideDir1 = LLVector3::y_axis; + mScaleSnapUnit2 = grid_scale.mV[VY]; + mSnapGuideDir2 = LLVector3::z_axis; + mScaleSnapUnit1 = grid_scale.mV[VZ]; + } + else if (y_axis_proj > z_axis_proj) + { + mSnapGuideDir1 = LLVector3::x_axis; + mScaleSnapUnit2 = grid_scale.mV[VX]; + mSnapGuideDir2 = LLVector3::z_axis; + mScaleSnapUnit1 = grid_scale.mV[VZ]; + } + else + { + mSnapGuideDir1 = LLVector3::x_axis; + mScaleSnapUnit2 = grid_scale.mV[VX]; + mSnapGuideDir2 = LLVector3::y_axis; + mScaleSnapUnit1 = grid_scale.mV[VY]; + } + + LLVector3 snap_guide_flip(1.f, 1.f, 1.f); + switch (mManipPart) + { + case LL_CORNER_NNN: + break; + case LL_CORNER_NNP: + snap_guide_flip.setVec(1.f, 1.f, -1.f); + break; + case LL_CORNER_NPN: + snap_guide_flip.setVec(1.f, -1.f, 1.f); + break; + case LL_CORNER_NPP: + snap_guide_flip.setVec(1.f, -1.f, -1.f); + break; + case LL_CORNER_PNN: + snap_guide_flip.setVec(-1.f, 1.f, 1.f); + break; + case LL_CORNER_PNP: + snap_guide_flip.setVec(-1.f, 1.f, -1.f); + break; + case LL_CORNER_PPN: + snap_guide_flip.setVec(-1.f, -1.f, 1.f); + break; + case LL_CORNER_PPP: + snap_guide_flip.setVec(-1.f, -1.f, -1.f); + break; + default: + break; + } + mSnapGuideDir1.scaleVec(snap_guide_flip); + mSnapGuideDir2.scaleVec(snap_guide_flip); + mSnapGuideDir1.rotVec(bbox.getRotation()); + mSnapGuideDir2.rotVec(bbox.getRotation()); + mSnapDir1 = -1.f * mSnapGuideDir2; + mSnapDir2 = -1.f * mSnapGuideDir1; + } + + mScalePlaneNormal1 = mSnapGuideDir1 % mScaleDir; + mScalePlaneNormal1.normVec(); + + mScalePlaneNormal2 = mSnapGuideDir2 % mScaleDir; + mScalePlaneNormal2.normVec(); + + mScaleSnapUnit1 = mScaleSnapUnit1 / (mSnapDir1 * mScaleDir); + mScaleSnapUnit2 = mScaleSnapUnit2 / (mSnapDir2 * mScaleDir); +} + +void LLManipScale::renderSnapGuides(const LLBBox& bbox) +{ + if (!gSavedSettings.getBOOL("SnapEnabled")) + { + return; + } + + F32 max_subdivisions = sGridMaxSubdivisionLevel; + F32 grid_alpha = gSavedSettings.getF32("GridOpacity"); + + F32 max_point_on_scale_line = partToMaxScale(mManipPart, bbox); + LLVector3 drag_point = gAgent.getPosAgentFromGlobal(mDragPointGlobal); + + updateGridSettings(); + + S32 pass; + for (pass = 0; pass < 3; pass++) + { + LLColor4 tick_color = setupSnapGuideRenderPass(pass); + + glBegin(GL_LINES); + LLVector3 line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir1 * mSnapRegimeOffset); + LLVector3 line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f))); + LLVector3 line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f)); + + glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f); + glVertex3fv(line_start.mV); + glColor4fv(tick_color.mV); + glVertex3fv(line_mid.mV); + glVertex3fv(line_mid.mV); + glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f); + glVertex3fv(line_end.mV); + + line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir2 * mSnapRegimeOffset); + line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f))); + line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f)); + glVertex3fv(line_start.mV); + glColor4fv(tick_color.mV); + glVertex3fv(line_mid.mV); + glVertex3fv(line_mid.mV); + glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f); + glVertex3fv(line_end.mV); + glEnd(); + } + + { + LLGLDepthTest gls_depth(GL_FALSE); + + F32 dist_grid_axis = (drag_point - mScaleCenter) * mScaleDir; + // find distance to nearest smallest grid unit + F32 grid_offset1 = fmodf(dist_grid_axis, mScaleSnapUnit1 / max_subdivisions); + F32 grid_offset2 = fmodf(dist_grid_axis, mScaleSnapUnit2 / max_subdivisions); + + // how many smallest grid units are we away from largest grid scale? + S32 sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 / sGridMinSubdivisionLevel) / (mScaleSnapUnit1 / max_subdivisions)); + S32 sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 / sGridMinSubdivisionLevel) / (mScaleSnapUnit2 / max_subdivisions)); + + S32 num_ticks_per_side1 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit1 / max_subdivisions))); + S32 num_ticks_per_side2 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit2 / max_subdivisions))); + F32 dist_scale_units_1 = dist_grid_axis / (mScaleSnapUnit1 / max_subdivisions); + F32 dist_scale_units_2 = dist_grid_axis / (mScaleSnapUnit2 / max_subdivisions); + S32 ticks_from_scale_center_1 = lltrunc(dist_scale_units_1); + S32 ticks_from_scale_center_2 = lltrunc(dist_scale_units_2); + S32 max_ticks1 = llceil(max_point_on_scale_line / (mScaleSnapUnit1 / max_subdivisions) - dist_scale_units_1); + S32 max_ticks2 = llceil(max_point_on_scale_line / (mScaleSnapUnit2 / max_subdivisions) - dist_scale_units_2); + S32 start_tick = 0; + S32 stop_tick = 0; + + if (mInSnapRegime) + { + // draw snap guide line + glBegin(GL_LINES); + LLVector3 snap_line_center = mScaleCenter + (mScaleSnapValue * mScaleDir); + + LLVector3 snap_line_start = snap_line_center + (mSnapGuideDir1 * mSnapRegimeOffset); + LLVector3 snap_line_end = snap_line_center + (mSnapGuideDir2 * mSnapRegimeOffset); + + glColor4f(1.f, 1.f, 1.f, grid_alpha); + glVertex3fv(snap_line_start.mV); + glVertex3fv(snap_line_center.mV); + glVertex3fv(snap_line_center.mV); + glVertex3fv(snap_line_end.mV); + glEnd(); + + // draw snap guide arrow + glBegin(GL_TRIANGLES); + { + //gGLSNoCullFaces.set(); + glColor4f(1.f, 1.f, 1.f, grid_alpha); + + LLVector3 arrow_dir; + LLVector3 arrow_span = mScaleDir; + + arrow_dir = snap_line_start - snap_line_center; + arrow_dir.normVec(); + glVertex3fv((snap_line_start + arrow_dir * mBoxHandleSize).mV); + glVertex3fv((snap_line_start + arrow_span * mBoxHandleSize).mV); + glVertex3fv((snap_line_start - arrow_span * mBoxHandleSize).mV); + + arrow_dir = snap_line_end - snap_line_center; + arrow_dir.normVec(); + glVertex3fv((snap_line_end + arrow_dir * mBoxHandleSize).mV); + glVertex3fv((snap_line_end + arrow_span * mBoxHandleSize).mV); + glVertex3fv((snap_line_end - arrow_span * mBoxHandleSize).mV); + } + glEnd(); + } + + LLVector2 screen_translate_axis(llabs(mScaleDir * gCamera->getLeftAxis()), llabs(mScaleDir * gCamera->getUpAxis())); + screen_translate_axis.normVec(); + + S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing); + + for (pass = 0; pass < 3; pass++) + { + LLColor4 tick_color = setupSnapGuideRenderPass(pass); + + start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1)); + stop_tick = llmin(max_ticks1, num_ticks_per_side1); + + glBegin(GL_LINES); + // draw first row of ticks + for (S32 i = start_tick; i <= stop_tick; i++) + { + F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side1))); + LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1)); + + F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel); + + if (fmodf((F32)(i + sub_div_offset_1), (max_subdivisions / cur_subdivisions)) != 0.f) + { + continue; + } + + 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_1), division_level) == 0.f) + { + break; + } + tick_scale *= 0.7f; + } + + glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha); + LLVector3 tick_start = tick_pos + (mSnapGuideDir1 * mSnapRegimeOffset); + LLVector3 tick_end = tick_start + (mSnapGuideDir1 * mSnapRegimeOffset * tick_scale); + glVertex3fv(tick_start.mV); + glVertex3fv(tick_end.mV); + } + + // draw opposite row of ticks + start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2)); + stop_tick = llmin(max_ticks2, num_ticks_per_side2); + for (S32 i = start_tick; i <= stop_tick; i++) + { + F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side2))); + LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2)); + + F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel); + + if (fmodf((F32)(i + sub_div_offset_2), (max_subdivisions / cur_subdivisions)) != 0.f) + { + continue; + } + + 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_2), division_level) == 0.f) + { + break; + } + tick_scale *= 0.7f; + } + + glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha); + LLVector3 tick_start = tick_pos + (mSnapGuideDir2 * mSnapRegimeOffset); + LLVector3 tick_end = tick_start + (mSnapGuideDir2 * mSnapRegimeOffset * tick_scale); + glVertex3fv(tick_start.mV); + glVertex3fv(tick_end.mV); + } + glEnd(); + } + + // render tick labels + start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1)); + stop_tick = llmin(max_ticks1, num_ticks_per_side1); + + F32 grid_resolution = gSelectMgr->getSelectType() == SELECT_TYPE_HUD ? 0.25f : llmax(gSavedSettings.getF32("GridResolution"), 0.001f); + S32 label_sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 * 32.f) / (mScaleSnapUnit1 / max_subdivisions)); + S32 label_sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 * 32.f) / (mScaleSnapUnit2 / max_subdivisions)); + + for (S32 i = start_tick; i <= stop_tick; i++) + { + F32 tick_scale = 1.f; + F32 alpha = grid_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side1))); + LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1)); + + for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f) + { + if (fmodf((F32)(i + label_sub_div_offset_1), division_level) == 0.f) + { + break; + } + tick_scale *= 0.7f; + } + + if (fmodf((F32)(i + label_sub_div_offset_1), (max_subdivisions / llmin(sGridMaxSubdivisionLevel, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1, tick_label_spacing)))) == 0.f) + { + LLVector3 text_origin = tick_pos + + (mSnapGuideDir1 * mSnapRegimeOffset * (1.f + tick_scale)); + + EGridMode grid_mode = gSelectMgr->getGridMode(); + F32 tick_val; + if (grid_mode == GRID_MODE_WORLD) + { + tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 / grid_resolution); + } + else + { + tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 * 2.f); + } + + if (getUniform()) + { + tick_val *= 2.f; + } + + F32 text_highlight = 0.8f; + + if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime) + { + text_highlight = 1.f; + } + + renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha)); + } + } + + // label ticks on opposite side + if (mScaleSnapUnit2 != mScaleSnapUnit1) + { + start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2)); + stop_tick = llmin(max_ticks2, num_ticks_per_side2); + for (S32 i = start_tick; i <= stop_tick; i++) + { + F32 tick_scale = 1.f; + F32 alpha = grid_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side2))); + LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2)); + + for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f) + { + if (fmodf((F32)(i + label_sub_div_offset_2), division_level) == 0.f) + { + break; + } + tick_scale *= 0.7f; + } + + if (fmodf((F32)(i + label_sub_div_offset_2), (max_subdivisions / llmin(max_subdivisions, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2, tick_label_spacing)))) == 0.f) + { + LLVector3 text_origin = tick_pos + + (mSnapGuideDir2 * mSnapRegimeOffset * (1.f + tick_scale)); + + EGridMode grid_mode = gSelectMgr->getGridMode(); + F32 tick_val; + if (grid_mode == GRID_MODE_WORLD) + { + tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 / grid_resolution); + } + else + { + tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 * 2.f); + } + + if (getUniform()) + { + tick_val *= 2.f; + } + + F32 text_highlight = 0.8f; + + if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime) + { + text_highlight = 1.f; + } + + renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha)); + } + } + } + + + // render help text + if (gSelectMgr->getSelectType() != SELECT_TYPE_HUD) + { + if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText) + { + LLVector3 selection_center_start = gSelectMgr->getSavedBBoxOfSelection().getCenterAgent(); + + LLVector3 offset_dir; + if (mSnapGuideDir1 * gCamera->getAtAxis() > mSnapGuideDir2 * gCamera->getAtAxis()) + { + offset_dir = mSnapGuideDir2; + } + else + { + offset_dir = mSnapGuideDir1; + } + + LLVector3 help_text_pos = selection_center_start + (mSnapRegimeOffset * 5.f * offset_dir); + const LLFontGL* big_fontp = LLFontGL::sSansSerif; + + std::string help_text = "Move mouse cursor over ruler"; + LLColor4 help_text_color = LLColor4::white; + help_text_color.mV[VALPHA] = clamp_rescale(mHelpTextTimer.getElapsedTimeF32(), sHelpTextVisibleTime, sHelpTextVisibleTime + sHelpTextFadeTime, grid_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, gSelectMgr->getSelectType() == SELECT_TYPE_HUD); + help_text = "to snap to grid"; + help_text_pos -= gCamera->getUpAxis() * mSnapRegimeOffset * 0.4f; + hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD); + } + } + } +} + +// Returns unit vector in direction of part of an origin-centered cube +LLVector3 LLManipScale::partToUnitVector( S32 part ) const +{ + if( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) ) + { + return faceToUnitVector( part ); + } + else + if( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) ) + { + return cornerToUnitVector( part ); + } + else + if( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX ) ) + { + return edgeToUnitVector( part ); + } + return LLVector3(); +} + + +// Returns unit vector in direction of face of an origin-centered cube +LLVector3 LLManipScale::faceToUnitVector( S32 part ) const +{ + llassert( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) ); + switch( part ) + { + case LL_FACE_POSX: + return LLVector3( 1.f, 0.f, 0.f ); + + case LL_FACE_NEGX: + return LLVector3( -1.f, 0.f, 0.f ); + + case LL_FACE_POSY: + return LLVector3( 0.f, 1.f, 0.f ); + + case LL_FACE_NEGY: + return LLVector3( 0.f, -1.f, 0.f ); + + case LL_FACE_POSZ: + return LLVector3( 0.f, 0.f, 1.f ); + + case LL_FACE_NEGZ: + return LLVector3( 0.f, 0.f, -1.f ); + } + return LLVector3(); +} + + +// Returns unit vector in direction of corner of an origin-centered cube +LLVector3 LLManipScale::cornerToUnitVector( S32 part ) const +{ + llassert( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) ); + LLVector3 vec; + switch(part) + { + case LL_CORNER_NNN: + vec.setVec(-F_SQRT3, -F_SQRT3, -F_SQRT3); + break; + case LL_CORNER_NNP: + vec.setVec(-F_SQRT3, -F_SQRT3, F_SQRT3); + break; + case LL_CORNER_NPN: + vec.setVec(-F_SQRT3, F_SQRT3, -F_SQRT3); + break; + case LL_CORNER_NPP: + vec.setVec(-F_SQRT3, F_SQRT3, F_SQRT3); + break; + case LL_CORNER_PNN: + vec.setVec(F_SQRT3, -F_SQRT3, -F_SQRT3); + break; + case LL_CORNER_PNP: + vec.setVec(F_SQRT3, -F_SQRT3, F_SQRT3); + break; + case LL_CORNER_PPN: + vec.setVec(F_SQRT3, F_SQRT3, -F_SQRT3); + break; + case LL_CORNER_PPP: + vec.setVec(F_SQRT3, F_SQRT3, F_SQRT3); + break; + default: + vec.clearVec(); + } + + return vec; +} + +// Returns unit vector in direction of edge of an origin-centered cube +LLVector3 LLManipScale::edgeToUnitVector( S32 part ) const +{ + llassert( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX) ); + //FIXME + part -= LL_EDGE_MIN; + S32 rotation = part >> 2; // Edge between which faces: 0 => XY, 1 => YZ, 2 => ZX + LLVector3 v; + v.mV[rotation] = (part & 1) ? F_SQRT2 : -F_SQRT2; + v.mV[(rotation+1) % 3] = (part & 2) ? F_SQRT2 : -F_SQRT2; + // v.mV[(rotation+2) % 3] defaults to 0. + return v; +} + +// Non-linear scale of origin-centered unit cube to non-origin-centered, non-symetrical bounding box +LLVector3 LLManipScale::unitVectorToLocalBBoxExtent( const LLVector3& v, const LLBBox& bbox ) const +{ + const LLVector3& min = bbox.getMinLocal(); + const LLVector3& max = bbox.getMaxLocal(); + LLVector3 ctr = bbox.getCenterLocal(); + + return LLVector3( + v.mV[0] ? (v.mV[0]>0 ? max.mV[0] : min.mV[0] ) : ctr.mV[0], + v.mV[1] ? (v.mV[1]>0 ? max.mV[1] : min.mV[1] ) : ctr.mV[1], + v.mV[2] ? (v.mV[2]>0 ? max.mV[2] : min.mV[2] ) : ctr.mV[2] ); +} + +// returns max allowable scale along a given stretch axis +F32 LLManipScale::partToMaxScale( S32 part, const LLBBox &bbox ) const +{ + F32 max_scale_factor = 0.f; + LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox ); + bbox_extents.abs(); + F32 max_extent = 0.f; + for (U32 i = VX; i <= VZ; i++) + { + if (bbox_extents.mV[i] > max_extent) + { + max_extent = bbox_extents.mV[i]; + } + } + max_scale_factor = bbox_extents.magVec() * MAX_OBJECT_SCALE / max_extent; + + if (getUniform()) + { + max_scale_factor *= 0.5f; + } + + return max_scale_factor; +} + +// returns min allowable scale along a given stretch axis +F32 LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const +{ + LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox ); + bbox_extents.abs(); + F32 min_extent = MAX_OBJECT_SCALE; + for (U32 i = VX; i <= VZ; i++) + { + if (bbox_extents.mV[i] > 0.f && bbox_extents.mV[i] < min_extent) + { + min_extent = bbox_extents.mV[i]; + } + } + F32 min_scale_factor = bbox_extents.magVec() * MIN_OBJECT_SCALE / min_extent; + + if (getUniform()) + { + min_scale_factor *= 0.5f; + } + + return min_scale_factor; +} + +// Returns the axis aligned unit vector closest to v. +LLVector3 LLManipScale::nearestAxis( const LLVector3& v ) const +{ + // Note: yes, this is a slow but easy implementation + // assumes v is normalized + + F32 coords[][3] = + { + { 1.f, 0.f, 0.f }, + { 0.f, 1.f, 0.f }, + { 0.f, 0.f, 1.f }, + {-1.f, 0.f, 0.f }, + { 0.f,-1.f, 0.f }, + { 0.f, 0.f,-1.f } + }; + + F32 cosine[6]; + cosine[0] = v * LLVector3( coords[0] ); + cosine[1] = v * LLVector3( coords[1] ); + cosine[2] = v * LLVector3( coords[2] ); + cosine[3] = -cosine[0]; + cosine[4] = -cosine[1]; + cosine[5] = -cosine[2]; + + F32 greatest_cos = cosine[0]; + S32 greatest_index = 0; + for( S32 i=1; i<6; i++ ) + { + if( greatest_cos < cosine[i] ) + { + greatest_cos = cosine[i]; + greatest_index = i; + } + } + + return LLVector3( coords[greatest_index] ); +} + +BOOL LLManipScale::isSelectionScalable() const +{ + // An selection is scalable if you are allowed to both edit and move + // everything in it, and it does not have any sitting agents + BOOL scalable = gSelectMgr->getFirstObject() ? TRUE : FALSE; + for(LLViewerObject* cur = gSelectMgr->getFirstObject(); + cur; + cur = gSelectMgr->getNextObject() ) + { + if( !(cur->permModify() && cur->permMove()) + || cur->isSeat()) + { + scalable = FALSE; + break; + } + } + return scalable; +} |