aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llmanipscale.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llmanipscale.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/newview/llmanipscale.cpp')
-rw-r--r--linden/indra/newview/llmanipscale.cpp2042
1 files changed, 2042 insertions, 0 deletions
diff --git a/linden/indra/newview/llmanipscale.cpp b/linden/indra/newview/llmanipscale.cpp
new file mode 100644
index 0000000..f143c5a
--- /dev/null
+++ b/linden/indra/newview/llmanipscale.cpp
@@ -0,0 +1,2042 @@
1/**
2 * @file llmanipscale.cpp
3 * @brief LLManipScale class implementation
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "llviewerprecompiledheaders.h"
29
30#include "llmanipscale.h"
31
32// library includes
33#include "llmath.h"
34#include "v3math.h"
35#include "llquaternion.h"
36#include "llgl.h"
37#include "v4color.h"
38#include "llprimitive.h"
39
40// viewer includes
41#include "llagent.h"
42#include "llbbox.h"
43#include "llbox.h"
44#include "llviewercontrol.h"
45#include "llcriticaldamp.h"
46#include "llcylinder.h"
47#include "lldrawable.h"
48#include "llfloatertools.h"
49#include "llglheaders.h"
50#include "llselectmgr.h"
51#include "llstatusbar.h"
52#include "llui.h"
53#include "llviewercamera.h"
54#include "llviewerobject.h"
55#include "llviewerwindow.h"
56#include "llhudrender.h"
57#include "llworld.h"
58#include "v2math.h"
59#include "llvoavatar.h"
60
61
62const F32 MAX_MANIP_SELECT_DISTANCE_SQUARED = 11.f * 11.f;
63const F32 SNAP_GUIDE_SCREEN_OFFSET = 0.05f;
64const F32 SNAP_GUIDE_SCREEN_LENGTH = 0.7f;
65const F32 SELECTED_MANIPULATOR_SCALE = 1.2f;
66const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
67const S32 NUM_MANIPULATORS = 14;
68
69const LLManip::EManipPart MANIPULATOR_IDS[NUM_MANIPULATORS] =
70{
71 LLManip::LL_CORNER_NNN,
72 LLManip::LL_CORNER_NNP,
73 LLManip::LL_CORNER_NPN,
74 LLManip::LL_CORNER_NPP,
75 LLManip::LL_CORNER_PNN,
76 LLManip::LL_CORNER_PNP,
77 LLManip::LL_CORNER_PPN,
78 LLManip::LL_CORNER_PPP,
79 LLManip::LL_FACE_POSZ,
80 LLManip::LL_FACE_POSX,
81 LLManip::LL_FACE_POSY,
82 LLManip::LL_FACE_NEGX,
83 LLManip::LL_FACE_NEGY,
84 LLManip::LL_FACE_NEGZ
85};
86
87
88
89// static
90void LLManipScale::setUniform(BOOL b)
91{
92 gSavedSettings.setBOOL("ScaleUniform", b);
93}
94
95// static
96void LLManipScale::setShowAxes(BOOL b)
97{
98 gSavedSettings.setBOOL("ScaleShowAxes", b);
99}
100
101// static
102void LLManipScale::setStretchTextures(BOOL b)
103{
104 gSavedSettings.setBOOL("ScaleStretchTextures", b);
105}
106
107// static
108BOOL LLManipScale::getUniform()
109{
110 return gSavedSettings.getBOOL("ScaleUniform");
111}
112
113// static
114BOOL LLManipScale::getShowAxes()
115{
116 return gSavedSettings.getBOOL("ScaleShowAxes");
117}
118
119// static
120BOOL LLManipScale::getStretchTextures()
121{
122 return gSavedSettings.getBOOL("ScaleStretchTextures");
123}
124
125inline void LLManipScale::conditionalHighlight( U32 part, const LLColor4* highlight, const LLColor4* normal )
126{
127 LLColor4 default_highlight( 1.f, 1.f, 1.f, 1.f );
128 LLColor4 default_normal( 0.7f, 0.7f, 0.7f, 0.6f );
129 LLColor4 invisible(0.f, 0.f, 0.f, 0.f);
130 F32 manipulator_scale = 1.f;
131
132 for (S32 i = 0; i < NUM_MANIPULATORS; i++)
133 {
134 if((U32)MANIPULATOR_IDS[i] == part)
135 {
136 manipulator_scale = mManipulatorScales[i];
137 break;
138 }
139 }
140
141 mScaledBoxHandleSize = mBoxHandleSize * manipulator_scale;
142 if (mManipPart != (S32)LL_NO_PART && mManipPart != (S32)part)
143 {
144 glColor4fv( invisible.mV );
145 }
146 else if( mHighlightedPart == (S32)part )
147 {
148 glColor4fv( highlight ? highlight->mV : default_highlight.mV );
149 }
150 else
151 {
152 glColor4fv( normal ? normal->mV : default_normal.mV );
153 }
154}
155
156void LLManipScale::handleSelect()
157{
158 LLBBox bbox = gSelectMgr->getBBoxOfSelection();
159 updateSnapGuides(bbox);
160 gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
161 gFloaterTools->setStatusText("Click and drag to stretch selected side");
162}
163
164void LLManipScale::handleDeselect()
165{
166 mHighlightedPart = LL_NO_PART;
167 mManipPart = LL_NO_PART;
168 gFloaterTools->setStatusText("");
169}
170
171BOOL sort_manip_by_z(LLManipScale::ManipulatorHandle *new_manip, LLManipScale::ManipulatorHandle *test_manip)
172{
173 return ((new_manip->mType < test_manip->mType) || (new_manip->mPosition.mV[VZ] < test_manip->mPosition.mV[VZ]));
174}
175
176LLManipScale::LLManipScale( LLToolComposite* composite )
177 :
178 LLManip( "Scale", composite ),
179 mBoxHandleSize( 1.f ),
180 mScaledBoxHandleSize( 1.f ),
181 mManipPart( LL_NO_PART ),
182 mHighlightedPart( LL_NO_PART ),
183 mLastMouseX( -1 ),
184 mLastMouseY( -1 ),
185 mSendUpdateOnMouseUp( FALSE ),
186 mLastUpdateFlags( 0 ),
187 mScaleSnapUnit1(1.f),
188 mScaleSnapUnit2(1.f),
189 mSnapRegimeOffset(0.f),
190 mSnapGuideLength(0.f),
191 mScaleSnapValue(0.f)
192{
193 mProjectedManipulators.setInsertBefore(sort_manip_by_z);
194 mManipulatorScales = new F32[NUM_MANIPULATORS];
195 for (S32 i = 0; i < NUM_MANIPULATORS; i++)
196 {
197 mManipulatorScales[i] = 1.f;
198 }
199}
200
201LLManipScale::~LLManipScale()
202{
203 delete []mManipulatorScales;
204}
205
206void LLManipScale::render()
207{
208 LLGLSUIDefault gls_ui;
209 LLGLSNoTexture gls_no_texture;
210 LLGLDepthTest gls_depth(GL_TRUE);
211 LLGLEnable gl_blend(GL_BLEND);
212 LLGLEnable gls_alpha_test(GL_ALPHA_TEST);
213
214 if( isSelectionScalable() )
215 {
216 glMatrixMode(GL_MODELVIEW);
217 glPushMatrix();
218 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
219 {
220 F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
221 glScalef(zoom, zoom, zoom);
222 }
223
224 ////////////////////////////////////////////////////////////////////////
225 // Calculate size of drag handles
226
227 const F32 BOX_HANDLE_BASE_SIZE = 50.0f; // box size in pixels = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR
228 const F32 BOX_HANDLE_BASE_FACTOR = 0.2f;
229
230 LLVector3 center_agent = gAgent.getPosAgentFromGlobal(gSelectMgr->getSelectionCenterGlobal());
231
232 F32 range;
233 F32 range_from_agent;
234 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
235 {
236 mBoxHandleSize = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR / (F32) gCamera->getViewHeightInPixels();
237 mBoxHandleSize /= gAgent.getAvatarObject()->mHUDCurZoom;
238 }
239 else
240 {
241 range = dist_vec(gAgent.getCameraPositionAgent(), center_agent);
242 range_from_agent = dist_vec(gAgent.getPositionAgent(), center_agent);
243
244 // Don't draw manip if object too far away
245 if (gSavedSettings.getBOOL("LimitSelectDistance"))
246 {
247 F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance");
248 if (range_from_agent > max_select_distance)
249 {
250 return;
251 }
252 }
253
254 if (range > 0.001f)
255 {
256 // range != zero
257 F32 fraction_of_fov = BOX_HANDLE_BASE_SIZE / (F32) gCamera->getViewHeightInPixels();
258 F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
259 mBoxHandleSize = range * tan(apparent_angle) * BOX_HANDLE_BASE_FACTOR;
260 }
261 else
262 {
263 // range == zero
264 mBoxHandleSize = BOX_HANDLE_BASE_FACTOR;
265 }
266 }
267
268 ////////////////////////////////////////////////////////////////////////
269 // Draw bounding box
270
271 LLBBox bbox = gSelectMgr->getBBoxOfSelection();
272 LLVector3 pos_agent = bbox.getPositionAgent();
273 LLQuaternion rot = bbox.getRotation();
274
275 glMatrixMode(GL_MODELVIEW);
276 glPushMatrix();
277 {
278 glTranslatef(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ]);
279
280 F32 angle_radians, x, y, z;
281 rot.getAngleAxis(&angle_radians, &x, &y, &z);
282 glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
283
284
285 {
286 LLGLEnable poly_offset(GL_POLYGON_OFFSET_FILL);
287 glPolygonOffset( -2.f, -2.f);
288
289 // JC - Band-aid until edge stretch working similar to side stretch
290 // in non-uniform.
291 // renderEdges( bbox );
292
293 renderCorners( bbox );
294 renderFaces( bbox );
295
296 if (mManipPart != LL_NO_PART)
297 {
298 renderGuidelinesPart( bbox );
299 }
300
301 glPolygonOffset( 0.f, 0.f);
302 }
303 }
304 glPopMatrix();
305
306 if (mManipPart != LL_NO_PART)
307 {
308 renderSnapGuides(bbox);
309 }
310 glPopMatrix();
311
312 renderXYZ(bbox.getExtentLocal());
313 }
314}
315
316BOOL LLManipScale::handleMouseDown(S32 x, S32 y, MASK mask)
317{
318 BOOL handled = FALSE;
319
320 LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
321 if( hit_obj ||
322 (mHighlightedPart != LL_NO_PART) )
323 {
324 handled = handleMouseDownOnPart( x, y, mask );
325 }
326
327 return handled;
328}
329
330// Assumes that one of the arrows on an object was hit.
331BOOL LLManipScale::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
332{
333 BOOL can_scale = gSelectMgr->getObjectCount() != 0;
334 for (LLViewerObject* objectp = gSelectMgr->getFirstObject();
335 objectp;
336 objectp = gSelectMgr->getNextObject())
337 {
338 can_scale = can_scale && objectp->permModify() && objectp->permMove() && !objectp->isSeat();
339 }
340
341 if (!can_scale)
342 {
343 return FALSE;
344 }
345
346 highlightManipulators(x, y);
347 S32 hit_part = mHighlightedPart;
348
349 gSelectMgr->enableSilhouette(FALSE);
350 mManipPart = (EManipPart)hit_part;
351
352 LLBBox bbox = gSelectMgr->getBBoxOfSelection();
353 LLVector3 box_center_agent = bbox.getCenterAgent();
354 LLVector3 box_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ) );
355
356 updateSnapGuides(bbox);
357
358 mDragStartPointGlobal = gAgent.getPosGlobalFromAgent(box_corner_agent);
359 mDragStartCenterGlobal = gAgent.getPosGlobalFromAgent(box_center_agent);
360 LLVector3 far_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox ) );
361 mDragFarHitGlobal = gAgent.getPosGlobalFromAgent(far_corner_agent);
362 mDragPointGlobal = mDragStartPointGlobal;
363
364 // we just started a drag, so save initial object positions, orientations, and scales
365 gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_SCALE);
366 // Route future Mouse messages here preemptively. (Release on mouse up.)
367 setMouseCapture( TRUE );
368
369 mHelpTextTimer.reset();
370 sNumTimesHelpTextShown++;
371 return TRUE;
372}
373
374
375BOOL LLManipScale::handleMouseUp(S32 x, S32 y, MASK mask)
376{
377 // first, perform normal processing in case this was a quick-click
378 handleHover(x, y, mask);
379
380 gSelectMgr->enableSilhouette(TRUE);
381 mManipPart = LL_NO_PART;
382
383 // Might have missed last update due to UPDATE_DELAY timing
384 if (mSendUpdateOnMouseUp)
385 {
386 gSelectMgr->sendMultipleUpdate( mLastUpdateFlags );
387 }
388
389 //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
390 gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
391 return LLManip::handleMouseUp(x, y, mask);
392}
393
394
395BOOL LLManipScale::handleHover(S32 x, S32 y, MASK mask)
396{
397 if( hasMouseCapture() )
398 {
399 if( gSelectMgr->isEmpty() )
400 {
401 // Somehow the object got deselected while we were dragging it.
402 setMouseCapture( FALSE );
403 }
404 else
405 {
406 drag( x, y );
407 }
408 lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (active)" << llendl;
409 }
410 else
411 {
412 mInSnapRegime = FALSE;
413 // not dragging...
414 highlightManipulators(x, y);
415 }
416
417 // Patch up textures, if possible.
418 gSelectMgr->adjustTexturesByScale(TRUE, getStretchTextures());
419
420 gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLSCALE);
421 return TRUE;
422}
423
424void LLManipScale::highlightManipulators(S32 x, S32 y)
425{
426 mHighlightedPart = LL_NO_PART;
427
428 // If we have something selected, try to hit its manipulator handles.
429 // Don't do this with nothing selected, as it kills the framerate.
430 LLBBox bbox = gSelectMgr->getBBoxOfSelection();
431
432 if( isSelectionScalable() )
433 {
434 LLMatrix4 transform;
435 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
436 {
437 LLVector4 translation(bbox.getPositionAgent());
438 transform.initRotTrans(bbox.getRotation(), translation);
439 LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
440 transform *= cfr;
441 LLMatrix4 window_scale;
442 F32 zoom_level = 2.f * gAgent.getAvatarObject()->mHUDCurZoom;
443 window_scale.initAll(LLVector3(zoom_level / gCamera->getAspect(), zoom_level, 0.f),
444 LLQuaternion::DEFAULT,
445 LLVector3::zero);
446 transform *= window_scale;
447 }
448 else
449 {
450 LLMatrix4 projMatrix = gCamera->getProjection();
451 LLMatrix4 modelView = gCamera->getModelview();
452 transform.initAll(LLVector3(1.f, 1.f, 1.f), bbox.getRotation(), bbox.getPositionAgent());
453
454 transform *= modelView;
455 transform *= projMatrix;
456 }
457
458 LLVector3 min = bbox.getMinLocal();
459 LLVector3 max = bbox.getMaxLocal();
460 LLVector3 ctr = bbox.getCenterLocal();
461
462 mProjectedManipulators.deleteAllData();
463
464 S32 numManips = 0;
465 // corners
466 mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], min.mV[VZ], 1.f);
467 mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], max.mV[VZ], 1.f);
468 mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], min.mV[VZ], 1.f);
469 mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], max.mV[VZ], 1.f);
470 mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], min.mV[VZ], 1.f);
471 mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], max.mV[VZ], 1.f);
472 mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], min.mV[VZ], 1.f);
473 mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], max.mV[VZ], 1.f);
474
475 // 1-D highlights are applicable iff one object is selected
476 if( gSelectMgr->getObjectCount() == 1 )
477 {
478 // face centers
479 mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], max.mV[VZ], 1.f);
480 mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f);
481 mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], max.mV[VY], ctr.mV[VZ], 1.f);
482 mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f);
483 mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], min.mV[VY], ctr.mV[VZ], 1.f);
484 mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], min.mV[VZ], 1.f);
485 }
486
487 for (S32 i = 0; i < numManips; i++)
488 {
489 LLVector4 projectedVertex = mManipulatorVertices[i] * transform;
490 projectedVertex = projectedVertex * (1.f / projectedVertex.mV[VW]);
491
492 ManipulatorHandle* projManipulator = new ManipulatorHandle(LLVector3(projectedVertex.mV[VX], projectedVertex.mV[VY],
493 projectedVertex.mV[VZ]), MANIPULATOR_IDS[i], (i < 7) ? SCALE_MANIP_CORNER : SCALE_MANIP_FACE);
494 mProjectedManipulators.addDataSorted(projManipulator);
495 }
496
497 F32 half_width = (F32)gViewerWindow->getWindowWidth() / 2.f;
498 F32 half_height = (F32)gViewerWindow->getWindowHeight() / 2.f;
499 LLVector2 manip2d;
500 LLVector2 mousePos((F32)x - half_width, (F32)y - half_height);
501 LLVector2 delta;
502
503 mHighlightedPart = LL_NO_PART;
504
505 for (ManipulatorHandle* manipulator = mProjectedManipulators.getFirstData();
506 manipulator;
507 manipulator = mProjectedManipulators.getNextData())
508 {
509 manip2d.setVec(manipulator->mPosition.mV[VX] * half_width, manipulator->mPosition.mV[VY] * half_height);
510
511 delta = manip2d - mousePos;
512 if (delta.magVecSquared() < MAX_MANIP_SELECT_DISTANCE_SQUARED)
513 {
514 mHighlightedPart = manipulator->mManipID;
515
516 //llinfos << "Tried: " << mHighlightedPart << llendl;
517 break;
518 }
519 }
520 }
521
522 for (S32 i = 0; i < NUM_MANIPULATORS; i++)
523 {
524 if (mHighlightedPart == MANIPULATOR_IDS[i])
525 {
526 mManipulatorScales[i] = lerp(mManipulatorScales[i], SELECTED_MANIPULATOR_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
527 }
528 else
529 {
530 mManipulatorScales[i] = lerp(mManipulatorScales[i], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
531 }
532 }
533
534 lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (inactive)" << llendl;
535}
536
537
538void LLManipScale::renderFaces( const LLBBox& bbox )
539{
540 // Don't bother to render the drag handles for 1-D scaling if
541 // more than one object is selected or if it is an attachment
542 if ( gSelectMgr->getObjectCount() > 1 )
543 {
544 return;
545 }
546
547 // This is a flattened representation of the box as render here
548 // .
549 // (+++) (++-) /|\t
550 // +------------+ | (texture coordinates)
551 // | | |
552 // | 1 | (*) --->s
553 // | +X |
554 // | |
555 // (+++) (+-+)| |(+--) (++-) (+++)
556 // +------------+------------+------------+------------+
557 // |0 3|3 7|7 4|4 0|
558 // | 0 | 4 | 5 | 2 |
559 // | +Z | -Y | -Z | +Y |
560 // | | | | |
561 // |1 2|2 6|6 5|5 1|
562 // +------------+------------+------------+------------+
563 // (-++) (--+)| |(---) (-+-) (-++)
564 // | 3 |
565 // | -X |
566 // | |
567 // | |
568 // +------------+
569 // (-++) (-+-)
570
571 LLColor4 highlight_color( 1.f, 1.f, 1.f, 0.5f);
572 LLColor4 normal_color( 1.f, 1.f, 1.f, 0.3f);
573
574 LLColor4 x_highlight_color( 1.f, 0.2f, 0.2f, 1.0f);
575 LLColor4 x_normal_color( 0.6f, 0.f, 0.f, 0.4f);
576
577 LLColor4 y_highlight_color( 0.2f, 1.f, 0.2f, 1.0f);
578 LLColor4 y_normal_color( 0.f, 0.6f, 0.f, 0.4f);
579
580 LLColor4 z_highlight_color( 0.2f, 0.2f, 1.f, 1.0f);
581 LLColor4 z_normal_color( 0.f, 0.f, 0.6f, 0.4f);
582
583 LLColor4 default_normal_color( 0.7f, 0.7f, 0.7f, 0.15f );
584
585 const LLVector3& min = bbox.getMinLocal();
586 const LLVector3& max = bbox.getMaxLocal();
587 LLVector3 ctr = bbox.getCenterLocal();
588
589 if (mManipPart == LL_NO_PART)
590 {
591 glColor4fv( default_normal_color.mV );
592 glBegin(GL_QUADS);
593 {
594 // Face 0
595 glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
596 glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
597 glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
598 glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
599
600 // Face 1
601 glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
602 glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
603 glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
604 glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
605
606 // Face 2
607 glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
608 glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
609 glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
610 glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
611
612 // Face 3
613 glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
614 glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
615 glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
616 glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
617
618 // Face 4
619 glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
620 glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
621 glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
622 glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
623
624 // Face 5
625 glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
626 glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
627 glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
628 glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
629 }
630 glEnd();
631 }
632
633 // Find nearest vertex
634 LLVector3 orientWRTHead = bbox.agentToLocalBasis( bbox.getCenterAgent() - gAgent.getCameraPositionAgent() );
635 U32 nearest =
636 (orientWRTHead.mV[0] < 0.0f ? 1 : 0) +
637 (orientWRTHead.mV[1] < 0.0f ? 2 : 0) +
638 (orientWRTHead.mV[2] < 0.0f ? 4 : 0);
639
640 // opposite faces on Linden cubes:
641 // 0 & 5
642 // 1 & 3
643 // 2 & 4
644
645 // Table of order to draw faces, based on nearest vertex
646 static U32 face_list[8][6] = {
647 { 2,0,1, 4,5,3 }, // v6 F201 F453
648 { 2,0,3, 4,5,1 }, // v7 F203 F451
649 { 4,0,1, 2,5,3 }, // v5 F401 F253
650 { 4,0,3, 2,5,1 }, // v4 F403 F251
651 { 2,5,1, 4,0,3 }, // v2 F251 F403
652 { 2,5,3, 4,0,1 }, // v3 F253 F401
653 { 4,5,1, 2,0,3 }, // v1 F451 F203
654 { 4,5,3, 2,0,1 } // v0 F453 F201
655 };
656
657 {
658 LLGLDepthTest gls_depth(GL_FALSE);
659
660 for (S32 i = 0; i < 6; i++)
661 {
662 U32 face = face_list[nearest][i];
663 switch( face )
664 {
665 case 0:
666 conditionalHighlight( LL_FACE_POSZ, &z_highlight_color, &z_normal_color );
667 renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], max.mV[VZ] ) );
668 break;
669
670 case 1:
671 conditionalHighlight( LL_FACE_POSX, &x_highlight_color, &x_normal_color );
672 renderAxisHandle( ctr, LLVector3( max.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) );
673 break;
674
675 case 2:
676 conditionalHighlight( LL_FACE_POSY, &y_highlight_color, &y_normal_color );
677 renderAxisHandle( ctr, LLVector3( ctr.mV[VX], max.mV[VY], ctr.mV[VZ] ) );
678 break;
679
680 case 3:
681 conditionalHighlight( LL_FACE_NEGX, &x_highlight_color, &x_normal_color );
682 renderAxisHandle( ctr, LLVector3( min.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) );
683 break;
684
685 case 4:
686 conditionalHighlight( LL_FACE_NEGY, &y_highlight_color, &y_normal_color );
687 renderAxisHandle( ctr, LLVector3( ctr.mV[VX], min.mV[VY], ctr.mV[VZ] ) );
688 break;
689
690 case 5:
691 conditionalHighlight( LL_FACE_NEGZ, &z_highlight_color, &z_normal_color );
692 renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], min.mV[VZ] ) );
693 break;
694 }
695 }
696 }
697}
698
699void LLManipScale::renderEdges( const LLBBox& bbox )
700{
701 LLVector3 extent = bbox.getExtentLocal();
702 F32 edge_width = mBoxHandleSize * .6f;
703
704 for( U32 part = LL_EDGE_MIN; part <= LL_EDGE_MAX; part++ )
705 {
706 LLVector3 direction = edgeToUnitVector( part );
707 LLVector3 center_to_edge = unitVectorToLocalBBoxExtent( direction, bbox );
708
709 glPushMatrix();
710 {
711 glTranslatef( center_to_edge.mV[0], center_to_edge.mV[1], center_to_edge.mV[2] );
712 conditionalHighlight( part );
713 glScalef(
714 direction.mV[0] ? edge_width : extent.mV[VX],
715 direction.mV[1] ? edge_width : extent.mV[VY],
716 direction.mV[2] ? edge_width : extent.mV[VZ] );
717 gBox.render();
718 }
719 glPopMatrix();
720 }
721}
722
723
724void LLManipScale::renderCorners( const LLBBox& bbox )
725{
726 U32 part = LL_CORNER_NNN;
727
728 F32 x_offset = bbox.getMinLocal().mV[VX];
729 for( S32 i=0; i < 2; i++ )
730 {
731 F32 y_offset = bbox.getMinLocal().mV[VY];
732 for( S32 j=0; j < 2; j++ )
733 {
734 F32 z_offset = bbox.getMinLocal().mV[VZ];
735 for( S32 k=0; k < 2; k++ )
736 {
737 conditionalHighlight( part );
738 renderBoxHandle( x_offset, y_offset, z_offset );
739 part++;
740
741 z_offset = bbox.getMaxLocal().mV[VZ];
742
743 }
744 y_offset = bbox.getMaxLocal().mV[VY];
745 }
746 x_offset = bbox.getMaxLocal().mV[VX];
747 }
748}
749
750
751void LLManipScale::renderBoxHandle( F32 x, F32 y, F32 z )
752{
753 LLGLDisable gls_tex(GL_TEXTURE_2D);
754
755 glPushMatrix();
756 {
757 glTranslatef( x, y, z );
758 glScalef( mScaledBoxHandleSize, mScaledBoxHandleSize, mScaledBoxHandleSize );
759 gBox.render();
760 }
761 glPopMatrix();
762}
763
764
765void LLManipScale::renderAxisHandle( const LLVector3& start, const LLVector3& end )
766{
767 if( getShowAxes() )
768 {
769 // Draws a single "jacks" style handle: a long, retangular box from start to end.
770 LLVector3 offset_start = end - start;
771 offset_start.normVec();
772 offset_start = start + mBoxHandleSize * offset_start;
773
774 LLVector3 delta = end - offset_start;
775 LLVector3 pos = offset_start + 0.5f * delta;
776
777 glPushMatrix();
778 {
779 glTranslatef( pos.mV[VX], pos.mV[VY], pos.mV[VZ] );
780 glScalef(
781 mBoxHandleSize + llabs(delta.mV[VX]),
782 mBoxHandleSize + llabs(delta.mV[VY]),
783 mBoxHandleSize + llabs(delta.mV[VZ]));
784 gBox.render();
785 }
786 glPopMatrix();
787 }
788 else
789 {
790 renderBoxHandle( end.mV[VX], end.mV[VY], end.mV[VZ] );
791 }
792}
793
794
795void LLManipScale::drag( S32 x, S32 y )
796{
797 if( (LL_FACE_MIN <= (S32)mManipPart)
798 && ((S32)mManipPart <= LL_FACE_MAX) )
799 {
800 dragFace( x, y );
801 }
802 else
803 if( (LL_CORNER_MIN <= (S32)mManipPart)
804 && ((S32)mManipPart <= LL_CORNER_MAX) )
805 {
806 dragCorner( x, y );
807 }
808
809 //gAgent.setObjectTracking(FALSE);
810 gAgent.clearFocusObject();
811}
812
813// Scale around the
814void LLManipScale::dragCorner( S32 x, S32 y )
815{
816 LLBBox bbox = gSelectMgr->getBBoxOfSelection();
817
818 // Suppress scale if mouse hasn't moved.
819 if (x == mLastMouseX && y == mLastMouseY)
820 {
821 if (mSendUpdateOnMouseUp)
822 {
823 sendUpdates(TRUE,TRUE,TRUE);
824 }
825 return;
826 }
827
828 mLastMouseX = x;
829 mLastMouseY = y;
830
831 LLVector3d drag_start_point_global = mDragStartPointGlobal;
832 LLVector3d drag_start_center_global = mDragStartCenterGlobal;
833 LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global);
834 LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global);
835
836 LLVector3d drag_start_dir_d;
837 drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global);
838 LLVector3 drag_start_dir_f;
839 drag_start_dir_f.setVec(drag_start_dir_d);
840
841 F32 s = 0;
842 F32 t = 0;
843
844 nearestPointOnLineFromMouse(x, y,
845 drag_start_center_agent,
846 drag_start_point_agent,
847 s, t );
848
849 F32 drag_start_dist = dist_vec(drag_start_point_agent, drag_start_center_agent);
850
851 if( s <= 0 ) // we only care about intersections in front of the camera
852 {
853 return;
854 }
855
856 LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d;
857
858 F32 scale_factor = t;
859
860 BOOL uniform = LLManipScale::getUniform();
861
862 if( !uniform )
863 {
864 scale_factor = 0.5f + (scale_factor * 0.5f);
865 }
866
867 // check for snapping
868 LLVector3 drag_center_agent = gAgent.getPosAgentFromGlobal(drag_point_global);
869 LLVector3 mouse_on_plane1;
870 getMousePointOnPlaneAgent(mouse_on_plane1, x, y, drag_center_agent, mScalePlaneNormal1);
871 LLVector3 mouse_on_plane2;
872 getMousePointOnPlaneAgent(mouse_on_plane2, x, y, drag_center_agent, mScalePlaneNormal2);
873 LLVector3 mouse_dir_1 = mouse_on_plane1 - mScaleCenter;
874 LLVector3 mouse_dir_2 = mouse_on_plane2 - mScaleCenter;
875 LLVector3 mouse_to_scale_line_1 = mouse_dir_1 - projected_vec(mouse_dir_1, mScaleDir);
876 LLVector3 mouse_to_scale_line_2 = mouse_dir_2 - projected_vec(mouse_dir_2, mScaleDir);
877 LLVector3 mouse_to_scale_line_dir_1 = mouse_to_scale_line_1;
878 mouse_to_scale_line_dir_1.normVec();
879 if (mouse_to_scale_line_dir_1 * mSnapGuideDir1 < 0.f)
880 {
881 // need to keep sign of mouse offset wrt to snap guide direction
882 mouse_to_scale_line_dir_1 *= -1.f;
883 }
884 LLVector3 mouse_to_scale_line_dir_2 = mouse_to_scale_line_2;
885 mouse_to_scale_line_dir_2.normVec();
886 if (mouse_to_scale_line_dir_2 * mSnapGuideDir2 < 0.f)
887 {
888 // need to keep sign of mouse offset wrt to snap guide direction
889 mouse_to_scale_line_dir_2 *= -1.f;
890 }
891
892 F32 snap_dir_dot_mouse_offset1 = mSnapGuideDir1 * mouse_to_scale_line_dir_1;
893 F32 snap_dir_dot_mouse_offset2 = mSnapGuideDir2 * mouse_to_scale_line_dir_2;
894
895 F32 dist_from_scale_line_1 = mouse_to_scale_line_1 * mouse_to_scale_line_dir_1;
896 F32 dist_from_scale_line_2 = mouse_to_scale_line_2 * mouse_to_scale_line_dir_2;
897
898 F32 max_scale = partToMaxScale(mManipPart, bbox);
899 F32 min_scale = partToMinScale(mManipPart, bbox);
900
901 BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled");
902 if (snap_enabled && dist_from_scale_line_1 > mSnapRegimeOffset * snap_dir_dot_mouse_offset1)
903 {
904 mInSnapRegime = TRUE;
905 LLVector3 projected_drag_pos = mouse_on_plane1 - (dist_from_scale_line_1 / snap_dir_dot_mouse_offset1) * mSnapGuideDir1;
906 F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir;
907
908 F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
909 F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions);
910 F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions);
911
912 mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale);
913
914 scale_factor = mScaleSnapValue / drag_start_dist;
915 if( !uniform )
916 {
917 scale_factor *= 0.5f;
918 }
919 }
920 else if (snap_enabled && dist_from_scale_line_2 > mSnapRegimeOffset * snap_dir_dot_mouse_offset2)
921 {
922 mInSnapRegime = TRUE;
923 LLVector3 projected_drag_pos = mouse_on_plane2 - (dist_from_scale_line_2 / snap_dir_dot_mouse_offset2) * mSnapGuideDir2;
924 F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir;
925
926 F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
927 F32 snap_dist = mScaleSnapUnit2 / (2.f * cur_subdivisions);
928 F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit2 / cur_subdivisions);
929
930 mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale);
931
932 scale_factor = mScaleSnapValue / drag_start_dist;
933 if( !uniform )
934 {
935 scale_factor *= 0.5f;
936 }
937 }
938 else
939 {
940 mInSnapRegime = FALSE;
941 }
942
943 F32 max_scale_factor = MAX_OBJECT_SCALE / MIN_OBJECT_SCALE;
944 F32 min_scale_factor = MIN_OBJECT_SCALE / MAX_OBJECT_SCALE;
945
946 // find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale
947 LLSelectNode* selectNode;
948 for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
949 {
950 LLViewerObject* cur = selectNode->getObject();
951 if( cur->permModify() && cur->permMove() && !cur->isAvatar() )
952 {
953 const LLVector3& scale = selectNode->mSavedScale;
954
955 F32 cur_max_scale_factor = llmin( MAX_OBJECT_SCALE / scale.mV[VX], MAX_OBJECT_SCALE / scale.mV[VY], MAX_OBJECT_SCALE / scale.mV[VZ] );
956 max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor );
957
958 F32 cur_min_scale_factor = llmax( MIN_OBJECT_SCALE / scale.mV[VX], MIN_OBJECT_SCALE / scale.mV[VY], MIN_OBJECT_SCALE / scale.mV[VZ] );
959 min_scale_factor = llmax( min_scale_factor, cur_min_scale_factor );
960 }
961 }
962
963 scale_factor = llclamp( scale_factor, min_scale_factor, max_scale_factor );
964
965 LLVector3d drag_global = uniform ? mDragStartCenterGlobal : mDragFarHitGlobal;
966
967 // do the root objects i.e. (TRUE == cur->isRootEdit())
968 for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
969 {
970 LLViewerObject* cur = selectNode->getObject();
971 if( cur->permModify() && cur->permMove() && !cur->isAvatar() && cur->isRootEdit() )
972 {
973 const LLVector3& scale = selectNode->mSavedScale;
974 cur->setScale( scale_factor * scale );
975
976 LLVector3 delta_pos;
977 LLVector3 original_pos = cur->getPositionEdit();
978 LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor;
979 if (!cur->isAttachment())
980 {
981 new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
982 }
983 cur->setPositionAbsoluteGlobal( new_pos_global );
984 rebuild(cur);
985
986 delta_pos = cur->getPositionEdit() - original_pos;
987
988 if (selectNode->mIndividualSelection)
989 {
990 // counter-translate child objects if we are moving the root as an individual
991 for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++)
992 {
993 LLViewerObject* childp = cur->mChildList[child_num];
994
995 if (cur->isAttachment())
996 {
997 LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
998 childp->setPosition(child_pos);
999 }
1000 else
1001 {
1002 LLVector3d child_pos_delta(delta_pos);
1003 // RN: this updates drawable position instantly
1004 childp->setPositionAbsoluteGlobal(childp->getPositionGlobal() - child_pos_delta);
1005 }
1006 rebuild(childp);
1007 }
1008 }
1009 }
1010 }
1011 // do the child objects i.e. (FALSE == cur->isRootEdit())
1012 for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
1013 {
1014 LLViewerObject*cur = selectNode->getObject();
1015 if( cur->permModify() && cur->permMove() && !cur->isAvatar() && !cur->isRootEdit() )
1016 {
1017 const LLVector3& scale = selectNode->mSavedScale;
1018 cur->setScale( scale_factor * scale, FALSE );
1019
1020 if (!selectNode->mIndividualSelection)
1021 {
1022 cur->setPosition(selectNode->mSavedPositionLocal * scale_factor);
1023 continue;
1024 }
1025
1026 LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor;
1027 cur->setPositionAbsoluteGlobal( new_pos_global );
1028 rebuild(cur);
1029 }
1030 }
1031
1032
1033
1034 mDragPointGlobal = drag_point_global;
1035
1036 sendUpdates( TRUE, TRUE, TRUE );
1037}
1038
1039
1040void LLManipScale::dragFace( S32 x, S32 y )
1041{
1042 // Suppress scale if mouse hasn't moved.
1043 if (x == mLastMouseX && y == mLastMouseY)
1044 {
1045 if (mSendUpdateOnMouseUp)
1046 {
1047 sendUpdates(TRUE,TRUE,FALSE);
1048 }
1049 return;
1050 }
1051
1052 mLastMouseX = x;
1053 mLastMouseY = y;
1054
1055 LLVector3d drag_start_point_global = mDragStartPointGlobal;
1056 LLVector3d drag_start_center_global = mDragStartCenterGlobal;
1057 LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global);
1058 LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global);
1059
1060 LLVector3d drag_start_dir_d;
1061 drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global);
1062 LLVector3 drag_start_dir_f;
1063 drag_start_dir_f.setVec(drag_start_dir_d);
1064
1065 LLBBox bbox = gSelectMgr->getBBoxOfSelection();
1066
1067 F32 s = 0;
1068 F32 t = 0;
1069
1070 nearestPointOnLineFromMouse(x,
1071 y,
1072 drag_start_center_agent,
1073 drag_start_point_agent,
1074 s, t );
1075
1076 if( s <= 0 ) // we only care about intersections in front of the camera
1077 {
1078 return;
1079 }
1080
1081 LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d;
1082 LLVector3 part_dir_local = partToUnitVector( mManipPart );
1083
1084 // check for snapping
1085 LLVector3 mouse_on_plane;
1086 getMousePointOnPlaneAgent(mouse_on_plane, x, y, mScaleCenter, mScalePlaneNormal1 );
1087
1088 LLVector3 mouse_on_scale_line = mScaleCenter + projected_vec(mouse_on_plane - mScaleCenter, mScaleDir);
1089 LLVector3 drag_delta(mouse_on_scale_line - drag_start_point_agent);
1090 F32 max_drag_dist = partToMaxScale(mManipPart, bbox);
1091 F32 min_drag_dist = partToMinScale(mManipPart, bbox);
1092
1093 BOOL uniform = LLManipScale::getUniform();
1094 if( uniform )
1095 {
1096 drag_delta *= 2.f;
1097 }
1098
1099 LLVector3 scale_center_to_mouse = mouse_on_plane - mScaleCenter;
1100 F32 dist_from_scale_line = dist_vec(scale_center_to_mouse, (mouse_on_scale_line - mScaleCenter));
1101 F32 dist_along_scale_line = scale_center_to_mouse * mScaleDir;
1102
1103 BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled");
1104
1105 if (snap_enabled && dist_from_scale_line > mSnapRegimeOffset)
1106 {
1107 mInSnapRegime = TRUE;
1108
1109 if (dist_along_scale_line > max_drag_dist)
1110 {
1111 mScaleSnapValue = max_drag_dist;
1112
1113 LLVector3 clamp_point = mScaleCenter + max_drag_dist * mScaleDir;
1114 drag_delta.setVec(clamp_point - drag_start_point_agent);
1115 }
1116 else if (dist_along_scale_line < min_drag_dist)
1117 {
1118 mScaleSnapValue = min_drag_dist;
1119
1120 LLVector3 clamp_point = mScaleCenter + min_drag_dist * mScaleDir;
1121 drag_delta.setVec(clamp_point - drag_start_point_agent);
1122 }
1123 else
1124 {
1125 F32 drag_dist = scale_center_to_mouse * mScaleDir;
1126 F32 cur_subdivisions = llclamp(getSubdivisionLevel(mScaleCenter + mScaleDir * drag_dist, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
1127 F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions);
1128 F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions);
1129 relative_snap_dist -= snap_dist;
1130
1131 //make sure that values that the scale is "snapped to"
1132 //do not exceed/go under the applicable max/mins
1133 //this causes the box to shift displacements ever so slightly
1134 //although the "snap value" should go down to 0
1135 //see Jira 1027
1136 relative_snap_dist = llclamp(relative_snap_dist,
1137 drag_dist - max_drag_dist,
1138 drag_dist - min_drag_dist);
1139
1140 mScaleSnapValue = drag_dist - relative_snap_dist;
1141
1142 if (llabs(relative_snap_dist) < snap_dist)
1143 {
1144 LLVector3 drag_correction = relative_snap_dist * mScaleDir;
1145 if (uniform)
1146 {
1147 drag_correction *= 2.f;
1148 }
1149
1150 drag_delta -= drag_correction;
1151 }
1152 }
1153 }
1154 else
1155 {
1156 mInSnapRegime = FALSE;
1157 }
1158
1159 BOOL send_scale_update = FALSE;
1160 BOOL send_position_update = FALSE;
1161
1162 LLVector3 dir_agent;
1163 if( part_dir_local.mV[VX] )
1164 {
1165 dir_agent = bbox.localToAgentBasis( LLVector3::x_axis );
1166 }
1167 else if( part_dir_local.mV[VY] )
1168 {
1169 dir_agent = bbox.localToAgentBasis( LLVector3::y_axis );
1170 }
1171 else if( part_dir_local.mV[VZ] )
1172 {
1173 dir_agent = bbox.localToAgentBasis( LLVector3::z_axis );
1174 }
1175 stretchFace(
1176 projected_vec(drag_start_dir_f, dir_agent) + drag_start_center_agent,
1177 projected_vec(drag_delta, dir_agent));
1178 send_position_update = TRUE;
1179 send_scale_update = TRUE;
1180
1181 mDragPointGlobal = drag_point_global;
1182
1183 sendUpdates( send_position_update, send_scale_update );
1184}
1185
1186void LLManipScale::sendUpdates( BOOL send_position_update, BOOL send_scale_update, BOOL corner )
1187{
1188 // Throttle updates to 10 per second.
1189 static LLTimer update_timer;
1190 F32 elapsed_time = update_timer.getElapsedTimeF32();
1191 const F32 UPDATE_DELAY = 0.1f; // min time between transmitted updates
1192
1193 if( send_scale_update || send_position_update )
1194 {
1195 U32 update_flags = UPD_NONE;
1196 if (send_position_update) update_flags |= UPD_POSITION;
1197 if (send_scale_update) update_flags |= UPD_SCALE;
1198
1199// BOOL send_type = SEND_INDIVIDUALS;
1200 if (corner)
1201 {
1202 update_flags |= UPD_UNIFORM;
1203 }
1204 // keep this up to date for sendonmouseup
1205 mLastUpdateFlags = update_flags;
1206
1207 // enforce minimum update delay and don't stream updates on sub-object selections
1208 if( elapsed_time > UPDATE_DELAY && gSavedSettings.getBOOL("SelectLinkedSet") )
1209 {
1210 gSelectMgr->sendMultipleUpdate( update_flags );
1211 update_timer.reset();
1212 mSendUpdateOnMouseUp = FALSE;
1213 }
1214 else
1215 {
1216 mSendUpdateOnMouseUp = TRUE;
1217 }
1218
1219 gSelectMgr->updateSelectionCenter();
1220 dialog_refresh_all();
1221 }
1222}
1223
1224// Rescales in a single dimension. Either uniform (standard) or one-sided (scale plus translation)
1225// depending on mUniform. Handles multiple selection and objects that are not aligned to the bounding box.
1226void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVector3& drag_delta_agent )
1227{
1228 LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(mDragStartCenterGlobal);
1229
1230 LLSelectNode *selectNode;
1231 for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
1232 {
1233 LLViewerObject*cur = selectNode->getObject();
1234 if( cur->permModify() && cur->permMove() && !cur->isAvatar() )
1235 {
1236 LLBBox cur_bbox = cur->getBoundingBoxAgent();
1237 LLVector3 start_local = cur_bbox.agentToLocal( drag_start_agent );
1238 LLVector3 end_local = cur_bbox.agentToLocal( drag_start_agent + drag_delta_agent);
1239 LLVector3 start_center_local = cur_bbox.agentToLocal( drag_start_center_agent );
1240 LLVector3 axis = nearestAxis( start_local - start_center_local );
1241 S32 axis_index = axis.mV[0] ? 0 : (axis.mV[1] ? 1 : 2 );
1242
1243 LLVector3 delta_local = end_local - start_local;
1244 F32 delta_local_mag = delta_local.magVec();
1245 LLVector3 dir_local;
1246 if (delta_local_mag == 0.f)
1247 {
1248 dir_local = axis;
1249 }
1250 else
1251 {
1252 dir_local = delta_local / delta_local_mag; // normalized delta_local
1253 }
1254
1255 F32 denom = axis * dir_local;
1256 F32 desired_delta_size = is_approx_zero(denom) ? 0.f : (delta_local_mag / denom); // in meters
1257 F32 desired_scale = llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_OBJECT_SCALE, MAX_OBJECT_SCALE);
1258 // propagate scale constraint back to position offset
1259 desired_delta_size = desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position
1260
1261 LLVector3 scale = cur->getScale();
1262 scale.mV[axis_index] = desired_scale;
1263 cur->setScale(scale, FALSE);
1264 rebuild(cur);
1265 LLVector3 delta_pos;
1266 if( !getUniform() )
1267 {
1268 LLVector3 delta_pos_local = axis * (0.5f * desired_delta_size);
1269 LLVector3d delta_pos_global;
1270 delta_pos_global.setVec(cur_bbox.localToAgent( delta_pos_local ) - cur_bbox.getCenterAgent());
1271 LLVector3 cur_pos = cur->getPositionEdit();
1272
1273 if (cur->isRootEdit() && !cur->isAttachment())
1274 {
1275 LLVector3d new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, selectNode->mSavedPositionGlobal + delta_pos_global);
1276 cur->setPositionGlobal( new_pos_global );
1277 }
1278 else
1279 {
1280 LLXform* parent_xform = cur->mDrawable->getXform()->getParent();
1281 LLVector3 new_pos_local;
1282 // this works in attachment point space using world space delta
1283 if (parent_xform)
1284 {
1285 new_pos_local = selectNode->mSavedPositionLocal + (LLVector3(delta_pos_global) * ~parent_xform->getWorldRotation());
1286 }
1287 else
1288 {
1289 new_pos_local = selectNode->mSavedPositionLocal + LLVector3(delta_pos_global);
1290 }
1291 cur->setPosition(new_pos_local);
1292 }
1293 delta_pos = cur->getPositionEdit() - cur_pos;
1294 }
1295 if (cur->isRootEdit() && selectNode->mIndividualSelection)
1296 {
1297 // counter-translate child objects if we are moving the root as an individual
1298 for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++)
1299 {
1300 LLViewerObject* childp = cur->mChildList[child_num];
1301 if (!getUniform())
1302 {
1303 LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
1304 childp->setPosition(child_pos);
1305 rebuild(childp);
1306 }
1307 }
1308 }
1309 }
1310 }
1311}
1312
1313
1314void LLManipScale::renderGuidelinesPart( const LLBBox& bbox )
1315{
1316 LLVector3 guideline_start = bbox.getCenterLocal();
1317
1318 LLVector3 guideline_end = unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox );
1319
1320 if (!getUniform())
1321 {
1322 guideline_start = unitVectorToLocalBBoxExtent( -partToUnitVector( mManipPart ), bbox );
1323 }
1324
1325 guideline_end -= guideline_start;
1326 guideline_end.normVec();
1327 guideline_end *= gWorldPointer->getRegionWidthInMeters();
1328 guideline_end += guideline_start;
1329
1330 {
1331 LLGLDepthTest gls_depth(GL_TRUE);
1332 gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.5f) );
1333 }
1334 {
1335 LLGLDepthTest gls_depth(GL_FALSE);
1336 gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.25f) );
1337 }
1338}
1339
1340void LLManipScale::updateSnapGuides(const LLBBox& bbox)
1341{
1342 LLVector3 grid_origin;
1343 LLVector3 grid_scale;
1344 LLQuaternion grid_rotation;
1345 gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
1346
1347 LLVector3 box_corner_agent = bbox.localToAgent(unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ));
1348 mScaleCenter = getUniform() ? bbox.getCenterAgent() : bbox.localToAgent(unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox ));
1349 mScaleDir = box_corner_agent - mScaleCenter;
1350 mScaleDir.normVec();
1351
1352 if(gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
1353 {
1354 mSnapRegimeOffset = SNAP_GUIDE_SCREEN_OFFSET / gAgent.getAvatarObject()->mHUDCurZoom;
1355
1356 }
1357 else
1358 {
1359 F32 object_distance = dist_vec(mScaleCenter, gCamera->getOrigin());
1360 mSnapRegimeOffset = (SNAP_GUIDE_SCREEN_OFFSET * gViewerWindow->getWindowWidth() * object_distance) / gCamera->getPixelMeterRatio();
1361 }
1362 LLVector3 cam_at_axis;
1363 F32 snap_guide_length;
1364 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
1365 {
1366 cam_at_axis.setVec(1.f, 0.f, 0.f);
1367 snap_guide_length = SNAP_GUIDE_SCREEN_LENGTH / gAgent.getAvatarObject()->mHUDCurZoom;
1368 }
1369 else
1370 {
1371 cam_at_axis = gCamera->getAtAxis();
1372 F32 manipulator_distance = dist_vec(box_corner_agent, gCamera->getOrigin());
1373 snap_guide_length = (SNAP_GUIDE_SCREEN_LENGTH * gViewerWindow->getWindowWidth() * manipulator_distance) / gCamera->getPixelMeterRatio();
1374 }
1375
1376 mSnapGuideLength = snap_guide_length / llmax(0.1f, (llmin(mSnapGuideDir1 * cam_at_axis, mSnapGuideDir2 * cam_at_axis)));
1377
1378 LLVector3 off_axis_dir = mScaleDir % cam_at_axis;
1379 off_axis_dir.normVec();
1380
1381 if( (LL_FACE_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_FACE_MAX) )
1382 {
1383 LLVector3 object_scale = bbox.getMaxLocal();
1384 object_scale.scaleVec(off_axis_dir * ~bbox.getRotation());
1385 object_scale.abs();
1386 if (object_scale.mV[VX] > object_scale.mV[VY] && object_scale.mV[VX] > object_scale.mV[VZ])
1387 {
1388 mSnapGuideDir1 = LLVector3::x_axis * bbox.getRotation();
1389 }
1390 else if (object_scale.mV[VY] > object_scale.mV[VZ])
1391 {
1392 mSnapGuideDir1 = LLVector3::y_axis * bbox.getRotation();
1393 }
1394 else
1395 {
1396 mSnapGuideDir1 = LLVector3::z_axis * bbox.getRotation();
1397 }
1398
1399 LLVector3 scale_snap = grid_scale;
1400 mScaleSnapUnit1 = scale_snap.scaleVec(partToUnitVector( mManipPart )).magVec();
1401 mScaleSnapUnit2 = mScaleSnapUnit1;
1402 mSnapGuideDir1 *= mSnapGuideDir1 * gCamera->getUpAxis() > 0.f ? 1.f : -1.f;
1403 mSnapGuideDir2 = mSnapGuideDir1 * -1.f;
1404 mSnapDir1 = mScaleDir;
1405 mSnapDir2 = mScaleDir;
1406 }
1407 else if( (LL_CORNER_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_CORNER_MAX) )
1408 {
1409 LLVector3 local_scale_dir = partToUnitVector( mManipPart );
1410 LLVector3 local_camera_dir;
1411 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
1412 {
1413 local_camera_dir = LLVector3(-1.f, 0.f, 0.f) * ~bbox.getRotation();
1414 }
1415 else
1416 {
1417 local_camera_dir = (gCamera->getOrigin() - bbox.getCenterAgent()) * ~bbox.getRotation();
1418 local_camera_dir.normVec();
1419 }
1420 local_scale_dir -= projected_vec(local_scale_dir, local_camera_dir);
1421 local_scale_dir.normVec();
1422 LLVector3 x_axis_proj_camera = LLVector3::x_axis - projected_vec(LLVector3::x_axis, local_camera_dir);
1423 x_axis_proj_camera.normVec();
1424 LLVector3 y_axis_proj_camera = LLVector3::y_axis - projected_vec(LLVector3::y_axis, local_camera_dir);
1425 y_axis_proj_camera.normVec();
1426 LLVector3 z_axis_proj_camera = LLVector3::z_axis - projected_vec(LLVector3::z_axis, local_camera_dir);
1427 z_axis_proj_camera.normVec();
1428 F32 x_axis_proj = llabs(local_scale_dir * x_axis_proj_camera);
1429 F32 y_axis_proj = llabs(local_scale_dir * y_axis_proj_camera);
1430 F32 z_axis_proj = llabs(local_scale_dir * z_axis_proj_camera);
1431
1432 if (x_axis_proj > y_axis_proj && x_axis_proj > z_axis_proj)
1433 {
1434 mSnapGuideDir1 = LLVector3::y_axis;
1435 mScaleSnapUnit2 = grid_scale.mV[VY];
1436 mSnapGuideDir2 = LLVector3::z_axis;
1437 mScaleSnapUnit1 = grid_scale.mV[VZ];
1438 }
1439 else if (y_axis_proj > z_axis_proj)
1440 {
1441 mSnapGuideDir1 = LLVector3::x_axis;
1442 mScaleSnapUnit2 = grid_scale.mV[VX];
1443 mSnapGuideDir2 = LLVector3::z_axis;
1444 mScaleSnapUnit1 = grid_scale.mV[VZ];
1445 }
1446 else
1447 {
1448 mSnapGuideDir1 = LLVector3::x_axis;
1449 mScaleSnapUnit2 = grid_scale.mV[VX];
1450 mSnapGuideDir2 = LLVector3::y_axis;
1451 mScaleSnapUnit1 = grid_scale.mV[VY];
1452 }
1453
1454 LLVector3 snap_guide_flip(1.f, 1.f, 1.f);
1455 switch (mManipPart)
1456 {
1457 case LL_CORNER_NNN:
1458 break;
1459 case LL_CORNER_NNP:
1460 snap_guide_flip.setVec(1.f, 1.f, -1.f);
1461 break;
1462 case LL_CORNER_NPN:
1463 snap_guide_flip.setVec(1.f, -1.f, 1.f);
1464 break;
1465 case LL_CORNER_NPP:
1466 snap_guide_flip.setVec(1.f, -1.f, -1.f);
1467 break;
1468 case LL_CORNER_PNN:
1469 snap_guide_flip.setVec(-1.f, 1.f, 1.f);
1470 break;
1471 case LL_CORNER_PNP:
1472 snap_guide_flip.setVec(-1.f, 1.f, -1.f);
1473 break;
1474 case LL_CORNER_PPN:
1475 snap_guide_flip.setVec(-1.f, -1.f, 1.f);
1476 break;
1477 case LL_CORNER_PPP:
1478 snap_guide_flip.setVec(-1.f, -1.f, -1.f);
1479 break;
1480 default:
1481 break;
1482 }
1483 mSnapGuideDir1.scaleVec(snap_guide_flip);
1484 mSnapGuideDir2.scaleVec(snap_guide_flip);
1485 mSnapGuideDir1.rotVec(bbox.getRotation());
1486 mSnapGuideDir2.rotVec(bbox.getRotation());
1487 mSnapDir1 = -1.f * mSnapGuideDir2;
1488 mSnapDir2 = -1.f * mSnapGuideDir1;
1489 }
1490
1491 mScalePlaneNormal1 = mSnapGuideDir1 % mScaleDir;
1492 mScalePlaneNormal1.normVec();
1493
1494 mScalePlaneNormal2 = mSnapGuideDir2 % mScaleDir;
1495 mScalePlaneNormal2.normVec();
1496
1497 mScaleSnapUnit1 = mScaleSnapUnit1 / (mSnapDir1 * mScaleDir);
1498 mScaleSnapUnit2 = mScaleSnapUnit2 / (mSnapDir2 * mScaleDir);
1499}
1500
1501void LLManipScale::renderSnapGuides(const LLBBox& bbox)
1502{
1503 if (!gSavedSettings.getBOOL("SnapEnabled"))
1504 {
1505 return;
1506 }
1507
1508 F32 max_subdivisions = sGridMaxSubdivisionLevel;
1509 F32 grid_alpha = gSavedSettings.getF32("GridOpacity");
1510
1511 F32 max_point_on_scale_line = partToMaxScale(mManipPart, bbox);
1512 LLVector3 drag_point = gAgent.getPosAgentFromGlobal(mDragPointGlobal);
1513
1514 updateGridSettings();
1515
1516 S32 pass;
1517 for (pass = 0; pass < 3; pass++)
1518 {
1519 LLColor4 tick_color = setupSnapGuideRenderPass(pass);
1520
1521 glBegin(GL_LINES);
1522 LLVector3 line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir1 * mSnapRegimeOffset);
1523 LLVector3 line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f)));
1524 LLVector3 line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f));
1525
1526 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
1527 glVertex3fv(line_start.mV);
1528 glColor4fv(tick_color.mV);
1529 glVertex3fv(line_mid.mV);
1530 glVertex3fv(line_mid.mV);
1531 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
1532 glVertex3fv(line_end.mV);
1533
1534 line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir2 * mSnapRegimeOffset);
1535 line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f)));
1536 line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f));
1537 glVertex3fv(line_start.mV);
1538 glColor4fv(tick_color.mV);
1539 glVertex3fv(line_mid.mV);
1540 glVertex3fv(line_mid.mV);
1541 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
1542 glVertex3fv(line_end.mV);
1543 glEnd();
1544 }
1545
1546 {
1547 LLGLDepthTest gls_depth(GL_FALSE);
1548
1549 F32 dist_grid_axis = (drag_point - mScaleCenter) * mScaleDir;
1550 // find distance to nearest smallest grid unit
1551 F32 grid_offset1 = fmodf(dist_grid_axis, mScaleSnapUnit1 / max_subdivisions);
1552 F32 grid_offset2 = fmodf(dist_grid_axis, mScaleSnapUnit2 / max_subdivisions);
1553
1554 // how many smallest grid units are we away from largest grid scale?
1555 S32 sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 / sGridMinSubdivisionLevel) / (mScaleSnapUnit1 / max_subdivisions));
1556 S32 sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 / sGridMinSubdivisionLevel) / (mScaleSnapUnit2 / max_subdivisions));
1557
1558 S32 num_ticks_per_side1 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit1 / max_subdivisions)));
1559 S32 num_ticks_per_side2 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit2 / max_subdivisions)));
1560 F32 dist_scale_units_1 = dist_grid_axis / (mScaleSnapUnit1 / max_subdivisions);
1561 F32 dist_scale_units_2 = dist_grid_axis / (mScaleSnapUnit2 / max_subdivisions);
1562 S32 ticks_from_scale_center_1 = lltrunc(dist_scale_units_1);
1563 S32 ticks_from_scale_center_2 = lltrunc(dist_scale_units_2);
1564 S32 max_ticks1 = llceil(max_point_on_scale_line / (mScaleSnapUnit1 / max_subdivisions) - dist_scale_units_1);
1565 S32 max_ticks2 = llceil(max_point_on_scale_line / (mScaleSnapUnit2 / max_subdivisions) - dist_scale_units_2);
1566 S32 start_tick = 0;
1567 S32 stop_tick = 0;
1568
1569 if (mInSnapRegime)
1570 {
1571 // draw snap guide line
1572 glBegin(GL_LINES);
1573 LLVector3 snap_line_center = mScaleCenter + (mScaleSnapValue * mScaleDir);
1574
1575 LLVector3 snap_line_start = snap_line_center + (mSnapGuideDir1 * mSnapRegimeOffset);
1576 LLVector3 snap_line_end = snap_line_center + (mSnapGuideDir2 * mSnapRegimeOffset);
1577
1578 glColor4f(1.f, 1.f, 1.f, grid_alpha);
1579 glVertex3fv(snap_line_start.mV);
1580 glVertex3fv(snap_line_center.mV);
1581 glVertex3fv(snap_line_center.mV);
1582 glVertex3fv(snap_line_end.mV);
1583 glEnd();
1584
1585 // draw snap guide arrow
1586 glBegin(GL_TRIANGLES);
1587 {
1588 //gGLSNoCullFaces.set();
1589 glColor4f(1.f, 1.f, 1.f, grid_alpha);
1590
1591 LLVector3 arrow_dir;
1592 LLVector3 arrow_span = mScaleDir;
1593
1594 arrow_dir = snap_line_start - snap_line_center;
1595 arrow_dir.normVec();
1596 glVertex3fv((snap_line_start + arrow_dir * mBoxHandleSize).mV);
1597 glVertex3fv((snap_line_start + arrow_span * mBoxHandleSize).mV);
1598 glVertex3fv((snap_line_start - arrow_span * mBoxHandleSize).mV);
1599
1600 arrow_dir = snap_line_end - snap_line_center;
1601 arrow_dir.normVec();
1602 glVertex3fv((snap_line_end + arrow_dir * mBoxHandleSize).mV);
1603 glVertex3fv((snap_line_end + arrow_span * mBoxHandleSize).mV);
1604 glVertex3fv((snap_line_end - arrow_span * mBoxHandleSize).mV);
1605 }
1606 glEnd();
1607 }
1608
1609 LLVector2 screen_translate_axis(llabs(mScaleDir * gCamera->getLeftAxis()), llabs(mScaleDir * gCamera->getUpAxis()));
1610 screen_translate_axis.normVec();
1611
1612 S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing);
1613
1614 for (pass = 0; pass < 3; pass++)
1615 {
1616 LLColor4 tick_color = setupSnapGuideRenderPass(pass);
1617
1618 start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1));
1619 stop_tick = llmin(max_ticks1, num_ticks_per_side1);
1620
1621 glBegin(GL_LINES);
1622 // draw first row of ticks
1623 for (S32 i = start_tick; i <= stop_tick; i++)
1624 {
1625 F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side1)));
1626 LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1));
1627
1628 F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
1629
1630 if (fmodf((F32)(i + sub_div_offset_1), (max_subdivisions / cur_subdivisions)) != 0.f)
1631 {
1632 continue;
1633 }
1634
1635 F32 tick_scale = 1.f;
1636 for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
1637 {
1638 if (fmodf((F32)(i + sub_div_offset_1), division_level) == 0.f)
1639 {
1640 break;
1641 }
1642 tick_scale *= 0.7f;
1643 }
1644
1645 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha);
1646 LLVector3 tick_start = tick_pos + (mSnapGuideDir1 * mSnapRegimeOffset);
1647 LLVector3 tick_end = tick_start + (mSnapGuideDir1 * mSnapRegimeOffset * tick_scale);
1648 glVertex3fv(tick_start.mV);
1649 glVertex3fv(tick_end.mV);
1650 }
1651
1652 // draw opposite row of ticks
1653 start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2));
1654 stop_tick = llmin(max_ticks2, num_ticks_per_side2);
1655 for (S32 i = start_tick; i <= stop_tick; i++)
1656 {
1657 F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side2)));
1658 LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2));
1659
1660 F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
1661
1662 if (fmodf((F32)(i + sub_div_offset_2), (max_subdivisions / cur_subdivisions)) != 0.f)
1663 {
1664 continue;
1665 }
1666
1667 F32 tick_scale = 1.f;
1668 for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
1669 {
1670 if (fmodf((F32)(i + sub_div_offset_2), division_level) == 0.f)
1671 {
1672 break;
1673 }
1674 tick_scale *= 0.7f;
1675 }
1676
1677 glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha);
1678 LLVector3 tick_start = tick_pos + (mSnapGuideDir2 * mSnapRegimeOffset);
1679 LLVector3 tick_end = tick_start + (mSnapGuideDir2 * mSnapRegimeOffset * tick_scale);
1680 glVertex3fv(tick_start.mV);
1681 glVertex3fv(tick_end.mV);
1682 }
1683 glEnd();
1684 }
1685
1686 // render tick labels
1687 start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1));
1688 stop_tick = llmin(max_ticks1, num_ticks_per_side1);
1689
1690 F32 grid_resolution = gSelectMgr->getSelectType() == SELECT_TYPE_HUD ? 0.25f : llmax(gSavedSettings.getF32("GridResolution"), 0.001f);
1691 S32 label_sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 * 32.f) / (mScaleSnapUnit1 / max_subdivisions));
1692 S32 label_sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 * 32.f) / (mScaleSnapUnit2 / max_subdivisions));
1693
1694 for (S32 i = start_tick; i <= stop_tick; i++)
1695 {
1696 F32 tick_scale = 1.f;
1697 F32 alpha = grid_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side1)));
1698 LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1));
1699
1700 for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
1701 {
1702 if (fmodf((F32)(i + label_sub_div_offset_1), division_level) == 0.f)
1703 {
1704 break;
1705 }
1706 tick_scale *= 0.7f;
1707 }
1708
1709 if (fmodf((F32)(i + label_sub_div_offset_1), (max_subdivisions / llmin(sGridMaxSubdivisionLevel, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1, tick_label_spacing)))) == 0.f)
1710 {
1711 LLVector3 text_origin = tick_pos +
1712 (mSnapGuideDir1 * mSnapRegimeOffset * (1.f + tick_scale));
1713
1714 EGridMode grid_mode = gSelectMgr->getGridMode();
1715 F32 tick_val;
1716 if (grid_mode == GRID_MODE_WORLD)
1717 {
1718 tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 / grid_resolution);
1719 }
1720 else
1721 {
1722 tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 * 2.f);
1723 }
1724
1725 if (getUniform())
1726 {
1727 tick_val *= 2.f;
1728 }
1729
1730 F32 text_highlight = 0.8f;
1731
1732 if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime)
1733 {
1734 text_highlight = 1.f;
1735 }
1736
1737 renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
1738 }
1739 }
1740
1741 // label ticks on opposite side
1742 if (mScaleSnapUnit2 != mScaleSnapUnit1)
1743 {
1744 start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2));
1745 stop_tick = llmin(max_ticks2, num_ticks_per_side2);
1746 for (S32 i = start_tick; i <= stop_tick; i++)
1747 {
1748 F32 tick_scale = 1.f;
1749 F32 alpha = grid_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side2)));
1750 LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2));
1751
1752 for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
1753 {
1754 if (fmodf((F32)(i + label_sub_div_offset_2), division_level) == 0.f)
1755 {
1756 break;
1757 }
1758 tick_scale *= 0.7f;
1759 }
1760
1761 if (fmodf((F32)(i + label_sub_div_offset_2), (max_subdivisions / llmin(max_subdivisions, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2, tick_label_spacing)))) == 0.f)
1762 {
1763 LLVector3 text_origin = tick_pos +
1764 (mSnapGuideDir2 * mSnapRegimeOffset * (1.f + tick_scale));
1765
1766 EGridMode grid_mode = gSelectMgr->getGridMode();
1767 F32 tick_val;
1768 if (grid_mode == GRID_MODE_WORLD)
1769 {
1770 tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 / grid_resolution);
1771 }
1772 else
1773 {
1774 tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 * 2.f);
1775 }
1776
1777 if (getUniform())
1778 {
1779 tick_val *= 2.f;
1780 }
1781
1782 F32 text_highlight = 0.8f;
1783
1784 if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime)
1785 {
1786 text_highlight = 1.f;
1787 }
1788
1789 renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
1790 }
1791 }
1792 }
1793
1794
1795 // render help text
1796 if (gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
1797 {
1798 if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText)
1799 {
1800 LLVector3 selection_center_start = gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
1801
1802 LLVector3 offset_dir;
1803 if (mSnapGuideDir1 * gCamera->getAtAxis() > mSnapGuideDir2 * gCamera->getAtAxis())
1804 {
1805 offset_dir = mSnapGuideDir2;
1806 }
1807 else
1808 {
1809 offset_dir = mSnapGuideDir1;
1810 }
1811
1812 LLVector3 help_text_pos = selection_center_start + (mSnapRegimeOffset * 5.f * offset_dir);
1813 const LLFontGL* big_fontp = LLFontGL::sSansSerif;
1814
1815 std::string help_text = "Move mouse cursor over ruler";
1816 LLColor4 help_text_color = LLColor4::white;
1817 help_text_color.mV[VALPHA] = clamp_rescale(mHelpTextTimer.getElapsedTimeF32(), sHelpTextVisibleTime, sHelpTextVisibleTime + sHelpTextFadeTime, grid_alpha, 0.f);
1818 hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
1819 help_text = "to snap to grid";
1820 help_text_pos -= gCamera->getUpAxis() * mSnapRegimeOffset * 0.4f;
1821 hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
1822 }
1823 }
1824 }
1825}
1826
1827// Returns unit vector in direction of part of an origin-centered cube
1828LLVector3 LLManipScale::partToUnitVector( S32 part ) const
1829{
1830 if( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) )
1831 {
1832 return faceToUnitVector( part );
1833 }
1834 else
1835 if( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) )
1836 {
1837 return cornerToUnitVector( part );
1838 }
1839 else
1840 if( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX ) )
1841 {
1842 return edgeToUnitVector( part );
1843 }
1844 return LLVector3();
1845}
1846
1847
1848// Returns unit vector in direction of face of an origin-centered cube
1849LLVector3 LLManipScale::faceToUnitVector( S32 part ) const
1850{
1851 llassert( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) );
1852 switch( part )
1853 {
1854 case LL_FACE_POSX:
1855 return LLVector3( 1.f, 0.f, 0.f );
1856
1857 case LL_FACE_NEGX:
1858 return LLVector3( -1.f, 0.f, 0.f );
1859
1860 case LL_FACE_POSY:
1861 return LLVector3( 0.f, 1.f, 0.f );
1862
1863 case LL_FACE_NEGY:
1864 return LLVector3( 0.f, -1.f, 0.f );
1865
1866 case LL_FACE_POSZ:
1867 return LLVector3( 0.f, 0.f, 1.f );
1868
1869 case LL_FACE_NEGZ:
1870 return LLVector3( 0.f, 0.f, -1.f );
1871 }
1872 return LLVector3();
1873}
1874
1875
1876// Returns unit vector in direction of corner of an origin-centered cube
1877LLVector3 LLManipScale::cornerToUnitVector( S32 part ) const
1878{
1879 llassert( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) );
1880 LLVector3 vec;
1881 switch(part)
1882 {
1883 case LL_CORNER_NNN:
1884 vec.setVec(-F_SQRT3, -F_SQRT3, -F_SQRT3);
1885 break;
1886 case LL_CORNER_NNP:
1887 vec.setVec(-F_SQRT3, -F_SQRT3, F_SQRT3);
1888 break;
1889 case LL_CORNER_NPN:
1890 vec.setVec(-F_SQRT3, F_SQRT3, -F_SQRT3);
1891 break;
1892 case LL_CORNER_NPP:
1893 vec.setVec(-F_SQRT3, F_SQRT3, F_SQRT3);
1894 break;
1895 case LL_CORNER_PNN:
1896 vec.setVec(F_SQRT3, -F_SQRT3, -F_SQRT3);
1897 break;
1898 case LL_CORNER_PNP:
1899 vec.setVec(F_SQRT3, -F_SQRT3, F_SQRT3);
1900 break;
1901 case LL_CORNER_PPN:
1902 vec.setVec(F_SQRT3, F_SQRT3, -F_SQRT3);
1903 break;
1904 case LL_CORNER_PPP:
1905 vec.setVec(F_SQRT3, F_SQRT3, F_SQRT3);
1906 break;
1907 default:
1908 vec.clearVec();
1909 }
1910
1911 return vec;
1912}
1913
1914// Returns unit vector in direction of edge of an origin-centered cube
1915LLVector3 LLManipScale::edgeToUnitVector( S32 part ) const
1916{
1917 llassert( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX) );
1918 part -= LL_EDGE_MIN;
1919 S32 rotation = part >> 2; // Edge between which faces: 0 => XY, 1 => YZ, 2 => ZX
1920 LLVector3 v;
1921 v.mV[rotation] = (part & 1) ? F_SQRT2 : -F_SQRT2;
1922 v.mV[(rotation+1) % 3] = (part & 2) ? F_SQRT2 : -F_SQRT2;
1923 // v.mV[(rotation+2) % 3] defaults to 0.
1924 return v;
1925}
1926
1927// Non-linear scale of origin-centered unit cube to non-origin-centered, non-symetrical bounding box
1928LLVector3 LLManipScale::unitVectorToLocalBBoxExtent( const LLVector3& v, const LLBBox& bbox ) const
1929{
1930 const LLVector3& min = bbox.getMinLocal();
1931 const LLVector3& max = bbox.getMaxLocal();
1932 LLVector3 ctr = bbox.getCenterLocal();
1933
1934 return LLVector3(
1935 v.mV[0] ? (v.mV[0]>0 ? max.mV[0] : min.mV[0] ) : ctr.mV[0],
1936 v.mV[1] ? (v.mV[1]>0 ? max.mV[1] : min.mV[1] ) : ctr.mV[1],
1937 v.mV[2] ? (v.mV[2]>0 ? max.mV[2] : min.mV[2] ) : ctr.mV[2] );
1938}
1939
1940// returns max allowable scale along a given stretch axis
1941F32 LLManipScale::partToMaxScale( S32 part, const LLBBox &bbox ) const
1942{
1943 F32 max_scale_factor = 0.f;
1944 LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
1945 bbox_extents.abs();
1946 F32 max_extent = 0.f;
1947 for (U32 i = VX; i <= VZ; i++)
1948 {
1949 if (bbox_extents.mV[i] > max_extent)
1950 {
1951 max_extent = bbox_extents.mV[i];
1952 }
1953 }
1954 max_scale_factor = bbox_extents.magVec() * MAX_OBJECT_SCALE / max_extent;
1955
1956 if (getUniform())
1957 {
1958 max_scale_factor *= 0.5f;
1959 }
1960
1961 return max_scale_factor;
1962}
1963
1964// returns min allowable scale along a given stretch axis
1965F32 LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const
1966{
1967 LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
1968 bbox_extents.abs();
1969 F32 min_extent = MAX_OBJECT_SCALE;
1970 for (U32 i = VX; i <= VZ; i++)
1971 {
1972 if (bbox_extents.mV[i] > 0.f && bbox_extents.mV[i] < min_extent)
1973 {
1974 min_extent = bbox_extents.mV[i];
1975 }
1976 }
1977 F32 min_scale_factor = bbox_extents.magVec() * MIN_OBJECT_SCALE / min_extent;
1978
1979 if (getUniform())
1980 {
1981 min_scale_factor *= 0.5f;
1982 }
1983
1984 return min_scale_factor;
1985}
1986
1987// Returns the axis aligned unit vector closest to v.
1988LLVector3 LLManipScale::nearestAxis( const LLVector3& v ) const
1989{
1990 // Note: yes, this is a slow but easy implementation
1991 // assumes v is normalized
1992
1993 F32 coords[][3] =
1994 {
1995 { 1.f, 0.f, 0.f },
1996 { 0.f, 1.f, 0.f },
1997 { 0.f, 0.f, 1.f },
1998 {-1.f, 0.f, 0.f },
1999 { 0.f,-1.f, 0.f },
2000 { 0.f, 0.f,-1.f }
2001 };
2002
2003 F32 cosine[6];
2004 cosine[0] = v * LLVector3( coords[0] );
2005 cosine[1] = v * LLVector3( coords[1] );
2006 cosine[2] = v * LLVector3( coords[2] );
2007 cosine[3] = -cosine[0];
2008 cosine[4] = -cosine[1];
2009 cosine[5] = -cosine[2];
2010
2011 F32 greatest_cos = cosine[0];
2012 S32 greatest_index = 0;
2013 for( S32 i=1; i<6; i++ )
2014 {
2015 if( greatest_cos < cosine[i] )
2016 {
2017 greatest_cos = cosine[i];
2018 greatest_index = i;
2019 }
2020 }
2021
2022 return LLVector3( coords[greatest_index] );
2023}
2024
2025BOOL LLManipScale::isSelectionScalable() const
2026{
2027 // An selection is scalable if you are allowed to both edit and move
2028 // everything in it, and it does not have any sitting agents
2029 BOOL scalable = gSelectMgr->getFirstObject() ? TRUE : FALSE;
2030 for(LLViewerObject* cur = gSelectMgr->getFirstObject();
2031 cur;
2032 cur = gSelectMgr->getNextObject() )
2033 {
2034 if( !(cur->permModify() && cur->permMove())
2035 || cur->isSeat())
2036 {
2037 scalable = FALSE;
2038 break;
2039 }
2040 }
2041 return scalable;
2042}