diff options
Diffstat (limited to 'indra/newview/llmaniprotate.cpp')
-rw-r--r-- | indra/newview/llmaniprotate.cpp | 3900 |
1 files changed, 1950 insertions, 1950 deletions
diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp index 73424cf90e..c2f4200852 100644 --- a/indra/newview/llmaniprotate.cpp +++ b/indra/newview/llmaniprotate.cpp @@ -1,1950 +1,1950 @@ -/**
- * @file llmaniprotate.cpp
- * @brief LLManipRotate class implementation
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llmaniprotate.h"
-
-// library includes
-#include "llmath.h"
-#include "llgl.h"
-#include "llrender.h"
-#include "v4color.h"
-#include "llprimitive.h"
-#include "llview.h"
-#include "llfontgl.h"
-
-// viewer includes
-#include "llagent.h"
-#include "llagentcamera.h"
-#include "llbox.h"
-#include "llbutton.h"
-#include "llviewercontrol.h"
-#include "llcriticaldamp.h"
-#include "lltooltip.h"
-#include "llfloatertools.h"
-#include "llselectmgr.h"
-#include "llstatusbar.h"
-#include "llui.h"
-#include "llvoavatar.h"
-#include "llviewercamera.h"
-#include "llviewerobject.h"
-#include "llviewerobject.h"
-#include "llviewershadermgr.h"
-#include "llviewerwindow.h"
-#include "llworld.h"
-#include "pipeline.h"
-#include "lldrawable.h"
-#include "llglheaders.h"
-#include "lltrans.h"
-#include "llvoavatarself.h"
-#include "llhudrender.h"
-
-const F32 RADIUS_PIXELS = 100.f; // size in screen space
-const F32 SQ_RADIUS = RADIUS_PIXELS * RADIUS_PIXELS;
-const F32 WIDTH_PIXELS = 8;
-const S32 CIRCLE_STEPS = 100;
-const F32 MAX_MANIP_SELECT_DISTANCE = 100.f;
-const F32 SNAP_ANGLE_INCREMENT = 5.625f;
-const F32 SNAP_ANGLE_DETENTE = SNAP_ANGLE_INCREMENT;
-const F32 SNAP_GUIDE_RADIUS_1 = 2.8f;
-const F32 SNAP_GUIDE_RADIUS_2 = 2.4f;
-const F32 SNAP_GUIDE_RADIUS_3 = 2.2f;
-const F32 SNAP_GUIDE_RADIUS_4 = 2.1f;
-const F32 SNAP_GUIDE_RADIUS_5 = 2.05f;
-const F32 SNAP_GUIDE_INNER_RADIUS = 2.f;
-const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 80.f * DEG_TO_RAD );
-const F32 SELECTED_MANIPULATOR_SCALE = 1.05f;
-const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
-
-extern void handle_reset_rotation(void*); // in LLViewerWindow
-
-LLManipRotate::LLManipRotate( LLToolComposite* composite )
-: LLManip( std::string("Rotate"), composite ),
- mRotationCenter(),
- mCenterScreen(),
- mRotation(),
- mMouseDown(),
- mMouseCur(),
- mRadiusMeters(0.f),
- mCenterToCam(),
- mCenterToCamNorm(),
- mCenterToCamMag(0.f),
- mCenterToProfilePlane(),
- mCenterToProfilePlaneMag(0.f),
- mSendUpdateOnMouseUp( false ),
- mSmoothRotate( false ),
- mCamEdgeOn(false),
- mManipulatorScales(1.f, 1.f, 1.f, 1.f)
-{ }
-
-void LLManipRotate::handleSelect()
-{
- // *FIX: put this in mouseDown?
- LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
- if (gFloaterTools)
- {
- gFloaterTools->setStatusText("rotate");
- }
- LLManip::handleSelect();
-}
-
-void LLManipRotate::render()
-{
- LLGLSUIDefault gls_ui;
- gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep);
- LLGLDepthTest gls_depth(GL_TRUE);
- LLGLEnable gl_blend(GL_BLEND);
-
- // You can rotate if you can move
- LLViewerObject* first_object = mObjectSelection->getFirstMoveableObject(true);
- if( !first_object )
- {
- return;
- }
-
- if( !updateVisiblity() )
- {
- return;
- }
-
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- gGL.pushMatrix();
- if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
- {
- F32 zoom = gAgentCamera.mHUDCurZoom;
- gGL.scalef(zoom, zoom, zoom);
- }
-
-
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
-
- LLColor4 highlight_outside( 1.f, 1.f, 0.f, 1.f );
- LLColor4 highlight_inside( 0.7f, 0.7f, 0.f, 0.5f );
- F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
-
- gGL.pushMatrix();
- {
-
- // are we in the middle of a constrained drag?
- if (mManipPart >= LL_ROT_X && mManipPart <= LL_ROT_Z)
- {
- renderSnapGuides();
- }
- else
- {
- gDebugProgram.bind();
-
- LLGLEnable cull_face(GL_CULL_FACE);
- LLGLDepthTest gls_depth(GL_FALSE);
- gGL.pushMatrix();
- {
- // Draw "sphere" (intersection of sphere with tangent cone that has apex at camera)
- gGL.translatef( mCenterToProfilePlane.mV[VX], mCenterToProfilePlane.mV[VY], mCenterToProfilePlane.mV[VZ] );
- gGL.translatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
-
- // Inverse change of basis vectors
- LLVector3 forward = mCenterToCamNorm;
- LLVector3 left = gAgent.getUpAxis() % forward;
- left.normVec();
- LLVector3 up = forward % left;
-
- LLVector4 a(-forward);
- a.mV[3] = 0;
- LLVector4 b(up);
- b.mV[3] = 0;
- LLVector4 c(left);
- c.mV[3] = 0;
- LLMatrix4 mat;
- mat.initRows(a, b, c, LLVector4(0.f, 0.f, 0.f, 1.f));
-
- gGL.multMatrix( &mat.mMatrix[0][0] );
-
- gGL.rotatef( -90, 0.f, 1.f, 0.f);
- LLColor4 color;
- if (mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
- {
- color.setVec(0.8f, 0.8f, 0.8f, 0.8f);
- gGL.scalef(mManipulatorScales.mV[VW], mManipulatorScales.mV[VW], mManipulatorScales.mV[VW]);
- }
- else
- {
- color.setVec( 0.7f, 0.7f, 0.7f, 0.6f );
- }
- gGL.diffuseColor4fv(color.mV);
- gl_washer_2d(mRadiusMeters + width_meters, mRadiusMeters, CIRCLE_STEPS, color, color);
-
-
- if (mManipPart == LL_NO_PART)
- {
- gGL.color4f( 0.7f, 0.7f, 0.7f, 0.3f );
- gGL.diffuseColor4f(0.7f, 0.7f, 0.7f, 0.3f);
- gl_circle_2d( 0, 0, mRadiusMeters, CIRCLE_STEPS, true );
- }
-
- gGL.flush();
- }
- gGL.popMatrix();
-
- gUIProgram.bind();
- }
-
- gGL.translatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
-
- LLQuaternion rot;
- F32 angle_radians, x, y, z;
-
- LLVector3 grid_origin;
- LLVector3 grid_scale;
- LLQuaternion grid_rotation;
-
- LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
-
- grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
- gGL.rotatef(angle_radians * RAD_TO_DEG, x, y, z);
-
-
- gDebugProgram.bind();
-
- if (mManipPart == LL_ROT_Z)
- {
- mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
- gGL.pushMatrix();
- {
- // selected part
- gGL.scalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
- renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f) , LLColor4( 0.f, 0.f, 1.f, 0.3f ));
- }
- gGL.popMatrix();
- }
- else if (mManipPart == LL_ROT_Y)
- {
- mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
- gGL.pushMatrix();
- {
- gGL.rotatef( 90.f, 1.f, 0.f, 0.f );
- gGL.scalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
- renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f), LLColor4( 0.f, 1.f, 0.f, 0.3f));
- }
- gGL.popMatrix();
- }
- else if (mManipPart == LL_ROT_X)
- {
- mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
- gGL.pushMatrix();
- {
- gGL.rotatef( 90.f, 0.f, 1.f, 0.f );
- gGL.scalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
- renderActiveRing( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f), LLColor4( 1.f, 0.f, 0.f, 0.3f));
- }
- gGL.popMatrix();
- }
- else if (mManipPart == LL_ROT_ROLL)
- {
- mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
- }
- else if (mManipPart == LL_NO_PART)
- {
- if (mHighlightedPart == LL_NO_PART)
- {
- mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
- }
-
- LLGLEnable cull_face(GL_CULL_FACE);
- LLGLEnable clip_plane0(GL_CLIP_PLANE0);
- LLGLDepthTest gls_depth(GL_FALSE);
- //LLGLDisable gls_stencil(GL_STENCIL_TEST);
-
- // First pass: centers. Second pass: sides.
- for( S32 i=0; i<2; i++ )
- {
-
- gGL.pushMatrix();
- {
- if (mHighlightedPart == LL_ROT_Z)
- {
- mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
- gGL.scalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
- // hovering over part
- gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f ), LLColor4( 0.f, 0.f, 1.f, 0.5f ), CIRCLE_STEPS, i);
- }
- else
- {
- // default
- gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 0.8f, 0.8f ), LLColor4( 0.f, 0.f, 0.8f, 0.4f ), CIRCLE_STEPS, i);
- }
- }
- gGL.popMatrix();
-
- gGL.pushMatrix();
- {
- gGL.rotatef( 90.f, 1.f, 0.f, 0.f );
- if (mHighlightedPart == LL_ROT_Y)
- {
- mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
- gGL.scalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
- // hovering over part
- gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f ), LLColor4( 0.f, 1.f, 0.f, 0.5f ), CIRCLE_STEPS, i);
- }
- else
- {
- // default
- gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.8f, 0.f, 0.8f ), LLColor4( 0.f, 0.8f, 0.f, 0.4f ), CIRCLE_STEPS, i);
- }
- }
- gGL.popMatrix();
-
- gGL.pushMatrix();
- {
- gGL.rotatef( 90.f, 0.f, 1.f, 0.f );
- if (mHighlightedPart == LL_ROT_X)
- {
- mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
- gGL.scalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
-
- // hovering over part
- gl_ring( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f ), LLColor4( 1.f, 0.f, 0.f, 0.5f ), CIRCLE_STEPS, i);
- }
- else
- {
- // default
- gl_ring( mRadiusMeters, width_meters, LLColor4( 0.8f, 0.f, 0.f, 0.8f ), LLColor4( 0.8f, 0.f, 0.f, 0.4f ), CIRCLE_STEPS, i);
- }
- }
- gGL.popMatrix();
-
- if (mHighlightedPart == LL_ROT_ROLL)
- {
- mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
- }
-
- }
-
- }
-
- gUIProgram.bind();
- }
- gGL.popMatrix();
- gGL.popMatrix();
-
-
- LLVector3 euler_angles;
- LLQuaternion object_rot = first_object->getRotationEdit();
- object_rot.getEulerAngles(&(euler_angles.mV[VX]), &(euler_angles.mV[VY]), &(euler_angles.mV[VZ]));
- euler_angles *= RAD_TO_DEG;
- euler_angles.mV[VX] = ll_round(fmodf(euler_angles.mV[VX] + 360.f, 360.f), 0.05f);
- euler_angles.mV[VY] = ll_round(fmodf(euler_angles.mV[VY] + 360.f, 360.f), 0.05f);
- euler_angles.mV[VZ] = ll_round(fmodf(euler_angles.mV[VZ] + 360.f, 360.f), 0.05f);
-
- renderXYZ(euler_angles);
-}
-
-bool LLManipRotate::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- LLViewerObject* first_object = mObjectSelection->getFirstMoveableObject(true);
- if( first_object )
- {
- if( mHighlightedPart != LL_NO_PART )
- {
- handled = handleMouseDownOnPart( x, y, mask );
- }
- }
-
- return handled;
-}
-
-// Assumes that one of the parts of the manipulator was hit.
-bool LLManipRotate::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
-{
- bool can_rotate = canAffectSelection();
- if (!can_rotate)
- {
- return false;
- }
-
- highlightManipulators(x, y);
- S32 hit_part = mHighlightedPart;
- // we just started a drag, so save initial object positions
- LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_ROTATE);
-
- // save selection center
- mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() ); //LLSelectMgr::getInstance()->getSelectionCenterGlobal();
-
- mManipPart = (EManipPart)hit_part;
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
-
- if( mManipPart == LL_ROT_GENERAL)
- {
- mMouseDown = intersectMouseWithSphere( x, y, center, mRadiusMeters);
- }
- else
- {
- // Project onto the plane of the ring
- LLVector3 axis = getConstraintAxis();
-
- F32 axis_onto_cam = llabs( axis * mCenterToCamNorm );
- const F32 AXIS_ONTO_CAM_TOL = cos( 85.f * DEG_TO_RAD );
- if( axis_onto_cam < AXIS_ONTO_CAM_TOL )
- {
- LLVector3 up_from_axis = mCenterToCamNorm % axis;
- up_from_axis.normVec();
- LLVector3 cur_intersection;
- getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
- cur_intersection -= center;
- mMouseDown = projected_vec(cur_intersection, up_from_axis);
- F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
- F32 mouse_dist_sqrd = mMouseDown.magVecSquared();
- if (mouse_dist_sqrd > 0.0001f)
- {
- mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
- mouse_dist_sqrd);
- }
- LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, axis);
- mMouseDown += mouse_depth * projected_center_to_cam;
-
- }
- else
- {
- mMouseDown = findNearestPointOnRing( x, y, center, axis ) - center;
- mMouseDown.normVec();
- }
- }
-
- mMouseCur = mMouseDown;
- mAgentSelfAtAxis = gAgent.getAtAxis(); // no point checking if avatar was selected, just save the value
-
- // Route future Mouse messages here preemptively. (Release on mouse up.)
- setMouseCapture( true );
- LLSelectMgr::getInstance()->enableSilhouette(false);
-
- mHelpTextTimer.reset();
- sNumTimesHelpTextShown++;
- return true;
-}
-
-
-LLVector3 LLManipRotate::findNearestPointOnRing( S32 x, S32 y, const LLVector3& center, const LLVector3& axis )
-{
- // Project the delta onto the ring and rescale it by the radius so that it's _on_ the ring.
- LLVector3 proj_onto_ring;
- getMousePointOnPlaneAgent(proj_onto_ring, x, y, center, axis);
- proj_onto_ring -= center;
- proj_onto_ring.normVec();
-
- return center + proj_onto_ring * mRadiusMeters;
-}
-
-bool LLManipRotate::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- // first, perform normal processing in case this was a quick-click
- handleHover(x, y, mask);
-
- if( hasMouseCapture() )
- {
- for (LLObjectSelection::iterator iter = mObjectSelection->begin();
- iter != mObjectSelection->end(); iter++)
- {
- LLSelectNode* selectNode = *iter;
- LLViewerObject* object = selectNode->getObject();
- LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit();
-
- // have permission to move and object is root of selection or individually selected
- if (object->permMove() && !object->isPermanentEnforced() &&
- ((root_object == NULL) || !root_object->isPermanentEnforced()) &&
- (object->isRootEdit() || selectNode->mIndividualSelection))
- {
- object->mUnselectedChildrenPositions.clear() ;
- }
- }
-
- mManipPart = LL_NO_PART;
-
- // Might have missed last update due to timing.
- LLSelectMgr::getInstance()->sendMultipleUpdate( UPD_ROTATION | UPD_POSITION );
- LLSelectMgr::getInstance()->enableSilhouette(true);
- //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
-
- LLSelectMgr::getInstance()->updateSelectionCenter();
- LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
- }
-
- return LLManip::handleMouseUp(x, y, mask);
-}
-
-
-bool LLManipRotate::handleHover(S32 x, S32 y, MASK mask)
-{
- if( hasMouseCapture() )
- {
- if( mObjectSelection->isEmpty() )
- {
- // Somehow the object got deselected while we were dragging it.
- setMouseCapture( false );
- }
- else
- {
- drag(x, y);
- }
-
- LL_DEBUGS("UserInput") << "hover handled by LLManipRotate (active)" << LL_ENDL;
- }
- else
- {
- highlightManipulators(x, y);
- LL_DEBUGS("UserInput") << "hover handled by LLManipRotate (inactive)" << LL_ENDL;
- }
-
- gViewerWindow->setCursor(UI_CURSOR_TOOLROTATE);
- return true;
-}
-
-
-LLVector3 LLManipRotate::projectToSphere( F32 x, F32 y, bool* on_sphere )
-{
- F32 z = 0.f;
- F32 dist_squared = x*x + y*y;
-
- *on_sphere = dist_squared <= SQ_RADIUS;
- if( *on_sphere )
- {
- z = sqrt(SQ_RADIUS - dist_squared);
- }
- return LLVector3( x, y, z );
-}
-
-// Freeform rotation
-void LLManipRotate::drag( S32 x, S32 y )
-{
- if( !updateVisiblity() )
- {
- return;
- }
-
- if( mManipPart == LL_ROT_GENERAL )
- {
- mRotation = dragUnconstrained(x, y);
- }
- else
- {
- mRotation = dragConstrained(x, y);
- }
-
- bool damped = mSmoothRotate;
- mSmoothRotate = false;
-
- for (LLObjectSelection::iterator iter = mObjectSelection->begin();
- iter != mObjectSelection->end(); iter++)
- {
- LLSelectNode* selectNode = *iter;
- LLViewerObject* object = selectNode->getObject();
- LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit();
-
- // have permission to move and object is root of selection or individually selected
- if (object->permMove() && !object->isPermanentEnforced() &&
- ((root_object == NULL) || !root_object->isPermanentEnforced()) &&
- (object->isRootEdit() || selectNode->mIndividualSelection))
- {
- 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;
- }
- }
-
- LLQuaternion new_rot = selectNode->mSavedRotation * mRotation;
- std::vector<LLVector3>& child_positions = object->mUnselectedChildrenPositions ;
- std::vector<LLQuaternion> child_rotations;
- if (object->isRootEdit() && selectNode->mIndividualSelection)
- {
- object->saveUnselectedChildrenRotation(child_rotations) ;
- object->saveUnselectedChildrenPosition(child_positions) ;
- }
-
- if (object->getParent() && object->mDrawable.notNull())
- {
- LLQuaternion invParentRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
- invParentRotation.transQuat();
-
- object->setRotation(new_rot * invParentRotation, damped);
- rebuild(object);
- }
- else
- {
- object->setRotation(new_rot, damped);
- LLVOAvatar* avatar = object->asAvatar();
- if (avatar && avatar->isSelf()
- && LLSelectMgr::getInstance()->mAllowSelectAvatar
- && !object->getParent())
- {
- // Normal avatars use object's orienttion, but self uses
- // separate LLCoordFrame
- // See LVOAvatar::updateOrientation()
- if (gAgentCamera.getFocusOnAvatar())
- {
- //Don't rotate camera with avatar
- gAgentCamera.setFocusOnAvatar(false, false, false);
- }
-
- LLVector3 at_axis = mAgentSelfAtAxis;
- at_axis *= mRotation;
- at_axis.mV[VZ] = 0.f;
- at_axis.normalize();
- gAgent.resetAxes(at_axis);
- }
- rebuild(object);
- }
-
- // for individually selected roots, we need to counterrotate all the children
- if (object->isRootEdit() && selectNode->mIndividualSelection)
- {
- //RN: must do non-damped updates on these objects so relative rotation appears constant
- // instead of having two competing slerps making the child objects appear to "wobble"
- object->resetChildrenRotationAndPosition(child_rotations, child_positions) ;
- }
- }
- }
-
- // update positions
- for (LLObjectSelection::iterator iter = mObjectSelection->begin();
- iter != mObjectSelection->end(); iter++)
- {
- LLSelectNode* selectNode = *iter;
- LLViewerObject* object = selectNode->getObject();
- LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit();
-
- // to avoid cumulative position changes we calculate the objects new position using its saved position
- if (object && object->permMove() && !object->isPermanentEnforced() &&
- ((root_object == NULL) || !root_object->isPermanentEnforced()))
- {
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
-
- LLVector3 old_position;
- LLVector3 new_position;
-
- if (object->isAttachment() && object->mDrawable.notNull())
- {
- // need to work in drawable space to handle selected items from multiple attachments
- // (which have no shared frame of reference other than their render positions)
- LLXform* parent_xform = object->mDrawable->getXform()->getParent();
- new_position = (selectNode->mSavedPositionLocal * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
- old_position = (object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();//object->getRenderPosition();
- }
- else
- {
- new_position = gAgent.getPosAgentFromGlobal( selectNode->mSavedPositionGlobal );
- old_position = object->getPositionAgent();
- }
-
- new_position = (new_position - center) * mRotation; // new relative rotated position
- new_position += center;
-
- if (object->isRootEdit() && !object->isAttachment())
- {
- LLVector3d new_pos_global = gAgent.getPosGlobalFromAgent(new_position);
- new_pos_global = LLWorld::getInstance()->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
- new_position = gAgent.getPosAgentFromGlobal(new_pos_global);
- }
-
- // for individually selected child objects
- if (!object->isRootEdit() && selectNode->mIndividualSelection)
- {
- LLViewerObject* parentp = (LLViewerObject*)object->getParent();
- if (!parentp->isSelected())
- {
- if (object->isAttachment() && object->mDrawable.notNull())
- {
- // find position relative to render position of parent
- object->setPosition((new_position - parentp->getRenderPosition()) * ~parentp->getRenderRotation());
- rebuild(object);
- }
- else
- {
- object->setPositionParent((new_position - parentp->getPositionAgent()) * ~parentp->getRotationRegion());
- rebuild(object);
- }
- }
- }
- else if (object->isRootEdit())
- {
- if (object->isAttachment() && object->mDrawable.notNull())
- {
- LLXform* parent_xform = object->mDrawable->getXform()->getParent();
- object->setPosition((new_position - parent_xform->getWorldPosition()) * ~parent_xform->getWorldRotation());
- rebuild(object);
- }
- else
- {
- object->setPositionAgent(new_position);
- rebuild(object);
- }
- }
-
- // for individually selected roots, we need to counter-translate all unselected children
- if (object->isRootEdit() && selectNode->mIndividualSelection)
- {
- // only offset by parent's translation as we've already countered parent's rotation
- rebuild(object);
- object->resetChildrenPosition(old_position - new_position) ;
- }
- }
- }
-
- // store changes to override updates
- for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin();
- iter != LLSelectMgr::getInstance()->getSelection()->end(); iter++)
- {
- LLSelectNode* selectNode = *iter;
- LLViewerObject*cur = selectNode->getObject();
- LLViewerObject *root_object = (cur == NULL) ? NULL : cur->getRootEdit();
- if( cur->permModify() && cur->permMove() && !cur->isPermanentEnforced() &&
- ((root_object == NULL) || !root_object->isPermanentEnforced()) &&
- (!cur->isAvatar() || LLSelectMgr::getInstance()->mAllowSelectAvatar))
- {
- selectNode->mLastRotation = cur->getRotation();
- selectNode->mLastPositionLocal = cur->getPosition();
- }
- }
-
- LLSelectMgr::getInstance()->updateSelectionCenter();
-
- // RN: just clear focus so camera doesn't follow spurious object updates
- gAgentCamera.clearFocusObject();
- dialog_refresh_all();
-}
-
-void LLManipRotate::renderActiveRing( F32 radius, F32 width, const LLColor4& front_color, const LLColor4& back_color)
-{
- LLGLEnable cull_face(GL_CULL_FACE);
- {
- gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, false);
- gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, true);
- }
- {
- LLGLDepthTest gls_depth(GL_FALSE);
- gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, false);
- gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, true);
- }
-}
-
-void LLManipRotate::renderSnapGuides()
-{
- static LLCachedControl<bool> snap_enabled(gSavedSettings, "SnapEnabled", true);
- if (!snap_enabled)
- {
- return;
- }
-
- LLVector3 grid_origin;
- LLVector3 grid_scale;
- LLQuaternion grid_rotation;
-
- LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale, true);
-
- LLVector3 constraint_axis = getConstraintAxis();
-
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
- LLVector3 cam_at_axis;
- if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
- {
- cam_at_axis.setVec(1.f, 0.f, 0.f);
- }
- else
- {
- cam_at_axis = center - gAgentCamera.getCameraPositionAgent();
- cam_at_axis.normVec();
- }
-
- LLVector3 world_snap_axis;
- LLVector3 test_axis = constraint_axis;
-
- bool constrain_to_ref_object = false;
- if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid())
- {
- test_axis = test_axis * ~grid_rotation;
- }
- else if (LLSelectMgr::getInstance()->getGridMode() == GRID_MODE_REF_OBJECT)
- {
- test_axis = test_axis * ~grid_rotation;
- constrain_to_ref_object = true;
- }
-
- test_axis.abs();
-
- // find closest global/reference axis to local constraint axis;
- if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
- {
- world_snap_axis = LLVector3::y_axis;
- }
- else if (test_axis.mV[VY] > test_axis.mV[VZ])
- {
- world_snap_axis = LLVector3::z_axis;
- }
- else
- {
- world_snap_axis = LLVector3::x_axis;
- }
-
- LLVector3 projected_snap_axis = world_snap_axis;
- if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid())
- {
- projected_snap_axis = projected_snap_axis * grid_rotation;
- }
- else if (constrain_to_ref_object)
- {
- projected_snap_axis = projected_snap_axis * grid_rotation;
- }
-
- // project world snap axis onto constraint plane
- projected_snap_axis -= projected_vec(projected_snap_axis, constraint_axis);
- projected_snap_axis.normVec();
-
- S32 num_rings = mCamEdgeOn ? 2 : 1;
- for (S32 ring_num = 0; ring_num < num_rings; ring_num++)
- {
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
-
- if (mCamEdgeOn)
- {
- // draw two opposing rings
- if (ring_num == 0)
- {
- center += constraint_axis * mRadiusMeters * 0.5f;
- }
- else
- {
- center -= constraint_axis * mRadiusMeters * 0.5f;
- }
- }
-
- LLGLDepthTest gls_depth(GL_FALSE);
- for (S32 pass = 0; pass < 3; pass++)
- {
- // render snap guide ring
- gGL.pushMatrix();
-
- LLQuaternion snap_guide_rot;
- F32 angle_radians, x, y, z;
- snap_guide_rot.shortestArc(LLVector3::z_axis, getConstraintAxis());
- snap_guide_rot.getAngleAxis(&angle_radians, &x, &y, &z);
- gGL.translatef(center.mV[VX], center.mV[VY], center.mV[VZ]);
- gGL.rotatef(angle_radians * RAD_TO_DEG, x, y, z);
-
- LLColor4 line_color = setupSnapGuideRenderPass(pass);
-
- gGL.color4fv(line_color.mV);
-
- if (mCamEdgeOn)
- {
- // render an arc
- LLVector3 edge_normal = cam_at_axis % constraint_axis;
- edge_normal.normVec();
- LLVector3 x_axis_snap = LLVector3::x_axis * snap_guide_rot;
- LLVector3 y_axis_snap = LLVector3::y_axis * snap_guide_rot;
-
- F32 end_angle = atan2(y_axis_snap * edge_normal, x_axis_snap * edge_normal);
- //F32 start_angle = angle_between((-1.f * LLVector3::x_axis) * snap_guide_rot, edge_normal);
- F32 start_angle = end_angle - F_PI;
- gl_arc_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, false, start_angle, end_angle);
- }
- else
- {
- gl_circle_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, false);
- }
- gGL.popMatrix();
-
- for (S32 i = 0; i < 64; i++)
- {
- bool render_text = true;
- F32 deg = 5.625f * (F32)i;
- LLVector3 inner_point;
- LLVector3 outer_point;
- LLVector3 text_point;
- LLQuaternion rot(deg * DEG_TO_RAD, constraint_axis);
- gGL.begin(LLRender::LINES);
- {
- inner_point = (projected_snap_axis * mRadiusMeters * SNAP_GUIDE_INNER_RADIUS * rot) + center;
- F32 tick_length = 0.f;
- if (i % 16 == 0)
- {
- tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_1 - SNAP_GUIDE_INNER_RADIUS);
- }
- else if (i % 8 == 0)
- {
- tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_2 - SNAP_GUIDE_INNER_RADIUS);
- }
- else if (i % 4 == 0)
- {
- tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_3 - SNAP_GUIDE_INNER_RADIUS);
- }
- else if (i % 2 == 0)
- {
- tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_4 - SNAP_GUIDE_INNER_RADIUS);
- }
- else
- {
- tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_5 - SNAP_GUIDE_INNER_RADIUS);
- }
-
- if (mCamEdgeOn)
- {
- // don't draw ticks that are on back side of circle
- F32 dot = cam_at_axis * (projected_snap_axis * rot);
- if (dot > 0.f)
- {
- outer_point = inner_point;
- render_text = false;
- }
- else
- {
- if (ring_num == 0)
- {
- outer_point = inner_point + (constraint_axis * tick_length) * rot;
- }
- else
- {
- outer_point = inner_point - (constraint_axis * tick_length) * rot;
- }
- }
- }
- else
- {
- outer_point = inner_point + (projected_snap_axis * tick_length) * rot;
- }
-
- text_point = outer_point + (projected_snap_axis * mRadiusMeters * 0.1f) * rot;
-
- gGL.vertex3fv(inner_point.mV);
- gGL.vertex3fv(outer_point.mV);
- }
- gGL.end();
-
- //RN: text rendering does own shadow pass, so only render once
- if (pass == 1 && render_text && i % 16 == 0)
- {
- if (world_snap_axis.mV[VX])
- {
- if (i == 0)
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Forward") : LLTrans::getString("Direction_East"), LLColor4::white);
- }
- else if (i == 16)
- {
- if (constraint_axis.mV[VZ] > 0.f)
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Left") : LLTrans::getString("Direction_North"), LLColor4::white);
- }
- else
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Right") : LLTrans::getString("Direction_South"), LLColor4::white);
- }
- }
- else if (i == 32)
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Back") : LLTrans::getString("Direction_West"), LLColor4::white);
- }
- else
- {
- if (constraint_axis.mV[VZ] > 0.f)
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Right") : LLTrans::getString("Direction_South"), LLColor4::white);
- }
- else
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Left") : LLTrans::getString("Direction_North"), LLColor4::white);
- }
- }
- }
- else if (world_snap_axis.mV[VY])
- {
- if (i == 0)
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Left") : LLTrans::getString("Direction_North"), LLColor4::white);
- }
- else if (i == 16)
- {
- if (constraint_axis.mV[VX] > 0.f)
- {
- renderTickText(text_point, LLTrans::getString("Direction_Up"), LLColor4::white);
- }
- else
- {
- renderTickText(text_point, LLTrans::getString("Direction_Down"), LLColor4::white);
- }
- }
- else if (i == 32)
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Right") : LLTrans::getString("Direction_South"), LLColor4::white);
- }
- else
- {
- if (constraint_axis.mV[VX] > 0.f)
- {
- renderTickText(text_point, LLTrans::getString("Direction_Down"), LLColor4::white);
- }
- else
- {
- renderTickText(text_point, LLTrans::getString("Direction_Up"), LLColor4::white);
- }
- }
- }
- else if (world_snap_axis.mV[VZ])
- {
- if (i == 0)
- {
- renderTickText(text_point, LLTrans::getString("Direction_Up"), LLColor4::white);
- }
- else if (i == 16)
- {
- if (constraint_axis.mV[VY] > 0.f)
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Forward") : LLTrans::getString("Direction_East"), LLColor4::white);
- }
- else
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Back") : LLTrans::getString("Direction_West"), LLColor4::white);
- }
- }
- else if (i == 32)
- {
- renderTickText(text_point, LLTrans::getString("Direction_Down"), LLColor4::white);
- }
- else
- {
- if (constraint_axis.mV[VY] > 0.f)
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Back") : LLTrans::getString("Direction_West"), LLColor4::white);
- }
- else
- {
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Forward") : LLTrans::getString("Direction_East"), LLColor4::white);
- }
- }
- }
- }
- gGL.color4fv(line_color.mV);
- }
-
- // now render projected object axis
- if (mInSnapRegime)
- {
- LLVector3 object_axis;
- getObjectAxisClosestToMouse(object_axis);
-
- // project onto constraint plane
- LLSelectNode* first_node = mObjectSelection->getFirstMoveableNode(true);
- object_axis = object_axis * first_node->getObject()->getRenderRotation();
- object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
- object_axis.normVec();
- object_axis = object_axis * SNAP_GUIDE_INNER_RADIUS * mRadiusMeters + center;
- LLVector3 line_start = center;
-
- gGL.begin(LLRender::LINES);
- {
- gGL.vertex3fv(line_start.mV);
- gGL.vertex3fv(object_axis.mV);
- }
- gGL.end();
-
- // draw snap guide arrow
- gGL.begin(LLRender::TRIANGLES);
- {
- LLVector3 arrow_dir;
- LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
- arrow_span.normVec();
-
- arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
- arrow_dir.normVec();
- if (ring_num == 1)
- {
- arrow_dir *= -1.f;
- }
- gGL.vertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
- gGL.vertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
- gGL.vertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
- }
- gGL.end();
-
- {
- LLGLDepthTest gls_depth(GL_TRUE);
- gGL.begin(LLRender::LINES);
- {
- gGL.vertex3fv(line_start.mV);
- gGL.vertex3fv(object_axis.mV);
- }
- gGL.end();
-
- // draw snap guide arrow
- gGL.begin(LLRender::TRIANGLES);
- {
- LLVector3 arrow_dir;
- LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
- arrow_span.normVec();
-
- arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
- arrow_dir.normVec();
- if (ring_num == 1)
- {
- arrow_dir *= -1.f;
- }
-
- gGL.vertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
- gGL.vertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
- gGL.vertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
- }
- gGL.end();
- }
- }
- }
- }
-
-
- // render help text
- if (mObjectSelection->getSelectType() != SELECT_TYPE_HUD)
- {
- if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText)
- {
- LLVector3 selection_center_start = LLSelectMgr::getInstance()->getSavedBBoxOfSelection().getCenterAgent();
-
- LLVector3 offset_dir = LLViewerCamera::getInstance()->getUpAxis();
-
- F32 line_alpha = gSavedSettings.getF32("GridOpacity");
-
- LLVector3 help_text_pos = selection_center_start + (mRadiusMeters * 3.f * offset_dir);
- const LLFontGL* big_fontp = LLFontGL::getFontSansSerif();
-
- std::string help_text = LLTrans::getString("manip_hint1");
- 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, LLFontGL::NO_SHADOW, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, false);
- help_text = LLTrans::getString("manip_hint2");
- help_text_pos -= offset_dir * mRadiusMeters * 0.4f;
- hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, false);
- }
- }
-}
-
-// Returns true if center of sphere is visible. Also sets a bunch of member variables that are used later (e.g. mCenterToCam)
-bool LLManipRotate::updateVisiblity()
-{
- // Don't want to recalculate the center of the selection during a drag.
- // Due to packet delays, sometimes half the objects in the selection have their
- // new position and half have their old one. This creates subtle errors in the
- // computed center position for that frame. Unfortunately, these errors
- // accumulate. The result is objects seem to "fly apart" during rotations.
- // JC - 03.26.2002
- if (!hasMouseCapture())
- {
- mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() );//LLSelectMgr::getInstance()->getSelectionCenterGlobal();
- }
-
- bool visible = false;
-
- //Assume that UI scale factor is equivalent for X and Y axis
- F32 ui_scale_factor = LLUI::getScaleFactor().mV[VX];
-
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
- if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
- {
- mCenterToCam = LLVector3(-1.f / gAgentCamera.mHUDCurZoom, 0.f, 0.f);
- mCenterToCamNorm = mCenterToCam;
- mCenterToCamMag = mCenterToCamNorm.normVec();
-
- mRadiusMeters = RADIUS_PIXELS / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
- mRadiusMeters /= gAgentCamera.mHUDCurZoom;
- mRadiusMeters *= ui_scale_factor;
-
- mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
- mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
-
- // x axis range is (-aspect * 0.5f, +aspect * 0.5)
- // y axis range is (-0.5, 0.5)
- // so use getWorldViewHeightRaw as scale factor when converting to pixel coordinates
- mCenterScreen.set((S32)((0.5f - center.mV[VY]) / gAgentCamera.mHUDCurZoom * gViewerWindow->getWorldViewHeightScaled()),
- (S32)((center.mV[VZ] + 0.5f) / gAgentCamera.mHUDCurZoom * gViewerWindow->getWorldViewHeightScaled()));
- visible = true;
- }
- else
- {
- visible = LLViewerCamera::getInstance()->projectPosAgentToScreen(center, mCenterScreen );
- if( visible )
- {
- mCenterToCam = gAgentCamera.getCameraPositionAgent() - center;
- mCenterToCamNorm = mCenterToCam;
- mCenterToCamMag = mCenterToCamNorm.normVec();
- LLVector3 cameraAtAxis = LLViewerCamera::getInstance()->getAtAxis();
- cameraAtAxis.normVec();
-
- F32 z_dist = -1.f * (mCenterToCam * cameraAtAxis);
-
- // Don't drag manip if object too far away
- if (gSavedSettings.getBOOL("LimitSelectDistance"))
- {
- F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance");
- if (dist_vec_squared(gAgent.getPositionAgent(), center) > (max_select_distance * max_select_distance))
- {
- visible = false;
- }
- }
-
- if (mCenterToCamMag > 0.001f)
- {
- F32 fraction_of_fov = RADIUS_PIXELS / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
- F32 apparent_angle = fraction_of_fov * LLViewerCamera::getInstance()->getView(); // radians
- mRadiusMeters = z_dist * tan(apparent_angle);
- mRadiusMeters *= ui_scale_factor;
-
- mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
- mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
- }
- else
- {
- visible = false;
- }
- }
- }
-
- mCamEdgeOn = false;
- F32 axis_onto_cam = mManipPart >= LL_ROT_X ? llabs( getConstraintAxis() * mCenterToCamNorm ) : 0.f;
- if( axis_onto_cam < AXIS_ONTO_CAM_TOLERANCE )
- {
- mCamEdgeOn = true;
- }
-
- return visible;
-}
-
-LLQuaternion LLManipRotate::dragUnconstrained( S32 x, S32 y )
-{
- LLVector3 cam = gAgentCamera.getCameraPositionAgent();
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
-
- mMouseCur = intersectMouseWithSphere( x, y, center, mRadiusMeters);
-
- F32 delta_x = (F32)(mCenterScreen.mX - x);
- F32 delta_y = (F32)(mCenterScreen.mY - y);
-
- F32 dist_from_sphere_center = sqrt(delta_x * delta_x + delta_y * delta_y);
-
- LLVector3 axis = mMouseDown % mMouseCur;
- F32 angle = atan2(sqrtf(axis * axis), mMouseDown * mMouseCur);
- axis.normVec();
- LLQuaternion sphere_rot( angle, axis );
-
- if (is_approx_zero(1.f - mMouseDown * mMouseCur))
- {
- return LLQuaternion::DEFAULT;
- }
- else if (dist_from_sphere_center < RADIUS_PIXELS)
- {
- return sphere_rot;
- }
- else
- {
- LLVector3 intersection;
- getMousePointOnPlaneAgent( intersection, x, y, center + mCenterToProfilePlane, mCenterToCamNorm );
-
- // amount dragging in sphere from center to periphery would rotate object
- F32 in_sphere_angle = F_PI_BY_TWO;
- F32 dist_to_tangent_point = mRadiusMeters;
- if( !is_approx_zero( mCenterToProfilePlaneMag ) )
- {
- dist_to_tangent_point = sqrt( mRadiusMeters * mRadiusMeters - mCenterToProfilePlaneMag * mCenterToProfilePlaneMag );
- in_sphere_angle = atan2( dist_to_tangent_point, mCenterToProfilePlaneMag );
- }
-
- LLVector3 profile_center_to_intersection = intersection - (center + mCenterToProfilePlane);
- F32 dist_to_intersection = profile_center_to_intersection.normVec();
- F32 angle = (-1.f + dist_to_intersection / dist_to_tangent_point) * in_sphere_angle;
-
- LLVector3 axis;
- if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
- {
- axis = LLVector3(-1.f, 0.f, 0.f) % profile_center_to_intersection;
- }
- else
- {
- axis = (cam - center) % profile_center_to_intersection;
- axis.normVec();
- }
- return sphere_rot * LLQuaternion( angle, axis );
- }
-}
-
-LLVector3 LLManipRotate::getConstraintAxis()
-{
- LLVector3 axis;
- if( LL_ROT_ROLL == mManipPart )
- {
- axis = mCenterToCamNorm;
- }
- else
- {
- S32 axis_dir = mManipPart - LL_ROT_X;
- if ((axis_dir >= LL_NO_PART) && (axis_dir < LL_Z_ARROW))
- {
- axis.mV[axis_dir] = 1.f;
- }
- else
- {
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- LL_ERRS() << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << LL_ENDL;
-#else
- LL_WARNS() << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << LL_ENDL;
-#endif
- axis.mV[0] = 1.f;
- }
-
- LLVector3 grid_origin;
- LLVector3 grid_scale;
- LLQuaternion grid_rotation;
-
- LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
-
- LLSelectNode* first_node = mObjectSelection->getFirstMoveableNode(true);
- if (first_node)
- {
- // *FIX: get agent local attachment grid working
- // Put rotation into frame of first selected root object
- axis = axis * grid_rotation;
- }
- }
-
- return axis;
-}
-
-LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y )
-{
- LLSelectNode* first_object_node = mObjectSelection->getFirstMoveableNode(true);
- LLVector3 constraint_axis = getConstraintAxis();
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
-
- F32 angle = 0.f;
-
- // build snap axes
- LLVector3 grid_origin;
- LLVector3 grid_scale;
- LLQuaternion grid_rotation;
-
- LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
-
- LLVector3 axis1;
- LLVector3 axis2;
-
- LLVector3 test_axis = constraint_axis;
- if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid())
- {
- test_axis = test_axis * ~grid_rotation;
- }
- else if (LLSelectMgr::getInstance()->getGridMode() == GRID_MODE_REF_OBJECT)
- {
- test_axis = test_axis * ~grid_rotation;
- }
- test_axis.abs();
-
- // find closest global axis to constraint axis;
- if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
- {
- axis1 = LLVector3::y_axis;
- }
- else if (test_axis.mV[VY] > test_axis.mV[VZ])
- {
- axis1 = LLVector3::z_axis;
- }
- else
- {
- axis1 = LLVector3::x_axis;
- }
-
- if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid())
- {
- axis1 = axis1 * grid_rotation;
- }
- else if (LLSelectMgr::getInstance()->getGridMode() == GRID_MODE_REF_OBJECT)
- {
- axis1 = axis1 * grid_rotation;
- }
-
- //project axis onto constraint plane
- axis1 -= (axis1 * constraint_axis) * constraint_axis;
- axis1.normVec();
-
- // calculate third and final axis
- axis2 = constraint_axis % axis1;
-
- //F32 axis_onto_cam = llabs( constraint_axis * mCenterToCamNorm );
- if( mCamEdgeOn )
- {
- // We're looking at the ring edge-on.
- LLVector3 snap_plane_center = (center + (constraint_axis * mRadiusMeters * 0.5f));
- LLVector3 cam_to_snap_plane;
- if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
- {
- cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
- }
- else
- {
- cam_to_snap_plane = snap_plane_center - gAgentCamera.getCameraPositionAgent();
- cam_to_snap_plane.normVec();
- }
-
- LLVector3 projected_mouse;
- bool hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
- projected_mouse -= snap_plane_center;
-
- if (gSavedSettings.getBOOL("SnapEnabled")) {
- S32 snap_plane = 0;
-
- F32 dot = cam_to_snap_plane * constraint_axis;
- if (llabs(dot) < 0.01f)
- {
- // looking at ring edge on, project onto view plane and check if mouse is past ring
- getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
- projected_mouse -= snap_plane_center;
- dot = projected_mouse * constraint_axis;
- if (projected_mouse * constraint_axis > 0)
- {
- snap_plane = 1;
- }
- projected_mouse -= dot * constraint_axis;
- }
- else if (dot > 0.f)
- {
- // look for mouse position outside and in front of snap circle
- if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
- {
- snap_plane = 1;
- }
- }
- else
- {
- // look for mouse position inside or in back of snap circle
- if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
- {
- snap_plane = 1;
- }
- }
-
- if (snap_plane == 0)
- {
- // try other plane
- snap_plane_center = (center - (constraint_axis * mRadiusMeters * 0.5f));
- if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
- {
- cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
- }
- else
- {
- cam_to_snap_plane = snap_plane_center - gAgentCamera.getCameraPositionAgent();
- cam_to_snap_plane.normVec();
- }
-
- hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
- projected_mouse -= snap_plane_center;
-
- dot = cam_to_snap_plane * constraint_axis;
- if (llabs(dot) < 0.01f)
- {
- // looking at ring edge on, project onto view plane and check if mouse is past ring
- getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
- projected_mouse -= snap_plane_center;
- dot = projected_mouse * constraint_axis;
- if (projected_mouse * constraint_axis < 0)
- {
- snap_plane = 2;
- }
- projected_mouse -= dot * constraint_axis;
- }
- else if (dot < 0.f)
- {
- // look for mouse position outside and in front of snap circle
- if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
- {
- snap_plane = 2;
- }
- }
- else
- {
- // look for mouse position inside or in back of snap circle
- if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
- {
- snap_plane = 2;
- }
- }
- }
-
- if (snap_plane > 0)
- {
- LLVector3 cam_at_axis;
- if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
- {
- cam_at_axis.setVec(1.f, 0.f, 0.f);
- }
- else
- {
- cam_at_axis = snap_plane_center - gAgentCamera.getCameraPositionAgent();
- cam_at_axis.normVec();
- }
-
- // first, project mouse onto screen plane at point tangent to rotation radius.
- getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_at_axis);
- // project that point onto rotation plane
- projected_mouse -= snap_plane_center;
- projected_mouse -= projected_vec(projected_mouse, constraint_axis);
-
- F32 mouse_lateral_dist = llmin(SNAP_GUIDE_INNER_RADIUS * mRadiusMeters, projected_mouse.magVec());
- F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
- if (llabs(mouse_lateral_dist) > 0.01f)
- {
- mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
- (mouse_lateral_dist * mouse_lateral_dist));
- }
- LLVector3 projected_camera_at = cam_at_axis - projected_vec(cam_at_axis, constraint_axis);
- projected_mouse -= mouse_depth * projected_camera_at;
-
- if (!mInSnapRegime)
- {
- mSmoothRotate = true;
- }
- mInSnapRegime = true;
- // 0 to 360 deg
- F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
-
- F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
-
- LLVector3 object_axis;
- getObjectAxisClosestToMouse(object_axis);
- if (first_object_node)
- {
- object_axis = object_axis * first_object_node->mSavedRotation;
- }
-
- // project onto constraint plane
- object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
- object_axis.normVec();
-
- if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
- {
- F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
- angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
- }
- else
- {
- angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
- }
- return LLQuaternion( -angle, constraint_axis );
- }
- else
- {
- if (mInSnapRegime)
- {
- mSmoothRotate = true;
- }
- mInSnapRegime = false;
- }
- }
- else {
- if (mInSnapRegime)
- {
- mSmoothRotate = true;
- }
- mInSnapRegime = false;
- }
-
- if (!mInSnapRegime)
- {
- LLVector3 up_from_axis = mCenterToCamNorm % constraint_axis;
- up_from_axis.normVec();
- LLVector3 cur_intersection;
- getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
- cur_intersection -= center;
- mMouseCur = projected_vec(cur_intersection, up_from_axis);
- F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
- F32 mouse_dist_sqrd = mMouseCur.magVecSquared();
- if (mouse_dist_sqrd > 0.0001f)
- {
- mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
- mouse_dist_sqrd);
- }
- LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, constraint_axis);
- mMouseCur += mouse_depth * projected_center_to_cam;
-
- F32 dist = (cur_intersection * up_from_axis) - (mMouseDown * up_from_axis);
- angle = dist / (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * -F_PI_BY_TWO;
- }
- }
- else
- {
- LLVector3 projected_mouse;
- getMousePointOnPlaneAgent(projected_mouse, x, y, center, constraint_axis);
- projected_mouse -= center;
- mMouseCur = projected_mouse;
- mMouseCur.normVec();
-
- if (!first_object_node)
- {
- return LLQuaternion::DEFAULT;
- }
-
- if (gSavedSettings.getBOOL("SnapEnabled") && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters)
- {
- if (!mInSnapRegime)
- {
- mSmoothRotate = true;
- }
- mInSnapRegime = true;
- // 0 to 360 deg
- F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
-
- F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
-
- LLVector3 object_axis;
- getObjectAxisClosestToMouse(object_axis);
- object_axis = object_axis * first_object_node->mSavedRotation;
-
- // project onto constraint plane
- object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
- object_axis.normVec();
-
- if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
- {
- F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
- angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
- }
- else
- {
- angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
- }
- return LLQuaternion( -angle, constraint_axis );
- }
- else
- {
- if (mInSnapRegime)
- {
- mSmoothRotate = true;
- }
- mInSnapRegime = false;
- }
-
- LLVector3 cross_product = mMouseDown % mMouseCur;
- angle = atan2(sqrtf(cross_product * cross_product), mMouseCur * mMouseDown);
- F32 dir = cross_product * constraint_axis; // cross product
- if( dir < 0.f )
- {
- angle *= -1.f;
- }
- }
-
- F32 rot_step = gSavedSettings.getF32("RotationStep");
- F32 step_size = DEG_TO_RAD * rot_step;
- angle -= fmod(angle, step_size);
-
- return LLQuaternion( angle, constraint_axis );
-}
-
-
-
-LLVector3 LLManipRotate::intersectMouseWithSphere( S32 x, S32 y, const LLVector3& sphere_center, F32 sphere_radius)
-{
- LLVector3 ray_pt;
- LLVector3 ray_dir;
- mouseToRay( x, y, &ray_pt, &ray_dir);
- return intersectRayWithSphere( ray_pt, ray_dir, sphere_center, sphere_radius );
-}
-
-LLVector3 LLManipRotate::intersectRayWithSphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius)
-{
- LLVector3 ray_pt_to_center = sphere_center - ray_pt;
- F32 center_distance = ray_pt_to_center.normVec();
-
- F32 dot = ray_dir * ray_pt_to_center;
-
- if (dot == 0.f)
- {
- return LLVector3::zero;
- }
-
- // point which ray hits plane centered on sphere origin, facing ray origin
- LLVector3 intersection_sphere_plane = ray_pt + (ray_dir * center_distance / dot);
- // vector from sphere origin to the point, normalized to sphere radius
- LLVector3 sphere_center_to_intersection = (intersection_sphere_plane - sphere_center) / sphere_radius;
-
- F32 dist_squared = sphere_center_to_intersection.magVecSquared();
- LLVector3 result;
-
- if (dist_squared > 1.f)
- {
- result = sphere_center_to_intersection;
- result.normVec();
- }
- else
- {
- result = sphere_center_to_intersection - ray_dir * sqrt(1.f - dist_squared);
- }
-
- return result;
-}
-
-// Utility function. Should probably be moved to another class.
-// x,y - mouse position in scaled window coordinates (NOT GL viewport coordinates)
-//static
-void LLManipRotate::mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_dir )
-{
- if (LLSelectMgr::getInstance()->getSelection()->getSelectType() == SELECT_TYPE_HUD)
- {
- F32 mouse_x = (((F32)x / gViewerWindow->getWorldViewRectScaled().getWidth()) - 0.5f) / gAgentCamera.mHUDCurZoom;
- F32 mouse_y = ((((F32)y) / gViewerWindow->getWorldViewRectScaled().getHeight()) - 0.5f) / gAgentCamera.mHUDCurZoom;
-
- *ray_pt = LLVector3(-1.f, -mouse_x, mouse_y);
- *ray_dir = LLVector3(1.f, 0.f, 0.f);
- }
- else
- {
- *ray_pt = gAgentCamera.getCameraPositionAgent();
- *ray_dir = gViewerWindow->mouseDirectionGlobal(x, y);
- }
-}
-
-void LLManipRotate::highlightManipulators( S32 x, S32 y )
-{
- mHighlightedPart = LL_NO_PART;
-
- //LLBBox bbox = LLSelectMgr::getInstance()->getBBoxOfSelection();
- LLViewerObject *first_object = mObjectSelection->getFirstMoveableObject(true);
-
- if (!first_object)
- {
- return;
- }
-
- LLVector3 rotation_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
- LLVector3 mouse_dir_x;
- LLVector3 mouse_dir_y;
- LLVector3 mouse_dir_z;
- LLVector3 intersection_roll;
-
- LLVector3 grid_origin;
- LLVector3 grid_scale;
- LLQuaternion grid_rotation;
-
- LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
-
- LLVector3 rot_x_axis = LLVector3::x_axis * grid_rotation;
- LLVector3 rot_y_axis = LLVector3::y_axis * grid_rotation;
- LLVector3 rot_z_axis = LLVector3::z_axis * grid_rotation;
-
- F32 proj_rot_x_axis = llabs(rot_x_axis * mCenterToCamNorm);
- F32 proj_rot_y_axis = llabs(rot_y_axis * mCenterToCamNorm);
- F32 proj_rot_z_axis = llabs(rot_z_axis * mCenterToCamNorm);
-
- F32 min_select_distance = 0.f;
- F32 cur_select_distance = 0.f;
-
- // test x
- getMousePointOnPlaneAgent(mouse_dir_x, x, y, rotation_center, rot_x_axis);
- mouse_dir_x -= rotation_center;
- // push intersection point out when working at obtuse angle to make ring easier to hit
- mouse_dir_x *= 1.f + (1.f - llabs(rot_x_axis * mCenterToCamNorm)) * 0.1f;
-
- // test y
- getMousePointOnPlaneAgent(mouse_dir_y, x, y, rotation_center, rot_y_axis);
- mouse_dir_y -= rotation_center;
- mouse_dir_y *= 1.f + (1.f - llabs(rot_y_axis * mCenterToCamNorm)) * 0.1f;
-
- // test z
- getMousePointOnPlaneAgent(mouse_dir_z, x, y, rotation_center, rot_z_axis);
- mouse_dir_z -= rotation_center;
- mouse_dir_z *= 1.f + (1.f - llabs(rot_z_axis * mCenterToCamNorm)) * 0.1f;
-
- // test roll
- getMousePointOnPlaneAgent(intersection_roll, x, y, rotation_center, mCenterToCamNorm);
- intersection_roll -= rotation_center;
-
- F32 dist_x = mouse_dir_x.normVec();
- F32 dist_y = mouse_dir_y.normVec();
- F32 dist_z = mouse_dir_z.normVec();
-
- F32 distance_threshold = (MAX_MANIP_SELECT_DISTANCE * mRadiusMeters) / gViewerWindow->getWorldViewHeightScaled();
-
- if (llabs(dist_x - mRadiusMeters) * llmax(0.05f, proj_rot_x_axis) < distance_threshold)
- {
- // selected x
- cur_select_distance = dist_x * mouse_dir_x * mCenterToCamNorm;
- if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
- {
- min_select_distance = cur_select_distance;
- mHighlightedPart = LL_ROT_X;
- }
- }
- if (llabs(dist_y - mRadiusMeters) * llmax(0.05f, proj_rot_y_axis) < distance_threshold)
- {
- // selected y
- cur_select_distance = dist_y * mouse_dir_y * mCenterToCamNorm;
- if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
- {
- min_select_distance = cur_select_distance;
- mHighlightedPart = LL_ROT_Y;
- }
- }
- if (llabs(dist_z - mRadiusMeters) * llmax(0.05f, proj_rot_z_axis) < distance_threshold)
- {
- // selected z
- cur_select_distance = dist_z * mouse_dir_z * mCenterToCamNorm;
- if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
- {
- min_select_distance = cur_select_distance;
- mHighlightedPart = LL_ROT_Z;
- }
- }
-
- // test for edge-on intersections
- if (proj_rot_x_axis < 0.05f)
- {
- if ((proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_x_axis) < distance_threshold) && dist_y < mRadiusMeters) ||
- (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_x_axis) < distance_threshold) && dist_z < mRadiusMeters))
- {
- mHighlightedPart = LL_ROT_X;
- }
- }
-
- if (proj_rot_y_axis < 0.05f)
- {
- if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_y_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
- (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_y_axis) < distance_threshold) && dist_z < mRadiusMeters))
- {
- mHighlightedPart = LL_ROT_Y;
- }
- }
-
- if (proj_rot_z_axis < 0.05f)
- {
- if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_z_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
- (proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_z_axis) < distance_threshold) && dist_y < mRadiusMeters))
- {
- mHighlightedPart = LL_ROT_Z;
- }
- }
-
- // test for roll
- if (mHighlightedPart == LL_NO_PART)
- {
- F32 roll_distance = intersection_roll.magVec();
- F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
-
- // use larger distance threshold for roll as it is checked only if something else wasn't highlighted
- if (llabs(roll_distance - (mRadiusMeters + (width_meters * 2.f))) < distance_threshold * 2.f)
- {
- mHighlightedPart = LL_ROT_ROLL;
- }
- else if (roll_distance < mRadiusMeters)
- {
- mHighlightedPart = LL_ROT_GENERAL;
- }
- }
-}
-
-S32 LLManipRotate::getObjectAxisClosestToMouse(LLVector3& object_axis)
-{
- LLSelectNode* first_object_node = mObjectSelection->getFirstMoveableNode(true);
-
- if (!first_object_node)
- {
- object_axis.clearVec();
- return -1;
- }
-
- LLQuaternion obj_rotation = first_object_node->mSavedRotation;
- LLVector3 mouse_down_object = mMouseDown * ~obj_rotation;
- LLVector3 mouse_down_abs = mouse_down_object;
- mouse_down_abs.abs();
-
- S32 axis_index = 0;
- if (mouse_down_abs.mV[VX] > mouse_down_abs.mV[VY] && mouse_down_abs.mV[VX] > mouse_down_abs.mV[VZ])
- {
- if (mouse_down_object.mV[VX] > 0.f)
- {
- object_axis = LLVector3::x_axis;
- }
- else
- {
- object_axis = LLVector3::x_axis_neg;
- }
- axis_index = VX;
- }
- else if (mouse_down_abs.mV[VY] > mouse_down_abs.mV[VZ])
- {
- if (mouse_down_object.mV[VY] > 0.f)
- {
- object_axis = LLVector3::y_axis;
- }
- else
- {
- object_axis = LLVector3::y_axis_neg;
- }
- axis_index = VY;
- }
- else
- {
- if (mouse_down_object.mV[VZ] > 0.f)
- {
- object_axis = LLVector3::z_axis;
- }
- else
- {
- object_axis = LLVector3::z_axis_neg;
- }
- axis_index = VZ;
- }
-
- return axis_index;
-}
-
-//virtual
-bool LLManipRotate::canAffectSelection()
-{
- bool can_rotate = mObjectSelection->getObjectCount() != 0;
- if (can_rotate)
- {
- struct f : public LLSelectedObjectFunctor
- {
- virtual bool apply(LLViewerObject* objectp)
- {
- LLViewerObject *root_object = (objectp == NULL) ? NULL : objectp->getRootEdit();
- return objectp->permMove() && !objectp->isPermanentEnforced() &&
- ((root_object == NULL) || !root_object->isPermanentEnforced()) &&
- (objectp->permModify() || !gSavedSettings.getBOOL("EditLinkedParts"));
- }
- } func;
- can_rotate = mObjectSelection->applyToObjects(&func);
- }
- return can_rotate;
-}
-
+/** + * @file llmaniprotate.cpp + * @brief LLManipRotate class implementation + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llmaniprotate.h" + +// library includes +#include "llmath.h" +#include "llgl.h" +#include "llrender.h" +#include "v4color.h" +#include "llprimitive.h" +#include "llview.h" +#include "llfontgl.h" + +// viewer includes +#include "llagent.h" +#include "llagentcamera.h" +#include "llbox.h" +#include "llbutton.h" +#include "llviewercontrol.h" +#include "llcriticaldamp.h" +#include "lltooltip.h" +#include "llfloatertools.h" +#include "llselectmgr.h" +#include "llstatusbar.h" +#include "llui.h" +#include "llvoavatar.h" +#include "llviewercamera.h" +#include "llviewerobject.h" +#include "llviewerobject.h" +#include "llviewershadermgr.h" +#include "llviewerwindow.h" +#include "llworld.h" +#include "pipeline.h" +#include "lldrawable.h" +#include "llglheaders.h" +#include "lltrans.h" +#include "llvoavatarself.h" +#include "llhudrender.h" + +const F32 RADIUS_PIXELS = 100.f; // size in screen space +const F32 SQ_RADIUS = RADIUS_PIXELS * RADIUS_PIXELS; +const F32 WIDTH_PIXELS = 8; +const S32 CIRCLE_STEPS = 100; +const F32 MAX_MANIP_SELECT_DISTANCE = 100.f; +const F32 SNAP_ANGLE_INCREMENT = 5.625f; +const F32 SNAP_ANGLE_DETENTE = SNAP_ANGLE_INCREMENT; +const F32 SNAP_GUIDE_RADIUS_1 = 2.8f; +const F32 SNAP_GUIDE_RADIUS_2 = 2.4f; +const F32 SNAP_GUIDE_RADIUS_3 = 2.2f; +const F32 SNAP_GUIDE_RADIUS_4 = 2.1f; +const F32 SNAP_GUIDE_RADIUS_5 = 2.05f; +const F32 SNAP_GUIDE_INNER_RADIUS = 2.f; +const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 80.f * DEG_TO_RAD ); +const F32 SELECTED_MANIPULATOR_SCALE = 1.05f; +const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f; + +extern void handle_reset_rotation(void*); // in LLViewerWindow + +LLManipRotate::LLManipRotate( LLToolComposite* composite ) +: LLManip( std::string("Rotate"), composite ), + mRotationCenter(), + mCenterScreen(), + mRotation(), + mMouseDown(), + mMouseCur(), + mRadiusMeters(0.f), + mCenterToCam(), + mCenterToCamNorm(), + mCenterToCamMag(0.f), + mCenterToProfilePlane(), + mCenterToProfilePlaneMag(0.f), + mSendUpdateOnMouseUp( false ), + mSmoothRotate( false ), + mCamEdgeOn(false), + mManipulatorScales(1.f, 1.f, 1.f, 1.f) +{ } + +void LLManipRotate::handleSelect() +{ + // *FIX: put this in mouseDown? + LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + if (gFloaterTools) + { + gFloaterTools->setStatusText("rotate"); + } + LLManip::handleSelect(); +} + +void LLManipRotate::render() +{ + LLGLSUIDefault gls_ui; + gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep); + LLGLDepthTest gls_depth(GL_TRUE); + LLGLEnable gl_blend(GL_BLEND); + + // You can rotate if you can move + LLViewerObject* first_object = mObjectSelection->getFirstMoveableObject(true); + if( !first_object ) + { + return; + } + + if( !updateVisiblity() ) + { + return; + } + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD) + { + F32 zoom = gAgentCamera.mHUDCurZoom; + gGL.scalef(zoom, zoom, zoom); + } + + + LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter ); + + LLColor4 highlight_outside( 1.f, 1.f, 0.f, 1.f ); + LLColor4 highlight_inside( 0.7f, 0.7f, 0.f, 0.5f ); + F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS; + + gGL.pushMatrix(); + { + + // are we in the middle of a constrained drag? + if (mManipPart >= LL_ROT_X && mManipPart <= LL_ROT_Z) + { + renderSnapGuides(); + } + else + { + gDebugProgram.bind(); + + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest gls_depth(GL_FALSE); + gGL.pushMatrix(); + { + // Draw "sphere" (intersection of sphere with tangent cone that has apex at camera) + gGL.translatef( mCenterToProfilePlane.mV[VX], mCenterToProfilePlane.mV[VY], mCenterToProfilePlane.mV[VZ] ); + gGL.translatef( center.mV[VX], center.mV[VY], center.mV[VZ] ); + + // Inverse change of basis vectors + LLVector3 forward = mCenterToCamNorm; + LLVector3 left = gAgent.getUpAxis() % forward; + left.normVec(); + LLVector3 up = forward % left; + + LLVector4 a(-forward); + a.mV[3] = 0; + LLVector4 b(up); + b.mV[3] = 0; + LLVector4 c(left); + c.mV[3] = 0; + LLMatrix4 mat; + mat.initRows(a, b, c, LLVector4(0.f, 0.f, 0.f, 1.f)); + + gGL.multMatrix( &mat.mMatrix[0][0] ); + + gGL.rotatef( -90, 0.f, 1.f, 0.f); + LLColor4 color; + if (mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL) + { + color.setVec(0.8f, 0.8f, 0.8f, 0.8f); + gGL.scalef(mManipulatorScales.mV[VW], mManipulatorScales.mV[VW], mManipulatorScales.mV[VW]); + } + else + { + color.setVec( 0.7f, 0.7f, 0.7f, 0.6f ); + } + gGL.diffuseColor4fv(color.mV); + gl_washer_2d(mRadiusMeters + width_meters, mRadiusMeters, CIRCLE_STEPS, color, color); + + + if (mManipPart == LL_NO_PART) + { + gGL.color4f( 0.7f, 0.7f, 0.7f, 0.3f ); + gGL.diffuseColor4f(0.7f, 0.7f, 0.7f, 0.3f); + gl_circle_2d( 0, 0, mRadiusMeters, CIRCLE_STEPS, true ); + } + + gGL.flush(); + } + gGL.popMatrix(); + + gUIProgram.bind(); + } + + gGL.translatef( center.mV[VX], center.mV[VY], center.mV[VZ] ); + + LLQuaternion rot; + F32 angle_radians, x, y, z; + + LLVector3 grid_origin; + LLVector3 grid_scale; + LLQuaternion grid_rotation; + + LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale); + + grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z); + gGL.rotatef(angle_radians * RAD_TO_DEG, x, y, z); + + + gDebugProgram.bind(); + + if (mManipPart == LL_ROT_Z) + { + mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + gGL.pushMatrix(); + { + // selected part + gGL.scalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]); + renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f) , LLColor4( 0.f, 0.f, 1.f, 0.3f )); + } + gGL.popMatrix(); + } + else if (mManipPart == LL_ROT_Y) + { + mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + gGL.pushMatrix(); + { + gGL.rotatef( 90.f, 1.f, 0.f, 0.f ); + gGL.scalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]); + renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f), LLColor4( 0.f, 1.f, 0.f, 0.3f)); + } + gGL.popMatrix(); + } + else if (mManipPart == LL_ROT_X) + { + mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + gGL.pushMatrix(); + { + gGL.rotatef( 90.f, 0.f, 1.f, 0.f ); + gGL.scalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]); + renderActiveRing( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f), LLColor4( 1.f, 0.f, 0.f, 0.3f)); + } + gGL.popMatrix(); + } + else if (mManipPart == LL_ROT_ROLL) + { + mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + } + else if (mManipPart == LL_NO_PART) + { + if (mHighlightedPart == LL_NO_PART) + { + mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + } + + LLGLEnable cull_face(GL_CULL_FACE); + LLGLEnable clip_plane0(GL_CLIP_PLANE0); + LLGLDepthTest gls_depth(GL_FALSE); + //LLGLDisable gls_stencil(GL_STENCIL_TEST); + + // First pass: centers. Second pass: sides. + for( S32 i=0; i<2; i++ ) + { + + gGL.pushMatrix(); + { + if (mHighlightedPart == LL_ROT_Z) + { + mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + gGL.scalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]); + // hovering over part + gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f ), LLColor4( 0.f, 0.f, 1.f, 0.5f ), CIRCLE_STEPS, i); + } + else + { + // default + gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 0.8f, 0.8f ), LLColor4( 0.f, 0.f, 0.8f, 0.4f ), CIRCLE_STEPS, i); + } + } + gGL.popMatrix(); + + gGL.pushMatrix(); + { + gGL.rotatef( 90.f, 1.f, 0.f, 0.f ); + if (mHighlightedPart == LL_ROT_Y) + { + mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + gGL.scalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]); + // hovering over part + gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f ), LLColor4( 0.f, 1.f, 0.f, 0.5f ), CIRCLE_STEPS, i); + } + else + { + // default + gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.8f, 0.f, 0.8f ), LLColor4( 0.f, 0.8f, 0.f, 0.4f ), CIRCLE_STEPS, i); + } + } + gGL.popMatrix(); + + gGL.pushMatrix(); + { + gGL.rotatef( 90.f, 0.f, 1.f, 0.f ); + if (mHighlightedPart == LL_ROT_X) + { + mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + gGL.scalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]); + + // hovering over part + gl_ring( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f ), LLColor4( 1.f, 0.f, 0.f, 0.5f ), CIRCLE_STEPS, i); + } + else + { + // default + gl_ring( mRadiusMeters, width_meters, LLColor4( 0.8f, 0.f, 0.f, 0.8f ), LLColor4( 0.8f, 0.f, 0.f, 0.4f ), CIRCLE_STEPS, i); + } + } + gGL.popMatrix(); + + if (mHighlightedPart == LL_ROT_ROLL) + { + mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLSmoothInterpolation::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE)); + } + + } + + } + + gUIProgram.bind(); + } + gGL.popMatrix(); + gGL.popMatrix(); + + + LLVector3 euler_angles; + LLQuaternion object_rot = first_object->getRotationEdit(); + object_rot.getEulerAngles(&(euler_angles.mV[VX]), &(euler_angles.mV[VY]), &(euler_angles.mV[VZ])); + euler_angles *= RAD_TO_DEG; + euler_angles.mV[VX] = ll_round(fmodf(euler_angles.mV[VX] + 360.f, 360.f), 0.05f); + euler_angles.mV[VY] = ll_round(fmodf(euler_angles.mV[VY] + 360.f, 360.f), 0.05f); + euler_angles.mV[VZ] = ll_round(fmodf(euler_angles.mV[VZ] + 360.f, 360.f), 0.05f); + + renderXYZ(euler_angles); +} + +bool LLManipRotate::handleMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + LLViewerObject* first_object = mObjectSelection->getFirstMoveableObject(true); + if( first_object ) + { + if( mHighlightedPart != LL_NO_PART ) + { + handled = handleMouseDownOnPart( x, y, mask ); + } + } + + return handled; +} + +// Assumes that one of the parts of the manipulator was hit. +bool LLManipRotate::handleMouseDownOnPart( S32 x, S32 y, MASK mask ) +{ + bool can_rotate = canAffectSelection(); + if (!can_rotate) + { + return false; + } + + highlightManipulators(x, y); + S32 hit_part = mHighlightedPart; + // we just started a drag, so save initial object positions + LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_ROTATE); + + // save selection center + mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() ); //LLSelectMgr::getInstance()->getSelectionCenterGlobal(); + + mManipPart = (EManipPart)hit_part; + LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter ); + + if( mManipPart == LL_ROT_GENERAL) + { + mMouseDown = intersectMouseWithSphere( x, y, center, mRadiusMeters); + } + else + { + // Project onto the plane of the ring + LLVector3 axis = getConstraintAxis(); + + F32 axis_onto_cam = llabs( axis * mCenterToCamNorm ); + const F32 AXIS_ONTO_CAM_TOL = cos( 85.f * DEG_TO_RAD ); + if( axis_onto_cam < AXIS_ONTO_CAM_TOL ) + { + LLVector3 up_from_axis = mCenterToCamNorm % axis; + up_from_axis.normVec(); + LLVector3 cur_intersection; + getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam); + cur_intersection -= center; + mMouseDown = projected_vec(cur_intersection, up_from_axis); + F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters; + F32 mouse_dist_sqrd = mMouseDown.magVecSquared(); + if (mouse_dist_sqrd > 0.0001f) + { + mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) - + mouse_dist_sqrd); + } + LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, axis); + mMouseDown += mouse_depth * projected_center_to_cam; + + } + else + { + mMouseDown = findNearestPointOnRing( x, y, center, axis ) - center; + mMouseDown.normVec(); + } + } + + mMouseCur = mMouseDown; + mAgentSelfAtAxis = gAgent.getAtAxis(); // no point checking if avatar was selected, just save the value + + // Route future Mouse messages here preemptively. (Release on mouse up.) + setMouseCapture( true ); + LLSelectMgr::getInstance()->enableSilhouette(false); + + mHelpTextTimer.reset(); + sNumTimesHelpTextShown++; + return true; +} + + +LLVector3 LLManipRotate::findNearestPointOnRing( S32 x, S32 y, const LLVector3& center, const LLVector3& axis ) +{ + // Project the delta onto the ring and rescale it by the radius so that it's _on_ the ring. + LLVector3 proj_onto_ring; + getMousePointOnPlaneAgent(proj_onto_ring, x, y, center, axis); + proj_onto_ring -= center; + proj_onto_ring.normVec(); + + return center + proj_onto_ring * mRadiusMeters; +} + +bool LLManipRotate::handleMouseUp(S32 x, S32 y, MASK mask) +{ + // first, perform normal processing in case this was a quick-click + handleHover(x, y, mask); + + if( hasMouseCapture() ) + { + for (LLObjectSelection::iterator iter = mObjectSelection->begin(); + iter != mObjectSelection->end(); iter++) + { + LLSelectNode* selectNode = *iter; + LLViewerObject* object = selectNode->getObject(); + LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit(); + + // have permission to move and object is root of selection or individually selected + if (object->permMove() && !object->isPermanentEnforced() && + ((root_object == NULL) || !root_object->isPermanentEnforced()) && + (object->isRootEdit() || selectNode->mIndividualSelection)) + { + object->mUnselectedChildrenPositions.clear() ; + } + } + + mManipPart = LL_NO_PART; + + // Might have missed last update due to timing. + LLSelectMgr::getInstance()->sendMultipleUpdate( UPD_ROTATION | UPD_POSITION ); + LLSelectMgr::getInstance()->enableSilhouette(true); + //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject")); + + LLSelectMgr::getInstance()->updateSelectionCenter(); + LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + } + + return LLManip::handleMouseUp(x, y, mask); +} + + +bool LLManipRotate::handleHover(S32 x, S32 y, MASK mask) +{ + if( hasMouseCapture() ) + { + if( mObjectSelection->isEmpty() ) + { + // Somehow the object got deselected while we were dragging it. + setMouseCapture( false ); + } + else + { + drag(x, y); + } + + LL_DEBUGS("UserInput") << "hover handled by LLManipRotate (active)" << LL_ENDL; + } + else + { + highlightManipulators(x, y); + LL_DEBUGS("UserInput") << "hover handled by LLManipRotate (inactive)" << LL_ENDL; + } + + gViewerWindow->setCursor(UI_CURSOR_TOOLROTATE); + return true; +} + + +LLVector3 LLManipRotate::projectToSphere( F32 x, F32 y, bool* on_sphere ) +{ + F32 z = 0.f; + F32 dist_squared = x*x + y*y; + + *on_sphere = dist_squared <= SQ_RADIUS; + if( *on_sphere ) + { + z = sqrt(SQ_RADIUS - dist_squared); + } + return LLVector3( x, y, z ); +} + +// Freeform rotation +void LLManipRotate::drag( S32 x, S32 y ) +{ + if( !updateVisiblity() ) + { + return; + } + + if( mManipPart == LL_ROT_GENERAL ) + { + mRotation = dragUnconstrained(x, y); + } + else + { + mRotation = dragConstrained(x, y); + } + + bool damped = mSmoothRotate; + mSmoothRotate = false; + + for (LLObjectSelection::iterator iter = mObjectSelection->begin(); + iter != mObjectSelection->end(); iter++) + { + LLSelectNode* selectNode = *iter; + LLViewerObject* object = selectNode->getObject(); + LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit(); + + // have permission to move and object is root of selection or individually selected + if (object->permMove() && !object->isPermanentEnforced() && + ((root_object == NULL) || !root_object->isPermanentEnforced()) && + (object->isRootEdit() || selectNode->mIndividualSelection)) + { + 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; + } + } + + LLQuaternion new_rot = selectNode->mSavedRotation * mRotation; + std::vector<LLVector3>& child_positions = object->mUnselectedChildrenPositions ; + std::vector<LLQuaternion> child_rotations; + if (object->isRootEdit() && selectNode->mIndividualSelection) + { + object->saveUnselectedChildrenRotation(child_rotations) ; + object->saveUnselectedChildrenPosition(child_positions) ; + } + + if (object->getParent() && object->mDrawable.notNull()) + { + LLQuaternion invParentRotation = object->mDrawable->mXform.getParent()->getWorldRotation(); + invParentRotation.transQuat(); + + object->setRotation(new_rot * invParentRotation, damped); + rebuild(object); + } + else + { + object->setRotation(new_rot, damped); + LLVOAvatar* avatar = object->asAvatar(); + if (avatar && avatar->isSelf() + && LLSelectMgr::getInstance()->mAllowSelectAvatar + && !object->getParent()) + { + // Normal avatars use object's orienttion, but self uses + // separate LLCoordFrame + // See LVOAvatar::updateOrientation() + if (gAgentCamera.getFocusOnAvatar()) + { + //Don't rotate camera with avatar + gAgentCamera.setFocusOnAvatar(false, false, false); + } + + LLVector3 at_axis = mAgentSelfAtAxis; + at_axis *= mRotation; + at_axis.mV[VZ] = 0.f; + at_axis.normalize(); + gAgent.resetAxes(at_axis); + } + rebuild(object); + } + + // for individually selected roots, we need to counterrotate all the children + if (object->isRootEdit() && selectNode->mIndividualSelection) + { + //RN: must do non-damped updates on these objects so relative rotation appears constant + // instead of having two competing slerps making the child objects appear to "wobble" + object->resetChildrenRotationAndPosition(child_rotations, child_positions) ; + } + } + } + + // update positions + for (LLObjectSelection::iterator iter = mObjectSelection->begin(); + iter != mObjectSelection->end(); iter++) + { + LLSelectNode* selectNode = *iter; + LLViewerObject* object = selectNode->getObject(); + LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit(); + + // to avoid cumulative position changes we calculate the objects new position using its saved position + if (object && object->permMove() && !object->isPermanentEnforced() && + ((root_object == NULL) || !root_object->isPermanentEnforced())) + { + LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter ); + + LLVector3 old_position; + LLVector3 new_position; + + if (object->isAttachment() && object->mDrawable.notNull()) + { + // need to work in drawable space to handle selected items from multiple attachments + // (which have no shared frame of reference other than their render positions) + LLXform* parent_xform = object->mDrawable->getXform()->getParent(); + new_position = (selectNode->mSavedPositionLocal * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition(); + old_position = (object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();//object->getRenderPosition(); + } + else + { + new_position = gAgent.getPosAgentFromGlobal( selectNode->mSavedPositionGlobal ); + old_position = object->getPositionAgent(); + } + + new_position = (new_position - center) * mRotation; // new relative rotated position + new_position += center; + + if (object->isRootEdit() && !object->isAttachment()) + { + LLVector3d new_pos_global = gAgent.getPosGlobalFromAgent(new_position); + new_pos_global = LLWorld::getInstance()->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global); + new_position = gAgent.getPosAgentFromGlobal(new_pos_global); + } + + // for individually selected child objects + if (!object->isRootEdit() && selectNode->mIndividualSelection) + { + LLViewerObject* parentp = (LLViewerObject*)object->getParent(); + if (!parentp->isSelected()) + { + if (object->isAttachment() && object->mDrawable.notNull()) + { + // find position relative to render position of parent + object->setPosition((new_position - parentp->getRenderPosition()) * ~parentp->getRenderRotation()); + rebuild(object); + } + else + { + object->setPositionParent((new_position - parentp->getPositionAgent()) * ~parentp->getRotationRegion()); + rebuild(object); + } + } + } + else if (object->isRootEdit()) + { + if (object->isAttachment() && object->mDrawable.notNull()) + { + LLXform* parent_xform = object->mDrawable->getXform()->getParent(); + object->setPosition((new_position - parent_xform->getWorldPosition()) * ~parent_xform->getWorldRotation()); + rebuild(object); + } + else + { + object->setPositionAgent(new_position); + rebuild(object); + } + } + + // for individually selected roots, we need to counter-translate all unselected children + if (object->isRootEdit() && selectNode->mIndividualSelection) + { + // only offset by parent's translation as we've already countered parent's rotation + rebuild(object); + object->resetChildrenPosition(old_position - new_position) ; + } + } + } + + // store changes to override updates + for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin(); + iter != LLSelectMgr::getInstance()->getSelection()->end(); iter++) + { + LLSelectNode* selectNode = *iter; + LLViewerObject*cur = selectNode->getObject(); + LLViewerObject *root_object = (cur == NULL) ? NULL : cur->getRootEdit(); + if( cur->permModify() && cur->permMove() && !cur->isPermanentEnforced() && + ((root_object == NULL) || !root_object->isPermanentEnforced()) && + (!cur->isAvatar() || LLSelectMgr::getInstance()->mAllowSelectAvatar)) + { + selectNode->mLastRotation = cur->getRotation(); + selectNode->mLastPositionLocal = cur->getPosition(); + } + } + + LLSelectMgr::getInstance()->updateSelectionCenter(); + + // RN: just clear focus so camera doesn't follow spurious object updates + gAgentCamera.clearFocusObject(); + dialog_refresh_all(); +} + +void LLManipRotate::renderActiveRing( F32 radius, F32 width, const LLColor4& front_color, const LLColor4& back_color) +{ + LLGLEnable cull_face(GL_CULL_FACE); + { + gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, false); + gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, true); + } + { + LLGLDepthTest gls_depth(GL_FALSE); + gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, false); + gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, true); + } +} + +void LLManipRotate::renderSnapGuides() +{ + static LLCachedControl<bool> snap_enabled(gSavedSettings, "SnapEnabled", true); + if (!snap_enabled) + { + return; + } + + LLVector3 grid_origin; + LLVector3 grid_scale; + LLQuaternion grid_rotation; + + LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale, true); + + LLVector3 constraint_axis = getConstraintAxis(); + + LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter ); + LLVector3 cam_at_axis; + if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD) + { + cam_at_axis.setVec(1.f, 0.f, 0.f); + } + else + { + cam_at_axis = center - gAgentCamera.getCameraPositionAgent(); + cam_at_axis.normVec(); + } + + LLVector3 world_snap_axis; + LLVector3 test_axis = constraint_axis; + + bool constrain_to_ref_object = false; + if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid()) + { + test_axis = test_axis * ~grid_rotation; + } + else if (LLSelectMgr::getInstance()->getGridMode() == GRID_MODE_REF_OBJECT) + { + test_axis = test_axis * ~grid_rotation; + constrain_to_ref_object = true; + } + + test_axis.abs(); + + // find closest global/reference axis to local constraint axis; + if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ]) + { + world_snap_axis = LLVector3::y_axis; + } + else if (test_axis.mV[VY] > test_axis.mV[VZ]) + { + world_snap_axis = LLVector3::z_axis; + } + else + { + world_snap_axis = LLVector3::x_axis; + } + + LLVector3 projected_snap_axis = world_snap_axis; + if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid()) + { + projected_snap_axis = projected_snap_axis * grid_rotation; + } + else if (constrain_to_ref_object) + { + projected_snap_axis = projected_snap_axis * grid_rotation; + } + + // project world snap axis onto constraint plane + projected_snap_axis -= projected_vec(projected_snap_axis, constraint_axis); + projected_snap_axis.normVec(); + + S32 num_rings = mCamEdgeOn ? 2 : 1; + for (S32 ring_num = 0; ring_num < num_rings; ring_num++) + { + LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter ); + + if (mCamEdgeOn) + { + // draw two opposing rings + if (ring_num == 0) + { + center += constraint_axis * mRadiusMeters * 0.5f; + } + else + { + center -= constraint_axis * mRadiusMeters * 0.5f; + } + } + + LLGLDepthTest gls_depth(GL_FALSE); + for (S32 pass = 0; pass < 3; pass++) + { + // render snap guide ring + gGL.pushMatrix(); + + LLQuaternion snap_guide_rot; + F32 angle_radians, x, y, z; + snap_guide_rot.shortestArc(LLVector3::z_axis, getConstraintAxis()); + snap_guide_rot.getAngleAxis(&angle_radians, &x, &y, &z); + gGL.translatef(center.mV[VX], center.mV[VY], center.mV[VZ]); + gGL.rotatef(angle_radians * RAD_TO_DEG, x, y, z); + + LLColor4 line_color = setupSnapGuideRenderPass(pass); + + gGL.color4fv(line_color.mV); + + if (mCamEdgeOn) + { + // render an arc + LLVector3 edge_normal = cam_at_axis % constraint_axis; + edge_normal.normVec(); + LLVector3 x_axis_snap = LLVector3::x_axis * snap_guide_rot; + LLVector3 y_axis_snap = LLVector3::y_axis * snap_guide_rot; + + F32 end_angle = atan2(y_axis_snap * edge_normal, x_axis_snap * edge_normal); + //F32 start_angle = angle_between((-1.f * LLVector3::x_axis) * snap_guide_rot, edge_normal); + F32 start_angle = end_angle - F_PI; + gl_arc_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, false, start_angle, end_angle); + } + else + { + gl_circle_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, false); + } + gGL.popMatrix(); + + for (S32 i = 0; i < 64; i++) + { + bool render_text = true; + F32 deg = 5.625f * (F32)i; + LLVector3 inner_point; + LLVector3 outer_point; + LLVector3 text_point; + LLQuaternion rot(deg * DEG_TO_RAD, constraint_axis); + gGL.begin(LLRender::LINES); + { + inner_point = (projected_snap_axis * mRadiusMeters * SNAP_GUIDE_INNER_RADIUS * rot) + center; + F32 tick_length = 0.f; + if (i % 16 == 0) + { + tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_1 - SNAP_GUIDE_INNER_RADIUS); + } + else if (i % 8 == 0) + { + tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_2 - SNAP_GUIDE_INNER_RADIUS); + } + else if (i % 4 == 0) + { + tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_3 - SNAP_GUIDE_INNER_RADIUS); + } + else if (i % 2 == 0) + { + tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_4 - SNAP_GUIDE_INNER_RADIUS); + } + else + { + tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_5 - SNAP_GUIDE_INNER_RADIUS); + } + + if (mCamEdgeOn) + { + // don't draw ticks that are on back side of circle + F32 dot = cam_at_axis * (projected_snap_axis * rot); + if (dot > 0.f) + { + outer_point = inner_point; + render_text = false; + } + else + { + if (ring_num == 0) + { + outer_point = inner_point + (constraint_axis * tick_length) * rot; + } + else + { + outer_point = inner_point - (constraint_axis * tick_length) * rot; + } + } + } + else + { + outer_point = inner_point + (projected_snap_axis * tick_length) * rot; + } + + text_point = outer_point + (projected_snap_axis * mRadiusMeters * 0.1f) * rot; + + gGL.vertex3fv(inner_point.mV); + gGL.vertex3fv(outer_point.mV); + } + gGL.end(); + + //RN: text rendering does own shadow pass, so only render once + if (pass == 1 && render_text && i % 16 == 0) + { + if (world_snap_axis.mV[VX]) + { + if (i == 0) + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Forward") : LLTrans::getString("Direction_East"), LLColor4::white); + } + else if (i == 16) + { + if (constraint_axis.mV[VZ] > 0.f) + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Left") : LLTrans::getString("Direction_North"), LLColor4::white); + } + else + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Right") : LLTrans::getString("Direction_South"), LLColor4::white); + } + } + else if (i == 32) + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Back") : LLTrans::getString("Direction_West"), LLColor4::white); + } + else + { + if (constraint_axis.mV[VZ] > 0.f) + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Right") : LLTrans::getString("Direction_South"), LLColor4::white); + } + else + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Left") : LLTrans::getString("Direction_North"), LLColor4::white); + } + } + } + else if (world_snap_axis.mV[VY]) + { + if (i == 0) + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Left") : LLTrans::getString("Direction_North"), LLColor4::white); + } + else if (i == 16) + { + if (constraint_axis.mV[VX] > 0.f) + { + renderTickText(text_point, LLTrans::getString("Direction_Up"), LLColor4::white); + } + else + { + renderTickText(text_point, LLTrans::getString("Direction_Down"), LLColor4::white); + } + } + else if (i == 32) + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Right") : LLTrans::getString("Direction_South"), LLColor4::white); + } + else + { + if (constraint_axis.mV[VX] > 0.f) + { + renderTickText(text_point, LLTrans::getString("Direction_Down"), LLColor4::white); + } + else + { + renderTickText(text_point, LLTrans::getString("Direction_Up"), LLColor4::white); + } + } + } + else if (world_snap_axis.mV[VZ]) + { + if (i == 0) + { + renderTickText(text_point, LLTrans::getString("Direction_Up"), LLColor4::white); + } + else if (i == 16) + { + if (constraint_axis.mV[VY] > 0.f) + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Forward") : LLTrans::getString("Direction_East"), LLColor4::white); + } + else + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Back") : LLTrans::getString("Direction_West"), LLColor4::white); + } + } + else if (i == 32) + { + renderTickText(text_point, LLTrans::getString("Direction_Down"), LLColor4::white); + } + else + { + if (constraint_axis.mV[VY] > 0.f) + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Back") : LLTrans::getString("Direction_West"), LLColor4::white); + } + else + { + renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Forward") : LLTrans::getString("Direction_East"), LLColor4::white); + } + } + } + } + gGL.color4fv(line_color.mV); + } + + // now render projected object axis + if (mInSnapRegime) + { + LLVector3 object_axis; + getObjectAxisClosestToMouse(object_axis); + + // project onto constraint plane + LLSelectNode* first_node = mObjectSelection->getFirstMoveableNode(true); + object_axis = object_axis * first_node->getObject()->getRenderRotation(); + object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis(); + object_axis.normVec(); + object_axis = object_axis * SNAP_GUIDE_INNER_RADIUS * mRadiusMeters + center; + LLVector3 line_start = center; + + gGL.begin(LLRender::LINES); + { + gGL.vertex3fv(line_start.mV); + gGL.vertex3fv(object_axis.mV); + } + gGL.end(); + + // draw snap guide arrow + gGL.begin(LLRender::TRIANGLES); + { + LLVector3 arrow_dir; + LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis(); + arrow_span.normVec(); + + arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start; + arrow_dir.normVec(); + if (ring_num == 1) + { + arrow_dir *= -1.f; + } + gGL.vertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV); + gGL.vertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV); + gGL.vertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV); + } + gGL.end(); + + { + LLGLDepthTest gls_depth(GL_TRUE); + gGL.begin(LLRender::LINES); + { + gGL.vertex3fv(line_start.mV); + gGL.vertex3fv(object_axis.mV); + } + gGL.end(); + + // draw snap guide arrow + gGL.begin(LLRender::TRIANGLES); + { + LLVector3 arrow_dir; + LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis(); + arrow_span.normVec(); + + arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start; + arrow_dir.normVec(); + if (ring_num == 1) + { + arrow_dir *= -1.f; + } + + gGL.vertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV); + gGL.vertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV); + gGL.vertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV); + } + gGL.end(); + } + } + } + } + + + // render help text + if (mObjectSelection->getSelectType() != SELECT_TYPE_HUD) + { + if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText) + { + LLVector3 selection_center_start = LLSelectMgr::getInstance()->getSavedBBoxOfSelection().getCenterAgent(); + + LLVector3 offset_dir = LLViewerCamera::getInstance()->getUpAxis(); + + F32 line_alpha = gSavedSettings.getF32("GridOpacity"); + + LLVector3 help_text_pos = selection_center_start + (mRadiusMeters * 3.f * offset_dir); + const LLFontGL* big_fontp = LLFontGL::getFontSansSerif(); + + std::string help_text = LLTrans::getString("manip_hint1"); + 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, LLFontGL::NO_SHADOW, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, false); + help_text = LLTrans::getString("manip_hint2"); + help_text_pos -= offset_dir * mRadiusMeters * 0.4f; + hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, false); + } + } +} + +// Returns true if center of sphere is visible. Also sets a bunch of member variables that are used later (e.g. mCenterToCam) +bool LLManipRotate::updateVisiblity() +{ + // Don't want to recalculate the center of the selection during a drag. + // Due to packet delays, sometimes half the objects in the selection have their + // new position and half have their old one. This creates subtle errors in the + // computed center position for that frame. Unfortunately, these errors + // accumulate. The result is objects seem to "fly apart" during rotations. + // JC - 03.26.2002 + if (!hasMouseCapture()) + { + mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() );//LLSelectMgr::getInstance()->getSelectionCenterGlobal(); + } + + bool visible = false; + + //Assume that UI scale factor is equivalent for X and Y axis + F32 ui_scale_factor = LLUI::getScaleFactor().mV[VX]; + + LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter ); + if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD) + { + mCenterToCam = LLVector3(-1.f / gAgentCamera.mHUDCurZoom, 0.f, 0.f); + mCenterToCamNorm = mCenterToCam; + mCenterToCamMag = mCenterToCamNorm.normVec(); + + mRadiusMeters = RADIUS_PIXELS / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels(); + mRadiusMeters /= gAgentCamera.mHUDCurZoom; + mRadiusMeters *= ui_scale_factor; + + mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag; + mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm; + + // x axis range is (-aspect * 0.5f, +aspect * 0.5) + // y axis range is (-0.5, 0.5) + // so use getWorldViewHeightRaw as scale factor when converting to pixel coordinates + mCenterScreen.set((S32)((0.5f - center.mV[VY]) / gAgentCamera.mHUDCurZoom * gViewerWindow->getWorldViewHeightScaled()), + (S32)((center.mV[VZ] + 0.5f) / gAgentCamera.mHUDCurZoom * gViewerWindow->getWorldViewHeightScaled())); + visible = true; + } + else + { + visible = LLViewerCamera::getInstance()->projectPosAgentToScreen(center, mCenterScreen ); + if( visible ) + { + mCenterToCam = gAgentCamera.getCameraPositionAgent() - center; + mCenterToCamNorm = mCenterToCam; + mCenterToCamMag = mCenterToCamNorm.normVec(); + LLVector3 cameraAtAxis = LLViewerCamera::getInstance()->getAtAxis(); + cameraAtAxis.normVec(); + + F32 z_dist = -1.f * (mCenterToCam * cameraAtAxis); + + // Don't drag manip if object too far away + if (gSavedSettings.getBOOL("LimitSelectDistance")) + { + F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance"); + if (dist_vec_squared(gAgent.getPositionAgent(), center) > (max_select_distance * max_select_distance)) + { + visible = false; + } + } + + if (mCenterToCamMag > 0.001f) + { + F32 fraction_of_fov = RADIUS_PIXELS / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels(); + F32 apparent_angle = fraction_of_fov * LLViewerCamera::getInstance()->getView(); // radians + mRadiusMeters = z_dist * tan(apparent_angle); + mRadiusMeters *= ui_scale_factor; + + mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag; + mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm; + } + else + { + visible = false; + } + } + } + + mCamEdgeOn = false; + F32 axis_onto_cam = mManipPart >= LL_ROT_X ? llabs( getConstraintAxis() * mCenterToCamNorm ) : 0.f; + if( axis_onto_cam < AXIS_ONTO_CAM_TOLERANCE ) + { + mCamEdgeOn = true; + } + + return visible; +} + +LLQuaternion LLManipRotate::dragUnconstrained( S32 x, S32 y ) +{ + LLVector3 cam = gAgentCamera.getCameraPositionAgent(); + LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter ); + + mMouseCur = intersectMouseWithSphere( x, y, center, mRadiusMeters); + + F32 delta_x = (F32)(mCenterScreen.mX - x); + F32 delta_y = (F32)(mCenterScreen.mY - y); + + F32 dist_from_sphere_center = sqrt(delta_x * delta_x + delta_y * delta_y); + + LLVector3 axis = mMouseDown % mMouseCur; + F32 angle = atan2(sqrtf(axis * axis), mMouseDown * mMouseCur); + axis.normVec(); + LLQuaternion sphere_rot( angle, axis ); + + if (is_approx_zero(1.f - mMouseDown * mMouseCur)) + { + return LLQuaternion::DEFAULT; + } + else if (dist_from_sphere_center < RADIUS_PIXELS) + { + return sphere_rot; + } + else + { + LLVector3 intersection; + getMousePointOnPlaneAgent( intersection, x, y, center + mCenterToProfilePlane, mCenterToCamNorm ); + + // amount dragging in sphere from center to periphery would rotate object + F32 in_sphere_angle = F_PI_BY_TWO; + F32 dist_to_tangent_point = mRadiusMeters; + if( !is_approx_zero( mCenterToProfilePlaneMag ) ) + { + dist_to_tangent_point = sqrt( mRadiusMeters * mRadiusMeters - mCenterToProfilePlaneMag * mCenterToProfilePlaneMag ); + in_sphere_angle = atan2( dist_to_tangent_point, mCenterToProfilePlaneMag ); + } + + LLVector3 profile_center_to_intersection = intersection - (center + mCenterToProfilePlane); + F32 dist_to_intersection = profile_center_to_intersection.normVec(); + F32 angle = (-1.f + dist_to_intersection / dist_to_tangent_point) * in_sphere_angle; + + LLVector3 axis; + if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD) + { + axis = LLVector3(-1.f, 0.f, 0.f) % profile_center_to_intersection; + } + else + { + axis = (cam - center) % profile_center_to_intersection; + axis.normVec(); + } + return sphere_rot * LLQuaternion( angle, axis ); + } +} + +LLVector3 LLManipRotate::getConstraintAxis() +{ + LLVector3 axis; + if( LL_ROT_ROLL == mManipPart ) + { + axis = mCenterToCamNorm; + } + else + { + S32 axis_dir = mManipPart - LL_ROT_X; + if ((axis_dir >= LL_NO_PART) && (axis_dir < LL_Z_ARROW)) + { + axis.mV[axis_dir] = 1.f; + } + else + { +#ifndef LL_RELEASE_FOR_DOWNLOAD + LL_ERRS() << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << LL_ENDL; +#else + LL_WARNS() << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << LL_ENDL; +#endif + axis.mV[0] = 1.f; + } + + LLVector3 grid_origin; + LLVector3 grid_scale; + LLQuaternion grid_rotation; + + LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale); + + LLSelectNode* first_node = mObjectSelection->getFirstMoveableNode(true); + if (first_node) + { + // *FIX: get agent local attachment grid working + // Put rotation into frame of first selected root object + axis = axis * grid_rotation; + } + } + + return axis; +} + +LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y ) +{ + LLSelectNode* first_object_node = mObjectSelection->getFirstMoveableNode(true); + LLVector3 constraint_axis = getConstraintAxis(); + LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter ); + + F32 angle = 0.f; + + // build snap axes + LLVector3 grid_origin; + LLVector3 grid_scale; + LLQuaternion grid_rotation; + + LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale); + + LLVector3 axis1; + LLVector3 axis2; + + LLVector3 test_axis = constraint_axis; + if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid()) + { + test_axis = test_axis * ~grid_rotation; + } + else if (LLSelectMgr::getInstance()->getGridMode() == GRID_MODE_REF_OBJECT) + { + test_axis = test_axis * ~grid_rotation; + } + test_axis.abs(); + + // find closest global axis to constraint axis; + if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ]) + { + axis1 = LLVector3::y_axis; + } + else if (test_axis.mV[VY] > test_axis.mV[VZ]) + { + axis1 = LLVector3::z_axis; + } + else + { + axis1 = LLVector3::x_axis; + } + + if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid()) + { + axis1 = axis1 * grid_rotation; + } + else if (LLSelectMgr::getInstance()->getGridMode() == GRID_MODE_REF_OBJECT) + { + axis1 = axis1 * grid_rotation; + } + + //project axis onto constraint plane + axis1 -= (axis1 * constraint_axis) * constraint_axis; + axis1.normVec(); + + // calculate third and final axis + axis2 = constraint_axis % axis1; + + //F32 axis_onto_cam = llabs( constraint_axis * mCenterToCamNorm ); + if( mCamEdgeOn ) + { + // We're looking at the ring edge-on. + LLVector3 snap_plane_center = (center + (constraint_axis * mRadiusMeters * 0.5f)); + LLVector3 cam_to_snap_plane; + if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD) + { + cam_to_snap_plane.setVec(1.f, 0.f, 0.f); + } + else + { + cam_to_snap_plane = snap_plane_center - gAgentCamera.getCameraPositionAgent(); + cam_to_snap_plane.normVec(); + } + + LLVector3 projected_mouse; + bool hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis); + projected_mouse -= snap_plane_center; + + if (gSavedSettings.getBOOL("SnapEnabled")) { + S32 snap_plane = 0; + + F32 dot = cam_to_snap_plane * constraint_axis; + if (llabs(dot) < 0.01f) + { + // looking at ring edge on, project onto view plane and check if mouse is past ring + getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane); + projected_mouse -= snap_plane_center; + dot = projected_mouse * constraint_axis; + if (projected_mouse * constraint_axis > 0) + { + snap_plane = 1; + } + projected_mouse -= dot * constraint_axis; + } + else if (dot > 0.f) + { + // look for mouse position outside and in front of snap circle + if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f) + { + snap_plane = 1; + } + } + else + { + // look for mouse position inside or in back of snap circle + if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit) + { + snap_plane = 1; + } + } + + if (snap_plane == 0) + { + // try other plane + snap_plane_center = (center - (constraint_axis * mRadiusMeters * 0.5f)); + if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD) + { + cam_to_snap_plane.setVec(1.f, 0.f, 0.f); + } + else + { + cam_to_snap_plane = snap_plane_center - gAgentCamera.getCameraPositionAgent(); + cam_to_snap_plane.normVec(); + } + + hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis); + projected_mouse -= snap_plane_center; + + dot = cam_to_snap_plane * constraint_axis; + if (llabs(dot) < 0.01f) + { + // looking at ring edge on, project onto view plane and check if mouse is past ring + getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane); + projected_mouse -= snap_plane_center; + dot = projected_mouse * constraint_axis; + if (projected_mouse * constraint_axis < 0) + { + snap_plane = 2; + } + projected_mouse -= dot * constraint_axis; + } + else if (dot < 0.f) + { + // look for mouse position outside and in front of snap circle + if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f) + { + snap_plane = 2; + } + } + else + { + // look for mouse position inside or in back of snap circle + if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit) + { + snap_plane = 2; + } + } + } + + if (snap_plane > 0) + { + LLVector3 cam_at_axis; + if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD) + { + cam_at_axis.setVec(1.f, 0.f, 0.f); + } + else + { + cam_at_axis = snap_plane_center - gAgentCamera.getCameraPositionAgent(); + cam_at_axis.normVec(); + } + + // first, project mouse onto screen plane at point tangent to rotation radius. + getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_at_axis); + // project that point onto rotation plane + projected_mouse -= snap_plane_center; + projected_mouse -= projected_vec(projected_mouse, constraint_axis); + + F32 mouse_lateral_dist = llmin(SNAP_GUIDE_INNER_RADIUS * mRadiusMeters, projected_mouse.magVec()); + F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters; + if (llabs(mouse_lateral_dist) > 0.01f) + { + mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) - + (mouse_lateral_dist * mouse_lateral_dist)); + } + LLVector3 projected_camera_at = cam_at_axis - projected_vec(cam_at_axis, constraint_axis); + projected_mouse -= mouse_depth * projected_camera_at; + + if (!mInSnapRegime) + { + mSmoothRotate = true; + } + mInSnapRegime = true; + // 0 to 360 deg + F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f); + + F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT); + + LLVector3 object_axis; + getObjectAxisClosestToMouse(object_axis); + if (first_object_node) + { + object_axis = object_axis * first_object_node->mSavedRotation; + } + + // project onto constraint plane + object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis(); + object_axis.normVec(); + + if (relative_mouse_angle < SNAP_ANGLE_DETENTE) + { + F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f)); + angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2); + } + else + { + angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2); + } + return LLQuaternion( -angle, constraint_axis ); + } + else + { + if (mInSnapRegime) + { + mSmoothRotate = true; + } + mInSnapRegime = false; + } + } + else { + if (mInSnapRegime) + { + mSmoothRotate = true; + } + mInSnapRegime = false; + } + + if (!mInSnapRegime) + { + LLVector3 up_from_axis = mCenterToCamNorm % constraint_axis; + up_from_axis.normVec(); + LLVector3 cur_intersection; + getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam); + cur_intersection -= center; + mMouseCur = projected_vec(cur_intersection, up_from_axis); + F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters; + F32 mouse_dist_sqrd = mMouseCur.magVecSquared(); + if (mouse_dist_sqrd > 0.0001f) + { + mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) - + mouse_dist_sqrd); + } + LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, constraint_axis); + mMouseCur += mouse_depth * projected_center_to_cam; + + F32 dist = (cur_intersection * up_from_axis) - (mMouseDown * up_from_axis); + angle = dist / (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * -F_PI_BY_TWO; + } + } + else + { + LLVector3 projected_mouse; + getMousePointOnPlaneAgent(projected_mouse, x, y, center, constraint_axis); + projected_mouse -= center; + mMouseCur = projected_mouse; + mMouseCur.normVec(); + + if (!first_object_node) + { + return LLQuaternion::DEFAULT; + } + + if (gSavedSettings.getBOOL("SnapEnabled") && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) + { + if (!mInSnapRegime) + { + mSmoothRotate = true; + } + mInSnapRegime = true; + // 0 to 360 deg + F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f); + + F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT); + + LLVector3 object_axis; + getObjectAxisClosestToMouse(object_axis); + object_axis = object_axis * first_object_node->mSavedRotation; + + // project onto constraint plane + object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis(); + object_axis.normVec(); + + if (relative_mouse_angle < SNAP_ANGLE_DETENTE) + { + F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f)); + angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2); + } + else + { + angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2); + } + return LLQuaternion( -angle, constraint_axis ); + } + else + { + if (mInSnapRegime) + { + mSmoothRotate = true; + } + mInSnapRegime = false; + } + + LLVector3 cross_product = mMouseDown % mMouseCur; + angle = atan2(sqrtf(cross_product * cross_product), mMouseCur * mMouseDown); + F32 dir = cross_product * constraint_axis; // cross product + if( dir < 0.f ) + { + angle *= -1.f; + } + } + + F32 rot_step = gSavedSettings.getF32("RotationStep"); + F32 step_size = DEG_TO_RAD * rot_step; + angle -= fmod(angle, step_size); + + return LLQuaternion( angle, constraint_axis ); +} + + + +LLVector3 LLManipRotate::intersectMouseWithSphere( S32 x, S32 y, const LLVector3& sphere_center, F32 sphere_radius) +{ + LLVector3 ray_pt; + LLVector3 ray_dir; + mouseToRay( x, y, &ray_pt, &ray_dir); + return intersectRayWithSphere( ray_pt, ray_dir, sphere_center, sphere_radius ); +} + +LLVector3 LLManipRotate::intersectRayWithSphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius) +{ + LLVector3 ray_pt_to_center = sphere_center - ray_pt; + F32 center_distance = ray_pt_to_center.normVec(); + + F32 dot = ray_dir * ray_pt_to_center; + + if (dot == 0.f) + { + return LLVector3::zero; + } + + // point which ray hits plane centered on sphere origin, facing ray origin + LLVector3 intersection_sphere_plane = ray_pt + (ray_dir * center_distance / dot); + // vector from sphere origin to the point, normalized to sphere radius + LLVector3 sphere_center_to_intersection = (intersection_sphere_plane - sphere_center) / sphere_radius; + + F32 dist_squared = sphere_center_to_intersection.magVecSquared(); + LLVector3 result; + + if (dist_squared > 1.f) + { + result = sphere_center_to_intersection; + result.normVec(); + } + else + { + result = sphere_center_to_intersection - ray_dir * sqrt(1.f - dist_squared); + } + + return result; +} + +// Utility function. Should probably be moved to another class. +// x,y - mouse position in scaled window coordinates (NOT GL viewport coordinates) +//static +void LLManipRotate::mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_dir ) +{ + if (LLSelectMgr::getInstance()->getSelection()->getSelectType() == SELECT_TYPE_HUD) + { + F32 mouse_x = (((F32)x / gViewerWindow->getWorldViewRectScaled().getWidth()) - 0.5f) / gAgentCamera.mHUDCurZoom; + F32 mouse_y = ((((F32)y) / gViewerWindow->getWorldViewRectScaled().getHeight()) - 0.5f) / gAgentCamera.mHUDCurZoom; + + *ray_pt = LLVector3(-1.f, -mouse_x, mouse_y); + *ray_dir = LLVector3(1.f, 0.f, 0.f); + } + else + { + *ray_pt = gAgentCamera.getCameraPositionAgent(); + *ray_dir = gViewerWindow->mouseDirectionGlobal(x, y); + } +} + +void LLManipRotate::highlightManipulators( S32 x, S32 y ) +{ + mHighlightedPart = LL_NO_PART; + + //LLBBox bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); + LLViewerObject *first_object = mObjectSelection->getFirstMoveableObject(true); + + if (!first_object) + { + return; + } + + LLVector3 rotation_center = gAgent.getPosAgentFromGlobal(mRotationCenter); + LLVector3 mouse_dir_x; + LLVector3 mouse_dir_y; + LLVector3 mouse_dir_z; + LLVector3 intersection_roll; + + LLVector3 grid_origin; + LLVector3 grid_scale; + LLQuaternion grid_rotation; + + LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale); + + LLVector3 rot_x_axis = LLVector3::x_axis * grid_rotation; + LLVector3 rot_y_axis = LLVector3::y_axis * grid_rotation; + LLVector3 rot_z_axis = LLVector3::z_axis * grid_rotation; + + F32 proj_rot_x_axis = llabs(rot_x_axis * mCenterToCamNorm); + F32 proj_rot_y_axis = llabs(rot_y_axis * mCenterToCamNorm); + F32 proj_rot_z_axis = llabs(rot_z_axis * mCenterToCamNorm); + + F32 min_select_distance = 0.f; + F32 cur_select_distance = 0.f; + + // test x + getMousePointOnPlaneAgent(mouse_dir_x, x, y, rotation_center, rot_x_axis); + mouse_dir_x -= rotation_center; + // push intersection point out when working at obtuse angle to make ring easier to hit + mouse_dir_x *= 1.f + (1.f - llabs(rot_x_axis * mCenterToCamNorm)) * 0.1f; + + // test y + getMousePointOnPlaneAgent(mouse_dir_y, x, y, rotation_center, rot_y_axis); + mouse_dir_y -= rotation_center; + mouse_dir_y *= 1.f + (1.f - llabs(rot_y_axis * mCenterToCamNorm)) * 0.1f; + + // test z + getMousePointOnPlaneAgent(mouse_dir_z, x, y, rotation_center, rot_z_axis); + mouse_dir_z -= rotation_center; + mouse_dir_z *= 1.f + (1.f - llabs(rot_z_axis * mCenterToCamNorm)) * 0.1f; + + // test roll + getMousePointOnPlaneAgent(intersection_roll, x, y, rotation_center, mCenterToCamNorm); + intersection_roll -= rotation_center; + + F32 dist_x = mouse_dir_x.normVec(); + F32 dist_y = mouse_dir_y.normVec(); + F32 dist_z = mouse_dir_z.normVec(); + + F32 distance_threshold = (MAX_MANIP_SELECT_DISTANCE * mRadiusMeters) / gViewerWindow->getWorldViewHeightScaled(); + + if (llabs(dist_x - mRadiusMeters) * llmax(0.05f, proj_rot_x_axis) < distance_threshold) + { + // selected x + cur_select_distance = dist_x * mouse_dir_x * mCenterToCamNorm; + if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance)) + { + min_select_distance = cur_select_distance; + mHighlightedPart = LL_ROT_X; + } + } + if (llabs(dist_y - mRadiusMeters) * llmax(0.05f, proj_rot_y_axis) < distance_threshold) + { + // selected y + cur_select_distance = dist_y * mouse_dir_y * mCenterToCamNorm; + if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance)) + { + min_select_distance = cur_select_distance; + mHighlightedPart = LL_ROT_Y; + } + } + if (llabs(dist_z - mRadiusMeters) * llmax(0.05f, proj_rot_z_axis) < distance_threshold) + { + // selected z + cur_select_distance = dist_z * mouse_dir_z * mCenterToCamNorm; + if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance)) + { + min_select_distance = cur_select_distance; + mHighlightedPart = LL_ROT_Z; + } + } + + // test for edge-on intersections + if (proj_rot_x_axis < 0.05f) + { + if ((proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_x_axis) < distance_threshold) && dist_y < mRadiusMeters) || + (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_x_axis) < distance_threshold) && dist_z < mRadiusMeters)) + { + mHighlightedPart = LL_ROT_X; + } + } + + if (proj_rot_y_axis < 0.05f) + { + if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_y_axis) < distance_threshold) && dist_x < mRadiusMeters) || + (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_y_axis) < distance_threshold) && dist_z < mRadiusMeters)) + { + mHighlightedPart = LL_ROT_Y; + } + } + + if (proj_rot_z_axis < 0.05f) + { + if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_z_axis) < distance_threshold) && dist_x < mRadiusMeters) || + (proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_z_axis) < distance_threshold) && dist_y < mRadiusMeters)) + { + mHighlightedPart = LL_ROT_Z; + } + } + + // test for roll + if (mHighlightedPart == LL_NO_PART) + { + F32 roll_distance = intersection_roll.magVec(); + F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS; + + // use larger distance threshold for roll as it is checked only if something else wasn't highlighted + if (llabs(roll_distance - (mRadiusMeters + (width_meters * 2.f))) < distance_threshold * 2.f) + { + mHighlightedPart = LL_ROT_ROLL; + } + else if (roll_distance < mRadiusMeters) + { + mHighlightedPart = LL_ROT_GENERAL; + } + } +} + +S32 LLManipRotate::getObjectAxisClosestToMouse(LLVector3& object_axis) +{ + LLSelectNode* first_object_node = mObjectSelection->getFirstMoveableNode(true); + + if (!first_object_node) + { + object_axis.clearVec(); + return -1; + } + + LLQuaternion obj_rotation = first_object_node->mSavedRotation; + LLVector3 mouse_down_object = mMouseDown * ~obj_rotation; + LLVector3 mouse_down_abs = mouse_down_object; + mouse_down_abs.abs(); + + S32 axis_index = 0; + if (mouse_down_abs.mV[VX] > mouse_down_abs.mV[VY] && mouse_down_abs.mV[VX] > mouse_down_abs.mV[VZ]) + { + if (mouse_down_object.mV[VX] > 0.f) + { + object_axis = LLVector3::x_axis; + } + else + { + object_axis = LLVector3::x_axis_neg; + } + axis_index = VX; + } + else if (mouse_down_abs.mV[VY] > mouse_down_abs.mV[VZ]) + { + if (mouse_down_object.mV[VY] > 0.f) + { + object_axis = LLVector3::y_axis; + } + else + { + object_axis = LLVector3::y_axis_neg; + } + axis_index = VY; + } + else + { + if (mouse_down_object.mV[VZ] > 0.f) + { + object_axis = LLVector3::z_axis; + } + else + { + object_axis = LLVector3::z_axis_neg; + } + axis_index = VZ; + } + + return axis_index; +} + +//virtual +bool LLManipRotate::canAffectSelection() +{ + bool can_rotate = mObjectSelection->getObjectCount() != 0; + if (can_rotate) + { + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* objectp) + { + LLViewerObject *root_object = (objectp == NULL) ? NULL : objectp->getRootEdit(); + return objectp->permMove() && !objectp->isPermanentEnforced() && + ((root_object == NULL) || !root_object->isPermanentEnforced()) && + (objectp->permModify() || !gSavedSettings.getBOOL("EditLinkedParts")); + } + } func; + can_rotate = mObjectSelection->applyToObjects(&func); + } + return can_rotate; +} + |