llmaniprotate.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "llmaniprotate.h"
00035 
00036 // library includes
00037 #include "llmath.h"
00038 #include "llgl.h"
00039 #include "llglimmediate.h"
00040 #include "v4color.h"
00041 #include "llprimitive.h"
00042 #include "llview.h"
00043 #include "llfontgl.h"
00044 
00045 // viewer includes
00046 #include "llagent.h"
00047 #include "llbox.h"
00048 #include "llbutton.h"
00049 #include "llviewercontrol.h"
00050 #include "llcriticaldamp.h"
00051 #include "llhoverview.h"
00052 #include "llfloatertools.h"
00053 #include "llselectmgr.h"
00054 #include "llstatusbar.h"
00055 #include "llui.h"
00056 #include "llvoavatar.h"
00057 #include "llviewercamera.h"
00058 #include "llviewerobject.h"
00059 #include "llviewerobject.h"
00060 #include "llviewerwindow.h"
00061 #include "llworld.h"
00062 #include "pipeline.h"
00063 #include "lldrawable.h"
00064 #include "llglheaders.h"
00065 
00066 const F32 RADIUS_PIXELS = 100.f;                // size in screen space
00067 const F32 SQ_RADIUS = RADIUS_PIXELS * RADIUS_PIXELS;
00068 const F32 WIDTH_PIXELS = 8;
00069 const S32 CIRCLE_STEPS = 100;
00070 const F32 DELTA = F_TWO_PI / CIRCLE_STEPS;
00071 const F32 SIN_DELTA = sin( DELTA );
00072 const F32 COS_DELTA = cos( DELTA );
00073 const F32 MAX_MANIP_SELECT_DISTANCE = 100.f;
00074 const F32 SNAP_ANGLE_INCREMENT = 5.625f;
00075 const F32 SNAP_ANGLE_DETENTE = SNAP_ANGLE_INCREMENT;
00076 const F32 SNAP_GUIDE_RADIUS_1 = 2.8f;
00077 const F32 SNAP_GUIDE_RADIUS_2 = 2.4f;
00078 const F32 SNAP_GUIDE_RADIUS_3 = 2.2f;
00079 const F32 SNAP_GUIDE_RADIUS_4 = 2.1f;
00080 const F32 SNAP_GUIDE_RADIUS_5 = 2.05f;
00081 const F32 SNAP_GUIDE_INNER_RADIUS = 2.f;
00082 const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 80.f * DEG_TO_RAD );
00083 const F32 SELECTED_MANIPULATOR_SCALE = 1.05f;
00084 const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
00085 
00086 extern void handle_reset_rotation(void*);  // in LLViewerWindow
00087 
00088 LLManipRotate::LLManipRotate( LLToolComposite* composite )
00089 :       LLManip( "Rotate", composite ),
00090         mRotationCenter(),
00091         mCenterScreen(),
00092         mRotation(),
00093         mMouseDown(),
00094         mMouseCur(),
00095         mRadiusMeters(0.f),
00096         mCenterToCam(),
00097         mCenterToCamNorm(),
00098         mCenterToCamMag(0.f),
00099         mCenterToProfilePlane(),
00100         mCenterToProfilePlaneMag(0.f),
00101         mManipPart( LL_NO_PART ),
00102         mSendUpdateOnMouseUp( FALSE ),
00103         mSmoothRotate( FALSE ),
00104         mCamEdgeOn(FALSE),
00105         mManipulatorScales(1.f, 1.f, 1.f, 1.f)
00106 { }
00107 
00108 void LLManipRotate::handleSelect()
00109 {
00110         // *FIX: put this in mouseDown?
00111         LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
00112         gFloaterTools->setStatusText("rotate");
00113         LLManip::handleSelect();
00114 }
00115 
00116 void LLManipRotate::handleDeselect()
00117 {
00118         mHighlightedPart = LL_NO_PART;
00119         mManipPart = LL_NO_PART;
00120         LLManip::handleDeselect();
00121 }
00122 
00123 void LLManipRotate::render()
00124 {
00125         LLGLSUIDefault gls_ui;
00126         LLGLSNoTexture gls_no_texture;
00127         LLGLDepthTest gls_depth(GL_TRUE);
00128         LLGLEnable gl_blend(GL_BLEND);
00129         LLGLEnable gls_alpha_test(GL_ALPHA_TEST);
00130         
00131         // You can rotate if you can move
00132         LLViewerObject* first_object = mObjectSelection->getFirstMoveableObject(TRUE);
00133         if( !first_object )
00134         {
00135                 return;
00136         }
00137 
00138         if( !updateVisiblity() )
00139         {
00140                 return;
00141         }
00142 
00143         glMatrixMode(GL_MODELVIEW);
00144         glPushMatrix();
00145         if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
00146         {
00147                 F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
00148                 glScalef(zoom, zoom, zoom);
00149         }
00150 
00151 
00152         LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
00153 
00154         LLColor4 highlight_outside( 1.f, 1.f, 0.f, 1.f );
00155         LLColor4 highlight_inside( 0.7f, 0.7f, 0.f, 0.5f );
00156         F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
00157 
00158         glPushMatrix();
00159         {
00160                 // are we in the middle of a constrained drag?
00161                 if (mManipPart >= LL_ROT_X && mManipPart <= LL_ROT_Z)
00162                 {
00163                         renderSnapGuides();
00164                 }
00165                 else
00166                 {
00167                         LLGLEnable cull_face(GL_CULL_FACE);
00168                         LLGLDepthTest gls_depth(GL_FALSE);
00169                         glPushMatrix();
00170                         {
00171                                 // Draw "sphere" (intersection of sphere with tangent cone that has apex at camera)
00172                                 glTranslatef( mCenterToProfilePlane.mV[VX], mCenterToProfilePlane.mV[VY], mCenterToProfilePlane.mV[VZ] );
00173                                 glTranslatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
00174 
00175                                 // Inverse change of basis vectors
00176                                 LLVector3 forward = mCenterToCamNorm;
00177                                 LLVector3 left = gAgent.getUpAxis() % forward;
00178                                 left.normVec();
00179                                 LLVector3 up = forward % left;
00180 
00181                                 LLVector4 a(-forward);
00182                                 a.mV[3] = 0;
00183                                 LLVector4 b(up);
00184                                 b.mV[3] = 0;
00185                                 LLVector4 c(left);
00186                                 c.mV[3] = 0;
00187                                 LLMatrix4 mat;
00188                                 mat.initRows(a, b, c, LLVector4(0.f, 0.f, 0.f, 1.f));
00189 
00190                                 glMultMatrixf( &mat.mMatrix[0][0] );
00191 
00192                                 glRotatef( -90, 0.f, 1.f, 0.f);
00193                                 LLColor4 color;
00194                                 if (mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
00195                                 {
00196                                         color.setVec(0.8f, 0.8f, 0.8f, 0.8f);
00197                                         glScalef(mManipulatorScales.mV[VW], mManipulatorScales.mV[VW], mManipulatorScales.mV[VW]);
00198                                 }
00199                                 else
00200                                 {
00201                                         color.setVec( 0.7f, 0.7f, 0.7f, 0.6f );
00202                                 }
00203                                 gl_washer_2d(mRadiusMeters + width_meters, mRadiusMeters, CIRCLE_STEPS, color, color);
00204 
00205 
00206                                 if (mManipPart == LL_NO_PART)
00207                                 {
00208                                         gGL.color4f( 0.7f, 0.7f, 0.7f, 0.3f );
00209                                         gl_circle_2d( 0, 0,  mRadiusMeters, CIRCLE_STEPS, TRUE );
00210                                 }
00211                                 
00212                                 GLdouble plane_eqn[] = { 0, 0, 1, 0 };
00213                                 glClipPlane( GL_CLIP_PLANE0, plane_eqn );
00214                         }
00215                         glPopMatrix();
00216                 }
00217 
00218                 glTranslatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
00219 
00220                 LLQuaternion rot;
00221                 F32 angle_radians, x, y, z;
00222 
00223                 LLVector3 grid_origin;
00224                 LLVector3 grid_scale;
00225                 LLQuaternion grid_rotation;
00226 
00227                 LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
00228 
00229                 grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
00230                 glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
00231 
00232 
00233                 if (mManipPart == LL_ROT_Z)
00234                 {
00235                         mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00236                         glPushMatrix();
00237                         {
00238                                 // selected part
00239                                 glScalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
00240                                 renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f) , LLColor4( 0.f, 0.f, 1.f, 0.3f ));
00241                         }
00242                         glPopMatrix();
00243                 }
00244                 else if (mManipPart == LL_ROT_Y)
00245                 {
00246                         mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00247                         glPushMatrix();
00248                         {
00249                                 glRotatef( 90.f, 1.f, 0.f, 0.f );
00250                                 glScalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
00251                                 renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f), LLColor4( 0.f, 1.f, 0.f, 0.3f));
00252                         }
00253                         glPopMatrix();
00254                 }
00255                 else if (mManipPart == LL_ROT_X)
00256                 {
00257                         mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00258                         glPushMatrix();
00259                         {
00260                                 glRotatef( 90.f, 0.f, 1.f, 0.f );
00261                                 glScalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
00262                                 renderActiveRing( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f), LLColor4( 1.f, 0.f, 0.f, 0.3f));
00263                         }
00264                         glPopMatrix();
00265                 }
00266                 else if (mManipPart == LL_ROT_ROLL)
00267                 {
00268                         mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00269                 }
00270                 else if (mManipPart == LL_NO_PART)
00271                 {
00272                         if (mHighlightedPart == LL_NO_PART)
00273                         {
00274                                 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00275                         }
00276 
00277                         LLGLEnable cull_face(GL_CULL_FACE);
00278                         LLGLEnable clip_plane0(GL_CLIP_PLANE0);
00279                         LLGLDepthTest gls_depth(GL_FALSE);
00280 
00281                         // First pass: centers. Second pass: sides.
00282                         for( S32 i=0; i<2; i++ )
00283                         {
00284                                 glPushMatrix();
00285                                 {
00286                                         if (mHighlightedPart == LL_ROT_Z)
00287                                         {
00288                                                 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00289                                                 glScalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
00290                                                 // hovering over part
00291                                                 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);
00292                                         }
00293                                         else
00294                                         {
00295                                                 // default
00296                                                 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);
00297                                         }
00298                                 }
00299                                 glPopMatrix();
00300 
00301                                 glPushMatrix();
00302                                 {
00303                                         glRotatef( 90.f, 1.f, 0.f, 0.f );
00304                                         if (mHighlightedPart == LL_ROT_Y)
00305                                         {
00306                                                 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00307                                                 glScalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
00308                                                 // hovering over part
00309                                                 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);
00310                                         }
00311                                         else
00312                                         {
00313                                                 // default
00314                                                 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);
00315                                         }                                               
00316                                 }
00317                                 glPopMatrix();
00318 
00319                                 glPushMatrix();
00320                                 {
00321                                         glRotatef( 90.f, 0.f, 1.f, 0.f );
00322                                         if (mHighlightedPart == LL_ROT_X)
00323                                         {
00324                                                 mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00325                                                 glScalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
00326         
00327                                                 // hovering over part
00328                                                 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);
00329                                         }
00330                                         else
00331                                         {
00332                                                 // default
00333                                                 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);
00334                                         }
00335                                 }
00336                                 glPopMatrix();
00337 
00338                                 if (mHighlightedPart == LL_ROT_ROLL)
00339                                 {
00340                                         mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00341                                 }
00342                         }
00343                 }
00344         }
00345         glPopMatrix();
00346         glPopMatrix();
00347 
00348         LLVector3 euler_angles;
00349         LLQuaternion object_rot = first_object->getRotationEdit();
00350         object_rot.getEulerAngles(&(euler_angles.mV[VX]), &(euler_angles.mV[VY]), &(euler_angles.mV[VZ]));
00351         euler_angles *= RAD_TO_DEG;
00352         euler_angles.mV[VX] = llround(fmodf(euler_angles.mV[VX] + 360.f, 360.f), 0.05f);
00353         euler_angles.mV[VY] = llround(fmodf(euler_angles.mV[VY] + 360.f, 360.f), 0.05f);
00354         euler_angles.mV[VZ] = llround(fmodf(euler_angles.mV[VZ] + 360.f, 360.f), 0.05f);
00355 
00356         renderXYZ(euler_angles);
00357 }
00358 
00359 BOOL LLManipRotate::handleMouseDown(S32 x, S32 y, MASK mask)
00360 {
00361         BOOL    handled = FALSE;
00362 
00363         LLViewerObject* first_object = mObjectSelection->getFirstMoveableObject(TRUE);
00364         if( first_object )
00365         {
00366                 LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
00367                 if( hit_obj && mHighlightedPart != LL_NO_PART )
00368                 {
00369                         handled = handleMouseDownOnPart( x, y, mask );
00370                 }
00371         }
00372 
00373         return handled;
00374 }
00375 
00376 // Assumes that one of the parts of the manipulator was hit.
00377 BOOL LLManipRotate::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
00378 {
00379         BOOL can_rotate = canAffectSelection();
00380         if (!can_rotate)
00381         {
00382                 return FALSE;
00383         }
00384 
00385         highlightManipulators(x, y);
00386         S32 hit_part = mHighlightedPart;
00387         // we just started a drag, so save initial object positions
00388         LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_ROTATE);
00389 
00390         // save selection center
00391         mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() ); //LLSelectMgr::getInstance()->getSelectionCenterGlobal();
00392 
00393         mManipPart = (EManipPart)hit_part;
00394         LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
00395 
00396         if( mManipPart == LL_ROT_GENERAL)
00397         {
00398                 mMouseDown = intersectMouseWithSphere( x, y, center, mRadiusMeters);
00399         }
00400         else
00401         {
00402                 // Project onto the plane of the ring
00403                 LLVector3 axis = getConstraintAxis();
00404 
00405                 F32 axis_onto_cam = llabs( axis * mCenterToCamNorm );
00406                 const F32 AXIS_ONTO_CAM_TOL = cos( 85.f * DEG_TO_RAD );
00407                 if( axis_onto_cam < AXIS_ONTO_CAM_TOL )
00408                 {
00409                         LLVector3 up_from_axis = mCenterToCamNorm % axis;
00410                         up_from_axis.normVec();
00411                         LLVector3 cur_intersection;
00412                         getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
00413                         cur_intersection -= center;
00414                         mMouseDown = projected_vec(cur_intersection, up_from_axis);
00415                         F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
00416                         F32 mouse_dist_sqrd = mMouseDown.magVecSquared();
00417                         if (mouse_dist_sqrd > 0.0001f)
00418                         {
00419                                 mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) - 
00420                                                                         mouse_dist_sqrd);
00421                         }
00422                         LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, axis);
00423                         mMouseDown += mouse_depth * projected_center_to_cam;
00424 
00425                 }
00426                 else
00427                 {
00428                         mMouseDown = findNearestPointOnRing( x, y, center, axis ) - center;
00429                         mMouseDown.normVec();
00430                 }
00431         }
00432 
00433         mMouseCur = mMouseDown;
00434 
00435         // Route future Mouse messages here preemptively.  (Release on mouse up.)
00436         setMouseCapture( TRUE );
00437         LLSelectMgr::getInstance()->enableSilhouette(FALSE);
00438         return TRUE;
00439 }
00440 
00441 
00442 LLVector3 LLManipRotate::findNearestPointOnRing( S32 x, S32 y, const LLVector3& center, const LLVector3& axis )
00443 {
00444         // Project the delta onto the ring and rescale it by the radius so that it's _on_ the ring.
00445         LLVector3 proj_onto_ring;
00446         getMousePointOnPlaneAgent(proj_onto_ring, x, y, center, axis);
00447         proj_onto_ring -= center;
00448         proj_onto_ring.normVec();
00449 
00450         return center + proj_onto_ring * mRadiusMeters;
00451 }
00452 
00453 BOOL LLManipRotate::handleMouseUp(S32 x, S32 y, MASK mask)
00454 {
00455         // first, perform normal processing in case this was a quick-click
00456         handleHover(x, y, mask);
00457 
00458         if( hasMouseCapture() )
00459         {
00460                 mManipPart = LL_NO_PART;
00461 
00462                 // Might have missed last update due to timing.
00463                 LLSelectMgr::getInstance()->sendMultipleUpdate( UPD_ROTATION | UPD_POSITION );
00464                 LLSelectMgr::getInstance()->enableSilhouette(TRUE);
00465                 //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
00466 
00467                 LLSelectMgr::getInstance()->updateSelectionCenter();
00468                 LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
00469         }
00470 
00471         return LLManip::handleMouseUp(x, y, mask);
00472 }
00473 
00474 
00475 BOOL LLManipRotate::handleHover(S32 x, S32 y, MASK mask)
00476 {
00477         if( hasMouseCapture() )
00478         {
00479                 if( mObjectSelection->isEmpty() )
00480                 {
00481                         // Somehow the object got deselected while we were dragging it.
00482                         setMouseCapture( FALSE );
00483                 }
00484                 else
00485                 {
00486                         drag(x, y);
00487                 }
00488 
00489                 lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipRotate (active)" << llendl;             
00490         }
00491         else
00492         {
00493                 highlightManipulators(x, y);
00494                 lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipRotate (inactive)" << llendl;           
00495         }
00496 
00497         gViewerWindow->setCursor(UI_CURSOR_TOOLROTATE);
00498         return TRUE;
00499 }
00500 
00501 
00502 LLVector3 LLManipRotate::projectToSphere( F32 x, F32 y, BOOL* on_sphere ) 
00503 {
00504         F32 z = 0.f;
00505         F32 dist_squared = x*x + y*y;
00506 
00507         *on_sphere = dist_squared <= SQ_RADIUS;
00508     if( *on_sphere )
00509         {    
00510         z = sqrt(SQ_RADIUS - dist_squared);
00511     }
00512         return LLVector3( x, y, z );
00513 }
00514 
00515 // Freeform rotation
00516 void LLManipRotate::drag( S32 x, S32 y )
00517 {
00518         if( !updateVisiblity() )
00519         {
00520                 return;
00521         }
00522 
00523         if( mManipPart == LL_ROT_GENERAL )
00524         {
00525                 mRotation = dragUnconstrained(x, y);
00526         }
00527         else
00528         {
00529                 mRotation = dragConstrained(x, y);
00530         }
00531 
00532         BOOL damped = mSmoothRotate;
00533         mSmoothRotate = FALSE;
00534 
00535         for (LLObjectSelection::iterator iter = mObjectSelection->begin();
00536                  iter != mObjectSelection->end(); iter++)
00537         {
00538                 LLSelectNode* selectNode = *iter;
00539                 LLViewerObject* object = selectNode->getObject();
00540 
00541                 // have permission to move and object is root of selection or individually selected
00542                 if (object->permMove() && (object->isRootEdit() || selectNode->mIndividualSelection))
00543                 {
00544                         if (!object->isRootEdit())
00545                         {
00546                                 // child objects should not update if parent is selected
00547                                 LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
00548                                 if (editable_root->isSelected())
00549                                 {
00550                                         // we will be moved properly by our parent, so skip
00551                                         continue;
00552                                 }
00553                         }
00554 
00555                         LLQuaternion new_rot = selectNode->mSavedRotation * mRotation;
00556                         std::vector<LLVector3> child_positions;
00557                         std::vector<LLQuaternion> child_rotations;
00558                         if (object->isRootEdit() && selectNode->mIndividualSelection)
00559                         {
00560                                 for (U32 i = 0; i < object->mChildList.size(); i++)
00561                                 {
00562                                         LLViewerObject* childp = object->mChildList[i];
00563                                         child_positions.push_back(childp->getPositionEdit());
00564                                         child_rotations.push_back(childp->getRotationEdit());
00565                                 }
00566                         }
00567 
00568                         if (object->getParent() && object->mDrawable.notNull())
00569                         {
00570                                 LLQuaternion invParentRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
00571                                 invParentRotation.transQuat();
00572 
00573                                 object->setRotation(new_rot * invParentRotation, damped);
00574                                 rebuild(object);
00575                         }
00576                         else
00577                         {
00578                                 object->setRotation(new_rot, damped);
00579                                 rebuild(object);
00580                         }
00581 
00582                         // for individually selected roots, we need to counterrotate all the children
00583                         if (object->isRootEdit() && selectNode->mIndividualSelection)
00584                         {
00585                                 //RN: must do non-damped updates on these objects so relative rotation appears constant
00586                                 // instead of having two competing slerps making the child objects appear to "wobble"
00587                                 for (U32 i = 0; i < object->mChildList.size(); i++)
00588                                 {
00589                                         LLViewerObject* childp = object->mChildList[i];
00590                                         LLVector3 child_offset = ((child_positions[i] - object->getPositionEdit()) * ~object->getRotationEdit()) - childp->getPosition();
00591                                         if (!childp->isSelected() && childp->mDrawable.notNull())
00592                                         {
00593                                                 childp->setRotation(child_rotations[i] * ~object->getRotationEdit());
00594                                                 childp->setPosition((child_positions[i] - object->getPositionEdit()) * ~object->getRotationEdit());
00595                                                 rebuild(childp);
00596                                         }
00597                                 }
00598                         }
00599                 }
00600         }
00601 
00602         // update positions
00603         for (LLObjectSelection::iterator iter = mObjectSelection->begin();
00604                  iter != mObjectSelection->end(); iter++)
00605         {
00606                 LLSelectNode* selectNode = *iter;
00607                 LLViewerObject* object = selectNode->getObject();
00608 
00609                 // to avoid cumulative position changes we calculate the objects new position using its saved position
00610                 if (object && object->permMove())
00611                 {
00612                         LLVector3 center   = gAgent.getPosAgentFromGlobal( mRotationCenter );
00613 
00614                         LLVector3 old_position;
00615                         LLVector3 new_position;
00616 
00617                         if (object->isAttachment() && object->mDrawable.notNull())
00618                         { 
00619                                 // need to work in drawable space to handle selected items from multiple attachments 
00620                                 // (which have no shared frame of reference other than their render positions)
00621                                 LLXform* parent_xform = object->mDrawable->getXform()->getParent();
00622                                 new_position = (selectNode->mSavedPositionLocal * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
00623                                 old_position = (object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();//object->getRenderPosition();
00624                         }
00625                         else
00626                         {
00627                                 new_position = gAgent.getPosAgentFromGlobal( selectNode->mSavedPositionGlobal );
00628                                 old_position = object->getPositionAgent();
00629                         }
00630 
00631                         new_position = (new_position - center) * mRotation;             // new relative rotated position
00632                         new_position += center;
00633                         
00634                         if (object->isRootEdit() && !object->isAttachment())
00635                         {
00636                                 LLVector3d new_pos_global = gAgent.getPosGlobalFromAgent(new_position);
00637                                 new_pos_global = LLWorld::getInstance()->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
00638                                 new_position = gAgent.getPosAgentFromGlobal(new_pos_global);
00639                         }
00640 
00641                         // for individually selected child objects
00642                         if (!object->isRootEdit() && selectNode->mIndividualSelection)
00643                         {
00644                                 LLViewerObject* parentp = (LLViewerObject*)object->getParent();
00645                                 if (!parentp->isSelected())
00646                                 {
00647                                         if (object->isAttachment() && object->mDrawable.notNull())
00648                                         {
00649                                                 // find position relative to render position of parent
00650                                                 object->setPosition((new_position - parentp->getRenderPosition()) * ~parentp->getRenderRotation());
00651                                                 rebuild(object);
00652                                         }
00653                                         else
00654                                         {
00655                                                 object->setPositionParent((new_position - parentp->getPositionAgent()) * ~parentp->getRotationRegion());
00656                                                 rebuild(object);
00657                                         }
00658                                 }
00659                         }
00660                         else if (object->isRootEdit())
00661                         {
00662                                 if (object->isAttachment() && object->mDrawable.notNull())
00663                                 {
00664                                         LLXform* parent_xform = object->mDrawable->getXform()->getParent();
00665                                         object->setPosition((new_position - parent_xform->getWorldPosition()) * ~parent_xform->getWorldRotation());
00666                                         rebuild(object);
00667                                 }
00668                                 else
00669                                 {
00670                                         object->setPositionAgent(new_position);
00671                                         rebuild(object);
00672                                 }
00673                         }
00674 
00675                         // for individually selected roots, we need to counter-translate all unselected children
00676                         if (object->isRootEdit() && selectNode->mIndividualSelection)
00677                         {
00678                                 // only offset by parent's translation as we've already countered parent's rotation
00679                                 LLVector3 child_offset;
00680                                 if (object->isAttachment() && object->mDrawable.notNull())
00681                                 {
00682                                         LLXform* attachment_point_xform = object->mDrawable->getXform()->getParent();
00683                                         LLQuaternion parent_rotation = object->getRotation() * attachment_point_xform->getWorldRotation();
00684                                         child_offset = LLVector3(old_position - new_position) * ~parent_rotation;
00685                                 }
00686                                 else
00687                                 {
00688                                         child_offset = LLVector3(old_position - new_position) * ~object->getRenderRotation();
00689                                 }
00690 
00691                                 rebuild(object);
00692                                 for (U32 i = 0; i < object->mChildList.size(); i++)
00693                                 {
00694                                         LLViewerObject* childp = object->mChildList[i];
00695                                         if (!childp->isSelected() && childp->mDrawable.notNull())
00696                                         {
00697                                                 childp->setPosition(childp->getPosition() + child_offset);
00698                                                 rebuild(childp);
00699                                         }
00700                                 }
00701                         }
00702                 }
00703         }
00704 
00705         // store changes to override updates
00706         for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin();
00707                  iter != LLSelectMgr::getInstance()->getSelection()->end(); iter++)
00708         {
00709                 LLSelectNode* selectNode = *iter;
00710                 LLViewerObject*cur = selectNode->getObject();
00711                 if( cur->permModify() && cur->permMove() && !cur->isAvatar())
00712                 {
00713                         selectNode->mLastRotation = cur->getRotation();
00714                         selectNode->mLastPositionLocal = cur->getPosition();
00715                 }
00716         }       
00717 
00718         LLSelectMgr::getInstance()->updateSelectionCenter();
00719 
00720         // RN: just clear focus so camera doesn't follow spurious object updates
00721         gAgent.clearFocusObject();
00722         dialog_refresh_all();
00723 }
00724 
00725 void LLManipRotate::renderActiveRing( F32 radius, F32 width, const LLColor4& front_color, const LLColor4& back_color)
00726 {
00727         LLGLEnable cull_face(GL_CULL_FACE);
00728         {
00729                 gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, FALSE);
00730                 gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, TRUE);
00731         }
00732         {
00733                 LLGLDepthTest gls_depth(GL_FALSE);
00734                 gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, FALSE);
00735                 gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, TRUE);
00736         }
00737 }
00738 
00739 void LLManipRotate::renderSnapGuides()
00740 {
00741         LLVector3 grid_origin;
00742         LLVector3 grid_scale;
00743         LLQuaternion grid_rotation;
00744         LLVector3 constraint_axis = getConstraintAxis();
00745 
00746         LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
00747 
00748         if (!gSavedSettings.getBOOL("SnapEnabled"))
00749         {
00750                 return;
00751         }
00752 
00753         LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
00754         LLVector3 cam_at_axis;
00755         if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
00756         {
00757                 cam_at_axis.setVec(1.f, 0.f, 0.f);
00758         }
00759         else
00760         {
00761                 cam_at_axis = center - gAgent.getCameraPositionAgent();
00762                 cam_at_axis.normVec();
00763         }
00764 
00765         LLVector3 world_snap_axis;
00766         LLVector3 test_axis = constraint_axis;
00767 
00768         BOOL constrain_to_ref_object = FALSE;
00769         if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
00770         {
00771                 test_axis = test_axis * ~grid_rotation;
00772         }
00773         else if (LLSelectMgr::getInstance()->getGridMode() == GRID_MODE_REF_OBJECT)
00774         {
00775                 test_axis = test_axis * ~grid_rotation;
00776                 constrain_to_ref_object = TRUE;
00777         }
00778 
00779         test_axis.abs();
00780 
00781         // find closest global/reference axis to local constraint axis;
00782         if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
00783         {
00784                 world_snap_axis = LLVector3::y_axis;
00785         }
00786         else if (test_axis.mV[VY] > test_axis.mV[VZ])
00787         {
00788                 world_snap_axis = LLVector3::z_axis;
00789         }
00790         else
00791         {
00792                 world_snap_axis = LLVector3::x_axis;
00793         }
00794 
00795         LLVector3 projected_snap_axis = world_snap_axis;
00796         if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
00797         {
00798                 projected_snap_axis = projected_snap_axis * grid_rotation;
00799         }
00800         else if (constrain_to_ref_object)
00801         {
00802                 projected_snap_axis = projected_snap_axis * grid_rotation;
00803         }
00804 
00805         // project world snap axis onto constraint plane
00806         projected_snap_axis -= projected_vec(projected_snap_axis, constraint_axis);
00807         projected_snap_axis.normVec();
00808 
00809         S32 num_rings = mCamEdgeOn ? 2 : 1;
00810         for (S32 ring_num = 0; ring_num < num_rings; ring_num++)
00811         {
00812                 LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
00813 
00814                 if (mCamEdgeOn)
00815                 {
00816                         // draw two opposing rings
00817                         if (ring_num == 0)
00818                         {
00819                                 center += constraint_axis * mRadiusMeters * 0.5f;
00820                         }
00821                         else
00822                         {
00823                                 center -= constraint_axis * mRadiusMeters * 0.5f;
00824                         }
00825                 }
00826 
00827                 LLGLDepthTest gls_depth(GL_FALSE);
00828                 for (S32 pass = 0; pass < 3; pass++)
00829                 {
00830                         // render snap guide ring
00831                         glPushMatrix();
00832                         
00833                         LLQuaternion snap_guide_rot;
00834                         F32 angle_radians, x, y, z;
00835                         snap_guide_rot.shortestArc(LLVector3::z_axis, getConstraintAxis());
00836                         snap_guide_rot.getAngleAxis(&angle_radians, &x, &y, &z);
00837                         glTranslatef(center.mV[VX], center.mV[VY], center.mV[VZ]);
00838                         glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
00839 
00840                         LLColor4 line_color = setupSnapGuideRenderPass(pass);
00841 
00842                         gGL.color4fv(line_color.mV);
00843 
00844                         if (mCamEdgeOn)
00845                         {
00846                                 // render an arc
00847                                 LLVector3 edge_normal = cam_at_axis % constraint_axis;
00848                                 edge_normal.normVec();
00849                                 LLVector3 x_axis_snap = LLVector3::x_axis * snap_guide_rot;
00850                                 LLVector3 y_axis_snap = LLVector3::y_axis * snap_guide_rot;
00851 
00852                                 F32 end_angle = atan2(y_axis_snap * edge_normal, x_axis_snap * edge_normal);
00853                                 //F32 start_angle = angle_between((-1.f * LLVector3::x_axis) * snap_guide_rot, edge_normal);
00854                                 F32 start_angle = end_angle - F_PI;
00855                                 gl_arc_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, FALSE, start_angle, end_angle);
00856                         }
00857                         else
00858                         {
00859                                 gl_circle_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, FALSE);
00860                         }
00861                         glPopMatrix();
00862 
00863                         for (S32 i = 0; i < 64; i++)
00864                         {
00865                                 BOOL render_text = TRUE;
00866                                 F32 deg = 5.625f * (F32)i;
00867                                 LLVector3 inner_point;
00868                                 LLVector3 outer_point;
00869                                 LLVector3 text_point;
00870                                 LLQuaternion rot(deg * DEG_TO_RAD, constraint_axis);
00871                                 gGL.begin(LLVertexBuffer::LINES);
00872                                 {
00873                                         inner_point = (projected_snap_axis * mRadiusMeters * SNAP_GUIDE_INNER_RADIUS * rot) + center;
00874                                         F32 tick_length = 0.f;
00875                                         if (i % 16 == 0)
00876                                         {
00877                                                 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_1 - SNAP_GUIDE_INNER_RADIUS);
00878                                         }
00879                                         else if (i % 8 == 0)
00880                                         {
00881                                                 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_2 - SNAP_GUIDE_INNER_RADIUS);
00882                                         }
00883                                         else if (i % 4 == 0)
00884                                         {
00885                                                 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_3 - SNAP_GUIDE_INNER_RADIUS);
00886                                         }
00887                                         else if (i % 2 == 0)
00888                                         {
00889                                                 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_4 - SNAP_GUIDE_INNER_RADIUS);
00890                                         }
00891                                         else
00892                                         {
00893                                                 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_5 - SNAP_GUIDE_INNER_RADIUS);
00894                                         }
00895                                         
00896                                         if (mCamEdgeOn)
00897                                         {
00898                                                 // don't draw ticks that are on back side of circle
00899                                                 F32 dot = cam_at_axis * (projected_snap_axis * rot);
00900                                                 if (dot > 0.f)
00901                                                 {
00902                                                         outer_point = inner_point;
00903                                                         render_text = FALSE;
00904                                                 }
00905                                                 else
00906                                                 {
00907                                                         if (ring_num == 0)
00908                                                         {
00909                                                                 outer_point = inner_point + (constraint_axis * tick_length) * rot;
00910                                                         }
00911                                                         else
00912                                                         {
00913                                                                 outer_point = inner_point - (constraint_axis * tick_length) * rot;
00914                                                         }
00915                                                 }
00916                                         }
00917                                         else
00918                                         {
00919                                                 outer_point = inner_point + (projected_snap_axis * tick_length) * rot;
00920                                         }
00921 
00922                                         text_point = outer_point + (projected_snap_axis * mRadiusMeters * 0.1f) * rot;
00923 
00924                                         gGL.vertex3fv(inner_point.mV);
00925                                         gGL.vertex3fv(outer_point.mV);
00926                                 }
00927                                 gGL.end();
00928 
00929                                 //RN: text rendering does own shadow pass, so only render once
00930                                 if (pass == 1 && render_text && i % 16 == 0)
00931                                 {
00932                                         if (world_snap_axis.mV[VX])
00933                                         {
00934                                                 if (i == 0)
00935                                                 {
00936                                                         renderTickText(text_point, mObjectSelection->isAttachment() ? "Forward" : "East", LLColor4::white);
00937                                                 }
00938                                                 else if (i == 16)
00939                                                 {
00940                                                         if (constraint_axis.mV[VZ] > 0.f)
00941                                                         {
00942                                                                 renderTickText(text_point, mObjectSelection->isAttachment() ? "Left" : "North", LLColor4::white);
00943                                                         }
00944                                                         else
00945                                                         {
00946                                                                 renderTickText(text_point, mObjectSelection->isAttachment() ? "Right" : "South", LLColor4::white);
00947                                                         }
00948                                                 }
00949                                                 else if (i == 32)
00950                                                 {
00951                                                         renderTickText(text_point, mObjectSelection->isAttachment() ? "Back" : "West", LLColor4::white);
00952                                                 }
00953                                                 else
00954                                                 {
00955                                                         if (constraint_axis.mV[VZ] > 0.f)
00956                                                         {
00957                                                                 renderTickText(text_point, mObjectSelection->isAttachment() ? "Right" : "South", LLColor4::white);
00958                                                         }
00959                                                         else
00960                                                         {
00961                                                                 renderTickText(text_point, mObjectSelection->isAttachment() ? "Left" : "North", LLColor4::white);
00962                                                         }
00963                                                 }
00964                                         }
00965                                         else if (world_snap_axis.mV[VY])
00966                                         {
00967                                                 if (i == 0)
00968                                                 {
00969                                                         renderTickText(text_point, mObjectSelection->isAttachment() ? "Left" : "North", LLColor4::white);
00970                                                 }
00971                                                 else if (i == 16)
00972                                                 {
00973                                                         if (constraint_axis.mV[VX] > 0.f)
00974                                                         {
00975                                                                 renderTickText(text_point, "Up", LLColor4::white);
00976                                                         }
00977                                                         else
00978                                                         {
00979                                                                 renderTickText(text_point, "Down", LLColor4::white);
00980                                                         }
00981                                                 }
00982                                                 else if (i == 32)
00983                                                 {
00984                                                         renderTickText(text_point, mObjectSelection->isAttachment() ? "Right" : "South", LLColor4::white);
00985                                                 }
00986                                                 else
00987                                                 {
00988                                                         if (constraint_axis.mV[VX] > 0.f)
00989                                                         {
00990                                                                 renderTickText(text_point, "Down", LLColor4::white);
00991                                                         }
00992                                                         else
00993                                                         {
00994                                                                 renderTickText(text_point, "Up", LLColor4::white);
00995                                                         }
00996                                                 }
00997                                         }
00998                                         else if (world_snap_axis.mV[VZ])
00999                                         {
01000                                                 if (i == 0)
01001                                                 {
01002                                                         renderTickText(text_point, "Up", LLColor4::white);
01003                                                 }
01004                                                 else if (i == 16)
01005                                                 {
01006                                                         if (constraint_axis.mV[VY] > 0.f)
01007                                                         {
01008                                                                 renderTickText(text_point, mObjectSelection->isAttachment() ? "Forward" : "East", LLColor4::white);
01009                                                         }
01010                                                         else
01011                                                         {
01012                                                                 renderTickText(text_point, mObjectSelection->isAttachment() ? "Back" : "West", LLColor4::white);
01013                                                         }
01014                                                 }
01015                                                 else if (i == 32)
01016                                                 {
01017                                                         renderTickText(text_point, "Down", LLColor4::white);
01018                                                 }
01019                                                 else
01020                                                 {
01021                                                         if (constraint_axis.mV[VY] > 0.f)
01022                                                         {
01023                                                                 renderTickText(text_point, mObjectSelection->isAttachment() ? "Back" : "West", LLColor4::white);
01024                                                         }
01025                                                         else
01026                                                         {
01027                                                                 renderTickText(text_point, mObjectSelection->isAttachment() ? "Forward" : "East", LLColor4::white);
01028                                                         }
01029                                                 }
01030                                         }
01031                                 }
01032                                 gGL.color4fv(line_color.mV);
01033                         }
01034 
01035                         // now render projected object axis
01036                         if (mInSnapRegime)
01037                         {
01038                                 LLVector3 object_axis;
01039                                 getObjectAxisClosestToMouse(object_axis);
01040 
01041                                 // project onto constraint plane
01042                                 LLSelectNode* first_node = mObjectSelection->getFirstMoveableNode(TRUE);
01043                                 object_axis = object_axis * first_node->getObject()->getRenderRotation();
01044                                 object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
01045                                 object_axis.normVec();
01046                                 object_axis = object_axis * SNAP_GUIDE_INNER_RADIUS * mRadiusMeters + center;
01047                                 LLVector3 line_start = center;
01048 
01049                                 gGL.begin(LLVertexBuffer::LINES);
01050                                 {
01051                                         gGL.vertex3fv(line_start.mV);
01052                                         gGL.vertex3fv(object_axis.mV);
01053                                 }
01054                                 gGL.end();
01055 
01056                                 // draw snap guide arrow
01057                                 gGL.begin(LLVertexBuffer::TRIANGLES);
01058                                 {
01059                                         LLVector3 arrow_dir;
01060                                         LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
01061                                         arrow_span.normVec();
01062 
01063                                         arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
01064                                         arrow_dir.normVec();
01065                                         if (ring_num == 1)
01066                                         {
01067                                                 arrow_dir *= -1.f;
01068                                         }
01069                                         gGL.vertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
01070                                         gGL.vertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
01071                                         gGL.vertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
01072                                 }
01073                                 gGL.end();
01074 
01075                                 {
01076                                         LLGLDepthTest gls_depth(GL_TRUE);
01077                                         gGL.begin(LLVertexBuffer::LINES);
01078                                         {
01079                                                 gGL.vertex3fv(line_start.mV);
01080                                                 gGL.vertex3fv(object_axis.mV);
01081                                         }
01082                                         gGL.end();
01083 
01084                                         // draw snap guide arrow
01085                                         gGL.begin(LLVertexBuffer::TRIANGLES);
01086                                         {
01087                                                 LLVector3 arrow_dir;
01088                                                 LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
01089                                                 arrow_span.normVec();
01090 
01091                                                 arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
01092                                                 arrow_dir.normVec();
01093                                                 if (ring_num == 1)
01094                                                 {
01095                                                         arrow_dir *= -1.f;
01096                                                 }
01097 
01098                                                 gGL.vertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
01099                                                 gGL.vertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
01100                                                 gGL.vertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
01101                                         }
01102                                         gGL.end();
01103                                 }
01104                         }
01105                 }
01106         }
01107 }
01108 
01109 // Returns TRUE if center of sphere is visible.  Also sets a bunch of member variables that are used later (e.g. mCenterToCam)
01110 BOOL LLManipRotate::updateVisiblity()
01111 {
01112         // Don't want to recalculate the center of the selection during a drag.
01113         // Due to packet delays, sometimes half the objects in the selection have their
01114         // new position and half have their old one.  This creates subtle errors in the
01115         // computed center position for that frame.  Unfortunately, these errors
01116         // accumulate.  The result is objects seem to "fly apart" during rotations.
01117         // JC - 03.26.2002
01118         if (!hasMouseCapture())
01119         {
01120                 mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() );//LLSelectMgr::getInstance()->getSelectionCenterGlobal();
01121         }
01122 
01123         BOOL visible = FALSE;
01124 
01125         LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
01126         if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
01127         {
01128                 mCenterToCam = LLVector3(-1.f / gAgent.getAvatarObject()->mHUDCurZoom, 0.f, 0.f);
01129                 mCenterToCamNorm = mCenterToCam;
01130                 mCenterToCamMag = mCenterToCamNorm.normVec();
01131 
01132                 mRadiusMeters = RADIUS_PIXELS / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
01133                 mRadiusMeters /= gAgent.getAvatarObject()->mHUDCurZoom;
01134 
01135                 mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
01136                 mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
01137 
01138                 mCenterScreen.set((S32)((0.5f - mRotationCenter.mdV[VY]) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowWidth()),
01139                                                         (S32)((mRotationCenter.mdV[VZ] + 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowHeight()));
01140                 visible = TRUE;
01141         }
01142         else
01143         {
01144                 visible = LLViewerCamera::getInstance()->projectPosAgentToScreen(center, mCenterScreen );
01145                 if( visible )
01146                 {
01147                         mCenterToCam = gAgent.getCameraPositionAgent() - center;
01148                         mCenterToCamNorm = mCenterToCam;
01149                         mCenterToCamMag = mCenterToCamNorm.normVec();
01150                         LLVector3 cameraAtAxis = LLViewerCamera::getInstance()->getAtAxis();
01151                         cameraAtAxis.normVec();
01152 
01153                         F32 z_dist = -1.f * (mCenterToCam * cameraAtAxis);
01154 
01155                         // Don't drag manip if object too far away
01156                         if (gSavedSettings.getBOOL("LimitSelectDistance"))
01157                         {
01158                                 F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance");
01159                                 if (dist_vec(gAgent.getPositionAgent(), center) > max_select_distance)
01160                                 {
01161                                         visible = FALSE;
01162                                 }
01163                         }
01164                         
01165                         if (mCenterToCamMag > 0.001f)
01166                         {
01167                                 F32 fraction_of_fov = RADIUS_PIXELS / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
01168                                 F32 apparent_angle = fraction_of_fov * LLViewerCamera::getInstance()->getView();  // radians
01169                                 mRadiusMeters = z_dist * tan(apparent_angle);
01170 
01171                                 mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
01172                                 mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
01173                         }
01174                         else
01175                         {
01176                                 visible = FALSE;
01177                         }
01178                 }
01179         }
01180 
01181         mCamEdgeOn = FALSE;
01182         F32 axis_onto_cam = mManipPart >= LL_ROT_X ? llabs( getConstraintAxis() * mCenterToCamNorm ) : 0.f;
01183         if( axis_onto_cam < AXIS_ONTO_CAM_TOLERANCE )
01184         {
01185                 mCamEdgeOn = TRUE;
01186         }
01187 
01188         return visible;
01189 }
01190 
01191 LLQuaternion LLManipRotate::dragUnconstrained( S32 x, S32 y )
01192 {
01193         LLVector3 cam = gAgent.getCameraPositionAgent();
01194         LLVector3 center =  gAgent.getPosAgentFromGlobal( mRotationCenter );
01195 
01196         mMouseCur = intersectMouseWithSphere( x, y, center, mRadiusMeters);
01197 
01198         F32 delta_x = (F32)(mCenterScreen.mX - x);
01199         F32 delta_y = (F32)(mCenterScreen.mY - y);
01200 
01201         F32 dist_from_sphere_center = sqrt(delta_x * delta_x + delta_y * delta_y);
01202 
01203         LLVector3 axis = mMouseDown % mMouseCur;
01204         axis.normVec();
01205         F32 angle = acos(mMouseDown * mMouseCur);
01206         LLQuaternion sphere_rot( angle, axis );
01207 
01208         if (is_approx_zero(1.f - mMouseDown * mMouseCur))
01209         {
01210                 return LLQuaternion::DEFAULT;
01211         }
01212         else if (dist_from_sphere_center < RADIUS_PIXELS)
01213         {
01214                 return sphere_rot;
01215         }
01216         else
01217         {
01218                 LLVector3 intersection;
01219                 getMousePointOnPlaneAgent( intersection, x, y, center + mCenterToProfilePlane, mCenterToCamNorm );
01220 
01221                 // amount dragging in sphere from center to periphery would rotate object
01222                 F32 in_sphere_angle = F_PI_BY_TWO;
01223                 F32 dist_to_tangent_point = mRadiusMeters;
01224                 if( !is_approx_zero( mCenterToProfilePlaneMag ) )
01225                 {
01226                         dist_to_tangent_point = sqrt( mRadiusMeters * mRadiusMeters - mCenterToProfilePlaneMag * mCenterToProfilePlaneMag );
01227                         in_sphere_angle = atan2( dist_to_tangent_point, mCenterToProfilePlaneMag );
01228                 }
01229 
01230                 LLVector3 profile_center_to_intersection = intersection - (center + mCenterToProfilePlane);
01231                 F32 dist_to_intersection = profile_center_to_intersection.normVec();
01232                 F32 angle = (-1.f + dist_to_intersection / dist_to_tangent_point) * in_sphere_angle;
01233 
01234                 LLVector3 axis;
01235                 if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
01236                 {
01237                         axis = LLVector3(-1.f, 0.f, 0.f) % profile_center_to_intersection;
01238                 }
01239                 else
01240                 {
01241                         axis = (cam - center) % profile_center_to_intersection;
01242                         axis.normVec();
01243                 }
01244                 return sphere_rot * LLQuaternion( angle, axis );
01245         }
01246 }
01247 
01248 LLVector3 LLManipRotate::getConstraintAxis()
01249 {
01250         LLVector3 axis;
01251         if( LL_ROT_ROLL == mManipPart )
01252         {
01253                 axis = mCenterToCamNorm;
01254         }
01255         else
01256         {
01257                 S32 axis_dir = mManipPart - LL_ROT_X;
01258                 if ((axis_dir >= 0) && (axis_dir < 3))
01259                 {
01260                         axis.mV[axis_dir] = 1.f;
01261                 }
01262                 else
01263                 {
01264 #ifndef LL_RELEASE_FOR_DOWNLOAD
01265                         llerrs << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << llendl
01266 #else
01267                         llwarns << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << llendl
01268 #endif
01269                         axis.mV[0] = 1.f;
01270                 }
01271 
01272                 LLVector3 grid_origin;
01273                 LLVector3 grid_scale;
01274                 LLQuaternion grid_rotation;
01275 
01276                 LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
01277 
01278                 LLSelectNode* first_node = mObjectSelection->getFirstMoveableNode(TRUE);
01279                 if (first_node)
01280                 {
01281                         // *FIX: get agent local attachment grid working
01282                         // Put rotation into frame of first selected root object
01283                         axis = axis * grid_rotation;
01284                 }
01285         }
01286 
01287         return axis;
01288 }
01289 
01290 LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y )
01291 {
01292         LLSelectNode* first_object_node = mObjectSelection->getFirstMoveableNode(TRUE);
01293         LLVector3 constraint_axis = getConstraintAxis();
01294         LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
01295 
01296         F32 angle = 0.f;
01297 
01298         // build snap axes
01299         LLVector3 grid_origin;
01300         LLVector3 grid_scale;
01301         LLQuaternion grid_rotation;
01302 
01303         LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
01304 
01305         LLVector3 axis1;
01306         LLVector3 axis2;
01307 
01308         LLVector3 test_axis = constraint_axis;
01309         if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
01310         {
01311                 test_axis = test_axis * ~grid_rotation;
01312         }
01313         else if (LLSelectMgr::getInstance()->getGridMode() == GRID_MODE_REF_OBJECT)
01314         {
01315                 test_axis = test_axis * ~grid_rotation;
01316         }
01317         test_axis.abs();
01318 
01319         // find closest global axis to constraint axis;
01320         if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
01321         {
01322                 axis1 = LLVector3::y_axis;
01323         }
01324         else if (test_axis.mV[VY] > test_axis.mV[VZ])
01325         {
01326                 axis1 = LLVector3::z_axis;
01327         }
01328         else
01329         {
01330                 axis1 = LLVector3::x_axis;
01331         }
01332 
01333         if (mObjectSelection->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
01334         {
01335                 axis1 = axis1 * grid_rotation;
01336         }
01337         else if (LLSelectMgr::getInstance()->getGridMode() == GRID_MODE_REF_OBJECT)
01338         {
01339                 axis1 = axis1 * grid_rotation;
01340         }
01341 
01342         //project axis onto constraint plane
01343         axis1 -= (axis1 * constraint_axis) * constraint_axis;
01344         axis1.normVec();
01345 
01346         // calculate third and final axis
01347         axis2 = constraint_axis % axis1;
01348 
01349         //F32 axis_onto_cam = llabs( constraint_axis * mCenterToCamNorm );
01350         if( mCamEdgeOn )
01351         {
01352                 // We're looking at the ring edge-on.
01353                 LLVector3 snap_plane_center = (center + (constraint_axis * mRadiusMeters * 0.5f));
01354                 LLVector3 cam_to_snap_plane;
01355                 if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
01356                 {
01357                         cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
01358                 }
01359                 else
01360                 {
01361                         cam_to_snap_plane = snap_plane_center - gAgent.getCameraPositionAgent();
01362                         cam_to_snap_plane.normVec();
01363                 }
01364 
01365                 LLVector3 projected_mouse;
01366                 BOOL hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
01367                 projected_mouse -= snap_plane_center;
01368 
01369                 S32 snap_plane = 0;
01370 
01371                 F32 dot = cam_to_snap_plane * constraint_axis;
01372                 if (llabs(dot) < 0.01f)
01373                 {
01374                         // looking at ring edge on, project onto view plane and check if mouse is past ring
01375                         getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
01376                         projected_mouse -= snap_plane_center;
01377                         dot = projected_mouse * constraint_axis;
01378                         if (projected_mouse * constraint_axis > 0)
01379                         {
01380                                 snap_plane = 1;
01381                         }
01382                         projected_mouse -= dot * constraint_axis;
01383                 }
01384                 else if (dot > 0.f)
01385                 {
01386                         // look for mouse position outside and in front of snap circle
01387                         if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
01388                         {
01389                                 snap_plane = 1;
01390                         }
01391                 }
01392                 else
01393                 {
01394                         // look for mouse position inside or in back of snap circle
01395                         if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
01396                         {
01397                                 snap_plane = 1;
01398                         }
01399                 }
01400 
01401                 if (snap_plane == 0)
01402                 {
01403                         // try other plane
01404                         snap_plane_center = (center - (constraint_axis * mRadiusMeters * 0.5f));
01405                         if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
01406                         {
01407                                 cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
01408                         }
01409                         else
01410                         {
01411                                 cam_to_snap_plane = snap_plane_center - gAgent.getCameraPositionAgent();
01412                                 cam_to_snap_plane.normVec();
01413                         }
01414 
01415                         hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
01416                         projected_mouse -= snap_plane_center;
01417 
01418                         dot = cam_to_snap_plane * constraint_axis;
01419                         if (llabs(dot) < 0.01f)
01420                         {
01421                                 // looking at ring edge on, project onto view plane and check if mouse is past ring
01422                                 getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
01423                                 projected_mouse -= snap_plane_center;
01424                                 dot = projected_mouse * constraint_axis;
01425                                 if (projected_mouse * constraint_axis < 0)
01426                                 {
01427                                         snap_plane = 2;
01428                                 }
01429                                 projected_mouse -= dot * constraint_axis;
01430                         }
01431                         else if (dot < 0.f)
01432                         {
01433                                 // look for mouse position outside and in front of snap circle
01434                                 if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
01435                                 {
01436                                         snap_plane = 2;
01437                                 }
01438                         }
01439                         else
01440                         {
01441                                 // look for mouse position inside or in back of snap circle
01442                                 if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
01443                                 {
01444                                         snap_plane = 2;
01445                                 }
01446                         }
01447                 }
01448 
01449                 if (snap_plane > 0)
01450                 {
01451                         LLVector3 cam_at_axis;
01452                         if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
01453                         {
01454                                 cam_at_axis.setVec(1.f, 0.f, 0.f);
01455                         }
01456                         else
01457                         {
01458                                 cam_at_axis = snap_plane_center - gAgent.getCameraPositionAgent();
01459                                 cam_at_axis.normVec();
01460                         }
01461 
01462                         // first, project mouse onto screen plane at point tangent to rotation radius. 
01463                         getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_at_axis);
01464                         // project that point onto rotation plane
01465                         projected_mouse -= snap_plane_center;
01466                         projected_mouse -= projected_vec(projected_mouse, constraint_axis);
01467 
01468                         F32 mouse_lateral_dist = llmin(SNAP_GUIDE_INNER_RADIUS * mRadiusMeters, projected_mouse.magVec());
01469                         F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
01470                         if (llabs(mouse_lateral_dist) > 0.01f)
01471                         {
01472                                 mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) - 
01473                                                                         (mouse_lateral_dist * mouse_lateral_dist));
01474                         }
01475                         LLVector3 projected_camera_at = cam_at_axis - projected_vec(cam_at_axis, constraint_axis);
01476                         projected_mouse -= mouse_depth * projected_camera_at;
01477 
01478                         if (!mInSnapRegime)
01479                         {
01480                                 mSmoothRotate = TRUE;
01481                         }
01482                         mInSnapRegime = TRUE;
01483                         // 0 to 360 deg
01484                         F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
01485                         
01486                         F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
01487                         //fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f);
01488 
01489                         LLVector3 object_axis;
01490                         getObjectAxisClosestToMouse(object_axis);
01491                         object_axis = object_axis * first_object_node->mSavedRotation;
01492 
01493                         // project onto constraint plane
01494                         object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
01495                         object_axis.normVec();
01496 
01497                         if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
01498                         {
01499                                 F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
01500                                 angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
01501                         }
01502                         else
01503                         {
01504                                 angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
01505                         }
01506                         return LLQuaternion( -angle, constraint_axis );
01507                 }
01508                 else
01509                 {
01510                         if (mInSnapRegime)
01511                         {
01512                                 mSmoothRotate = TRUE;
01513                         }
01514                         mInSnapRegime = FALSE;
01515 
01516                         LLVector3 up_from_axis = mCenterToCamNorm % constraint_axis;
01517                         up_from_axis.normVec();
01518                         LLVector3 cur_intersection;
01519                         getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
01520                         cur_intersection -= center;
01521                         mMouseCur = projected_vec(cur_intersection, up_from_axis);
01522                         F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
01523                         F32 mouse_dist_sqrd = mMouseCur.magVecSquared();
01524                         if (mouse_dist_sqrd > 0.0001f)
01525                         {
01526                                 mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) - 
01527                                                                         mouse_dist_sqrd);
01528                         }
01529                         LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, constraint_axis);
01530                         mMouseCur += mouse_depth * projected_center_to_cam;
01531 
01532                         F32 dist = (cur_intersection * up_from_axis) - (mMouseDown * up_from_axis);
01533                         angle = dist / (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * -F_PI_BY_TWO;
01534                 }
01535         }
01536         else
01537         {
01538                 LLVector3 projected_mouse;
01539                 getMousePointOnPlaneAgent(projected_mouse, x, y, center, constraint_axis);
01540                 projected_mouse -= center;
01541                 mMouseCur = projected_mouse;
01542                 mMouseCur.normVec();
01543 
01544                 if (!first_object_node)
01545                 {
01546                         return LLQuaternion::DEFAULT;
01547                 }
01548 
01549                 if (gSavedSettings.getBOOL("SnapEnabled") && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters)
01550                 {
01551                         if (!mInSnapRegime)
01552                         {
01553                                 mSmoothRotate = TRUE;
01554                         }
01555                         mInSnapRegime = TRUE;
01556                         // 0 to 360 deg
01557                         F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
01558                         
01559                         F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
01560                         //fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f);
01561 
01562                         LLVector3 object_axis;
01563                         getObjectAxisClosestToMouse(object_axis);
01564                         object_axis = object_axis * first_object_node->mSavedRotation;
01565 
01566                         // project onto constraint plane
01567                         object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
01568                         object_axis.normVec();
01569 
01570                         if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
01571                         {
01572                                 F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
01573                                 angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
01574                         }
01575                         else
01576                         {
01577                                 angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
01578                         }
01579                         return LLQuaternion( -angle, constraint_axis );
01580                 }
01581                 else
01582                 {
01583                         if (mInSnapRegime)
01584                         {
01585                                 mSmoothRotate = TRUE;
01586                         }
01587                         mInSnapRegime = FALSE;
01588                 }
01589 
01590                 angle = acos(mMouseCur * mMouseDown);
01591 
01592                 F32 dir = (mMouseDown % mMouseCur) * constraint_axis;  // cross product
01593                 if( dir < 0.f )
01594                 {
01595                         angle *= -1.f;
01596                 }
01597         }
01598 
01599         F32 rot_step = gSavedSettings.getF32("RotationStep");
01600         F32 step_size = DEG_TO_RAD * rot_step;
01601         angle -= fmod(angle, step_size);
01602                 
01603         return LLQuaternion( angle, constraint_axis );
01604 }
01605 
01606 
01607 
01608 LLVector3 LLManipRotate::intersectMouseWithSphere( S32 x, S32 y, const LLVector3& sphere_center, F32 sphere_radius)
01609 {
01610         LLVector3 ray_pt;
01611         LLVector3 ray_dir;
01612         mouseToRay( x, y, &ray_pt, &ray_dir);
01613         return intersectRayWithSphere( ray_pt, ray_dir, sphere_center, sphere_radius );
01614 }
01615 
01616 LLVector3 LLManipRotate::intersectRayWithSphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius)
01617 {
01618         LLVector3 ray_pt_to_center = sphere_center - ray_pt;
01619         F32 center_distance = ray_pt_to_center.normVec();
01620 
01621         F32 dot = ray_dir * ray_pt_to_center;
01622 
01623         if (dot == 0.f)
01624         {
01625                 return LLVector3::zero;
01626         }
01627 
01628         // point which ray hits plane centered on sphere origin, facing ray origin
01629         LLVector3 intersection_sphere_plane = ray_pt + (ray_dir * center_distance / dot); 
01630         // vector from sphere origin to the point, normalized to sphere radius
01631         LLVector3 sphere_center_to_intersection = (intersection_sphere_plane - sphere_center) / sphere_radius;
01632 
01633         F32 dist_squared = sphere_center_to_intersection.magVecSquared();
01634         LLVector3 result;
01635 
01636         if (dist_squared > 1.f)
01637         {
01638                 result = sphere_center_to_intersection;
01639                 result.normVec();
01640         }
01641         else
01642         {
01643                 result = sphere_center_to_intersection - ray_dir * sqrt(1.f - dist_squared);
01644         }
01645 
01646         return result;
01647 }
01648 
01649 // Utility function.  Should probably be moved to another class.
01650 //static
01651 void LLManipRotate::mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_dir )
01652 {
01653         if (LLSelectMgr::getInstance()->getSelection()->getSelectType() == SELECT_TYPE_HUD)
01654         {
01655                 F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
01656                 F32 mouse_y = ((((F32)y) / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
01657 
01658                 *ray_pt = LLVector3(-1.f, -mouse_x, mouse_y);
01659                 *ray_dir = LLVector3(1.f, 0.f, 0.f);
01660         }
01661         else
01662         {
01663                 *ray_pt = gAgent.getCameraPositionAgent();
01664                 LLViewerCamera::getInstance()->projectScreenToPosAgent(x, y, ray_dir);
01665                 *ray_dir -= *ray_pt;
01666                 ray_dir->normVec();
01667         }
01668 }
01669 
01670 void LLManipRotate::highlightManipulators( S32 x, S32 y )
01671 {
01672         mHighlightedPart = LL_NO_PART;
01673 
01674         //LLBBox bbox = LLSelectMgr::getInstance()->getBBoxOfSelection();
01675         LLViewerObject *first_object = mObjectSelection->getFirstMoveableObject(TRUE);
01676         
01677         if (!first_object)
01678         {
01679                 return;
01680         }
01681         
01682         LLQuaternion object_rot = first_object->getRenderRotation();
01683         LLVector3 rotation_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
01684         LLVector3 mouse_dir_x;
01685         LLVector3 mouse_dir_y;
01686         LLVector3 mouse_dir_z;
01687         LLVector3 intersection_roll;
01688 
01689         LLVector3 grid_origin;
01690         LLVector3 grid_scale;
01691         LLQuaternion grid_rotation;
01692 
01693         LLSelectMgr::getInstance()->getGrid(grid_origin, grid_rotation, grid_scale);
01694 
01695         LLVector3 rot_x_axis = LLVector3::x_axis * grid_rotation;
01696         LLVector3 rot_y_axis = LLVector3::y_axis * grid_rotation;
01697         LLVector3 rot_z_axis = LLVector3::z_axis * grid_rotation;
01698 
01699         F32 proj_rot_x_axis = llabs(rot_x_axis * mCenterToCamNorm);
01700         F32 proj_rot_y_axis = llabs(rot_y_axis * mCenterToCamNorm);
01701         F32 proj_rot_z_axis = llabs(rot_z_axis * mCenterToCamNorm);
01702 
01703         F32 min_select_distance = 0.f;
01704         F32 cur_select_distance = 0.f;
01705 
01706         // test x
01707         getMousePointOnPlaneAgent(mouse_dir_x, x, y, rotation_center, rot_x_axis);
01708         mouse_dir_x -= rotation_center;
01709         // push intersection point out when working at obtuse angle to make ring easier to hit
01710         mouse_dir_x *= 1.f + (1.f - llabs(rot_x_axis * mCenterToCamNorm)) * 0.1f;
01711 
01712         // test y
01713         getMousePointOnPlaneAgent(mouse_dir_y, x, y, rotation_center, rot_y_axis);
01714         mouse_dir_y -= rotation_center;
01715         mouse_dir_y *= 1.f + (1.f - llabs(rot_y_axis * mCenterToCamNorm)) * 0.1f;
01716 
01717         // test z
01718         getMousePointOnPlaneAgent(mouse_dir_z, x, y, rotation_center, rot_z_axis);
01719         mouse_dir_z -= rotation_center;
01720         mouse_dir_z *= 1.f + (1.f - llabs(rot_z_axis * mCenterToCamNorm)) * 0.1f;
01721 
01722         // test roll
01723         getMousePointOnPlaneAgent(intersection_roll, x, y, rotation_center, mCenterToCamNorm);
01724         intersection_roll -= rotation_center;
01725 
01726         F32 dist_x = mouse_dir_x.normVec();
01727         F32 dist_y = mouse_dir_y.normVec();
01728         F32 dist_z = mouse_dir_z.normVec();
01729 
01730         F32 distance_threshold = (MAX_MANIP_SELECT_DISTANCE * mRadiusMeters) / gViewerWindow->getWindowHeight();
01731 
01732         if (llabs(dist_x - mRadiusMeters) * llmax(0.05f, proj_rot_x_axis) < distance_threshold)
01733         {
01734                 // selected x
01735                 cur_select_distance = dist_x * mouse_dir_x * mCenterToCamNorm;
01736                 if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
01737                 {
01738                         min_select_distance = cur_select_distance;
01739                         mHighlightedPart = LL_ROT_X;
01740                 }
01741         }
01742         if (llabs(dist_y - mRadiusMeters) * llmax(0.05f, proj_rot_y_axis) < distance_threshold)
01743         {
01744                 // selected y
01745                 cur_select_distance = dist_y * mouse_dir_y * mCenterToCamNorm;
01746                 if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
01747                 {
01748                         min_select_distance = cur_select_distance;
01749                         mHighlightedPart = LL_ROT_Y;
01750                 }
01751         }
01752         if (llabs(dist_z - mRadiusMeters) * llmax(0.05f, proj_rot_z_axis) < distance_threshold)
01753         {
01754                 // selected z
01755                 cur_select_distance = dist_z * mouse_dir_z * mCenterToCamNorm;
01756                 if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
01757                 {
01758                         min_select_distance = cur_select_distance;
01759                         mHighlightedPart = LL_ROT_Z;
01760                 }
01761         }
01762 
01763         // test for edge-on intersections
01764         if (proj_rot_x_axis < 0.05f)
01765         {
01766                 if ((proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_x_axis) < distance_threshold) && dist_y < mRadiusMeters) ||
01767                         (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_x_axis) < distance_threshold) && dist_z < mRadiusMeters))
01768                 {
01769                         mHighlightedPart = LL_ROT_X;
01770                 }
01771         }
01772 
01773         if (proj_rot_y_axis < 0.05f)
01774         {
01775                 if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_y_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
01776                         (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_y_axis) < distance_threshold) && dist_z < mRadiusMeters))
01777                 {
01778                         mHighlightedPart = LL_ROT_Y;
01779                 }
01780         }
01781 
01782         if (proj_rot_z_axis < 0.05f)
01783         {
01784                 if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_z_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
01785                         (proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_z_axis) < distance_threshold) && dist_y < mRadiusMeters))
01786                 {
01787                         mHighlightedPart = LL_ROT_Z;
01788                 }
01789         }
01790 
01791         // test for roll
01792         if (mHighlightedPart == LL_NO_PART)
01793         {
01794                 F32 roll_distance = intersection_roll.magVec();
01795                 F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
01796 
01797                 // use larger distance threshold for roll as it is checked only if something else wasn't highlighted
01798                 if (llabs(roll_distance - (mRadiusMeters + (width_meters * 2.f))) < distance_threshold * 2.f)
01799                 {
01800                         mHighlightedPart = LL_ROT_ROLL;
01801                 }
01802                 else if (roll_distance < mRadiusMeters)
01803                 {
01804                         mHighlightedPart = LL_ROT_GENERAL;
01805                 }
01806         }
01807 }
01808 
01809 S32 LLManipRotate::getObjectAxisClosestToMouse(LLVector3& object_axis)
01810 {
01811         LLSelectNode* first_object_node = mObjectSelection->getFirstMoveableNode(TRUE);
01812 
01813         if (!first_object_node)
01814         {
01815                 object_axis.clearVec();
01816                 return -1;
01817         }
01818 
01819         LLQuaternion obj_rotation = first_object_node->mSavedRotation;
01820         LLVector3 mouse_down_object = mMouseDown * ~obj_rotation;
01821         LLVector3 mouse_down_abs = mouse_down_object;
01822         mouse_down_abs.abs();
01823 
01824         S32 axis_index = 0;
01825         if (mouse_down_abs.mV[VX] > mouse_down_abs.mV[VY] && mouse_down_abs.mV[VX] > mouse_down_abs.mV[VZ])
01826         {
01827                 if (mouse_down_object.mV[VX] > 0.f)
01828                 {
01829                         object_axis = LLVector3::x_axis;
01830                 }
01831                 else
01832                 {
01833                         object_axis = LLVector3::x_axis_neg;
01834                 }
01835                 axis_index = VX;
01836         }
01837         else if (mouse_down_abs.mV[VY] > mouse_down_abs.mV[VZ])
01838         {
01839                 if (mouse_down_object.mV[VY] > 0.f)
01840                 {
01841                         object_axis = LLVector3::y_axis;
01842                 }
01843                 else
01844                 {
01845                         object_axis = LLVector3::y_axis_neg;
01846                 }
01847                 axis_index = VY;
01848         }
01849         else
01850         {
01851                 if (mouse_down_object.mV[VZ] > 0.f)
01852                 {
01853                         object_axis = LLVector3::z_axis;
01854                 }
01855                 else
01856                 {
01857                         object_axis = LLVector3::z_axis_neg;
01858                 }
01859                 axis_index = VZ;
01860         }
01861 
01862         return axis_index;
01863 }
01864 
01865 //virtual
01866 BOOL LLManipRotate::canAffectSelection()
01867 {
01868         BOOL can_rotate = mObjectSelection->getObjectCount() != 0;
01869         if (can_rotate)
01870         {
01871                 struct f : public LLSelectedObjectFunctor
01872                 {
01873                         virtual bool apply(LLViewerObject* objectp)
01874                         {
01875                                 return objectp->permMove() && (objectp->permModify() || !gSavedSettings.getBOOL("EditLinkedParts"));
01876                         }
01877                 } func;
01878                 can_rotate = mObjectSelection->applyToObjects(&func);
01879         }
01880         return can_rotate;
01881 }
01882 

Generated on Fri May 16 08:33:46 2008 for SecondLife by  doxygen 1.5.5