llmanipscale.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "llmanipscale.h"
00035 
00036 // library includes
00037 #include "llmath.h"
00038 #include "v3math.h"
00039 #include "llquaternion.h"
00040 #include "llgl.h"
00041 #include "v4color.h"
00042 #include "llprimitive.h"
00043 
00044 // viewer includes
00045 #include "llagent.h"
00046 #include "llbbox.h"
00047 #include "llbox.h"
00048 #include "llviewercontrol.h"
00049 #include "llcriticaldamp.h"
00050 #include "llcylinder.h"
00051 #include "lldrawable.h"
00052 #include "llfloatertools.h"
00053 #include "llglheaders.h"
00054 #include "llselectmgr.h"
00055 #include "llstatusbar.h"
00056 #include "llui.h"
00057 #include "llviewercamera.h"
00058 #include "llviewerobject.h"
00059 #include "llviewerwindow.h"
00060 #include "llhudrender.h"
00061 #include "llworld.h"
00062 #include "v2math.h"
00063 #include "llvoavatar.h"
00064 
00065 
00066 const F32 MAX_MANIP_SELECT_DISTANCE_SQUARED = 11.f * 11.f;
00067 const F32 SNAP_GUIDE_SCREEN_OFFSET = 0.05f;
00068 const F32 SNAP_GUIDE_SCREEN_LENGTH = 0.7f;
00069 const F32 SELECTED_MANIPULATOR_SCALE = 1.2f;
00070 const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
00071 const S32 NUM_MANIPULATORS = 14;
00072 
00073 const LLManip::EManipPart MANIPULATOR_IDS[NUM_MANIPULATORS] = 
00074 {
00075         LLManip::LL_CORNER_NNN,
00076         LLManip::LL_CORNER_NNP,
00077         LLManip::LL_CORNER_NPN,
00078         LLManip::LL_CORNER_NPP,
00079         LLManip::LL_CORNER_PNN,
00080         LLManip::LL_CORNER_PNP,
00081         LLManip::LL_CORNER_PPN,
00082         LLManip::LL_CORNER_PPP,
00083         LLManip::LL_FACE_POSZ,
00084         LLManip::LL_FACE_POSX,
00085         LLManip::LL_FACE_POSY,
00086         LLManip::LL_FACE_NEGX,
00087         LLManip::LL_FACE_NEGY,
00088         LLManip::LL_FACE_NEGZ
00089 };
00090 
00091 
00092 
00093 // static
00094 void LLManipScale::setUniform(BOOL b)
00095 {
00096         gSavedSettings.setBOOL("ScaleUniform", b);
00097 }
00098 
00099 // static
00100 void LLManipScale::setShowAxes(BOOL b)
00101 {
00102         gSavedSettings.setBOOL("ScaleShowAxes", b);
00103 }
00104 
00105 // static
00106 void LLManipScale::setStretchTextures(BOOL b)
00107 {
00108         gSavedSettings.setBOOL("ScaleStretchTextures", b);
00109 }
00110 
00111 // static
00112 BOOL LLManipScale::getUniform()
00113 {
00114         return gSavedSettings.getBOOL("ScaleUniform");
00115 }
00116 
00117 // static
00118 BOOL LLManipScale::getShowAxes()
00119 {
00120         return gSavedSettings.getBOOL("ScaleShowAxes");
00121 }
00122 
00123 // static
00124 BOOL LLManipScale::getStretchTextures()
00125 {
00126         return gSavedSettings.getBOOL("ScaleStretchTextures");
00127 }
00128 
00129 inline void LLManipScale::conditionalHighlight( U32 part, const LLColor4* highlight, const LLColor4* normal )
00130 {
00131         LLColor4 default_highlight( 1.f, 1.f, 1.f, 1.f );
00132         LLColor4 default_normal( 0.7f, 0.7f, 0.7f, 0.6f );
00133         LLColor4 invisible(0.f, 0.f, 0.f, 0.f);
00134         F32 manipulator_scale = 1.f;
00135 
00136         for (S32 i = 0; i < NUM_MANIPULATORS; i++)
00137         {
00138                 if((U32)MANIPULATOR_IDS[i] == part)
00139                 {
00140                         manipulator_scale = mManipulatorScales[i];
00141                         break;
00142                 }
00143         }
00144 
00145         mScaledBoxHandleSize = mBoxHandleSize * manipulator_scale;
00146         if (mManipPart != (S32)LL_NO_PART && mManipPart != (S32)part)
00147         {
00148                 glColor4fv( invisible.mV );
00149         }
00150         else if( mHighlightedPart == (S32)part )
00151         {
00152                 glColor4fv( highlight ? highlight->mV : default_highlight.mV );
00153         }
00154         else
00155         {
00156                 glColor4fv( normal ? normal->mV : default_normal.mV  );
00157         }
00158 }
00159 
00160 void LLManipScale::handleSelect()
00161 {
00162         LLBBox bbox = gSelectMgr->getBBoxOfSelection();
00163         updateSnapGuides(bbox);
00164         gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
00165         gFloaterTools->setStatusText("scale");
00166         LLManip::handleSelect();
00167 }
00168 
00169 void LLManipScale::handleDeselect()
00170 {
00171         mHighlightedPart = LL_NO_PART;
00172         mManipPart = LL_NO_PART;
00173         LLManip::handleDeselect();
00174 }
00175 
00176 BOOL sort_manip_by_z(LLManipScale::ManipulatorHandle *new_manip, LLManipScale::ManipulatorHandle *test_manip)
00177 {
00178         return ((new_manip->mType < test_manip->mType) || (new_manip->mPosition.mV[VZ] < test_manip->mPosition.mV[VZ]));
00179 }
00180 
00181 LLManipScale::LLManipScale( LLToolComposite* composite )
00182         : 
00183         LLManip( "Scale", composite ),
00184         mBoxHandleSize( 1.f ),
00185         mScaledBoxHandleSize( 1.f ),
00186         mManipPart( LL_NO_PART ),
00187         mLastMouseX( -1 ),
00188         mLastMouseY( -1 ),
00189         mSendUpdateOnMouseUp( FALSE ),
00190         mLastUpdateFlags( 0 ),
00191         mScaleSnapUnit1(1.f),
00192         mScaleSnapUnit2(1.f),
00193         mSnapRegimeOffset(0.f),
00194         mSnapGuideLength(0.f),
00195         mScaleSnapValue(0.f)
00196 { 
00197         mProjectedManipulators.setInsertBefore(sort_manip_by_z);
00198         mManipulatorScales = new F32[NUM_MANIPULATORS];
00199         for (S32 i = 0; i < NUM_MANIPULATORS; i++)
00200         {
00201                 mManipulatorScales[i] = 1.f;
00202         }
00203 }
00204 
00205 LLManipScale::~LLManipScale()
00206 {
00207         delete []mManipulatorScales;
00208 }
00209 
00210 void LLManipScale::render()
00211 {
00212         LLGLSUIDefault gls_ui;
00213         LLGLSNoTexture gls_no_texture;
00214         LLGLDepthTest gls_depth(GL_TRUE);
00215         LLGLEnable gl_blend(GL_BLEND);
00216         LLGLEnable gls_alpha_test(GL_ALPHA_TEST);
00217         
00218         if( canAffectSelection() )
00219         {
00220                 glMatrixMode(GL_MODELVIEW);
00221                 glPushMatrix();
00222                 if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
00223                 {
00224                         F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
00225                         glScalef(zoom, zoom, zoom);
00226                 }
00227 
00229                 // Calculate size of drag handles       
00230 
00231                 const F32 BOX_HANDLE_BASE_SIZE          = 50.0f;   // box size in pixels = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR
00232                 const F32 BOX_HANDLE_BASE_FACTOR        = 0.2f;
00233                 
00234                 LLVector3 center_agent = gAgent.getPosAgentFromGlobal(gSelectMgr->getSelectionCenterGlobal());
00235 
00236                 F32 range;
00237                 F32 range_from_agent;
00238                 if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
00239                 {
00240                         mBoxHandleSize = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR / (F32) gCamera->getViewHeightInPixels();
00241                         mBoxHandleSize /= gAgent.getAvatarObject()->mHUDCurZoom;
00242                 }
00243                 else
00244                 {
00245                         range = dist_vec(gAgent.getCameraPositionAgent(), center_agent);
00246                         range_from_agent = dist_vec(gAgent.getPositionAgent(), center_agent);
00247 
00248                         // Don't draw manip if object too far away
00249                         if (gSavedSettings.getBOOL("LimitSelectDistance"))
00250                         {
00251                                 F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance");
00252                                 if (range_from_agent > max_select_distance)
00253                                 {
00254                                         return;
00255                                 }
00256                         }
00257 
00258                         if (range > 0.001f)
00259                         {
00260                                 // range != zero
00261                                 F32 fraction_of_fov = BOX_HANDLE_BASE_SIZE / (F32) gCamera->getViewHeightInPixels();
00262                                 F32 apparent_angle = fraction_of_fov * gCamera->getView();  // radians
00263                                 mBoxHandleSize = range * tan(apparent_angle) * BOX_HANDLE_BASE_FACTOR;
00264                         }
00265                         else
00266                         {
00267                                 // range == zero
00268                                 mBoxHandleSize = BOX_HANDLE_BASE_FACTOR;
00269                         }
00270                 }
00271 
00273                 // Draw bounding box
00274 
00275                 LLBBox bbox = gSelectMgr->getBBoxOfSelection();
00276                 LLVector3 pos_agent = bbox.getPositionAgent();
00277                 LLQuaternion rot = bbox.getRotation();
00278 
00279                 glMatrixMode(GL_MODELVIEW);
00280                 glPushMatrix();
00281                 {
00282                         glTranslatef(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ]);
00283 
00284                         F32 angle_radians, x, y, z;
00285                         rot.getAngleAxis(&angle_radians, &x, &y, &z);
00286                         glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
00287 
00288                         
00289                         {
00290                                 LLGLEnable poly_offset(GL_POLYGON_OFFSET_FILL);
00291                                 glPolygonOffset( -2.f, -2.f);
00292 
00293                                 // JC - Band-aid until edge stretch working similar to side stretch
00294                                 // in non-uniform.
00295                                 // renderEdges( bbox );
00296 
00297                                 renderCorners( bbox );
00298                                 renderFaces( bbox );
00299 
00300                                 if (mManipPart != LL_NO_PART)
00301                                 {
00302                                         renderGuidelinesPart( bbox );
00303                                 }
00304 
00305                                 glPolygonOffset( 0.f, 0.f);
00306                         }
00307                 }
00308                 glPopMatrix();
00309 
00310                 if (mManipPart != LL_NO_PART)
00311                 {
00312                         renderSnapGuides(bbox);
00313                 }
00314                 glPopMatrix();
00315 
00316                 renderXYZ(bbox.getExtentLocal());
00317         }
00318 }
00319 
00320 BOOL LLManipScale::handleMouseDown(S32 x, S32 y, MASK mask)
00321 {
00322         BOOL    handled = FALSE;
00323 
00324         LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
00325         if( hit_obj ||  
00326                 (mHighlightedPart != LL_NO_PART) )
00327         {
00328                 handled = handleMouseDownOnPart( x, y, mask );
00329         }
00330 
00331         return handled;
00332 }
00333 
00334 // Assumes that one of the arrows on an object was hit.
00335 BOOL LLManipScale::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
00336 {
00337         BOOL can_scale = canAffectSelection();
00338         if (!can_scale)
00339         {
00340                 return FALSE;
00341         }
00342 
00343         highlightManipulators(x, y);
00344         S32 hit_part = mHighlightedPart;
00345 
00346         gSelectMgr->enableSilhouette(FALSE);
00347         mManipPart = (EManipPart)hit_part;
00348 
00349         LLBBox bbox = gSelectMgr->getBBoxOfSelection();
00350         LLVector3 box_center_agent = bbox.getCenterAgent();
00351         LLVector3 box_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ) );
00352         
00353         updateSnapGuides(bbox);
00354 
00355         mDragStartPointGlobal = gAgent.getPosGlobalFromAgent(box_corner_agent);
00356         mDragStartCenterGlobal = gAgent.getPosGlobalFromAgent(box_center_agent);
00357         LLVector3 far_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox ) );
00358         mDragFarHitGlobal = gAgent.getPosGlobalFromAgent(far_corner_agent);
00359         mDragPointGlobal = mDragStartPointGlobal;
00360 
00361         // we just started a drag, so save initial object positions, orientations, and scales
00362         gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_SCALE);
00363         // Route future Mouse messages here preemptively.  (Release on mouse up.)
00364         setMouseCapture( TRUE );
00365 
00366         mHelpTextTimer.reset();
00367         sNumTimesHelpTextShown++;
00368         return TRUE;
00369 }
00370 
00371 
00372 BOOL LLManipScale::handleMouseUp(S32 x, S32 y, MASK mask)
00373 {
00374         // first, perform normal processing in case this was a quick-click
00375         handleHover(x, y, mask);
00376 
00377         if( hasMouseCapture() )
00378         {
00379                 if( (LL_FACE_MIN <= (S32)mManipPart) 
00380                         && ((S32)mManipPart <= LL_FACE_MAX) )
00381                 {
00382                         sendUpdates(TRUE,TRUE,FALSE);
00383                 }
00384                 else
00385                 if( (LL_CORNER_MIN <= (S32)mManipPart) 
00386                         && ((S32)mManipPart <= LL_CORNER_MAX) )
00387                 {
00388                         sendUpdates(TRUE,TRUE,TRUE);
00389                 }
00390                 
00391                 //send texture update
00392                 gSelectMgr->adjustTexturesByScale(TRUE, getStretchTextures());
00393                 
00394                 gSelectMgr->enableSilhouette(TRUE);
00395                 mManipPart = LL_NO_PART;
00396 
00397                 // Might have missed last update due to UPDATE_DELAY timing
00398                 gSelectMgr->sendMultipleUpdate( mLastUpdateFlags );
00399                 
00400                 //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
00401                 gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
00402         }
00403         return LLManip::handleMouseUp(x, y, mask);
00404 }
00405 
00406 
00407 BOOL LLManipScale::handleHover(S32 x, S32 y, MASK mask)
00408 {
00409         if( hasMouseCapture() )
00410         {
00411                 if( mObjectSelection->isEmpty() )
00412                 {
00413                         // Somehow the object got deselected while we were dragging it.
00414                         setMouseCapture( FALSE );
00415                 }
00416                 else
00417                 {
00418                         drag( x, y );
00419                 }
00420                 lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (active)" << llendl;              
00421         }
00422         else
00423         {
00424                 mInSnapRegime = FALSE;
00425                 // not dragging...
00426                 highlightManipulators(x, y);
00427         }
00428         
00429         // Patch up textures, if possible.
00430         gSelectMgr->adjustTexturesByScale(FALSE, getStretchTextures());
00431 
00432         gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLSCALE);
00433         return TRUE;
00434 }
00435 
00436 void LLManipScale::highlightManipulators(S32 x, S32 y)
00437 {
00438         mHighlightedPart = LL_NO_PART;
00439 
00440         // If we have something selected, try to hit its manipulator handles.
00441         // Don't do this with nothing selected, as it kills the framerate.
00442         LLBBox bbox = gSelectMgr->getBBoxOfSelection();
00443 
00444         if( canAffectSelection() )
00445         {
00446                 LLMatrix4 transform;
00447                 if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
00448                 {
00449                         LLVector4 translation(bbox.getPositionAgent());
00450                         transform.initRotTrans(bbox.getRotation(), translation);
00451                         LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
00452                         transform *= cfr;
00453                         LLMatrix4 window_scale;
00454                         F32 zoom_level = 2.f * gAgent.getAvatarObject()->mHUDCurZoom;
00455                         window_scale.initAll(LLVector3(zoom_level / gCamera->getAspect(), zoom_level, 0.f),
00456                                 LLQuaternion::DEFAULT,
00457                                 LLVector3::zero);
00458                         transform *= window_scale;
00459                 }
00460                 else
00461                 {
00462                         LLMatrix4 projMatrix = gCamera->getProjection();
00463                         LLMatrix4 modelView = gCamera->getModelview();
00464                         transform.initAll(LLVector3(1.f, 1.f, 1.f), bbox.getRotation(), bbox.getPositionAgent());
00465                         
00466                         transform *= modelView;
00467                         transform *= projMatrix;
00468                 }
00469 
00470                 LLVector3 min = bbox.getMinLocal();
00471                 LLVector3 max = bbox.getMaxLocal();
00472                 LLVector3 ctr = bbox.getCenterLocal();
00473 
00474                 mProjectedManipulators.deleteAllData();
00475 
00476                 S32 numManips = 0;
00477                 // corners
00478                 mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], min.mV[VZ], 1.f);
00479                 mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], max.mV[VZ], 1.f);
00480                 mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], min.mV[VZ], 1.f);
00481                 mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], max.mV[VZ], 1.f);
00482                 mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], min.mV[VZ], 1.f);
00483                 mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], max.mV[VZ], 1.f);
00484                 mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], min.mV[VZ], 1.f);
00485                 mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], max.mV[VZ], 1.f);
00486                 
00487                 // 1-D highlights are applicable iff one object is selected
00488                 if( mObjectSelection->getObjectCount() == 1 )
00489                 {
00490                         // face centers
00491                         mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], max.mV[VZ], 1.f);
00492                         mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f);
00493                         mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], max.mV[VY], ctr.mV[VZ], 1.f);
00494                         mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f);
00495                         mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], min.mV[VY], ctr.mV[VZ], 1.f);
00496                         mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], min.mV[VZ], 1.f);
00497                 }
00498 
00499                 for (S32 i = 0; i < numManips; i++)
00500                 {
00501                         LLVector4 projectedVertex = mManipulatorVertices[i] * transform;
00502                         projectedVertex = projectedVertex * (1.f / projectedVertex.mV[VW]);
00503 
00504                         ManipulatorHandle* projManipulator = new ManipulatorHandle(LLVector3(projectedVertex.mV[VX], projectedVertex.mV[VY], 
00505                                 projectedVertex.mV[VZ]), MANIPULATOR_IDS[i], (i < 7) ? SCALE_MANIP_CORNER : SCALE_MANIP_FACE);
00506                         mProjectedManipulators.addDataSorted(projManipulator);
00507                 }
00508 
00509                 F32 half_width = (F32)gViewerWindow->getWindowWidth() / 2.f;
00510                 F32 half_height = (F32)gViewerWindow->getWindowHeight() / 2.f;
00511                 LLVector2 manip2d;
00512                 LLVector2 mousePos((F32)x - half_width, (F32)y - half_height);
00513                 LLVector2 delta;
00514 
00515                 mHighlightedPart = LL_NO_PART;
00516 
00517                 for (ManipulatorHandle* manipulator = mProjectedManipulators.getFirstData();
00518                         manipulator;
00519                         manipulator = mProjectedManipulators.getNextData())
00520                         {
00521                                 manip2d.setVec(manipulator->mPosition.mV[VX] * half_width, manipulator->mPosition.mV[VY] * half_height);
00522                                 
00523                                 delta = manip2d - mousePos;
00524                                 if (delta.magVecSquared() < MAX_MANIP_SELECT_DISTANCE_SQUARED)
00525                                 {
00526                                         mHighlightedPart = manipulator->mManipID;
00527 
00528                                         //llinfos << "Tried: " << mHighlightedPart << llendl;
00529                                         break;
00530                                 }
00531                         }
00532         }
00533 
00534         for (S32 i = 0; i < NUM_MANIPULATORS; i++)
00535         {
00536                 if (mHighlightedPart == MANIPULATOR_IDS[i])
00537                 {
00538                         mManipulatorScales[i] = lerp(mManipulatorScales[i], SELECTED_MANIPULATOR_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00539                 }
00540                 else
00541                 {
00542                         mManipulatorScales[i] = lerp(mManipulatorScales[i], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
00543                 }
00544         }
00545 
00546         lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (inactive)" << llendl;
00547 }
00548 
00549 
00550 void LLManipScale::renderFaces( const LLBBox& bbox )
00551 {
00552         // Don't bother to render the drag handles for 1-D scaling if 
00553         // more than one object is selected or if it is an attachment
00554         if ( mObjectSelection->getObjectCount() > 1 )
00555         {
00556                 return;
00557         }
00558 
00559     // This is a flattened representation of the box as render here
00560     //                                       .
00561     //              (+++)        (++-)      /|\t
00562     //                +------------+         | (texture coordinates)
00563     //                |            |         |
00564     //                |     1      |        (*) --->s
00565     //                |    +X      |   
00566         //                |            |
00567     // (+++)     (+-+)|            |(+--)     (++-)        (+++)
00568     //   +------------+------------+------------+------------+
00569     //   |0          3|3          7|7          4|4          0|
00570     //   |     0      |     4      |     5      |     2      |
00571     //   |    +Z      |    -Y      |    -Z      |    +Y      |
00572     //   |                |            |            |            |
00573     //   |1          2|2          6|6          5|5          1|
00574     //   +------------+------------+------------+------------+
00575     // (-++)     (--+)|            |(---)     (-+-)        (-++)
00576     //                |     3      |
00577     //                |    -X      |
00578     //                |            |
00579     //                |            |
00580     //                +------------+
00581     //              (-++)        (-+-)
00582 
00583         LLColor4 highlight_color( 1.f, 1.f, 1.f, 0.5f);
00584         LLColor4 normal_color(  1.f, 1.f, 1.f, 0.3f);
00585 
00586         LLColor4 x_highlight_color( 1.f, 0.2f, 0.2f, 1.0f);
00587         LLColor4 x_normal_color(        0.6f, 0.f, 0.f, 0.4f);
00588 
00589         LLColor4 y_highlight_color( 0.2f, 1.f, 0.2f, 1.0f);
00590         LLColor4 y_normal_color(        0.f, 0.6f, 0.f, 0.4f);
00591 
00592         LLColor4 z_highlight_color( 0.2f, 0.2f, 1.f, 1.0f);
00593         LLColor4 z_normal_color(        0.f, 0.f, 0.6f, 0.4f);
00594 
00595         LLColor4 default_normal_color( 0.7f, 0.7f, 0.7f, 0.15f );
00596 
00597         const LLVector3& min = bbox.getMinLocal();
00598         const LLVector3& max = bbox.getMaxLocal();
00599         LLVector3 ctr = bbox.getCenterLocal();
00600 
00601         if (mManipPart == LL_NO_PART)
00602         {
00603                 glColor4fv( default_normal_color.mV );
00604                 LLGLDepthTest gls_depth(GL_FALSE);
00605                 glBegin(GL_QUADS); 
00606                 {
00607                         // Face 0
00608                         glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
00609                         glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
00610                         glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
00611                         glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
00612 
00613                         // Face 1
00614                         glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
00615                         glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
00616                         glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
00617                         glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
00618 
00619                         // Face 2
00620                         glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
00621                         glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
00622                         glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
00623                         glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
00624 
00625                         // Face 3
00626                         glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
00627                         glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
00628                         glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
00629                         glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
00630 
00631                         // Face 4
00632                         glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
00633                         glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
00634                         glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
00635                         glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
00636 
00637                         // Face 5
00638                         glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
00639                         glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
00640                         glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
00641                         glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
00642                 }
00643                 glEnd();
00644         }
00645 
00646         // Find nearest vertex
00647         LLVector3 orientWRTHead = bbox.agentToLocalBasis( bbox.getCenterAgent() - gAgent.getCameraPositionAgent() );
00648         U32 nearest = 
00649                 (orientWRTHead.mV[0] < 0.0f ? 1 : 0) + 
00650                 (orientWRTHead.mV[1] < 0.0f ? 2 : 0) + 
00651                 (orientWRTHead.mV[2] < 0.0f ? 4 : 0);
00652 
00653         // opposite faces on Linden cubes:
00654         // 0 & 5
00655         // 1 & 3
00656         // 2 & 4
00657 
00658         // Table of order to draw faces, based on nearest vertex
00659         static U32 face_list[8][6] = { 
00660                 { 2,0,1, 4,5,3 }, // v6  F201 F453
00661                 { 2,0,3, 4,5,1 }, // v7  F203 F451
00662                 { 4,0,1, 2,5,3 }, // v5  F401 F253
00663                 { 4,0,3, 2,5,1 }, // v4  F403 F251
00664                 { 2,5,1, 4,0,3 }, // v2  F251 F403
00665                 { 2,5,3, 4,0,1 }, // v3  F253 F401
00666                 { 4,5,1, 2,0,3 }, // v1  F451 F203
00667                 { 4,5,3, 2,0,1 }  // v0  F453 F201
00668         };
00669 
00670         {
00671                 LLGLDepthTest gls_depth(GL_FALSE);
00672 
00673                 for (S32 i = 0; i < 6; i++)
00674                 {
00675                         U32 face = face_list[nearest][i];
00676                         switch( face )
00677                         {
00678                           case 0:
00679                                 conditionalHighlight( LL_FACE_POSZ, &z_highlight_color, &z_normal_color );
00680                                 renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], max.mV[VZ] ) );
00681                                 break;
00682 
00683                           case 1:
00684                                 conditionalHighlight( LL_FACE_POSX, &x_highlight_color, &x_normal_color );
00685                                 renderAxisHandle( ctr, LLVector3( max.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) );
00686                                 break;
00687 
00688                           case 2:
00689                                 conditionalHighlight( LL_FACE_POSY, &y_highlight_color, &y_normal_color );
00690                                 renderAxisHandle( ctr, LLVector3( ctr.mV[VX], max.mV[VY], ctr.mV[VZ] ) );
00691                                 break;
00692 
00693                           case 3:
00694                                 conditionalHighlight( LL_FACE_NEGX, &x_highlight_color, &x_normal_color );
00695                                 renderAxisHandle( ctr, LLVector3( min.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) );
00696                                 break;
00697 
00698                           case 4:
00699                                 conditionalHighlight( LL_FACE_NEGY, &y_highlight_color, &y_normal_color );
00700                                 renderAxisHandle( ctr, LLVector3( ctr.mV[VX], min.mV[VY], ctr.mV[VZ] ) );
00701                                 break;
00702 
00703                           case 5:
00704                                 conditionalHighlight( LL_FACE_NEGZ, &z_highlight_color, &z_normal_color );
00705                                 renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], min.mV[VZ] ) );
00706                                 break;
00707                         }
00708                 }
00709         }
00710 }
00711 
00712 void LLManipScale::renderEdges( const LLBBox& bbox )
00713 {
00714         LLVector3 extent = bbox.getExtentLocal();
00715         F32 edge_width = mBoxHandleSize * .6f;
00716 
00717         for( U32 part = LL_EDGE_MIN; part <= LL_EDGE_MAX; part++ )
00718         {
00719                 LLVector3 direction = edgeToUnitVector( part );
00720                 LLVector3 center_to_edge = unitVectorToLocalBBoxExtent( direction, bbox );
00721 
00722                 glPushMatrix();
00723                 {
00724                         glTranslatef( center_to_edge.mV[0], center_to_edge.mV[1], center_to_edge.mV[2] );
00725                         conditionalHighlight( part );
00726                         glScalef( 
00727                                 direction.mV[0] ? edge_width : extent.mV[VX],
00728                                 direction.mV[1] ? edge_width : extent.mV[VY],
00729                                 direction.mV[2] ? edge_width : extent.mV[VZ] );
00730                         gBox.render();
00731                 }
00732                 glPopMatrix();
00733         }
00734 }
00735 
00736 
00737 void LLManipScale::renderCorners( const LLBBox& bbox )
00738 {
00739         U32 part = LL_CORNER_NNN;
00740 
00741         F32 x_offset = bbox.getMinLocal().mV[VX];
00742         for( S32 i=0; i < 2; i++ )
00743         {
00744                 F32 y_offset = bbox.getMinLocal().mV[VY];
00745                 for( S32 j=0; j < 2; j++ )
00746                 {
00747                         F32 z_offset = bbox.getMinLocal().mV[VZ];
00748                         for( S32 k=0; k < 2; k++ )
00749                         {
00750                                 conditionalHighlight( part );
00751                                 renderBoxHandle( x_offset, y_offset, z_offset );
00752                                 part++;
00753 
00754                                 z_offset = bbox.getMaxLocal().mV[VZ];
00755 
00756                         }
00757                         y_offset = bbox.getMaxLocal().mV[VY];
00758                 }
00759                 x_offset = bbox.getMaxLocal().mV[VX];
00760   }
00761 }
00762 
00763 
00764 void LLManipScale::renderBoxHandle( F32 x, F32 y, F32 z )
00765 {
00766         LLGLDisable gls_tex(GL_TEXTURE_2D);
00767         LLGLDepthTest gls_depth(GL_FALSE);
00768 
00769         glPushMatrix();
00770         {
00771                 glTranslatef( x, y, z );
00772                 glScalef( mScaledBoxHandleSize, mScaledBoxHandleSize, mScaledBoxHandleSize );
00773                 gBox.render();
00774         }
00775         glPopMatrix();
00776 }
00777 
00778 
00779 void LLManipScale::renderAxisHandle( const LLVector3& start, const LLVector3& end )
00780 {
00781         if( getShowAxes() )
00782         {
00783                 // Draws a single "jacks" style handle: a long, retangular box from start to end.
00784                 LLVector3 offset_start = end - start;
00785                 offset_start.normVec();
00786                 offset_start = start + mBoxHandleSize * offset_start;
00787 
00788                 LLVector3 delta = end - offset_start;
00789                 LLVector3 pos = offset_start + 0.5f * delta;
00790 
00791                 glPushMatrix();
00792                 {
00793                         glTranslatef( pos.mV[VX], pos.mV[VY], pos.mV[VZ] );
00794                         glScalef( 
00795                                 mBoxHandleSize + llabs(delta.mV[VX]),
00796                                 mBoxHandleSize + llabs(delta.mV[VY]),
00797                                 mBoxHandleSize + llabs(delta.mV[VZ]));
00798                         gBox.render();
00799                 }
00800                 glPopMatrix();
00801         }
00802         else
00803         {
00804                 renderBoxHandle( end.mV[VX], end.mV[VY], end.mV[VZ] );
00805         }
00806 }
00807 
00808 
00809 void LLManipScale::drag( S32 x, S32 y )
00810 {
00811         if( (LL_FACE_MIN <= (S32)mManipPart) 
00812                 && ((S32)mManipPart <= LL_FACE_MAX) )
00813         {
00814                 dragFace( x, y );
00815         }
00816         else
00817         if( (LL_CORNER_MIN <= (S32)mManipPart) 
00818                 && ((S32)mManipPart <= LL_CORNER_MAX) )
00819         {
00820                 dragCorner( x, y );
00821         }
00822         
00823         // store changes to override updates
00824         for (LLObjectSelection::iterator iter = gSelectMgr->getSelection()->begin();
00825                  iter != gSelectMgr->getSelection()->end(); iter++)
00826         {
00827                 LLSelectNode* selectNode = *iter;
00828                 LLViewerObject*cur = selectNode->getObject();
00829                 if( cur->permModify() && cur->permMove() && !cur->isAvatar())
00830                 {
00831                         selectNode->mLastScale = cur->getScale();
00832                         selectNode->mLastPositionLocal = cur->getPosition();
00833                 }
00834         }       
00835 
00836         gSelectMgr->updateSelectionCenter();
00837     gAgent.clearFocusObject();
00838 }
00839 
00840 // Scale around the 
00841 void LLManipScale::dragCorner( S32 x, S32 y )
00842 {
00843         LLBBox bbox     = gSelectMgr->getBBoxOfSelection();
00844 
00845         // Suppress scale if mouse hasn't moved.
00846         if (x == mLastMouseX && y == mLastMouseY)
00847         {
00848         //      sendUpdates(TRUE,TRUE,TRUE);
00849                 return;
00850         }
00851 
00852         mLastMouseX = x;
00853         mLastMouseY = y;
00854 
00855         LLVector3d drag_start_point_global      = mDragStartPointGlobal;
00856         LLVector3d drag_start_center_global = mDragStartCenterGlobal;
00857         LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global);
00858         LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global);
00859 
00860         LLVector3d drag_start_dir_d;
00861         drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global);
00862         LLVector3 drag_start_dir_f;
00863         drag_start_dir_f.setVec(drag_start_dir_d);
00864 
00865         F32 s = 0;
00866         F32 t = 0;
00867 
00868         nearestPointOnLineFromMouse(x, y, 
00869                                                 drag_start_center_agent,
00870                                                 drag_start_point_agent,
00871                                                 s, t );
00872 
00873         F32 drag_start_dist = dist_vec(drag_start_point_agent, drag_start_center_agent);
00874 
00875         if( s <= 0 )  // we only care about intersections in front of the camera
00876         {
00877                 return;
00878         }
00879 
00880         LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d;
00881 
00882         F32 scale_factor = t;
00883 
00884         BOOL uniform = LLManipScale::getUniform();
00885 
00886         if( !uniform )
00887         {
00888                 scale_factor = 0.5f + (scale_factor * 0.5f);
00889         }
00890 
00891         // check for snapping
00892         LLVector3 drag_center_agent = gAgent.getPosAgentFromGlobal(drag_point_global);
00893         LLVector3 mouse_on_plane1;
00894         getMousePointOnPlaneAgent(mouse_on_plane1, x, y, drag_center_agent, mScalePlaneNormal1);
00895         LLVector3 mouse_on_plane2;
00896         getMousePointOnPlaneAgent(mouse_on_plane2, x, y, drag_center_agent, mScalePlaneNormal2);
00897         LLVector3 mouse_dir_1 = mouse_on_plane1 - mScaleCenter;
00898         LLVector3 mouse_dir_2 = mouse_on_plane2 - mScaleCenter;
00899         LLVector3 mouse_to_scale_line_1 = mouse_dir_1 - projected_vec(mouse_dir_1, mScaleDir);
00900         LLVector3 mouse_to_scale_line_2 = mouse_dir_2 - projected_vec(mouse_dir_2, mScaleDir);
00901         LLVector3 mouse_to_scale_line_dir_1 = mouse_to_scale_line_1;
00902         mouse_to_scale_line_dir_1.normVec();
00903         if (mouse_to_scale_line_dir_1 * mSnapGuideDir1 < 0.f)
00904         {
00905                 // need to keep sign of mouse offset wrt to snap guide direction
00906                 mouse_to_scale_line_dir_1 *= -1.f;
00907         }
00908         LLVector3 mouse_to_scale_line_dir_2 = mouse_to_scale_line_2;
00909         mouse_to_scale_line_dir_2.normVec();
00910         if (mouse_to_scale_line_dir_2 * mSnapGuideDir2 < 0.f)
00911         {
00912                 // need to keep sign of mouse offset wrt to snap guide direction
00913                 mouse_to_scale_line_dir_2 *= -1.f;
00914         }
00915 
00916         F32 snap_dir_dot_mouse_offset1 = mSnapGuideDir1 * mouse_to_scale_line_dir_1;
00917         F32 snap_dir_dot_mouse_offset2 = mSnapGuideDir2 * mouse_to_scale_line_dir_2;
00918 
00919         F32 dist_from_scale_line_1 = mouse_to_scale_line_1 * mouse_to_scale_line_dir_1; 
00920         F32 dist_from_scale_line_2 = mouse_to_scale_line_2 * mouse_to_scale_line_dir_2;
00921 
00922         F32 max_scale = partToMaxScale(mManipPart, bbox);
00923         F32 min_scale = partToMinScale(mManipPart, bbox);
00924 
00925         BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled");
00926         if (snap_enabled && dist_from_scale_line_1 > mSnapRegimeOffset * snap_dir_dot_mouse_offset1)
00927         {
00928                 mInSnapRegime = TRUE;
00929                 LLVector3 projected_drag_pos = mouse_on_plane1 - (dist_from_scale_line_1 / snap_dir_dot_mouse_offset1) * mSnapGuideDir1;
00930                 F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir;
00931 
00932                 F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
00933                 F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions);
00934                 F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions);
00935 
00936                 mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale);
00937 
00938                 scale_factor = mScaleSnapValue / drag_start_dist;
00939                 if( !uniform )
00940                 {
00941                         scale_factor *= 0.5f;
00942                 }
00943         }
00944         else if (snap_enabled && dist_from_scale_line_2 > mSnapRegimeOffset * snap_dir_dot_mouse_offset2)
00945         {
00946                 mInSnapRegime = TRUE;
00947                 LLVector3 projected_drag_pos = mouse_on_plane2 - (dist_from_scale_line_2 / snap_dir_dot_mouse_offset2) * mSnapGuideDir2;
00948                 F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir;
00949 
00950                 F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
00951                 F32 snap_dist = mScaleSnapUnit2 / (2.f * cur_subdivisions);
00952                 F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit2 / cur_subdivisions);
00953 
00954                 mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale);
00955 
00956                 scale_factor = mScaleSnapValue / drag_start_dist;
00957                 if( !uniform )
00958                 {
00959                         scale_factor *= 0.5f;
00960                 }
00961         }
00962         else 
00963         {
00964                 mInSnapRegime = FALSE;
00965         }
00966 
00967         F32 max_scale_factor = MAX_OBJECT_SCALE / MIN_OBJECT_SCALE;
00968         F32 min_scale_factor = MIN_OBJECT_SCALE / MAX_OBJECT_SCALE;
00969 
00970         // find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale
00971         for (LLObjectSelection::iterator iter = mObjectSelection->begin();
00972                  iter != mObjectSelection->end(); iter++)
00973         {
00974                 LLSelectNode* selectNode = *iter;
00975                 LLViewerObject* cur = selectNode->getObject();
00976                 if(  cur->permModify() && cur->permMove() && !cur->isAvatar() )
00977                 {
00978                         const LLVector3& scale = selectNode->mSavedScale;
00979 
00980                         F32 cur_max_scale_factor = llmin( MAX_OBJECT_SCALE / scale.mV[VX], MAX_OBJECT_SCALE / scale.mV[VY], MAX_OBJECT_SCALE / scale.mV[VZ] );
00981                         max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor );
00982 
00983                         F32 cur_min_scale_factor = llmax( MIN_OBJECT_SCALE / scale.mV[VX], MIN_OBJECT_SCALE / scale.mV[VY], MIN_OBJECT_SCALE / scale.mV[VZ] );
00984                         min_scale_factor = llmax( min_scale_factor, cur_min_scale_factor );
00985                 }
00986         }
00987 
00988         scale_factor = llclamp( scale_factor, min_scale_factor, max_scale_factor );
00989 
00990         LLVector3d drag_global = uniform ? mDragStartCenterGlobal : mDragFarHitGlobal;
00991 
00992         // do the root objects i.e. (TRUE == cur->isRootEdit())
00993         for (LLObjectSelection::iterator iter = mObjectSelection->begin();
00994                  iter != mObjectSelection->end(); iter++)
00995         {
00996                 LLSelectNode* selectNode = *iter;
00997                 LLViewerObject* cur = selectNode->getObject();
00998                 if( cur->permModify() && cur->permMove() && !cur->isAvatar() && cur->isRootEdit() )
00999                 {
01000                         const LLVector3& scale = selectNode->mSavedScale;
01001                         cur->setScale( scale_factor * scale );
01002                         
01003                         LLVector3 delta_pos;
01004                         LLVector3 original_pos = cur->getPositionEdit();
01005                         LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor;
01006                         if (!cur->isAttachment())
01007                         {
01008                                 new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
01009                         }
01010                         cur->setPositionAbsoluteGlobal( new_pos_global );
01011                         rebuild(cur);
01012                         
01013                         delta_pos = cur->getPositionEdit() - original_pos;
01014 
01015                         if (selectNode->mIndividualSelection)
01016                         {
01017                                 // counter-translate child objects if we are moving the root as an individual
01018                                 for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++)
01019                                 {
01020                                         LLViewerObject* childp = cur->mChildList[child_num];
01021 
01022                                         if (cur->isAttachment())
01023                                         {
01024                                                 LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
01025                                                 childp->setPosition(child_pos);
01026                                         }
01027                                         else
01028                                         {
01029                                                 LLVector3d child_pos_delta(delta_pos);
01030                                                 // RN: this updates drawable position instantly
01031                                                 childp->setPositionAbsoluteGlobal(childp->getPositionGlobal() - child_pos_delta);
01032                                         }
01033                                         rebuild(childp);
01034                                 }
01035                         }
01036                 }
01037         }
01038         // do the child objects i.e. (FALSE == cur->isRootEdit())
01039         for (LLObjectSelection::iterator iter = mObjectSelection->begin();
01040                  iter != mObjectSelection->end(); iter++)
01041         {
01042                 LLSelectNode* selectNode = *iter;
01043                 LLViewerObject*cur = selectNode->getObject();
01044                 if( cur->permModify() && cur->permMove() && !cur->isAvatar() && !cur->isRootEdit() )
01045                 {
01046                         const LLVector3& scale = selectNode->mSavedScale;
01047                         cur->setScale( scale_factor * scale, FALSE );
01048                                                         
01049                         if (!selectNode->mIndividualSelection)
01050                         {
01051                                 cur->setPosition(selectNode->mSavedPositionLocal * scale_factor);
01052                         }
01053 
01054                         rebuild(cur);
01055                 }
01056         }
01057 
01058         
01059 
01060         mDragPointGlobal = drag_point_global;
01061 }
01062 
01063         
01064 void LLManipScale::dragFace( S32 x, S32 y )
01065 {
01066         // Suppress scale if mouse hasn't moved.
01067         if (x == mLastMouseX && y == mLastMouseY)
01068         {
01069         //      sendUpdates(TRUE,TRUE,FALSE);
01070                 return;
01071         }
01072 
01073         mLastMouseX = x;
01074         mLastMouseY = y;
01075 
01076         LLVector3d drag_start_point_global      = mDragStartPointGlobal;
01077         LLVector3d drag_start_center_global = mDragStartCenterGlobal;
01078         LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global);
01079         LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global);
01080 
01081         LLVector3d drag_start_dir_d;
01082         drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global);
01083         LLVector3 drag_start_dir_f;
01084         drag_start_dir_f.setVec(drag_start_dir_d);
01085 
01086         LLBBox bbox     = gSelectMgr->getBBoxOfSelection();
01087 
01088         F32 s = 0;
01089         F32 t = 0;
01090 
01091         nearestPointOnLineFromMouse(x,
01092                                                 y,
01093                                                 drag_start_center_agent,
01094                                                 drag_start_point_agent,
01095                                                 s, t );
01096 
01097         if( s <= 0 )  // we only care about intersections in front of the camera
01098         {
01099                 return;
01100         }
01101 
01102         LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d;
01103         LLVector3 part_dir_local        = partToUnitVector( mManipPart );
01104 
01105         // check for snapping
01106         LLVector3 mouse_on_plane;
01107         getMousePointOnPlaneAgent(mouse_on_plane, x, y, mScaleCenter, mScalePlaneNormal1 );
01108 
01109         LLVector3 mouse_on_scale_line = mScaleCenter + projected_vec(mouse_on_plane - mScaleCenter, mScaleDir);
01110         LLVector3 drag_delta(mouse_on_scale_line - drag_start_point_agent);
01111         F32 max_drag_dist = partToMaxScale(mManipPart, bbox);
01112         F32 min_drag_dist = partToMinScale(mManipPart, bbox);
01113 
01114         BOOL uniform = LLManipScale::getUniform();
01115         if( uniform )
01116         {
01117                 drag_delta *= 2.f;
01118         }
01119 
01120         LLVector3 scale_center_to_mouse = mouse_on_plane - mScaleCenter;
01121         F32 dist_from_scale_line = dist_vec(scale_center_to_mouse, (mouse_on_scale_line - mScaleCenter));
01122         F32 dist_along_scale_line = scale_center_to_mouse * mScaleDir;
01123 
01124         BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled");
01125 
01126         if (snap_enabled && dist_from_scale_line > mSnapRegimeOffset)
01127         {
01128                 mInSnapRegime = TRUE;
01129 
01130                 if (dist_along_scale_line > max_drag_dist)
01131                 {
01132                         mScaleSnapValue = max_drag_dist;
01133 
01134                         LLVector3 clamp_point = mScaleCenter + max_drag_dist * mScaleDir;
01135                         drag_delta.setVec(clamp_point - drag_start_point_agent);
01136                 }
01137                 else if (dist_along_scale_line < min_drag_dist)
01138                 {
01139                         mScaleSnapValue = min_drag_dist;
01140 
01141                         LLVector3 clamp_point = mScaleCenter + min_drag_dist * mScaleDir;
01142                         drag_delta.setVec(clamp_point - drag_start_point_agent);
01143                 }
01144                 else
01145                 {
01146                         F32 drag_dist = scale_center_to_mouse * mScaleDir;
01147                         F32 cur_subdivisions = llclamp(getSubdivisionLevel(mScaleCenter + mScaleDir * drag_dist, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
01148                         F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions);
01149                         F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions);
01150                         relative_snap_dist -= snap_dist;
01151 
01152                         //make sure that values that the scale is "snapped to"
01153                         //do not exceed/go under the applicable max/mins
01154                         //this causes the box to shift displacements ever so slightly
01155                         //although the "snap value" should go down to 0
01156                         //see Jira 1027
01157                         relative_snap_dist = llclamp(relative_snap_dist,
01158                                                                                  drag_dist - max_drag_dist,
01159                                                                                  drag_dist - min_drag_dist);
01160 
01161                         mScaleSnapValue = drag_dist - relative_snap_dist;
01162 
01163                         if (llabs(relative_snap_dist) < snap_dist)
01164                         {
01165                                 LLVector3 drag_correction = relative_snap_dist * mScaleDir;
01166                                 if (uniform)
01167                                 {
01168                                         drag_correction *= 2.f;
01169                                 }
01170 
01171                                 drag_delta -= drag_correction;
01172                         }
01173                 }
01174         }
01175         else 
01176         {
01177                 mInSnapRegime = FALSE;
01178         }
01179 
01180         BOOL send_scale_update = FALSE;
01181         BOOL send_position_update = FALSE;
01182 
01183         LLVector3 dir_agent;
01184         if( part_dir_local.mV[VX] )
01185         {
01186                 dir_agent = bbox.localToAgentBasis( LLVector3::x_axis );
01187         }
01188         else if( part_dir_local.mV[VY] )
01189         {
01190                 dir_agent = bbox.localToAgentBasis( LLVector3::y_axis );
01191         }
01192         else if( part_dir_local.mV[VZ] )
01193         {
01194                 dir_agent = bbox.localToAgentBasis( LLVector3::z_axis );
01195         }
01196         stretchFace( 
01197                 projected_vec(drag_start_dir_f, dir_agent) + drag_start_center_agent,
01198                 projected_vec(drag_delta, dir_agent));
01199         send_position_update = TRUE;
01200         send_scale_update = TRUE;
01201 
01202         mDragPointGlobal = drag_point_global;
01203 }
01204 
01205 void LLManipScale::sendUpdates( BOOL send_position_update, BOOL send_scale_update, BOOL corner )
01206 {
01207         // Throttle updates to 10 per second.
01208         static LLTimer  update_timer;
01209         F32 elapsed_time = update_timer.getElapsedTimeF32();
01210         const F32 UPDATE_DELAY = 0.1f;                                          //  min time between transmitted updates
01211 
01212         if( send_scale_update || send_position_update )
01213         {
01214                 U32 update_flags = UPD_NONE;
01215                 if (send_position_update)       update_flags |= UPD_POSITION;
01216                 if (send_scale_update)          update_flags |= UPD_SCALE;
01217                 
01218 //              BOOL send_type = SEND_INDIVIDUALS;
01219                 if (corner)
01220                 {
01221                         update_flags |= UPD_UNIFORM;
01222                 }
01223                 // keep this up to date for sendonmouseup
01224                 mLastUpdateFlags = update_flags;
01225 
01226                 // enforce minimum update delay and don't stream updates on sub-object selections
01227                 if( elapsed_time > UPDATE_DELAY && !gSavedSettings.getBOOL("EditLinkedParts") )
01228                 {
01229                         gSelectMgr->sendMultipleUpdate( update_flags );
01230                         update_timer.reset();
01231                         mSendUpdateOnMouseUp = FALSE;
01232                 }
01233                 else
01234                 {
01235                         mSendUpdateOnMouseUp = TRUE;
01236                 }
01237                 dialog_refresh_all();
01238         }
01239 }
01240 
01241 // Rescales in a single dimension.  Either uniform (standard) or one-sided (scale plus translation)
01242 // depending on mUniform.  Handles multiple selection and objects that are not aligned to the bounding box.
01243 void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVector3& drag_delta_agent )
01244 {
01245         LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(mDragStartCenterGlobal);
01246 
01247         for (LLObjectSelection::iterator iter = mObjectSelection->begin();
01248                  iter != mObjectSelection->end(); iter++)
01249         {
01250                 LLSelectNode* selectNode = *iter;
01251                 LLViewerObject*cur = selectNode->getObject();
01252                 if( cur->permModify() && cur->permMove() && !cur->isAvatar() )
01253                 {
01254                         LLBBox cur_bbox                 = cur->getBoundingBoxAgent();
01255                         LLVector3 start_local   = cur_bbox.agentToLocal( drag_start_agent );
01256                         LLVector3 end_local             = cur_bbox.agentToLocal( drag_start_agent + drag_delta_agent);
01257                         LLVector3 start_center_local = cur_bbox.agentToLocal( drag_start_center_agent );
01258                         LLVector3 axis                  = nearestAxis( start_local - start_center_local );
01259                         S32 axis_index                  = axis.mV[0] ? 0 : (axis.mV[1] ? 1 : 2 );
01260 
01261                         LLVector3 delta_local   = end_local - start_local;
01262                         F32 delta_local_mag             = delta_local.magVec();
01263                         LLVector3 dir_local;
01264                         if (delta_local_mag == 0.f)
01265                         {
01266                                 dir_local = axis;
01267                         }
01268                         else
01269                         {
01270                                 dir_local = delta_local / delta_local_mag; // normalized delta_local
01271                         }
01272 
01273                         F32 denom = axis * dir_local;
01274                         F32 desired_delta_size  = is_approx_zero(denom) ? 0.f : (delta_local_mag / denom);  // in meters
01275                         F32 desired_scale               = llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_OBJECT_SCALE, MAX_OBJECT_SCALE);
01276                         // propagate scale constraint back to position offset
01277                         desired_delta_size              = desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position
01278 
01279                         LLVector3 scale                 = cur->getScale();
01280                         scale.mV[axis_index]    = desired_scale;
01281                         cur->setScale(scale, FALSE);
01282                         rebuild(cur);                           
01283                         LLVector3 delta_pos;
01284                         if( !getUniform() )
01285                         {
01286                                 LLVector3 delta_pos_local = axis * (0.5f * desired_delta_size);
01287                                 LLVector3d delta_pos_global;
01288                                 delta_pos_global.setVec(cur_bbox.localToAgent( delta_pos_local ) - cur_bbox.getCenterAgent());
01289                                 LLVector3 cur_pos = cur->getPositionEdit();
01290 
01291                                 if (cur->isRootEdit() && !cur->isAttachment())
01292                                 {
01293                                         LLVector3d new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, selectNode->mSavedPositionGlobal + delta_pos_global);
01294                                         cur->setPositionGlobal( new_pos_global );
01295                                 }
01296                                 else
01297                                 {
01298                                         LLXform* parent_xform = cur->mDrawable->getXform()->getParent();
01299                                         LLVector3 new_pos_local;
01300                                         // this works in attachment point space using world space delta
01301                                         if (parent_xform)
01302                                         {
01303                                                 new_pos_local = selectNode->mSavedPositionLocal + (LLVector3(delta_pos_global) * ~parent_xform->getWorldRotation());
01304                                         }
01305                                         else
01306                                         {
01307                                                 new_pos_local = selectNode->mSavedPositionLocal + LLVector3(delta_pos_global);
01308                                         }
01309                                         cur->setPosition(new_pos_local);
01310                                 }
01311                                 delta_pos = cur->getPositionEdit() - cur_pos;
01312                         }
01313                         if (cur->isRootEdit() && selectNode->mIndividualSelection)
01314                         {
01315                                 // counter-translate child objects if we are moving the root as an individual
01316                                 for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++)
01317                                 {
01318                                         LLViewerObject* childp = cur->mChildList[child_num];
01319                                         if (!getUniform())
01320                                         {
01321                                                 LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
01322                                                 childp->setPosition(child_pos);
01323                                                 rebuild(childp);
01324                                         }
01325                                 }
01326                         }
01327                 }
01328         }
01329 }
01330 
01331 
01332 void LLManipScale::renderGuidelinesPart( const LLBBox& bbox )
01333 {
01334         LLVector3 guideline_start = bbox.getCenterLocal();
01335         
01336         LLVector3 guideline_end = unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox );
01337 
01338         if (!getUniform())
01339         {
01340                 guideline_start = unitVectorToLocalBBoxExtent( -partToUnitVector( mManipPart ), bbox );
01341         }
01342 
01343         guideline_end -= guideline_start;
01344         guideline_end.normVec();
01345         guideline_end *= gWorldPointer->getRegionWidthInMeters();
01346         guideline_end += guideline_start;
01347 
01348         {
01349                 LLGLDepthTest gls_depth(GL_TRUE);
01350                 gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.5f) );
01351         }
01352         {
01353                 LLGLDepthTest gls_depth(GL_FALSE);
01354                 gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.25f) );
01355         }
01356 }
01357 
01358 void LLManipScale::updateSnapGuides(const LLBBox& bbox)
01359 {
01360         LLVector3 grid_origin;
01361         LLVector3 grid_scale;
01362         LLQuaternion grid_rotation;
01363         gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
01364 
01365         LLVector3 box_corner_agent = bbox.localToAgent(unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ));
01366         mScaleCenter = getUniform() ? bbox.getCenterAgent() : bbox.localToAgent(unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox ));
01367         mScaleDir = box_corner_agent - mScaleCenter;
01368         mScaleDir.normVec();
01369 
01370         if(mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
01371         {
01372                 mSnapRegimeOffset = SNAP_GUIDE_SCREEN_OFFSET / gAgent.getAvatarObject()->mHUDCurZoom;
01373 
01374         }
01375         else
01376         {
01377                 F32 object_distance = dist_vec(mScaleCenter, gCamera->getOrigin());
01378                 mSnapRegimeOffset = (SNAP_GUIDE_SCREEN_OFFSET * gViewerWindow->getWindowWidth() * object_distance) / gCamera->getPixelMeterRatio();
01379         }
01380         LLVector3 cam_at_axis;
01381         F32 snap_guide_length;
01382         if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
01383         {
01384                 cam_at_axis.setVec(1.f, 0.f, 0.f);
01385                 snap_guide_length = SNAP_GUIDE_SCREEN_LENGTH / gAgent.getAvatarObject()->mHUDCurZoom;
01386         }
01387         else
01388         {
01389                 cam_at_axis = gCamera->getAtAxis();
01390                 F32 manipulator_distance = dist_vec(box_corner_agent, gCamera->getOrigin());
01391                 snap_guide_length = (SNAP_GUIDE_SCREEN_LENGTH * gViewerWindow->getWindowWidth() * manipulator_distance) / gCamera->getPixelMeterRatio();
01392         }
01393         
01394         mSnapGuideLength = snap_guide_length / llmax(0.1f, (llmin(mSnapGuideDir1 * cam_at_axis, mSnapGuideDir2 * cam_at_axis)));
01395 
01396         LLVector3 off_axis_dir = mScaleDir % cam_at_axis;
01397         off_axis_dir.normVec();
01398 
01399         if( (LL_FACE_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_FACE_MAX) )
01400         {
01401                 LLVector3 object_scale = bbox.getMaxLocal();
01402                 object_scale.scaleVec(off_axis_dir * ~bbox.getRotation());
01403                 object_scale.abs();
01404                 if (object_scale.mV[VX] > object_scale.mV[VY] && object_scale.mV[VX] > object_scale.mV[VZ])
01405                 {
01406                         mSnapGuideDir1 = LLVector3::x_axis * bbox.getRotation();
01407                 }
01408                 else if (object_scale.mV[VY] > object_scale.mV[VZ])
01409                 {
01410                         mSnapGuideDir1 = LLVector3::y_axis * bbox.getRotation();
01411                 }
01412                 else
01413                 {
01414                         mSnapGuideDir1 = LLVector3::z_axis * bbox.getRotation();
01415                 }
01416 
01417                 LLVector3 scale_snap = grid_scale;
01418                 mScaleSnapUnit1 = scale_snap.scaleVec(partToUnitVector( mManipPart )).magVec();
01419                 mScaleSnapUnit2 = mScaleSnapUnit1;
01420                 mSnapGuideDir1 *= mSnapGuideDir1 * gCamera->getUpAxis() > 0.f ? 1.f : -1.f;
01421                 mSnapGuideDir2 = mSnapGuideDir1 * -1.f;
01422                 mSnapDir1 = mScaleDir;
01423                 mSnapDir2 = mScaleDir;
01424         }
01425         else if( (LL_CORNER_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_CORNER_MAX) )
01426         {
01427                 LLVector3 local_scale_dir = partToUnitVector( mManipPart );
01428                 LLVector3 local_camera_dir;
01429                 if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
01430                 {
01431                         local_camera_dir = LLVector3(-1.f, 0.f, 0.f) * ~bbox.getRotation();
01432                 }
01433                 else
01434                 {
01435                         local_camera_dir = (gCamera->getOrigin() - bbox.getCenterAgent()) * ~bbox.getRotation();
01436                         local_camera_dir.normVec();
01437                 }
01438                 local_scale_dir -= projected_vec(local_scale_dir, local_camera_dir);
01439                 local_scale_dir.normVec();
01440                 LLVector3 x_axis_proj_camera = LLVector3::x_axis - projected_vec(LLVector3::x_axis, local_camera_dir);
01441                 x_axis_proj_camera.normVec();
01442                 LLVector3 y_axis_proj_camera = LLVector3::y_axis - projected_vec(LLVector3::y_axis, local_camera_dir);
01443                 y_axis_proj_camera.normVec();
01444                 LLVector3 z_axis_proj_camera = LLVector3::z_axis - projected_vec(LLVector3::z_axis, local_camera_dir);
01445                 z_axis_proj_camera.normVec();
01446                 F32 x_axis_proj = llabs(local_scale_dir * x_axis_proj_camera);
01447                 F32 y_axis_proj = llabs(local_scale_dir * y_axis_proj_camera);
01448                 F32 z_axis_proj = llabs(local_scale_dir * z_axis_proj_camera);
01449 
01450                 if (x_axis_proj > y_axis_proj && x_axis_proj > z_axis_proj)
01451                 {
01452                         mSnapGuideDir1 = LLVector3::y_axis;
01453                         mScaleSnapUnit2 = grid_scale.mV[VY];
01454                         mSnapGuideDir2 = LLVector3::z_axis;
01455                         mScaleSnapUnit1 = grid_scale.mV[VZ];
01456                 }
01457                 else if (y_axis_proj > z_axis_proj)
01458                 {
01459                         mSnapGuideDir1 = LLVector3::x_axis;
01460                         mScaleSnapUnit2 = grid_scale.mV[VX];
01461                         mSnapGuideDir2 = LLVector3::z_axis;
01462                         mScaleSnapUnit1 = grid_scale.mV[VZ];
01463                 }
01464                 else
01465                 {
01466                         mSnapGuideDir1 = LLVector3::x_axis;
01467                         mScaleSnapUnit2 = grid_scale.mV[VX];
01468                         mSnapGuideDir2 = LLVector3::y_axis;
01469                         mScaleSnapUnit1 = grid_scale.mV[VY];
01470                 }
01471 
01472                 LLVector3 snap_guide_flip(1.f, 1.f, 1.f);
01473                 switch (mManipPart)
01474                 {
01475                 case LL_CORNER_NNN:
01476                         break;
01477                 case LL_CORNER_NNP:
01478                         snap_guide_flip.setVec(1.f, 1.f, -1.f);
01479                         break;
01480                 case LL_CORNER_NPN:
01481                         snap_guide_flip.setVec(1.f, -1.f, 1.f);
01482                         break;
01483                 case LL_CORNER_NPP:
01484                         snap_guide_flip.setVec(1.f, -1.f, -1.f);
01485                         break;
01486                 case LL_CORNER_PNN:
01487                         snap_guide_flip.setVec(-1.f, 1.f, 1.f);
01488                         break;
01489                 case LL_CORNER_PNP:
01490                         snap_guide_flip.setVec(-1.f, 1.f, -1.f);
01491                         break;
01492                 case LL_CORNER_PPN:
01493                         snap_guide_flip.setVec(-1.f, -1.f, 1.f);
01494                         break;
01495                 case LL_CORNER_PPP:
01496                         snap_guide_flip.setVec(-1.f, -1.f, -1.f);
01497                         break;
01498                 default:
01499                         break;
01500                 }
01501                 mSnapGuideDir1.scaleVec(snap_guide_flip);
01502                 mSnapGuideDir2.scaleVec(snap_guide_flip);
01503                 mSnapGuideDir1.rotVec(bbox.getRotation());
01504                 mSnapGuideDir2.rotVec(bbox.getRotation());
01505                 mSnapDir1 = -1.f * mSnapGuideDir2;
01506                 mSnapDir2 = -1.f * mSnapGuideDir1;
01507         }
01508 
01509         mScalePlaneNormal1 = mSnapGuideDir1 % mScaleDir;
01510         mScalePlaneNormal1.normVec();
01511 
01512         mScalePlaneNormal2 = mSnapGuideDir2 % mScaleDir;
01513         mScalePlaneNormal2.normVec();   
01514 
01515         mScaleSnapUnit1 = mScaleSnapUnit1 / (mSnapDir1 * mScaleDir);
01516         mScaleSnapUnit2 = mScaleSnapUnit2 / (mSnapDir2 * mScaleDir);
01517 }
01518 
01519 void LLManipScale::renderSnapGuides(const LLBBox& bbox)
01520 {
01521         if (!gSavedSettings.getBOOL("SnapEnabled"))
01522         {
01523                 return;
01524         }
01525 
01526         F32 max_subdivisions = sGridMaxSubdivisionLevel;
01527         F32 grid_alpha = gSavedSettings.getF32("GridOpacity");
01528 
01529         F32 max_point_on_scale_line = partToMaxScale(mManipPart, bbox);
01530         LLVector3 drag_point = gAgent.getPosAgentFromGlobal(mDragPointGlobal);
01531 
01532         updateGridSettings();
01533 
01534         S32 pass;
01535         for (pass = 0; pass < 3; pass++)
01536         {
01537                 LLColor4 tick_color = setupSnapGuideRenderPass(pass);
01538 
01539                 glBegin(GL_LINES);
01540                 LLVector3 line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir1 * mSnapRegimeOffset);
01541                 LLVector3 line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f)));
01542                 LLVector3 line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f));
01543                 
01544                 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
01545                 glVertex3fv(line_start.mV);
01546                 glColor4fv(tick_color.mV);
01547                 glVertex3fv(line_mid.mV);
01548                 glVertex3fv(line_mid.mV);
01549                 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
01550                 glVertex3fv(line_end.mV);
01551 
01552                 line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir2 * mSnapRegimeOffset);
01553                 line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f)));
01554                 line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f));
01555                 glVertex3fv(line_start.mV);
01556                 glColor4fv(tick_color.mV);
01557                 glVertex3fv(line_mid.mV);
01558                 glVertex3fv(line_mid.mV);
01559                 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
01560                 glVertex3fv(line_end.mV);
01561                 glEnd();
01562         }
01563 
01564         {
01565                 LLGLDepthTest gls_depth(GL_FALSE);
01566 
01567                 F32 dist_grid_axis = (drag_point - mScaleCenter) * mScaleDir;
01568                 // find distance to nearest smallest grid unit
01569                 F32 grid_offset1 = fmodf(dist_grid_axis, mScaleSnapUnit1 / max_subdivisions);
01570                 F32 grid_offset2 = fmodf(dist_grid_axis, mScaleSnapUnit2 / max_subdivisions);
01571 
01572                 // how many smallest grid units are we away from largest grid scale?
01573                 S32 sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 / sGridMinSubdivisionLevel) / (mScaleSnapUnit1 / max_subdivisions));
01574                 S32 sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 / sGridMinSubdivisionLevel) / (mScaleSnapUnit2 / max_subdivisions));
01575 
01576                 S32 num_ticks_per_side1 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit1 / max_subdivisions)));
01577                 S32 num_ticks_per_side2 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit2 / max_subdivisions)));
01578                 F32 dist_scale_units_1 = dist_grid_axis / (mScaleSnapUnit1 / max_subdivisions);
01579                 F32 dist_scale_units_2 = dist_grid_axis / (mScaleSnapUnit2 / max_subdivisions);
01580                 S32 ticks_from_scale_center_1 = lltrunc(dist_scale_units_1);
01581                 S32 ticks_from_scale_center_2 = lltrunc(dist_scale_units_2);
01582                 S32 max_ticks1 = llceil(max_point_on_scale_line / (mScaleSnapUnit1 / max_subdivisions) - dist_scale_units_1);
01583                 S32 max_ticks2 = llceil(max_point_on_scale_line / (mScaleSnapUnit2 / max_subdivisions) - dist_scale_units_2);
01584                 S32 start_tick = 0;
01585                 S32 stop_tick = 0;
01586 
01587                 if (mInSnapRegime)
01588                 {
01589                         // draw snap guide line
01590                         glBegin(GL_LINES);
01591                         LLVector3 snap_line_center = mScaleCenter + (mScaleSnapValue * mScaleDir);
01592 
01593                         LLVector3 snap_line_start = snap_line_center + (mSnapGuideDir1 * mSnapRegimeOffset);
01594                         LLVector3 snap_line_end = snap_line_center + (mSnapGuideDir2 * mSnapRegimeOffset);
01595 
01596                         glColor4f(1.f, 1.f, 1.f, grid_alpha);
01597                         glVertex3fv(snap_line_start.mV);
01598                         glVertex3fv(snap_line_center.mV);
01599                         glVertex3fv(snap_line_center.mV);
01600                         glVertex3fv(snap_line_end.mV);
01601                         glEnd();
01602 
01603                         // draw snap guide arrow
01604                         glBegin(GL_TRIANGLES);
01605                         {
01606                                 //gGLSNoCullFaces.set();
01607                                 glColor4f(1.f, 1.f, 1.f, grid_alpha);
01608 
01609                                 LLVector3 arrow_dir;
01610                                 LLVector3 arrow_span = mScaleDir;
01611 
01612                                 arrow_dir = snap_line_start - snap_line_center;
01613                                 arrow_dir.normVec();
01614                                 glVertex3fv((snap_line_start + arrow_dir * mBoxHandleSize).mV);
01615                                 glVertex3fv((snap_line_start + arrow_span * mBoxHandleSize).mV);
01616                                 glVertex3fv((snap_line_start - arrow_span * mBoxHandleSize).mV);
01617 
01618                                 arrow_dir = snap_line_end - snap_line_center;
01619                                 arrow_dir.normVec();
01620                                 glVertex3fv((snap_line_end + arrow_dir * mBoxHandleSize).mV);
01621                                 glVertex3fv((snap_line_end + arrow_span * mBoxHandleSize).mV);
01622                                 glVertex3fv((snap_line_end - arrow_span * mBoxHandleSize).mV);
01623                         }
01624                         glEnd();
01625                 }
01626         
01627                 LLVector2 screen_translate_axis(llabs(mScaleDir * gCamera->getLeftAxis()), llabs(mScaleDir * gCamera->getUpAxis()));
01628                 screen_translate_axis.normVec();
01629 
01630                 S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing);
01631 
01632                 for (pass = 0; pass < 3; pass++)
01633                 {
01634                         LLColor4 tick_color = setupSnapGuideRenderPass(pass);
01635 
01636                         start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1));
01637                         stop_tick = llmin(max_ticks1, num_ticks_per_side1);
01638 
01639                         glBegin(GL_LINES);
01640                         // draw first row of ticks
01641                         for (S32 i = start_tick; i <= stop_tick; i++)
01642                         {
01643                                 F32 alpha = (1.f - (1.f *  ((F32)llabs(i) / (F32)num_ticks_per_side1)));
01644                                 LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1));
01645 
01646                                 F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
01647 
01648                                 if (fmodf((F32)(i + sub_div_offset_1), (max_subdivisions / cur_subdivisions)) != 0.f)
01649                                 {
01650                                         continue;
01651                                 }
01652 
01653                                 F32 tick_scale = 1.f;
01654                                 for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
01655                                 {
01656                                         if (fmodf((F32)(i + sub_div_offset_1), division_level) == 0.f)
01657                                         {
01658                                                 break;
01659                                         }
01660                                         tick_scale *= 0.7f;
01661                                 }
01662 
01663                                 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha);
01664                                 LLVector3 tick_start = tick_pos + (mSnapGuideDir1 * mSnapRegimeOffset);
01665                                 LLVector3 tick_end = tick_start + (mSnapGuideDir1 * mSnapRegimeOffset * tick_scale);
01666                                 glVertex3fv(tick_start.mV);
01667                                 glVertex3fv(tick_end.mV);
01668                         }
01669 
01670                         // draw opposite row of ticks
01671                         start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2));
01672                         stop_tick = llmin(max_ticks2, num_ticks_per_side2);
01673                         for (S32 i = start_tick; i <= stop_tick; i++)
01674                         {
01675                                 F32 alpha = (1.f - (1.f *  ((F32)llabs(i) / (F32)num_ticks_per_side2)));
01676                                 LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2));
01677 
01678                                 F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
01679 
01680                                 if (fmodf((F32)(i + sub_div_offset_2), (max_subdivisions / cur_subdivisions)) != 0.f)
01681                                 {
01682                                         continue;
01683                                 }
01684 
01685                                 F32 tick_scale = 1.f;
01686                                 for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
01687                                 {
01688                                         if (fmodf((F32)(i + sub_div_offset_2), division_level) == 0.f)
01689                                         {
01690                                                 break;
01691                                         }
01692                                         tick_scale *= 0.7f;
01693                                 }
01694 
01695                                 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha);
01696                                 LLVector3 tick_start = tick_pos + (mSnapGuideDir2 * mSnapRegimeOffset);
01697                                 LLVector3 tick_end = tick_start + (mSnapGuideDir2 * mSnapRegimeOffset * tick_scale);
01698                                 glVertex3fv(tick_start.mV);
01699                                 glVertex3fv(tick_end.mV);
01700                         }
01701                         glEnd();
01702                 }
01703 
01704                 // render tick labels
01705                 start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1));
01706                 stop_tick = llmin(max_ticks1, num_ticks_per_side1);
01707 
01708                 F32 grid_resolution = mObjectSelection->getSelectType() == SELECT_TYPE_HUD ? 0.25f : llmax(gSavedSettings.getF32("GridResolution"), 0.001f);
01709                 S32 label_sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1  * 32.f) / (mScaleSnapUnit1 / max_subdivisions));
01710                 S32 label_sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2  * 32.f) / (mScaleSnapUnit2 / max_subdivisions));
01711 
01712                 for (S32 i = start_tick; i <= stop_tick; i++)
01713                 {
01714                         F32 tick_scale = 1.f;
01715                         F32 alpha = grid_alpha * (1.f - (0.5f *  ((F32)llabs(i) / (F32)num_ticks_per_side1)));
01716                         LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1));
01717 
01718                         for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
01719                         {
01720                                 if (fmodf((F32)(i + label_sub_div_offset_1), division_level) == 0.f)
01721                                 {
01722                                         break;
01723                                 }
01724                                 tick_scale *= 0.7f;
01725                         }
01726 
01727                         if (fmodf((F32)(i + label_sub_div_offset_1), (max_subdivisions / llmin(sGridMaxSubdivisionLevel, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1, tick_label_spacing)))) == 0.f)
01728                         {
01729                                 LLVector3 text_origin = tick_pos + 
01730                                         (mSnapGuideDir1 * mSnapRegimeOffset * (1.f + tick_scale));
01731 
01732                                 EGridMode grid_mode = gSelectMgr->getGridMode();
01733                                 F32 tick_val;
01734                                 if (grid_mode == GRID_MODE_WORLD)
01735                                 {
01736                                         tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 / grid_resolution);
01737                                 }
01738                                 else
01739                                 {
01740                                         tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 * 2.f);
01741                                 }
01742 
01743                                 if (getUniform())
01744                                 {
01745                                         tick_val *= 2.f;
01746                                 }
01747 
01748                                 F32 text_highlight = 0.8f;
01749 
01750                                 if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime)
01751                                 {
01752                                         text_highlight = 1.f;
01753                                 }
01754 
01755                                 renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
01756                         }
01757                 }
01758 
01759                 // label ticks on opposite side
01760                 if (mScaleSnapUnit2 != mScaleSnapUnit1)
01761                 {
01762                         start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2));
01763                         stop_tick = llmin(max_ticks2, num_ticks_per_side2);
01764                         for (S32 i = start_tick; i <= stop_tick; i++)
01765                         {
01766                                 F32 tick_scale = 1.f;
01767                                 F32 alpha = grid_alpha * (1.f - (0.5f *  ((F32)llabs(i) / (F32)num_ticks_per_side2)));
01768                                 LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2));
01769 
01770                                 for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
01771                                 {
01772                                         if (fmodf((F32)(i + label_sub_div_offset_2), division_level) == 0.f)
01773                                         {
01774                                                 break;
01775                                         }
01776                                         tick_scale *= 0.7f;
01777                                 }
01778 
01779                                 if (fmodf((F32)(i + label_sub_div_offset_2), (max_subdivisions / llmin(max_subdivisions, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2, tick_label_spacing)))) == 0.f)
01780                                 {
01781                                         LLVector3 text_origin = tick_pos + 
01782                                                 (mSnapGuideDir2 * mSnapRegimeOffset * (1.f + tick_scale));
01783 
01784                                         EGridMode grid_mode = gSelectMgr->getGridMode();
01785                                         F32 tick_val;
01786                                         if (grid_mode == GRID_MODE_WORLD)
01787                                         {
01788                                                 tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 / grid_resolution);
01789                                         }
01790                                         else
01791                                         {
01792                                                 tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 * 2.f);
01793                                         }
01794 
01795                                         if (getUniform())
01796                                         {
01797                                                 tick_val *= 2.f;
01798                                         }
01799 
01800                                         F32 text_highlight = 0.8f;
01801 
01802                                         if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime)
01803                                         {
01804                                                 text_highlight = 1.f;
01805                                         }
01806 
01807                                         renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
01808                                 }
01809                         }
01810                 }
01811 
01812 
01813                 // render help text
01814                 if (mObjectSelection->getSelectType() != SELECT_TYPE_HUD)
01815                 {
01816                         if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText)
01817                         {
01818                                 LLVector3 selection_center_start = gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
01819 
01820                                 LLVector3 offset_dir;
01821                                 if (mSnapGuideDir1 * gCamera->getAtAxis() > mSnapGuideDir2 * gCamera->getAtAxis())
01822                                 {
01823                                         offset_dir = mSnapGuideDir2;
01824                                 }
01825                                 else
01826                                 {
01827                                         offset_dir = mSnapGuideDir1;
01828                                 }
01829 
01830                                 LLVector3 help_text_pos = selection_center_start + (mSnapRegimeOffset * 5.f * offset_dir);
01831                                 const LLFontGL* big_fontp = LLFontGL::sSansSerif;
01832 
01833                                 std::string help_text = "Move mouse cursor over ruler";
01834                                 LLColor4 help_text_color = LLColor4::white;
01835                                 help_text_color.mV[VALPHA] = clamp_rescale(mHelpTextTimer.getElapsedTimeF32(), sHelpTextVisibleTime, sHelpTextVisibleTime + sHelpTextFadeTime, grid_alpha, 0.f);
01836                                 hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, mObjectSelection->getSelectType() == SELECT_TYPE_HUD);
01837                                 help_text = "to snap to grid";
01838                                 help_text_pos -= gCamera->getUpAxis() * mSnapRegimeOffset * 0.4f;
01839                                 hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, mObjectSelection->getSelectType() == SELECT_TYPE_HUD);
01840                         }
01841                 }
01842         }
01843 }
01844 
01845 // Returns unit vector in direction of part of an origin-centered cube
01846 LLVector3 LLManipScale::partToUnitVector( S32 part ) const
01847 {
01848         if( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) )
01849         {
01850                 return faceToUnitVector( part );
01851         }
01852         else
01853         if( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) )
01854         {
01855                 return cornerToUnitVector( part );
01856         }
01857         else
01858         if( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX ) )
01859         {
01860                 return edgeToUnitVector( part );
01861         }
01862         return LLVector3();
01863 }
01864 
01865 
01866 // Returns unit vector in direction of face of an origin-centered cube
01867 LLVector3 LLManipScale::faceToUnitVector( S32 part ) const
01868 {
01869         llassert( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) );
01870         switch( part )
01871         {
01872                 case LL_FACE_POSX:
01873                         return LLVector3(  1.f,  0.f,  0.f );
01874 
01875                 case LL_FACE_NEGX:
01876                         return LLVector3( -1.f,  0.f,  0.f );
01877 
01878                 case LL_FACE_POSY:
01879                         return LLVector3(  0.f,  1.f,  0.f );
01880 
01881                 case LL_FACE_NEGY:
01882                         return LLVector3(  0.f, -1.f,  0.f );
01883 
01884                 case LL_FACE_POSZ:
01885                         return LLVector3(  0.f,  0.f,  1.f );
01886 
01887                 case LL_FACE_NEGZ:
01888                         return LLVector3(  0.f,  0.f, -1.f );
01889         }
01890         return LLVector3();
01891 }
01892 
01893 
01894 // Returns unit vector in direction of corner of an origin-centered cube
01895 LLVector3 LLManipScale::cornerToUnitVector( S32 part ) const
01896 {
01897         llassert( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) );
01898         LLVector3 vec;
01899         switch(part)
01900         {
01901                 case LL_CORNER_NNN:
01902                         vec.setVec(-F_SQRT3, -F_SQRT3, -F_SQRT3);
01903                         break;
01904                 case LL_CORNER_NNP:             
01905                         vec.setVec(-F_SQRT3, -F_SQRT3, F_SQRT3);
01906                         break;
01907                 case LL_CORNER_NPN:
01908                         vec.setVec(-F_SQRT3, F_SQRT3, -F_SQRT3);
01909                         break;
01910                 case LL_CORNER_NPP:
01911                         vec.setVec(-F_SQRT3, F_SQRT3, F_SQRT3);
01912                         break;
01913                 case LL_CORNER_PNN:
01914                         vec.setVec(F_SQRT3, -F_SQRT3, -F_SQRT3);
01915                         break;
01916                 case LL_CORNER_PNP:
01917                         vec.setVec(F_SQRT3, -F_SQRT3, F_SQRT3);
01918                         break;
01919                 case LL_CORNER_PPN:
01920                         vec.setVec(F_SQRT3, F_SQRT3, -F_SQRT3);
01921                         break;
01922                 case LL_CORNER_PPP:
01923                         vec.setVec(F_SQRT3, F_SQRT3, F_SQRT3);
01924                         break;
01925                 default:
01926                         vec.clearVec();
01927         }
01928 
01929         return vec;
01930 }
01931 
01932 // Returns unit vector in direction of edge of an origin-centered cube
01933 LLVector3 LLManipScale::edgeToUnitVector( S32 part ) const
01934 {
01935         llassert( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX) );
01936         part -= LL_EDGE_MIN;
01937         S32 rotation = part >> 2;                               // Edge between which faces: 0 => XY, 1 => YZ, 2 => ZX
01938         LLVector3 v;
01939         v.mV[rotation]                  = (part & 1) ? F_SQRT2 : -F_SQRT2;
01940         v.mV[(rotation+1) % 3]  = (part & 2) ? F_SQRT2 : -F_SQRT2;
01941         // v.mV[(rotation+2) % 3] defaults to 0.
01942         return v;
01943 }
01944 
01945 // Non-linear scale of origin-centered unit cube to non-origin-centered, non-symetrical bounding box
01946 LLVector3 LLManipScale::unitVectorToLocalBBoxExtent( const LLVector3& v, const LLBBox& bbox ) const
01947 {
01948         const LLVector3& min = bbox.getMinLocal();
01949         const LLVector3& max = bbox.getMaxLocal();
01950         LLVector3 ctr = bbox.getCenterLocal();
01951 
01952         return LLVector3(
01953                 v.mV[0] ? (v.mV[0]>0 ? max.mV[0] : min.mV[0] ) : ctr.mV[0], 
01954                 v.mV[1] ? (v.mV[1]>0 ? max.mV[1] : min.mV[1] ) : ctr.mV[1], 
01955                 v.mV[2] ? (v.mV[2]>0 ? max.mV[2] : min.mV[2] ) : ctr.mV[2] );
01956 }
01957 
01958 // returns max allowable scale along a given stretch axis
01959 F32             LLManipScale::partToMaxScale( S32 part, const LLBBox &bbox ) const
01960 {
01961         F32 max_scale_factor = 0.f;
01962         LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
01963         bbox_extents.abs();
01964         F32 max_extent = 0.f;
01965         for (U32 i = VX; i <= VZ; i++)
01966         {
01967                 if (bbox_extents.mV[i] > max_extent)
01968                 {
01969                         max_extent = bbox_extents.mV[i];
01970                 }
01971         }
01972         max_scale_factor = bbox_extents.magVec() * MAX_OBJECT_SCALE / max_extent;
01973 
01974         if (getUniform())
01975         {
01976                 max_scale_factor *= 0.5f;
01977         }
01978 
01979         return max_scale_factor;
01980 }
01981 
01982 // returns min allowable scale along a given stretch axis
01983 F32             LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const
01984 {
01985         LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
01986         bbox_extents.abs();
01987         F32 min_extent = MAX_OBJECT_SCALE;
01988         for (U32 i = VX; i <= VZ; i++)
01989         {
01990                 if (bbox_extents.mV[i] > 0.f && bbox_extents.mV[i] < min_extent)
01991                 {
01992                         min_extent = bbox_extents.mV[i];
01993                 }
01994         }
01995         F32 min_scale_factor = bbox_extents.magVec() * MIN_OBJECT_SCALE / min_extent;
01996 
01997         if (getUniform())
01998         {
01999                 min_scale_factor *= 0.5f;
02000         }
02001 
02002         return min_scale_factor;
02003 }
02004 
02005 // Returns the axis aligned unit vector closest to v.
02006 LLVector3 LLManipScale::nearestAxis( const LLVector3& v ) const
02007 {
02008         // Note: yes, this is a slow but easy implementation
02009         // assumes v is normalized
02010 
02011         F32 coords[][3] =
02012                 {
02013                         { 1.f, 0.f, 0.f },
02014                         { 0.f, 1.f, 0.f },
02015                         { 0.f, 0.f, 1.f },
02016                         {-1.f, 0.f, 0.f },
02017                         { 0.f,-1.f, 0.f },
02018                         { 0.f, 0.f,-1.f }
02019                 };
02020 
02021         F32 cosine[6];
02022         cosine[0] = v * LLVector3( coords[0] );
02023         cosine[1] = v * LLVector3( coords[1] );
02024         cosine[2] = v * LLVector3( coords[2] );
02025         cosine[3] = -cosine[0];
02026         cosine[4] = -cosine[1];
02027         cosine[5] = -cosine[2];
02028 
02029         F32 greatest_cos = cosine[0];
02030         S32 greatest_index = 0;
02031         for( S32 i=1; i<6; i++ )
02032         {
02033                 if( greatest_cos < cosine[i] )
02034                 {
02035                         greatest_cos = cosine[i];
02036                         greatest_index = i;
02037                 }
02038         }
02039 
02040         return LLVector3( coords[greatest_index] );
02041 }
02042 
02043 // virtual
02044 BOOL LLManipScale::canAffectSelection()
02045 {
02046         // An selection is scalable if you are allowed to both edit and move 
02047         // everything in it, and it does not have any sitting agents
02048         BOOL can_scale = mObjectSelection->getObjectCount() != 0;
02049         if (can_scale)
02050         {
02051                 struct f : public LLSelectedObjectFunctor
02052                 {
02053                         virtual bool apply(LLViewerObject* objectp)
02054                         {
02055                                 return objectp->permModify() && objectp->permMove() && !objectp->isSeat();
02056                         }
02057                 } func;
02058                 can_scale = mObjectSelection->applyToObjects(&func);
02059         }
02060         return can_scale;
02061 }

Generated on Thu Jul 1 06:08:50 2010 for Second Life Viewer by  doxygen 1.4.7