aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llmaniprotate.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/llmaniprotate.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/llmaniprotate.cpp')
-rw-r--r--linden/indra/newview/llmaniprotate.cpp1895
1 files changed, 1895 insertions, 0 deletions
diff --git a/linden/indra/newview/llmaniprotate.cpp b/linden/indra/newview/llmaniprotate.cpp
new file mode 100644
index 0000000..93cbfe4
--- /dev/null
+++ b/linden/indra/newview/llmaniprotate.cpp
@@ -0,0 +1,1895 @@
1/**
2 * @file llmaniprotate.cpp
3 * @brief LLManipRotate class implementation
4 *
5 * Copyright (c) 2002-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 "llmaniprotate.h"
31
32// library includes
33#include "llmath.h"
34#include "llgl.h"
35#include "v4color.h"
36#include "llprimitive.h"
37#include "llview.h"
38#include "llfontgl.h"
39
40// viewer includes
41#include "llagent.h"
42#include "llbox.h"
43#include "llbutton.h"
44#include "llviewercontrol.h"
45#include "llcriticaldamp.h"
46#include "llhoverview.h"
47#include "llfloatertools.h"
48#include "llselectmgr.h"
49#include "llstatusbar.h"
50#include "llui.h"
51#include "llvoavatar.h"
52#include "llviewborder.h"
53#include "llviewercamera.h"
54#include "llviewerobject.h"
55#include "llviewerobject.h"
56#include "llviewerwindow.h"
57#include "llworld.h"
58#include "pipeline.h"
59#include "viewer.h"
60#include "lldrawable.h"
61#include "llglheaders.h"
62
63const F32 RADIUS_PIXELS = 100.f; // size in screen space
64const F32 SQ_RADIUS = RADIUS_PIXELS * RADIUS_PIXELS;
65const F32 WIDTH_PIXELS = 8;
66const S32 CIRCLE_STEPS = 100;
67const F32 DELTA = F_TWO_PI / CIRCLE_STEPS;
68const F32 SIN_DELTA = sin( DELTA );
69const F32 COS_DELTA = cos( DELTA );
70const F32 MAX_MANIP_SELECT_DISTANCE = 100.f;
71const F32 SNAP_ANGLE_INCREMENT = 5.625f;
72const F32 SNAP_ANGLE_DETENTE = SNAP_ANGLE_INCREMENT;
73const F32 SNAP_GUIDE_RADIUS_1 = 2.8f;
74const F32 SNAP_GUIDE_RADIUS_2 = 2.4f;
75const F32 SNAP_GUIDE_RADIUS_3 = 2.2f;
76const F32 SNAP_GUIDE_RADIUS_4 = 2.1f;
77const F32 SNAP_GUIDE_RADIUS_5 = 2.05f;
78const F32 SNAP_GUIDE_INNER_RADIUS = 2.f;
79const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 80.f * DEG_TO_RAD );
80const F32 SELECTED_MANIPULATOR_SCALE = 1.05f;
81const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
82
83extern void handle_reset_rotation(void*); // in LLViewerWindow
84extern void handle_first_tool(void*);
85
86LLManipRotate::LLManipRotate( LLToolComposite* composite )
87: LLManip( "Rotate", composite ),
88 mRotationCenter(),
89 mCenterScreen(),
90 mRotation(),
91 mMouseDown(),
92 mMouseCur(),
93 mRadiusMeters(0.f),
94 mCenterToCam(),
95 mCenterToCamNorm(),
96 mCenterToCamMag(0.f),
97 mCenterToProfilePlane(),
98 mCenterToProfilePlaneMag(0.f),
99 mManipPart( LL_NO_PART ),
100 mSendUpdateOnMouseUp( FALSE ),
101 mSmoothRotate( FALSE ),
102 mCamEdgeOn(FALSE),
103 mManipulatorScales(1.f, 1.f, 1.f, 1.f)
104{ }
105
106void LLManipRotate::handleSelect()
107{
108 // *FIX: put this in mouseDown?
109 gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
110 gFloaterTools->setStatusText("Drag colored bands to rotate object");
111}
112
113void LLManipRotate::handleDeselect()
114{
115 mHighlightedPart = LL_NO_PART;
116 mManipPart = LL_NO_PART;
117
118 gFloaterTools->setStatusText("");
119}
120
121void LLManipRotate::render()
122{
123 LLGLSUIDefault gls_ui;
124 LLGLSNoTexture gls_no_texture;
125 LLGLDepthTest gls_depth(GL_TRUE);
126 LLGLEnable gl_blend(GL_BLEND);
127 LLGLEnable gls_alpha_test(GL_ALPHA_TEST);
128
129 // You can rotate if you can move
130 LLViewerObject* first_object = gSelectMgr->getFirstMoveableObject(TRUE);
131 if( !first_object )
132 {
133 return;
134 }
135
136 if( !updateVisiblity() )
137 {
138 return;
139 }
140
141 glMatrixMode(GL_MODELVIEW);
142 glPushMatrix();
143 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
144 {
145 F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
146 glScalef(zoom, zoom, zoom);
147 }
148
149
150 LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
151
152 LLColor4 highlight_outside( 1.f, 1.f, 0.f, 1.f );
153 LLColor4 highlight_inside( 0.7f, 0.7f, 0.f, 0.5f );
154 F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
155
156 glPushMatrix();
157 {
158 // are we in the middle of a constrained drag?
159 if (mManipPart >= LL_ROT_X && mManipPart <= LL_ROT_Z)
160 {
161 renderSnapGuides();
162 }
163 else
164 {
165 LLGLEnable cull_face(GL_CULL_FACE);
166 LLGLDepthTest gls_depth(GL_FALSE);
167 glPushMatrix();
168 {
169 // Draw "sphere" (intersection of sphere with tangent cone that has apex at camera)
170 glTranslatef( mCenterToProfilePlane.mV[VX], mCenterToProfilePlane.mV[VY], mCenterToProfilePlane.mV[VZ] );
171 glTranslatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
172
173 // Inverse change of basis vectors
174 LLVector3 forward = mCenterToCamNorm;
175 LLVector3 left = gAgent.getUpAxis() % forward;
176 left.normVec();
177 LLVector3 up = forward % left;
178
179 LLVector4 a(-forward);
180 a.mV[3] = 0;
181 LLVector4 b(up);
182 b.mV[3] = 0;
183 LLVector4 c(left);
184 c.mV[3] = 0;
185 LLMatrix4 mat;
186 mat.initRows(a, b, c, LLVector4(0.f, 0.f, 0.f, 1.f));
187
188 glMultMatrixf( &mat.mMatrix[0][0] );
189
190 glRotatef( -90, 0.f, 1.f, 0.f);
191 LLColor4 color;
192 if (mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
193 {
194 color.setVec(0.8f, 0.8f, 0.8f, 0.8f);
195 glScalef(mManipulatorScales.mV[VW], mManipulatorScales.mV[VW], mManipulatorScales.mV[VW]);
196 }
197 else
198 {
199 color.setVec( 0.7f, 0.7f, 0.7f, 0.6f );
200 }
201 gl_washer_2d(mRadiusMeters + width_meters, mRadiusMeters, CIRCLE_STEPS, color, color);
202
203
204 if (mManipPart == LL_NO_PART)
205 {
206 glColor4f( 0.7f, 0.7f, 0.7f, 0.3f );
207 gl_circle_2d( 0, 0, mRadiusMeters, CIRCLE_STEPS, TRUE );
208 }
209
210 GLdouble plane_eqn[] = { 0, 0, 1, 0 };
211 glClipPlane( GL_CLIP_PLANE0, plane_eqn );
212 }
213 glPopMatrix();
214 }
215
216 glTranslatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
217
218 LLQuaternion rot;
219 F32 angle_radians, x, y, z;
220
221 LLVector3 grid_origin;
222 LLVector3 grid_scale;
223 LLQuaternion grid_rotation;
224
225 gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
226
227 grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
228 glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
229
230
231 if (mManipPart == LL_ROT_Z)
232 {
233 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
234 glPushMatrix();
235 {
236 // selected part
237 glScalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
238 renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f) , LLColor4( 0.f, 0.f, 1.f, 0.3f ));
239 }
240 glPopMatrix();
241 }
242 else if (mManipPart == LL_ROT_Y)
243 {
244 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
245 glPushMatrix();
246 {
247 glRotatef( 90.f, 1.f, 0.f, 0.f );
248 glScalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
249 renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f), LLColor4( 0.f, 1.f, 0.f, 0.3f));
250 }
251 glPopMatrix();
252 }
253 else if (mManipPart == LL_ROT_X)
254 {
255 mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
256 glPushMatrix();
257 {
258 glRotatef( 90.f, 0.f, 1.f, 0.f );
259 glScalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
260 renderActiveRing( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f), LLColor4( 1.f, 0.f, 0.f, 0.3f));
261 }
262 glPopMatrix();
263 }
264 else if (mManipPart == LL_ROT_ROLL)
265 {
266 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
267 }
268 else if (mManipPart == LL_NO_PART)
269 {
270 if (mHighlightedPart == LL_NO_PART)
271 {
272 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
273 }
274
275 LLGLEnable cull_face(GL_CULL_FACE);
276 LLGLEnable clip_plane0(GL_CLIP_PLANE0);
277 LLGLDepthTest gls_depth(GL_FALSE);
278
279 // First pass: centers. Second pass: sides.
280 for( S32 i=0; i<2; i++ )
281 {
282 glPushMatrix();
283 {
284 if (mHighlightedPart == LL_ROT_Z)
285 {
286 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
287 glScalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
288 // hovering over part
289 gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f ), LLColor4( 0.f, 0.f, 1.f, 0.5f ), CIRCLE_STEPS, i);
290 }
291 else
292 {
293 // default
294 gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 0.8f, 0.8f ), LLColor4( 0.f, 0.f, 0.8f, 0.4f ), CIRCLE_STEPS, i);
295 }
296 }
297 glPopMatrix();
298
299 glPushMatrix();
300 {
301 glRotatef( 90.f, 1.f, 0.f, 0.f );
302 if (mHighlightedPart == LL_ROT_Y)
303 {
304 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
305 glScalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
306 // hovering over part
307 gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f ), LLColor4( 0.f, 1.f, 0.f, 0.5f ), CIRCLE_STEPS, i);
308 }
309 else
310 {
311 // default
312 gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.8f, 0.f, 0.8f ), LLColor4( 0.f, 0.8f, 0.f, 0.4f ), CIRCLE_STEPS, i);
313 }
314 }
315 glPopMatrix();
316
317 glPushMatrix();
318 {
319 glRotatef( 90.f, 0.f, 1.f, 0.f );
320 if (mHighlightedPart == LL_ROT_X)
321 {
322 mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
323 glScalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
324
325 // hovering over part
326 gl_ring( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f ), LLColor4( 1.f, 0.f, 0.f, 0.5f ), CIRCLE_STEPS, i);
327 }
328 else
329 {
330 // default
331 gl_ring( mRadiusMeters, width_meters, LLColor4( 0.8f, 0.f, 0.f, 0.8f ), LLColor4( 0.8f, 0.f, 0.f, 0.4f ), CIRCLE_STEPS, i);
332 }
333 }
334 glPopMatrix();
335
336 if (mHighlightedPart == LL_ROT_ROLL)
337 {
338 mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
339 }
340 }
341 }
342 }
343 glPopMatrix();
344 glPopMatrix();
345
346 LLVector3 euler_angles;
347 LLQuaternion object_rot = first_object->getRotationEdit();
348 object_rot.getEulerAngles(&(euler_angles.mV[VX]), &(euler_angles.mV[VY]), &(euler_angles.mV[VZ]));
349 euler_angles *= RAD_TO_DEG;
350 euler_angles.mV[VX] = llround(fmodf(euler_angles.mV[VX] + 360.f, 360.f), 0.05f);
351 euler_angles.mV[VY] = llround(fmodf(euler_angles.mV[VY] + 360.f, 360.f), 0.05f);
352 euler_angles.mV[VZ] = llround(fmodf(euler_angles.mV[VZ] + 360.f, 360.f), 0.05f);
353
354 renderXYZ(euler_angles);
355}
356
357BOOL LLManipRotate::handleMouseDown(S32 x, S32 y, MASK mask)
358{
359 BOOL handled = FALSE;
360
361 LLViewerObject* first_object = gSelectMgr->getFirstMoveableObject(TRUE);
362 if( first_object )
363 {
364 LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
365 if( hit_obj && mHighlightedPart != LL_NO_PART )
366 {
367 handled = handleMouseDownOnPart( x, y, mask );
368 }
369 }
370
371 return handled;
372}
373
374// Assumes that one of the parts of the manipulator was hit.
375BOOL LLManipRotate::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
376{
377 BOOL can_rotate = gSelectMgr->getObjectCount() != 0;
378 for (LLViewerObject* objectp = gSelectMgr->getFirstObject();
379 objectp;
380 objectp = gSelectMgr->getNextObject())
381 {
382 can_rotate = can_rotate && objectp->permMove() && (objectp->permModify() || gSavedSettings.getBOOL("SelectLinkedSet"));
383 }
384
385 if (!can_rotate)
386 {
387 return FALSE;
388 }
389
390 highlightManipulators(x, y);
391 S32 hit_part = mHighlightedPart;
392 // we just started a drag, so save initial object positions
393 gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_ROTATE);
394
395 // save selection center
396 mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() ); //gSelectMgr->getSelectionCenterGlobal();
397
398 mManipPart = (EManipPart)hit_part;
399 LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
400
401 if( mManipPart == LL_ROT_GENERAL)
402 {
403 mMouseDown = intersectMouseWithSphere( x, y, center, mRadiusMeters);
404 }
405 else
406 {
407 // Project onto the plane of the ring
408 LLVector3 axis = getConstraintAxis();
409
410 F32 axis_onto_cam = llabs( axis * mCenterToCamNorm );
411 const F32 AXIS_ONTO_CAM_TOL = cos( 85.f * DEG_TO_RAD );
412 if( axis_onto_cam < AXIS_ONTO_CAM_TOL )
413 {
414 LLVector3 up_from_axis = mCenterToCamNorm % axis;
415 up_from_axis.normVec();
416 LLVector3 cur_intersection;
417 getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
418 cur_intersection -= center;
419 mMouseDown = projected_vec(cur_intersection, up_from_axis);
420 F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
421 F32 mouse_dist_sqrd = mMouseDown.magVecSquared();
422 if (mouse_dist_sqrd > 0.0001f)
423 {
424 mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
425 mouse_dist_sqrd);
426 }
427 LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, axis);
428 mMouseDown += mouse_depth * projected_center_to_cam;
429
430 }
431 else
432 {
433 mMouseDown = findNearestPointOnRing( x, y, center, axis ) - center;
434 mMouseDown.normVec();
435 }
436 }
437
438 mMouseCur = mMouseDown;
439
440 // Route future Mouse messages here preemptively. (Release on mouse up.)
441 setMouseCapture( TRUE );
442 gSelectMgr->enableSilhouette(FALSE);
443 return TRUE;
444}
445
446
447LLVector3 LLManipRotate::findNearestPointOnRing( S32 x, S32 y, const LLVector3& center, const LLVector3& axis )
448{
449 // Project the delta onto the ring and rescale it by the radius so that it's _on_ the ring.
450 LLVector3 proj_onto_ring;
451 getMousePointOnPlaneAgent(proj_onto_ring, x, y, center, axis);
452 proj_onto_ring -= center;
453 proj_onto_ring.normVec();
454
455 return center + proj_onto_ring * mRadiusMeters;
456}
457
458BOOL LLManipRotate::handleMouseUp(S32 x, S32 y, MASK mask)
459{
460 // first, perform normal processing in case this was a quick-click
461 handleHover(x, y, mask);
462
463 mManipPart = LL_NO_PART;
464
465 // Might have missed last update due to timing.
466 if (mSendUpdateOnMouseUp)
467 {
468 gSelectMgr->sendMultipleUpdate( UPD_ROTATION | UPD_POSITION );
469 mSendUpdateOnMouseUp = FALSE;
470 }
471 gSelectMgr->enableSilhouette(TRUE);
472 //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
473
474 gSelectMgr->updateSelectionCenter();
475 gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
476
477 return LLManip::handleMouseUp(x, y, mask);
478}
479
480
481BOOL LLManipRotate::handleHover(S32 x, S32 y, MASK mask)
482{
483 if( hasMouseCapture() )
484 {
485 if( gSelectMgr->isEmpty() )
486 {
487 // Somehow the object got deselected while we were dragging it.
488 setMouseCapture( FALSE );
489 }
490 else
491 {
492 drag(x, y);
493 }
494
495 lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipRotate (active)" << llendl;
496 }
497 else
498 {
499 highlightManipulators(x, y);
500 lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipRotate (inactive)" << llendl;
501 }
502
503 gViewerWindow->setCursor(UI_CURSOR_TOOLROTATE);
504 return TRUE;
505}
506
507
508LLVector3 LLManipRotate::projectToSphere( F32 x, F32 y, BOOL* on_sphere )
509{
510 F32 z = 0.f;
511 F32 dist_squared = x*x + y*y;
512
513 *on_sphere = dist_squared <= SQ_RADIUS;
514 if( *on_sphere )
515 {
516 z = sqrt(SQ_RADIUS - dist_squared);
517 }
518 return LLVector3( x, y, z );
519}
520
521extern U32 gFrameCount;
522
523// Freeform rotation
524void LLManipRotate::drag( S32 x, S32 y )
525{
526 static LLTimer update_timer;
527 F32 elapsed_time = update_timer.getElapsedTimeF32();
528 const F32 UPDATE_DELAY = 0.1f; // min time between transmitted updates
529 BOOL send_rotation_update = FALSE;
530 BOOL send_position_update = FALSE;
531
532 if( !updateVisiblity() )
533 {
534 return;
535 }
536
537 if( mManipPart == LL_ROT_GENERAL )
538 {
539 mRotation = dragUnconstrained(x, y);
540 }
541 else
542 {
543 mRotation = dragConstrained(x, y);
544 }
545
546 BOOL damped = mSmoothRotate;
547 mSmoothRotate = FALSE;
548
549 LLViewerObject* object;
550 LLSelectNode* selectNode;
551 BOOL using_linked_selection = gSavedSettings.getBOOL("SelectLinkedSet");
552
553 for( selectNode = gSelectMgr->getFirstNode(); selectNode != NULL; selectNode = gSelectMgr->getNextNode() )
554 {
555 object = selectNode->getObject();
556
557 // have permission to move and object is root of selection or individually selected
558 if (object->permMove() && (object->isRootEdit() || selectNode->mIndividualSelection))
559 {
560 if (!object->isRootEdit())
561 {
562 // child objects should not update if parent is selected
563 LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
564 if (editable_root->isSelected())
565 {
566 // we will be moved properly by our parent, so skip
567 continue;
568 }
569 }
570
571 LLQuaternion new_rot = selectNode->mSavedRotation * mRotation;
572 std::vector<LLVector3> child_positions;
573 std::vector<LLQuaternion> child_rotations;
574 if (object->isRootEdit() && selectNode->mIndividualSelection)
575 {
576 for (U32 i = 0; i < object->mChildList.size(); i++)
577 {
578 LLViewerObject* childp = object->mChildList[i];
579 child_positions.push_back(childp->getPositionEdit());
580 child_rotations.push_back(childp->getRotationEdit());
581 }
582 }
583
584 if (object->getParent() && object->mDrawable.notNull())
585 {
586 LLQuaternion invParentRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
587 invParentRotation.transQuat();
588
589 object->setRotation(new_rot * invParentRotation, damped);
590 rebuild(object);
591 }
592 else
593 {
594 object->setRotation(new_rot, damped);
595 rebuild(object);
596 }
597
598 // don't send updates all the time for sub-objects
599 if (using_linked_selection && object->getRenderRotation() != new_rot)
600 {
601 send_rotation_update = TRUE;
602 }
603
604 // for individually selected roots, we need to counterrotate all the children
605 if (object->isRootEdit() && selectNode->mIndividualSelection)
606 {
607 //RN: must do non-damped updates on these objects so relative rotation appears constant
608 // instead of having two competing slerps making the child objects appear to "wobble"
609 for (U32 i = 0; i < object->mChildList.size(); i++)
610 {
611 LLViewerObject* childp = object->mChildList[i];
612 LLVector3 child_offset = ((child_positions[i] - object->getPositionEdit()) * ~object->getRotationEdit()) - childp->getPosition();
613 if (!childp->isSelected() && childp->mDrawable.notNull())
614 {
615 childp->setRotation(child_rotations[i] * ~object->getRotationEdit());
616 childp->setPosition((child_positions[i] - object->getPositionEdit()) * ~object->getRotationEdit());
617 rebuild(childp);
618 }
619 }
620 }
621 }
622 }
623
624 // update positions
625 for( selectNode = gSelectMgr->getFirstNode(); selectNode != NULL; selectNode = gSelectMgr->getNextNode() )
626 {
627 object = selectNode->getObject();
628
629 // to avoid cumulative position changes we calculate the objects new position using its saved position
630 if (object && object->permMove())
631 {
632 LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
633
634 LLVector3 old_position;
635 LLVector3 new_position;
636
637 if (object->isAttachment() && object->mDrawable.notNull())
638 {
639 // need to work in drawable space to handle selected items from multiple attachments
640 // (which have no shared frame of reference other than their render positions)
641 LLXform* parent_xform = object->mDrawable->getXform()->getParent();
642 new_position = (selectNode->mSavedPositionLocal * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
643 old_position = (object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();//object->getRenderPosition();
644 }
645 else
646 {
647 new_position = gAgent.getPosAgentFromGlobal( selectNode->mSavedPositionGlobal );
648 old_position = object->getPositionAgent();
649 }
650
651 new_position = (new_position - center) * mRotation; // new relative rotated position
652 new_position += center;
653
654 if (object->isRootEdit() && !object->isAttachment())
655 {
656 LLVector3d new_pos_global = gAgent.getPosGlobalFromAgent(new_position);
657 new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
658 new_position = gAgent.getPosAgentFromGlobal(new_pos_global);
659 }
660
661 // for individually selected child objects
662 if (!object->isRootEdit() && selectNode->mIndividualSelection)
663 {
664 LLViewerObject* parentp = (LLViewerObject*)object->getParent();
665 if (!parentp->isSelected())
666 {
667 if (object->isAttachment() && object->mDrawable.notNull())
668 {
669 // find position relative to render position of parent
670 object->setPosition((new_position - parentp->getRenderPosition()) * ~parentp->getRenderRotation());
671 rebuild(object);
672 }
673 else
674 {
675 object->setPositionParent((new_position - parentp->getPositionAgent()) * ~parentp->getRotationRegion());
676 rebuild(object);
677 }
678 }
679 }
680 else if (object->isRootEdit())
681 {
682 if (object->isAttachment() && object->mDrawable.notNull())
683 {
684 LLXform* parent_xform = object->mDrawable->getXform()->getParent();
685 object->setPosition((new_position - parent_xform->getWorldPosition()) * ~parent_xform->getWorldRotation());
686 rebuild(object);
687 }
688 else
689 {
690 object->setPositionAgent(new_position);
691 rebuild(object);
692 }
693 }
694
695 if (using_linked_selection && object->getPositionAgent() != new_position)
696 {
697 send_position_update = TRUE;
698 }
699
700 // for individually selected roots, we need to counter-translate all unselected children
701 if (object->isRootEdit() && selectNode->mIndividualSelection)
702 {
703 // only offset by parent's translation as we've already countered parent's rotation
704 LLVector3 child_offset;
705 if (object->isAttachment() && object->mDrawable.notNull())
706 {
707 LLXform* attachment_point_xform = object->mDrawable->getXform()->getParent();
708 LLQuaternion parent_rotation = object->getRotation() * attachment_point_xform->getWorldRotation();
709 child_offset = LLVector3(old_position - new_position) * ~parent_rotation;
710 }
711 else
712 {
713 child_offset = LLVector3(old_position - new_position) * ~object->getRenderRotation();
714 }
715
716 rebuild(object);
717 for (U32 i = 0; i < object->mChildList.size(); i++)
718 {
719 LLViewerObject* childp = object->mChildList[i];
720 if (!childp->isSelected() && childp->mDrawable.notNull())
721 {
722 childp->setPosition(childp->getPosition() + child_offset);
723 rebuild(childp);
724 }
725 }
726 }
727 }
728 }
729
730 if ((send_position_update || send_rotation_update) && (elapsed_time > UPDATE_DELAY))
731 {
732 U32 flag = UPD_NONE;
733 if (send_rotation_update)
734 {
735 flag |= UPD_ROTATION;
736 }
737 if (send_position_update)
738 {
739 flag |= UPD_POSITION;
740 }
741
742 gSelectMgr->sendMultipleUpdate( flag );
743 update_timer.reset();
744 mSendUpdateOnMouseUp = FALSE;
745 }
746 else
747 {
748 mSendUpdateOnMouseUp = TRUE;
749 }
750
751 gSelectMgr->updateSelectionCenter();
752
753 // RN: just clear focus so camera doesn't follow spurious object updates
754 gAgent.clearFocusObject();
755 dialog_refresh_all();
756}
757
758void LLManipRotate::renderActiveRing( F32 radius, F32 width, const LLColor4& front_color, const LLColor4& back_color)
759{
760 LLGLEnable cull_face(GL_CULL_FACE);
761 {
762 gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, FALSE);
763 gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, TRUE);
764 }
765 {
766 LLGLDepthTest gls_depth(GL_FALSE);
767 gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, FALSE);
768 gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, TRUE);
769 }
770}
771
772void LLManipRotate::renderSnapGuides()
773{
774 LLVector3 grid_origin;
775 LLVector3 grid_scale;
776 LLQuaternion grid_rotation;
777 LLVector3 constraint_axis = getConstraintAxis();
778
779 gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
780
781 if (!gSavedSettings.getBOOL("SnapEnabled"))
782 {
783 return;
784 }
785
786 LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
787 LLVector3 cam_at_axis;
788 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
789 {
790 cam_at_axis.setVec(1.f, 0.f, 0.f);
791 }
792 else
793 {
794 cam_at_axis = center - gAgent.getCameraPositionAgent();
795 cam_at_axis.normVec();
796 }
797
798 LLVector3 world_snap_axis;
799 LLVector3 test_axis = constraint_axis;
800
801 BOOL constrain_to_ref_object = FALSE;
802 if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
803 {
804 test_axis = test_axis * ~grid_rotation;
805 }
806 else if (gSelectMgr->getGridMode() == GRID_MODE_REF_OBJECT)
807 {
808 test_axis = test_axis * ~grid_rotation;
809 constrain_to_ref_object = TRUE;
810 }
811
812 test_axis.abs();
813
814 // find closest global/reference axis to local constraint axis;
815 if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
816 {
817 world_snap_axis = LLVector3::y_axis;
818 }
819 else if (test_axis.mV[VY] > test_axis.mV[VZ])
820 {
821 world_snap_axis = LLVector3::z_axis;
822 }
823 else
824 {
825 world_snap_axis = LLVector3::x_axis;
826 }
827
828 LLVector3 projected_snap_axis = world_snap_axis;
829 if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
830 {
831 projected_snap_axis = projected_snap_axis * grid_rotation;
832 }
833 else if (constrain_to_ref_object)
834 {
835 projected_snap_axis = projected_snap_axis * grid_rotation;
836 }
837
838 // project world snap axis onto constraint plane
839 projected_snap_axis -= projected_vec(projected_snap_axis, constraint_axis);
840 projected_snap_axis.normVec();
841
842 S32 num_rings = mCamEdgeOn ? 2 : 1;
843 for (S32 ring_num = 0; ring_num < num_rings; ring_num++)
844 {
845 LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
846
847 if (mCamEdgeOn)
848 {
849 // draw two opposing rings
850 if (ring_num == 0)
851 {
852 center += constraint_axis * mRadiusMeters * 0.5f;
853 }
854 else
855 {
856 center -= constraint_axis * mRadiusMeters * 0.5f;
857 }
858 }
859
860 LLGLDepthTest gls_depth(GL_FALSE);
861 for (S32 pass = 0; pass < 3; pass++)
862 {
863 // render snap guide ring
864 glPushMatrix();
865
866 LLQuaternion snap_guide_rot;
867 F32 angle_radians, x, y, z;
868 snap_guide_rot.shortestArc(LLVector3::z_axis, getConstraintAxis());
869 snap_guide_rot.getAngleAxis(&angle_radians, &x, &y, &z);
870 glTranslatef(center.mV[VX], center.mV[VY], center.mV[VZ]);
871 glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
872
873 LLColor4 line_color = setupSnapGuideRenderPass(pass);
874
875 glColor4fv(line_color.mV);
876
877 if (mCamEdgeOn)
878 {
879 // render an arc
880 LLVector3 edge_normal = cam_at_axis % constraint_axis;
881 edge_normal.normVec();
882 LLVector3 x_axis_snap = LLVector3::x_axis * snap_guide_rot;
883 LLVector3 y_axis_snap = LLVector3::y_axis * snap_guide_rot;
884
885 F32 end_angle = atan2(y_axis_snap * edge_normal, x_axis_snap * edge_normal);
886 //F32 start_angle = angle_between((-1.f * LLVector3::x_axis) * snap_guide_rot, edge_normal);
887 F32 start_angle = end_angle - F_PI;
888 gl_arc_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, FALSE, start_angle, end_angle);
889 }
890 else
891 {
892 gl_circle_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, FALSE);
893 }
894 glPopMatrix();
895
896 for (S32 i = 0; i < 64; i++)
897 {
898 BOOL render_text = TRUE;
899 F32 deg = 5.625f * (F32)i;
900 LLVector3 inner_point;
901 LLVector3 outer_point;
902 LLVector3 text_point;
903 LLQuaternion rot(deg * DEG_TO_RAD, constraint_axis);
904 glBegin(GL_LINES);
905 {
906 inner_point = (projected_snap_axis * mRadiusMeters * SNAP_GUIDE_INNER_RADIUS * rot) + center;
907 F32 tick_length = 0.f;
908 if (i % 16 == 0)
909 {
910 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_1 - SNAP_GUIDE_INNER_RADIUS);
911 }
912 else if (i % 8 == 0)
913 {
914 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_2 - SNAP_GUIDE_INNER_RADIUS);
915 }
916 else if (i % 4 == 0)
917 {
918 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_3 - SNAP_GUIDE_INNER_RADIUS);
919 }
920 else if (i % 2 == 0)
921 {
922 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_4 - SNAP_GUIDE_INNER_RADIUS);
923 }
924 else
925 {
926 tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_5 - SNAP_GUIDE_INNER_RADIUS);
927 }
928
929 if (mCamEdgeOn)
930 {
931 // don't draw ticks that are on back side of circle
932 F32 dot = cam_at_axis * (projected_snap_axis * rot);
933 if (dot > 0.f)
934 {
935 outer_point = inner_point;
936 render_text = FALSE;
937 }
938 else
939 {
940 if (ring_num == 0)
941 {
942 outer_point = inner_point + (constraint_axis * tick_length) * rot;
943 }
944 else
945 {
946 outer_point = inner_point - (constraint_axis * tick_length) * rot;
947 }
948 }
949 }
950 else
951 {
952 outer_point = inner_point + (projected_snap_axis * tick_length) * rot;
953 }
954
955 text_point = outer_point + (projected_snap_axis * mRadiusMeters * 0.1f) * rot;
956
957 glVertex3fv(inner_point.mV);
958 glVertex3fv(outer_point.mV);
959 }
960 glEnd();
961
962 //RN: text rendering does own shadow pass, so only render once
963 if (pass == 1 && render_text && i % 16 == 0)
964 {
965 if (world_snap_axis.mV[VX])
966 {
967 if (i == 0)
968 {
969 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Forward" : "East", LLColor4::white);
970 }
971 else if (i == 16)
972 {
973 if (constraint_axis.mV[VZ] > 0.f)
974 {
975 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Left" : "North", LLColor4::white);
976 }
977 else
978 {
979 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Right" : "South", LLColor4::white);
980 }
981 }
982 else if (i == 32)
983 {
984 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Back" : "West", LLColor4::white);
985 }
986 else
987 {
988 if (constraint_axis.mV[VZ] > 0.f)
989 {
990 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Right" : "South", LLColor4::white);
991 }
992 else
993 {
994 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Left" : "North", LLColor4::white);
995 }
996 }
997 }
998 else if (world_snap_axis.mV[VY])
999 {
1000 if (i == 0)
1001 {
1002 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Left" : "North", LLColor4::white);
1003 }
1004 else if (i == 16)
1005 {
1006 if (constraint_axis.mV[VX] > 0.f)
1007 {
1008 renderTickText(text_point, "Up", LLColor4::white);
1009 }
1010 else
1011 {
1012 renderTickText(text_point, "Down", LLColor4::white);
1013 }
1014 }
1015 else if (i == 32)
1016 {
1017 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Right" : "South", LLColor4::white);
1018 }
1019 else
1020 {
1021 if (constraint_axis.mV[VX] > 0.f)
1022 {
1023 renderTickText(text_point, "Down", LLColor4::white);
1024 }
1025 else
1026 {
1027 renderTickText(text_point, "Up", LLColor4::white);
1028 }
1029 }
1030 }
1031 else if (world_snap_axis.mV[VZ])
1032 {
1033 if (i == 0)
1034 {
1035 renderTickText(text_point, "Up", LLColor4::white);
1036 }
1037 else if (i == 16)
1038 {
1039 if (constraint_axis.mV[VY] > 0.f)
1040 {
1041 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Forward" : "East", LLColor4::white);
1042 }
1043 else
1044 {
1045 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Back" : "West", LLColor4::white);
1046 }
1047 }
1048 else if (i == 32)
1049 {
1050 renderTickText(text_point, "Down", LLColor4::white);
1051 }
1052 else
1053 {
1054 if (constraint_axis.mV[VY] > 0.f)
1055 {
1056 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Back" : "West", LLColor4::white);
1057 }
1058 else
1059 {
1060 renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Forward" : "East", LLColor4::white);
1061 }
1062 }
1063 }
1064 }
1065 glColor4fv(line_color.mV);
1066 }
1067
1068 // now render projected object axis
1069 if (mInSnapRegime)
1070 {
1071 LLVector3 object_axis;
1072 getObjectAxisClosestToMouse(object_axis);
1073
1074 // project onto constraint plane
1075 LLSelectNode* first_node = gSelectMgr->getFirstMoveableNode(TRUE);
1076 object_axis = object_axis * first_node->getObject()->getRenderRotation();
1077 object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
1078 object_axis.normVec();
1079 object_axis = object_axis * SNAP_GUIDE_INNER_RADIUS * mRadiusMeters + center;
1080 LLVector3 line_start = center;
1081
1082 glBegin(GL_LINES);
1083 {
1084 glVertex3fv(line_start.mV);
1085 glVertex3fv(object_axis.mV);
1086 }
1087 glEnd();
1088
1089 // draw snap guide arrow
1090 glBegin(GL_TRIANGLES);
1091 {
1092 LLVector3 arrow_dir;
1093 LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
1094 arrow_span.normVec();
1095
1096 arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
1097 arrow_dir.normVec();
1098 if (ring_num == 1)
1099 {
1100 arrow_dir *= -1.f;
1101 }
1102 glVertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
1103 glVertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
1104 glVertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
1105 }
1106 glEnd();
1107
1108 {
1109 LLGLDepthTest gls_depth(GL_TRUE);
1110 glBegin(GL_LINES);
1111 {
1112 glVertex3fv(line_start.mV);
1113 glVertex3fv(object_axis.mV);
1114 }
1115 glEnd();
1116
1117 // draw snap guide arrow
1118 glBegin(GL_TRIANGLES);
1119 {
1120 LLVector3 arrow_dir;
1121 LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
1122 arrow_span.normVec();
1123
1124 arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
1125 arrow_dir.normVec();
1126 if (ring_num == 1)
1127 {
1128 arrow_dir *= -1.f;
1129 }
1130
1131 glVertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
1132 glVertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
1133 glVertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
1134 }
1135 glEnd();
1136 }
1137 }
1138 }
1139 }
1140}
1141
1142// Returns TRUE if center of sphere is visible. Also sets a bunch of member variables that are used later (e.g. mCenterToCam)
1143BOOL LLManipRotate::updateVisiblity()
1144{
1145 // Don't want to recalculate the center of the selection during a drag.
1146 // Due to packet delays, sometimes half the objects in the selection have their
1147 // new position and half have their old one. This creates subtle errors in the
1148 // computed center position for that frame. Unfortunately, these errors
1149 // accumulate. The result is objects seem to "fly apart" during rotations.
1150 // JC - 03.26.2002
1151 if (!hasMouseCapture())
1152 {
1153 mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() );//gSelectMgr->getSelectionCenterGlobal();
1154 }
1155
1156 BOOL visible = FALSE;
1157
1158 LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
1159 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
1160 {
1161 mCenterToCam = LLVector3(-1.f / gAgent.getAvatarObject()->mHUDCurZoom, 0.f, 0.f);
1162 mCenterToCamNorm = mCenterToCam;
1163 mCenterToCamMag = mCenterToCamNorm.normVec();
1164
1165 mRadiusMeters = RADIUS_PIXELS / (F32) gCamera->getViewHeightInPixels();
1166 mRadiusMeters /= gAgent.getAvatarObject()->mHUDCurZoom;
1167
1168 mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
1169 mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
1170
1171 mCenterScreen.set((S32)((0.5f - mRotationCenter.mdV[VY]) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowWidth()),
1172 (S32)((mRotationCenter.mdV[VZ] + 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowHeight()));
1173 visible = TRUE;
1174 }
1175 else
1176 {
1177 visible = gCamera->projectPosAgentToScreen(center, mCenterScreen );
1178 if( visible )
1179 {
1180 mCenterToCam = gAgent.getCameraPositionAgent() - center;
1181 mCenterToCamNorm = mCenterToCam;
1182 mCenterToCamMag = mCenterToCamNorm.normVec();
1183 LLVector3 cameraAtAxis = gCamera->getAtAxis();
1184 cameraAtAxis.normVec();
1185
1186 F32 z_dist = -1.f * (mCenterToCam * cameraAtAxis);
1187
1188 // Don't drag manip if object too far away
1189 if (gSavedSettings.getBOOL("LimitSelectDistance"))
1190 {
1191 F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance");
1192 if (dist_vec(gAgent.getPositionAgent(), center) > max_select_distance)
1193 {
1194 visible = FALSE;
1195 }
1196 }
1197
1198 if (mCenterToCamMag > 0.001f)
1199 {
1200 F32 fraction_of_fov = RADIUS_PIXELS / (F32) gCamera->getViewHeightInPixels();
1201 F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
1202 mRadiusMeters = z_dist * tan(apparent_angle);
1203
1204 mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
1205 mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
1206 }
1207 else
1208 {
1209 visible = FALSE;
1210 }
1211 }
1212 }
1213
1214 mCamEdgeOn = FALSE;
1215 F32 axis_onto_cam = mManipPart >= LL_ROT_X ? llabs( getConstraintAxis() * mCenterToCamNorm ) : 0.f;
1216 if( axis_onto_cam < AXIS_ONTO_CAM_TOLERANCE )
1217 {
1218 mCamEdgeOn = TRUE;
1219 }
1220
1221 return visible;
1222}
1223
1224LLQuaternion LLManipRotate::dragUnconstrained( S32 x, S32 y )
1225{
1226 LLVector3 cam = gAgent.getCameraPositionAgent();
1227 LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
1228
1229 mMouseCur = intersectMouseWithSphere( x, y, center, mRadiusMeters);
1230
1231 F32 delta_x = (F32)(mCenterScreen.mX - x);
1232 F32 delta_y = (F32)(mCenterScreen.mY - y);
1233
1234 F32 dist_from_sphere_center = sqrt(delta_x * delta_x + delta_y * delta_y);
1235
1236 LLVector3 axis = mMouseDown % mMouseCur;
1237 axis.normVec();
1238 F32 angle = acos(mMouseDown * mMouseCur);
1239 LLQuaternion sphere_rot( angle, axis );
1240
1241 if (is_approx_zero(1.f - mMouseDown * mMouseCur))
1242 {
1243 return LLQuaternion::DEFAULT;
1244 }
1245 else if (dist_from_sphere_center < RADIUS_PIXELS)
1246 {
1247 return sphere_rot;
1248 }
1249 else
1250 {
1251 LLVector3 intersection;
1252 getMousePointOnPlaneAgent( intersection, x, y, center + mCenterToProfilePlane, mCenterToCamNorm );
1253
1254 // amount dragging in sphere from center to periphery would rotate object
1255 F32 in_sphere_angle = F_PI_BY_TWO;
1256 F32 dist_to_tangent_point = mRadiusMeters;
1257 if( !is_approx_zero( mCenterToProfilePlaneMag ) )
1258 {
1259 dist_to_tangent_point = sqrt( mRadiusMeters * mRadiusMeters - mCenterToProfilePlaneMag * mCenterToProfilePlaneMag );
1260 in_sphere_angle = atan2( dist_to_tangent_point, mCenterToProfilePlaneMag );
1261 }
1262
1263 LLVector3 profile_center_to_intersection = intersection - (center + mCenterToProfilePlane);
1264 F32 dist_to_intersection = profile_center_to_intersection.normVec();
1265 F32 angle = (-1.f + dist_to_intersection / dist_to_tangent_point) * in_sphere_angle;
1266
1267 LLVector3 axis;
1268 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
1269 {
1270 axis = LLVector3(-1.f, 0.f, 0.f) % profile_center_to_intersection;
1271 }
1272 else
1273 {
1274 axis = (cam - center) % profile_center_to_intersection;
1275 axis.normVec();
1276 }
1277 return sphere_rot * LLQuaternion( angle, axis );
1278 }
1279}
1280
1281LLVector3 LLManipRotate::getConstraintAxis()
1282{
1283 LLVector3 axis;
1284 if( LL_ROT_ROLL == mManipPart )
1285 {
1286 axis = mCenterToCamNorm;
1287 }
1288 else
1289 {
1290 S32 axis_dir = mManipPart - LL_ROT_X;
1291 if ((axis_dir >= 0) && (axis_dir < 3))
1292 {
1293 axis.mV[axis_dir] = 1.f;
1294 }
1295 else
1296 {
1297#ifndef LL_RELEASE_FOR_DOWNLOAD
1298 llerrs << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << llendl
1299#else
1300 llwarns << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << llendl
1301#endif
1302 axis.mV[0] = 1.f;
1303 }
1304
1305 LLVector3 grid_origin;
1306 LLVector3 grid_scale;
1307 LLQuaternion grid_rotation;
1308
1309 gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
1310
1311 LLSelectNode* first_node = gSelectMgr->getFirstMoveableNode(TRUE);
1312 if (first_node)
1313 {
1314 // *FIX: get agent local attachment grid working
1315 // Put rotation into frame of first selected root object
1316 axis = axis * grid_rotation;
1317 }
1318 }
1319
1320 return axis;
1321}
1322
1323LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y )
1324{
1325 LLSelectNode* first_object_node = gSelectMgr->getFirstMoveableNode(TRUE);
1326 LLVector3 constraint_axis = getConstraintAxis();
1327 LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
1328
1329 F32 angle = 0.f;
1330
1331 // build snap axes
1332 LLVector3 grid_origin;
1333 LLVector3 grid_scale;
1334 LLQuaternion grid_rotation;
1335
1336 gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
1337
1338 LLVector3 axis1;
1339 LLVector3 axis2;
1340
1341 LLVector3 test_axis = constraint_axis;
1342 if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
1343 {
1344 test_axis = test_axis * ~grid_rotation;
1345 }
1346 else if (gSelectMgr->getGridMode() == GRID_MODE_REF_OBJECT)
1347 {
1348 test_axis = test_axis * ~grid_rotation;
1349 }
1350 test_axis.abs();
1351
1352 // find closest global axis to constraint axis;
1353 if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
1354 {
1355 axis1 = LLVector3::y_axis;
1356 }
1357 else if (test_axis.mV[VY] > test_axis.mV[VZ])
1358 {
1359 axis1 = LLVector3::z_axis;
1360 }
1361 else
1362 {
1363 axis1 = LLVector3::x_axis;
1364 }
1365
1366 if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
1367 {
1368 axis1 = axis1 * grid_rotation;
1369 }
1370 else if (gSelectMgr->getGridMode() == GRID_MODE_REF_OBJECT)
1371 {
1372 axis1 = axis1 * grid_rotation;
1373 }
1374
1375 //project axis onto constraint plane
1376 axis1 -= (axis1 * constraint_axis) * constraint_axis;
1377 axis1.normVec();
1378
1379 // calculate third and final axis
1380 axis2 = constraint_axis % axis1;
1381
1382 //F32 axis_onto_cam = llabs( constraint_axis * mCenterToCamNorm );
1383 if( mCamEdgeOn )
1384 {
1385 // We're looking at the ring edge-on.
1386 LLVector3 snap_plane_center = (center + (constraint_axis * mRadiusMeters * 0.5f));
1387 LLVector3 cam_to_snap_plane;
1388 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
1389 {
1390 cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
1391 }
1392 else
1393 {
1394 cam_to_snap_plane = snap_plane_center - gAgent.getCameraPositionAgent();
1395 cam_to_snap_plane.normVec();
1396 }
1397
1398 LLVector3 projected_mouse;
1399 BOOL hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
1400 projected_mouse -= snap_plane_center;
1401
1402 S32 snap_plane = 0;
1403
1404 F32 dot = cam_to_snap_plane * constraint_axis;
1405 if (llabs(dot) < 0.01f)
1406 {
1407 // looking at ring edge on, project onto view plane and check if mouse is past ring
1408 getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
1409 projected_mouse -= snap_plane_center;
1410 dot = projected_mouse * constraint_axis;
1411 if (projected_mouse * constraint_axis > 0)
1412 {
1413 snap_plane = 1;
1414 }
1415 projected_mouse -= dot * constraint_axis;
1416 }
1417 else if (dot > 0.f)
1418 {
1419 // look for mouse position outside and in front of snap circle
1420 if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
1421 {
1422 snap_plane = 1;
1423 }
1424 }
1425 else
1426 {
1427 // look for mouse position inside or in back of snap circle
1428 if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
1429 {
1430 snap_plane = 1;
1431 }
1432 }
1433
1434 if (snap_plane == 0)
1435 {
1436 // try other plane
1437 snap_plane_center = (center - (constraint_axis * mRadiusMeters * 0.5f));
1438 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
1439 {
1440 cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
1441 }
1442 else
1443 {
1444 cam_to_snap_plane = snap_plane_center - gAgent.getCameraPositionAgent();
1445 cam_to_snap_plane.normVec();
1446 }
1447
1448 hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
1449 projected_mouse -= snap_plane_center;
1450
1451 dot = cam_to_snap_plane * constraint_axis;
1452 if (llabs(dot) < 0.01f)
1453 {
1454 // looking at ring edge on, project onto view plane and check if mouse is past ring
1455 getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
1456 projected_mouse -= snap_plane_center;
1457 dot = projected_mouse * constraint_axis;
1458 if (projected_mouse * constraint_axis < 0)
1459 {
1460 snap_plane = 2;
1461 }
1462 projected_mouse -= dot * constraint_axis;
1463 }
1464 else if (dot < 0.f)
1465 {
1466 // look for mouse position outside and in front of snap circle
1467 if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
1468 {
1469 snap_plane = 2;
1470 }
1471 }
1472 else
1473 {
1474 // look for mouse position inside or in back of snap circle
1475 if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
1476 {
1477 snap_plane = 2;
1478 }
1479 }
1480 }
1481
1482 if (snap_plane > 0)
1483 {
1484 LLVector3 cam_at_axis;
1485 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
1486 {
1487 cam_at_axis.setVec(1.f, 0.f, 0.f);
1488 }
1489 else
1490 {
1491 cam_at_axis = snap_plane_center - gAgent.getCameraPositionAgent();
1492 cam_at_axis.normVec();
1493 }
1494
1495 // first, project mouse onto screen plane at point tangent to rotation radius.
1496 getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_at_axis);
1497 // project that point onto rotation plane
1498 projected_mouse -= snap_plane_center;
1499 projected_mouse -= projected_vec(projected_mouse, constraint_axis);
1500
1501 F32 mouse_lateral_dist = llmin(SNAP_GUIDE_INNER_RADIUS * mRadiusMeters, projected_mouse.magVec());
1502 F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
1503 if (llabs(mouse_lateral_dist) > 0.01f)
1504 {
1505 mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
1506 (mouse_lateral_dist * mouse_lateral_dist));
1507 }
1508 LLVector3 projected_camera_at = cam_at_axis - projected_vec(cam_at_axis, constraint_axis);
1509 projected_mouse -= mouse_depth * projected_camera_at;
1510
1511 if (!mInSnapRegime)
1512 {
1513 mSmoothRotate = TRUE;
1514 }
1515 mInSnapRegime = TRUE;
1516 // 0 to 360 deg
1517 F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
1518
1519 F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
1520 //fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f);
1521
1522 LLVector3 object_axis;
1523 getObjectAxisClosestToMouse(object_axis);
1524 object_axis = object_axis * first_object_node->mSavedRotation;
1525
1526 // project onto constraint plane
1527 object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
1528 object_axis.normVec();
1529
1530 if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
1531 {
1532 F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
1533 angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
1534 }
1535 else
1536 {
1537 angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
1538 }
1539 return LLQuaternion( -angle, constraint_axis );
1540 }
1541 else
1542 {
1543 if (mInSnapRegime)
1544 {
1545 mSmoothRotate = TRUE;
1546 }
1547 mInSnapRegime = FALSE;
1548
1549 LLVector3 up_from_axis = mCenterToCamNorm % constraint_axis;
1550 up_from_axis.normVec();
1551 LLVector3 cur_intersection;
1552 getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
1553 cur_intersection -= center;
1554 mMouseCur = projected_vec(cur_intersection, up_from_axis);
1555 F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
1556 F32 mouse_dist_sqrd = mMouseCur.magVecSquared();
1557 if (mouse_dist_sqrd > 0.0001f)
1558 {
1559 mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
1560 mouse_dist_sqrd);
1561 }
1562 LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, constraint_axis);
1563 mMouseCur += mouse_depth * projected_center_to_cam;
1564
1565 F32 dist = (cur_intersection * up_from_axis) - (mMouseDown * up_from_axis);
1566 angle = dist / (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * -F_PI_BY_TWO;
1567 }
1568 }
1569 else
1570 {
1571 LLVector3 projected_mouse;
1572 getMousePointOnPlaneAgent(projected_mouse, x, y, center, constraint_axis);
1573 projected_mouse -= center;
1574 mMouseCur = projected_mouse;
1575 mMouseCur.normVec();
1576
1577 if (!first_object_node)
1578 {
1579 return LLQuaternion::DEFAULT;
1580 }
1581
1582 if (gSavedSettings.getBOOL("SnapEnabled") && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters)
1583 {
1584 if (!mInSnapRegime)
1585 {
1586 mSmoothRotate = TRUE;
1587 }
1588 mInSnapRegime = TRUE;
1589 // 0 to 360 deg
1590 F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
1591
1592 F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
1593 //fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f);
1594
1595 LLVector3 object_axis;
1596 getObjectAxisClosestToMouse(object_axis);
1597 object_axis = object_axis * first_object_node->mSavedRotation;
1598
1599 // project onto constraint plane
1600 object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
1601 object_axis.normVec();
1602
1603 if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
1604 {
1605 F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
1606 angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
1607 }
1608 else
1609 {
1610 angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
1611 }
1612 return LLQuaternion( -angle, constraint_axis );
1613 }
1614 else
1615 {
1616 if (mInSnapRegime)
1617 {
1618 mSmoothRotate = TRUE;
1619 }
1620 mInSnapRegime = FALSE;
1621 }
1622
1623 angle = acos(mMouseCur * mMouseDown);
1624
1625 F32 dir = (mMouseDown % mMouseCur) * constraint_axis; // cross product
1626 if( dir < 0.f )
1627 {
1628 angle *= -1.f;
1629 }
1630 }
1631
1632 F32 rot_step = gSavedSettings.getF32("RotationStep");
1633 F32 step_size = DEG_TO_RAD * rot_step;
1634 angle -= fmod(angle, step_size);
1635
1636 return LLQuaternion( angle, constraint_axis );
1637}
1638
1639
1640
1641LLVector3 LLManipRotate::intersectMouseWithSphere( S32 x, S32 y, const LLVector3& sphere_center, F32 sphere_radius)
1642{
1643 LLVector3 ray_pt;
1644 LLVector3 ray_dir;
1645 mouseToRay( x, y, &ray_pt, &ray_dir);
1646 return intersectRayWithSphere( ray_pt, ray_dir, sphere_center, sphere_radius );
1647}
1648
1649LLVector3 LLManipRotate::intersectRayWithSphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius)
1650{
1651 LLVector3 ray_pt_to_center = sphere_center - ray_pt;
1652 F32 center_distance = ray_pt_to_center.normVec();
1653
1654 F32 dot = ray_dir * ray_pt_to_center;
1655
1656 if (dot == 0.f)
1657 {
1658 return LLVector3::zero;
1659 }
1660
1661 // point which ray hits plane centered on sphere origin, facing ray origin
1662 LLVector3 intersection_sphere_plane = ray_pt + (ray_dir * center_distance / dot);
1663 // vector from sphere origin to the point, normalized to sphere radius
1664 LLVector3 sphere_center_to_intersection = (intersection_sphere_plane - sphere_center) / sphere_radius;
1665
1666 F32 dist_squared = sphere_center_to_intersection.magVecSquared();
1667 LLVector3 result;
1668
1669 if (dist_squared > 1.f)
1670 {
1671 result = sphere_center_to_intersection;
1672 result.normVec();
1673 }
1674 else
1675 {
1676 result = sphere_center_to_intersection - ray_dir * sqrt(1.f - dist_squared);
1677 }
1678
1679 return result;
1680}
1681
1682// Utility function. Should probably be moved to another class.
1683void LLManipRotate::mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_dir )
1684{
1685 if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
1686 {
1687 F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
1688 F32 mouse_y = ((((F32)y) / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
1689
1690 *ray_pt = LLVector3(-1.f, -mouse_x, mouse_y);
1691 *ray_dir = LLVector3(1.f, 0.f, 0.f);
1692 }
1693 else
1694 {
1695 *ray_pt = gAgent.getCameraPositionAgent();
1696 gCamera->projectScreenToPosAgent(x, y, ray_dir);
1697 *ray_dir -= *ray_pt;
1698 ray_dir->normVec();
1699 }
1700}
1701
1702void LLManipRotate::highlightManipulators( S32 x, S32 y )
1703{
1704 mHighlightedPart = LL_NO_PART;
1705
1706 //LLBBox bbox = gSelectMgr->getBBoxOfSelection();
1707 LLViewerObject *first_object = gSelectMgr->getFirstMoveableObject(TRUE);
1708
1709 if (!first_object)
1710 {
1711 return;
1712 }
1713
1714 LLQuaternion object_rot = first_object->getRenderRotation();
1715 LLVector3 rotation_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
1716 LLVector3 mouse_dir_x;
1717 LLVector3 mouse_dir_y;
1718 LLVector3 mouse_dir_z;
1719 LLVector3 intersection_roll;
1720
1721 LLVector3 grid_origin;
1722 LLVector3 grid_scale;
1723 LLQuaternion grid_rotation;
1724
1725 gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
1726
1727 LLVector3 rot_x_axis = LLVector3::x_axis * grid_rotation;
1728 LLVector3 rot_y_axis = LLVector3::y_axis * grid_rotation;
1729 LLVector3 rot_z_axis = LLVector3::z_axis * grid_rotation;
1730
1731 F32 proj_rot_x_axis = llabs(rot_x_axis * mCenterToCamNorm);
1732 F32 proj_rot_y_axis = llabs(rot_y_axis * mCenterToCamNorm);
1733 F32 proj_rot_z_axis = llabs(rot_z_axis * mCenterToCamNorm);
1734
1735 F32 min_select_distance = 0.f;
1736 F32 cur_select_distance = 0.f;
1737
1738 // test x
1739 getMousePointOnPlaneAgent(mouse_dir_x, x, y, rotation_center, rot_x_axis);
1740 mouse_dir_x -= rotation_center;
1741 // push intersection point out when working at obtuse angle to make ring easier to hit
1742 mouse_dir_x *= 1.f + (1.f - llabs(rot_x_axis * mCenterToCamNorm)) * 0.1f;
1743
1744 // test y
1745 getMousePointOnPlaneAgent(mouse_dir_y, x, y, rotation_center, rot_y_axis);
1746 mouse_dir_y -= rotation_center;
1747 mouse_dir_y *= 1.f + (1.f - llabs(rot_y_axis * mCenterToCamNorm)) * 0.1f;
1748
1749 // test z
1750 getMousePointOnPlaneAgent(mouse_dir_z, x, y, rotation_center, rot_z_axis);
1751 mouse_dir_z -= rotation_center;
1752 mouse_dir_z *= 1.f + (1.f - llabs(rot_z_axis * mCenterToCamNorm)) * 0.1f;
1753
1754 // test roll
1755 getMousePointOnPlaneAgent(intersection_roll, x, y, rotation_center, mCenterToCamNorm);
1756 intersection_roll -= rotation_center;
1757
1758 F32 dist_x = mouse_dir_x.normVec();
1759 F32 dist_y = mouse_dir_y.normVec();
1760 F32 dist_z = mouse_dir_z.normVec();
1761
1762 F32 distance_threshold = (MAX_MANIP_SELECT_DISTANCE * mRadiusMeters) / gViewerWindow->getWindowHeight();
1763
1764 if (llabs(dist_x - mRadiusMeters) * llmax(0.05f, proj_rot_x_axis) < distance_threshold)
1765 {
1766 // selected x
1767 cur_select_distance = dist_x * mouse_dir_x * mCenterToCamNorm;
1768 if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
1769 {
1770 min_select_distance = cur_select_distance;
1771 mHighlightedPart = LL_ROT_X;
1772 }
1773 }
1774 if (llabs(dist_y - mRadiusMeters) * llmax(0.05f, proj_rot_y_axis) < distance_threshold)
1775 {
1776 // selected y
1777 cur_select_distance = dist_y * mouse_dir_y * mCenterToCamNorm;
1778 if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
1779 {
1780 min_select_distance = cur_select_distance;
1781 mHighlightedPart = LL_ROT_Y;
1782 }
1783 }
1784 if (llabs(dist_z - mRadiusMeters) * llmax(0.05f, proj_rot_z_axis) < distance_threshold)
1785 {
1786 // selected z
1787 cur_select_distance = dist_z * mouse_dir_z * mCenterToCamNorm;
1788 if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
1789 {
1790 min_select_distance = cur_select_distance;
1791 mHighlightedPart = LL_ROT_Z;
1792 }
1793 }
1794
1795 // test for edge-on intersections
1796 if (proj_rot_x_axis < 0.05f)
1797 {
1798 if ((proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_x_axis) < distance_threshold) && dist_y < mRadiusMeters) ||
1799 (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_x_axis) < distance_threshold) && dist_z < mRadiusMeters))
1800 {
1801 mHighlightedPart = LL_ROT_X;
1802 }
1803 }
1804
1805 if (proj_rot_y_axis < 0.05f)
1806 {
1807 if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_y_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
1808 (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_y_axis) < distance_threshold) && dist_z < mRadiusMeters))
1809 {
1810 mHighlightedPart = LL_ROT_Y;
1811 }
1812 }
1813
1814 if (proj_rot_z_axis < 0.05f)
1815 {
1816 if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_z_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
1817 (proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_z_axis) < distance_threshold) && dist_y < mRadiusMeters))
1818 {
1819 mHighlightedPart = LL_ROT_Z;
1820 }
1821 }
1822
1823 // test for roll
1824 if (mHighlightedPart == LL_NO_PART)
1825 {
1826 F32 roll_distance = intersection_roll.magVec();
1827 F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
1828
1829 // use larger distance threshold for roll as it is checked only if something else wasn't highlighted
1830 if (llabs(roll_distance - (mRadiusMeters + (width_meters * 2.f))) < distance_threshold * 2.f)
1831 {
1832 mHighlightedPart = LL_ROT_ROLL;
1833 }
1834 else if (roll_distance < mRadiusMeters)
1835 {
1836 mHighlightedPart = LL_ROT_GENERAL;
1837 }
1838 }
1839}
1840
1841S32 LLManipRotate::getObjectAxisClosestToMouse(LLVector3& object_axis)
1842{
1843 LLSelectNode* first_object_node = gSelectMgr->getFirstMoveableNode(TRUE);
1844
1845 if (!first_object_node)
1846 {
1847 object_axis.clearVec();
1848 return -1;
1849 }
1850
1851 LLQuaternion obj_rotation = first_object_node->mSavedRotation;
1852 LLVector3 mouse_down_object = mMouseDown * ~obj_rotation;
1853 LLVector3 mouse_down_abs = mouse_down_object;
1854 mouse_down_abs.abs();
1855
1856 S32 axis_index = 0;
1857 if (mouse_down_abs.mV[VX] > mouse_down_abs.mV[VY] && mouse_down_abs.mV[VX] > mouse_down_abs.mV[VZ])
1858 {
1859 if (mouse_down_object.mV[VX] > 0.f)
1860 {
1861 object_axis = LLVector3::x_axis;
1862 }
1863 else
1864 {
1865 object_axis = LLVector3::x_axis_neg;
1866 }
1867 axis_index = VX;
1868 }
1869 else if (mouse_down_abs.mV[VY] > mouse_down_abs.mV[VZ])
1870 {
1871 if (mouse_down_object.mV[VY] > 0.f)
1872 {
1873 object_axis = LLVector3::y_axis;
1874 }
1875 else
1876 {
1877 object_axis = LLVector3::y_axis_neg;
1878 }
1879 axis_index = VY;
1880 }
1881 else
1882 {
1883 if (mouse_down_object.mV[VZ] > 0.f)
1884 {
1885 object_axis = LLVector3::z_axis;
1886 }
1887 else
1888 {
1889 object_axis = LLVector3::z_axis_neg;
1890 }
1891 axis_index = VZ;
1892 }
1893
1894 return axis_index;
1895}