diff options
author | Jacek Antonelli | 2010-10-21 22:13:28 -0500 |
---|---|---|
committer | McCabe Maxsted | 2010-10-23 18:47:52 -0700 |
commit | 36c25f06e38e357716ecd31df58d70b92a3d8e0e (patch) | |
tree | c47616efa283bf55484a6b59715dc1f3c6c56aa4 /linden | |
parent | Merge remote branch 'jacek/weekly' into weekly (diff) | |
download | meta-impy-36c25f06e38e357716ecd31df58d70b92a3d8e0e.zip meta-impy-36c25f06e38e357716ecd31df58d70b92a3d8e0e.tar.gz meta-impy-36c25f06e38e357716ecd31df58d70b92a3d8e0e.tar.bz2 meta-impy-36c25f06e38e357716ecd31df58d70b92a3d8e0e.tar.xz |
Prim alignment tool by Qarl, backported by Jacek.
Qarl has given permission to use this code under the terms
of the GPL v2 + FLOSS exception and/or the LGPL v2.1.
Diffstat (limited to 'linden')
-rw-r--r-- | linden/indra/newview/CMakeLists.txt | 2 | ||||
-rw-r--r-- | linden/indra/newview/llfloatertools.cpp | 6 | ||||
-rw-r--r-- | linden/indra/newview/llfloatertools.h | 1 | ||||
-rw-r--r-- | linden/indra/newview/qtoolalign.cpp | 585 | ||||
-rw-r--r-- | linden/indra/newview/qtoolalign.h | 50 | ||||
-rw-r--r-- | linden/indra/newview/skins/default/xui/en-us/floater_tools.xml | 3 |
6 files changed, 646 insertions, 1 deletions
diff --git a/linden/indra/newview/CMakeLists.txt b/linden/indra/newview/CMakeLists.txt index dfd35bf..1a6ad30 100644 --- a/linden/indra/newview/CMakeLists.txt +++ b/linden/indra/newview/CMakeLists.txt | |||
@@ -484,6 +484,7 @@ set(viewer_SOURCE_FILES | |||
484 | panelradarentry.cpp | 484 | panelradarentry.cpp |
485 | pipeline.cpp | 485 | pipeline.cpp |
486 | primbackup.cpp | 486 | primbackup.cpp |
487 | qtoolalign.cpp | ||
487 | rlvhandler.cpp | 488 | rlvhandler.cpp |
488 | rlvhelper.cpp | 489 | rlvhelper.cpp |
489 | rlvcommon.cpp | 490 | rlvcommon.cpp |
@@ -936,6 +937,7 @@ set(viewer_HEADER_FILES | |||
936 | panelradarentry.h | 937 | panelradarentry.h |
937 | pipeline.h | 938 | pipeline.h |
938 | primbackup.h | 939 | primbackup.h |
940 | qtoolalign.h | ||
939 | randgauss.h | 941 | randgauss.h |
940 | rlvdefines.h | 942 | rlvdefines.h |
941 | rlvhandler.h | 943 | rlvhandler.h |
diff --git a/linden/indra/newview/llfloatertools.cpp b/linden/indra/newview/llfloatertools.cpp index fc72467..daff573 100644 --- a/linden/indra/newview/llfloatertools.cpp +++ b/linden/indra/newview/llfloatertools.cpp | |||
@@ -84,7 +84,7 @@ | |||
84 | #include "llvograss.h" | 84 | #include "llvograss.h" |
85 | #include "llvotree.h" | 85 | #include "llvotree.h" |
86 | #include "lluictrlfactory.h" | 86 | #include "lluictrlfactory.h" |
87 | 87 | #include "qtoolalign.h" | |
88 | #include "hippoLimits.h" | 88 | #include "hippoLimits.h" |
89 | 89 | ||
90 | // Globals | 90 | // Globals |
@@ -273,6 +273,8 @@ BOOL LLFloaterTools::postBuild() | |||
273 | childSetCommitCallback("radio stretch",commit_select_tool,LLToolCompScale::getInstance()); | 273 | childSetCommitCallback("radio stretch",commit_select_tool,LLToolCompScale::getInstance()); |
274 | mRadioSelectFace = getChild<LLCheckBoxCtrl>("radio select face"); | 274 | mRadioSelectFace = getChild<LLCheckBoxCtrl>("radio select face"); |
275 | childSetCommitCallback("radio select face",commit_select_tool,LLToolFace::getInstance()); | 275 | childSetCommitCallback("radio select face",commit_select_tool,LLToolFace::getInstance()); |
276 | mRadioAlign = getChild<LLCheckBoxCtrl>("radio align"); | ||
277 | childSetCommitCallback("radio align",commit_select_tool,QToolAlign::getInstance()); | ||
276 | mCheckSelectIndividual = getChild<LLCheckBoxCtrl>("checkbox edit linked parts"); | 278 | mCheckSelectIndividual = getChild<LLCheckBoxCtrl>("checkbox edit linked parts"); |
277 | childSetValue("checkbox edit linked parts",(BOOL)gSavedSettings.getBOOL("EditLinkedParts")); | 279 | childSetValue("checkbox edit linked parts",(BOOL)gSavedSettings.getBOOL("EditLinkedParts")); |
278 | childSetCommitCallback("checkbox edit linked parts",commit_select_component,this); | 280 | childSetCommitCallback("checkbox edit linked parts",commit_select_component,this); |
@@ -698,6 +700,7 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) | |||
698 | tool == LLToolCompScale::getInstance() || | 700 | tool == LLToolCompScale::getInstance() || |
699 | tool == LLToolFace::getInstance() || | 701 | tool == LLToolFace::getInstance() || |
700 | tool == LLToolIndividual::getInstance() || | 702 | tool == LLToolIndividual::getInstance() || |
703 | tool == QToolAlign::getInstance() || | ||
701 | tool == LLToolPipette::getInstance(); | 704 | tool == LLToolPipette::getInstance(); |
702 | 705 | ||
703 | mBtnEdit ->setToggleState( edit_visible ); | 706 | mBtnEdit ->setToggleState( edit_visible ); |
@@ -720,6 +723,7 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) | |||
720 | mRadioPosition ->set( tool == LLToolCompTranslate::getInstance() ); | 723 | mRadioPosition ->set( tool == LLToolCompTranslate::getInstance() ); |
721 | mRadioRotate ->set( tool == LLToolCompRotate::getInstance() ); | 724 | mRadioRotate ->set( tool == LLToolCompRotate::getInstance() ); |
722 | mRadioStretch ->set( tool == LLToolCompScale::getInstance() ); | 725 | mRadioStretch ->set( tool == LLToolCompScale::getInstance() ); |
726 | mRadioAlign->set( tool == QToolAlign::getInstance() ); | ||
723 | 727 | ||
724 | if (mComboGridMode) | 728 | if (mComboGridMode) |
725 | { | 729 | { |
diff --git a/linden/indra/newview/llfloatertools.h b/linden/indra/newview/llfloatertools.h index ad5be6c..bbf07ec 100644 --- a/linden/indra/newview/llfloatertools.h +++ b/linden/indra/newview/llfloatertools.h | |||
@@ -140,6 +140,7 @@ public: | |||
140 | LLCheckBoxCtrl *mRadioRotate; | 140 | LLCheckBoxCtrl *mRadioRotate; |
141 | LLCheckBoxCtrl *mRadioStretch; | 141 | LLCheckBoxCtrl *mRadioStretch; |
142 | LLCheckBoxCtrl *mRadioSelectFace; | 142 | LLCheckBoxCtrl *mRadioSelectFace; |
143 | LLCheckBoxCtrl *mRadioAlign; | ||
143 | 144 | ||
144 | LLCheckBoxCtrl *mCheckSelectIndividual; | 145 | LLCheckBoxCtrl *mCheckSelectIndividual; |
145 | 146 | ||
diff --git a/linden/indra/newview/qtoolalign.cpp b/linden/indra/newview/qtoolalign.cpp new file mode 100644 index 0000000..92cab6e --- /dev/null +++ b/linden/indra/newview/qtoolalign.cpp | |||
@@ -0,0 +1,585 @@ | |||
1 | /** | ||
2 | * @file qtoolalign.cpp | ||
3 | * @brief A tool to align objects | ||
4 | * @author Karl Stiefvater (Qarl) | ||
5 | * | ||
6 | * Karl has given permission to use this code under the terms of | ||
7 | * the GNU GPL v2 plus FLOSS exception and/or the GNU LGPL v2.1. | ||
8 | * | ||
9 | * Backported for Viewer 1.X code base by Jacek Antonelli. | ||
10 | */ | ||
11 | |||
12 | #include "llviewerprecompiledheaders.h" | ||
13 | |||
14 | // File includes | ||
15 | #include "qtoolalign.h" | ||
16 | |||
17 | // Library includes | ||
18 | #include "llbbox.h" | ||
19 | #include "v3math.h" | ||
20 | |||
21 | // Viewer includes | ||
22 | #include "llagent.h" | ||
23 | #include "llbox.h" | ||
24 | #include "llcylinder.h" | ||
25 | #include "llfloatertools.h" | ||
26 | #include "llselectmgr.h" | ||
27 | #include "llviewercamera.h" | ||
28 | #include "llviewercontrol.h" | ||
29 | #include "llviewerobject.h" | ||
30 | #include "llviewerwindow.h" | ||
31 | |||
32 | |||
33 | const F32 MANIPULATOR_SIZE = 5.0; | ||
34 | const F32 MANIPULATOR_SELECT_SIZE = 20.0; | ||
35 | |||
36 | |||
37 | |||
38 | QToolAlign::QToolAlign() | ||
39 | : LLTool(std::string("Align")) | ||
40 | { | ||
41 | } | ||
42 | |||
43 | |||
44 | QToolAlign::~QToolAlign() | ||
45 | { | ||
46 | } | ||
47 | |||
48 | |||
49 | |||
50 | BOOL QToolAlign::handleMouseDown(S32 x, S32 y, MASK mask) | ||
51 | { | ||
52 | if (mHighlightedAxis != -1) | ||
53 | { | ||
54 | align(); | ||
55 | } | ||
56 | else | ||
57 | { | ||
58 | gViewerWindow->pickAsync(x, y, mask, pickCallback); | ||
59 | } | ||
60 | |||
61 | return TRUE; | ||
62 | } | ||
63 | |||
64 | |||
65 | |||
66 | void QToolAlign::pickCallback(const LLPickInfo& pick_info) | ||
67 | { | ||
68 | LLViewerObject* object = pick_info.getObject(); | ||
69 | |||
70 | if (object) | ||
71 | { | ||
72 | if (object->isAvatar()) | ||
73 | { | ||
74 | return; | ||
75 | } | ||
76 | |||
77 | if (pick_info.mKeyMask & MASK_SHIFT) | ||
78 | { | ||
79 | // If object not selected, select it | ||
80 | if ( !object->isSelected() ) | ||
81 | { | ||
82 | LLSelectMgr::getInstance()->selectObjectAndFamily(object); | ||
83 | } | ||
84 | else | ||
85 | { | ||
86 | LLSelectMgr::getInstance()->deselectObjectAndFamily(object); | ||
87 | } | ||
88 | } | ||
89 | else | ||
90 | { | ||
91 | LLSelectMgr::getInstance()->deselectAll(); | ||
92 | LLSelectMgr::getInstance()->selectObjectAndFamily(object); | ||
93 | } | ||
94 | |||
95 | } | ||
96 | else | ||
97 | { | ||
98 | if (!(pick_info.mKeyMask == MASK_SHIFT)) | ||
99 | { | ||
100 | LLSelectMgr::getInstance()->deselectAll(); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | LLSelectMgr::getInstance()->promoteSelectionToRoot(); | ||
105 | } | ||
106 | |||
107 | |||
108 | |||
109 | void QToolAlign::handleSelect() | ||
110 | { | ||
111 | // no parts, please | ||
112 | |||
113 | llwarns << "in select" << llendl; | ||
114 | LLSelectMgr::getInstance()->promoteSelectionToRoot(); | ||
115 | } | ||
116 | |||
117 | |||
118 | void QToolAlign::handleDeselect() | ||
119 | { | ||
120 | } | ||
121 | |||
122 | |||
123 | BOOL QToolAlign::findSelectedManipulator(S32 x, S32 y) | ||
124 | { | ||
125 | mHighlightedAxis = -1; | ||
126 | mHighlightedDirection = 0; | ||
127 | |||
128 | LLMatrix4 transform; | ||
129 | if (LLSelectMgr::getInstance()->getSelection()->getSelectType() == SELECT_TYPE_HUD) | ||
130 | { | ||
131 | LLVector4 translation(mBBox.getCenterAgent()); | ||
132 | transform.initRotTrans(mBBox.getRotation(), translation); | ||
133 | LLMatrix4 cfr(OGL_TO_CFR_ROTATION); | ||
134 | transform *= cfr; | ||
135 | LLMatrix4 window_scale; | ||
136 | F32 zoom_level = 2.f * gAgent.mHUDCurZoom; | ||
137 | window_scale.initAll(LLVector3(zoom_level / LLViewerCamera::getInstance()->getAspect(), zoom_level, 0.f), | ||
138 | LLQuaternion::DEFAULT, | ||
139 | LLVector3::zero); | ||
140 | transform *= window_scale; | ||
141 | } | ||
142 | else | ||
143 | { | ||
144 | transform.initAll(LLVector3(1.f, 1.f, 1.f), mBBox.getRotation(), mBBox.getCenterAgent()); | ||
145 | |||
146 | LLMatrix4 projection_matrix = LLViewerCamera::getInstance()->getProjection(); | ||
147 | LLMatrix4 model_matrix = LLViewerCamera::getInstance()->getModelview(); | ||
148 | |||
149 | transform *= model_matrix; | ||
150 | transform *= projection_matrix; | ||
151 | } | ||
152 | |||
153 | |||
154 | //LLRect world_view_rect = getWorldViewRectScaled(); | ||
155 | F32 half_width = (F32)gViewerWindow->getWindowWidth() / 2.f; | ||
156 | F32 half_height = (F32)gViewerWindow->getWindowHeight() / 2.f; | ||
157 | LLVector2 manip2d; | ||
158 | LLVector2 mousePos((F32)x - half_width, (F32)y - half_height); | ||
159 | LLVector2 delta; | ||
160 | |||
161 | LLVector3 bbox_scale = mBBox.getMaxLocal() - mBBox.getMinLocal(); | ||
162 | |||
163 | for (S32 axis = VX; axis <= VZ; axis++) | ||
164 | { | ||
165 | for (F32 direction = -1.0; direction <= 1.0; direction += 2.0) | ||
166 | { | ||
167 | LLVector3 axis_vector = LLVector3(0,0,0); | ||
168 | axis_vector.mV[axis] = direction * bbox_scale.mV[axis] / 2.0; | ||
169 | |||
170 | LLVector4 manipulator_center = LLVector4(axis_vector); | ||
171 | |||
172 | LLVector4 screen_center = manipulator_center * transform; | ||
173 | screen_center /= screen_center.mV[VW]; | ||
174 | |||
175 | manip2d.setVec(screen_center.mV[VX] * half_width, screen_center.mV[VY] * half_height); | ||
176 | |||
177 | delta = manip2d - mousePos; | ||
178 | |||
179 | if (delta.magVecSquared() < MANIPULATOR_SELECT_SIZE * MANIPULATOR_SELECT_SIZE) | ||
180 | { | ||
181 | mHighlightedAxis = axis; | ||
182 | mHighlightedDirection = direction; | ||
183 | return TRUE; | ||
184 | } | ||
185 | |||
186 | } | ||
187 | } | ||
188 | |||
189 | return FALSE; | ||
190 | } | ||
191 | |||
192 | |||
193 | BOOL QToolAlign::handleHover(S32 x, S32 y, MASK mask) | ||
194 | { | ||
195 | if (mask & MASK_SHIFT) | ||
196 | { | ||
197 | mForce = FALSE; | ||
198 | } | ||
199 | else | ||
200 | { | ||
201 | mForce = TRUE; | ||
202 | } | ||
203 | |||
204 | gViewerWindow->setCursor(UI_CURSOR_ARROW); | ||
205 | return findSelectedManipulator(x, y); | ||
206 | } | ||
207 | |||
208 | |||
209 | |||
210 | void setup_transforms_bbox(LLBBox bbox) | ||
211 | { | ||
212 | // translate to center | ||
213 | LLVector3 center = bbox.getCenterAgent(); | ||
214 | gGL.translatef(center.mV[VX], center.mV[VY], center.mV[VZ]); | ||
215 | |||
216 | // rotate | ||
217 | LLQuaternion rotation = bbox.getRotation(); | ||
218 | F32 angle_radians, x, y, z; | ||
219 | rotation.getAngleAxis(&angle_radians, &x, &y, &z); | ||
220 | // gGL has no rotate method (despite having translate and scale) presumably because | ||
221 | // its authors smoke crack. so we hack. | ||
222 | gGL.flush(); | ||
223 | glRotatef(angle_radians * RAD_TO_DEG, x, y, z); | ||
224 | |||
225 | // scale | ||
226 | LLVector3 scale = bbox.getMaxLocal() - bbox.getMinLocal(); | ||
227 | gGL.scalef(scale.mV[VX], scale.mV[VY], scale.mV[VZ]); | ||
228 | } | ||
229 | |||
230 | |||
231 | void render_bbox(LLBBox bbox) | ||
232 | { | ||
233 | glMatrixMode(GL_MODELVIEW); | ||
234 | gGL.pushMatrix(); | ||
235 | |||
236 | setup_transforms_bbox(bbox); | ||
237 | |||
238 | gGL.flush(); | ||
239 | gBox.render(); | ||
240 | |||
241 | gGL.popMatrix(); | ||
242 | } | ||
243 | |||
244 | void render_cone_bbox(LLBBox bbox) | ||
245 | { | ||
246 | glMatrixMode(GL_MODELVIEW); | ||
247 | gGL.pushMatrix(); | ||
248 | |||
249 | setup_transforms_bbox(bbox); | ||
250 | |||
251 | gGL.flush(); | ||
252 | gCone.render(CONE_LOD_HIGHEST); | ||
253 | |||
254 | gGL.popMatrix(); | ||
255 | } | ||
256 | |||
257 | |||
258 | |||
259 | // the selection bbox isn't axis aligned, so we must construct one | ||
260 | // should this be cached in the selection manager? yes. | ||
261 | LLBBox get_selection_axis_aligned_bbox() | ||
262 | { | ||
263 | LLBBox selection_bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); | ||
264 | LLVector3 position = selection_bbox.getPositionAgent(); | ||
265 | |||
266 | LLBBox axis_aligned_bbox = LLBBox(position, LLQuaternion(), LLVector3(), LLVector3()); | ||
267 | axis_aligned_bbox.addPointLocal(LLVector3()); | ||
268 | |||
269 | // cycle over the nodes in selection | ||
270 | for (LLObjectSelection::iterator selection_iter = LLSelectMgr::getInstance()->getSelection()->begin(); | ||
271 | selection_iter != LLSelectMgr::getInstance()->getSelection()->end(); | ||
272 | ++selection_iter) | ||
273 | { | ||
274 | LLSelectNode *select_node = *selection_iter; | ||
275 | if (select_node) | ||
276 | { | ||
277 | LLViewerObject* object = select_node->getObject(); | ||
278 | if (object) | ||
279 | { | ||
280 | axis_aligned_bbox.addBBoxAgent(object->getBoundingBoxAgent()); | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||
285 | |||
286 | return axis_aligned_bbox; | ||
287 | } | ||
288 | |||
289 | |||
290 | |||
291 | void QToolAlign::computeManipulatorSize() | ||
292 | { | ||
293 | if (LLSelectMgr::getInstance()->getSelection()->getSelectType() == SELECT_TYPE_HUD) | ||
294 | { | ||
295 | mManipulatorSize = MANIPULATOR_SIZE / (LLViewerCamera::getInstance()->getViewHeightInPixels() * | ||
296 | gAgent.mHUDCurZoom); | ||
297 | } | ||
298 | else | ||
299 | { | ||
300 | F32 distance = dist_vec(gAgent.getCameraPositionAgent(), mBBox.getCenterAgent()); | ||
301 | |||
302 | if (distance > 0.001f) | ||
303 | { | ||
304 | // range != zero | ||
305 | F32 fraction_of_fov = MANIPULATOR_SIZE /LLViewerCamera::getInstance()->getViewHeightInPixels(); | ||
306 | F32 apparent_angle = fraction_of_fov * LLViewerCamera::getInstance()->getView(); // radians | ||
307 | mManipulatorSize = MANIPULATOR_SIZE * distance * tan(apparent_angle); | ||
308 | } | ||
309 | else | ||
310 | { | ||
311 | // range == zero | ||
312 | mManipulatorSize = MANIPULATOR_SIZE; | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | |||
317 | |||
318 | LLColor4 manipulator_color[3] = { LLColor4(0.7f, 0.0f, 0.0f, 0.5f), | ||
319 | LLColor4(0.0f, 0.7f, 0.0f, 0.5f), | ||
320 | LLColor4(0.0f, 0.0f, 0.7f, 0.5f) }; | ||
321 | |||
322 | |||
323 | void QToolAlign::renderManipulators() | ||
324 | { | ||
325 | computeManipulatorSize(); | ||
326 | LLVector3 bbox_center = mBBox.getCenterAgent(); | ||
327 | LLVector3 bbox_scale = mBBox.getMaxLocal() - mBBox.getMinLocal(); | ||
328 | |||
329 | for (S32 axis = VX; axis <= VZ; axis++) | ||
330 | for (F32 direction = -1.0; direction <= 1.0; direction += 2.0) | ||
331 | { | ||
332 | F32 size = mManipulatorSize; | ||
333 | LLColor4 color = manipulator_color[axis]; | ||
334 | |||
335 | if ((axis == mHighlightedAxis) && (direction == mHighlightedDirection)) | ||
336 | { | ||
337 | size *= 2.0; | ||
338 | color *= 1.5; | ||
339 | } | ||
340 | |||
341 | S32 arrows = 1; | ||
342 | if (mForce) | ||
343 | { | ||
344 | arrows = 2; | ||
345 | } | ||
346 | |||
347 | for (S32 i = 0; i < arrows; i++) | ||
348 | { | ||
349 | LLVector3 axis_vector = LLVector3(0,0,0); | ||
350 | axis_vector.mV[axis] = direction * (bbox_scale.mV[axis] / 2.0 + i * (size/3.0)); | ||
351 | |||
352 | LLVector3 manipulator_center = bbox_center + axis_vector; | ||
353 | |||
354 | LLQuaternion manipulator_rotation; | ||
355 | manipulator_rotation.shortestArc(LLVector3(0,0,1), -1.0 * axis_vector); | ||
356 | |||
357 | LLBBox manipulator_bbox = LLBBox(manipulator_center, manipulator_rotation, | ||
358 | LLVector3(), LLVector3()); | ||
359 | |||
360 | manipulator_bbox.addPointLocal(LLVector3(-1, -1, -0.75) * size * 0.5); | ||
361 | manipulator_bbox.addPointLocal(LLVector3(1, 1, 0.75) * size * 0.5); | ||
362 | |||
363 | gGL.color4fv(color.mV); | ||
364 | // sadly, gCone doesn't use gGL like gBox does (presumably because its author smokes crack) so we | ||
365 | // also set the raw GL color. hopefully this won't screw-up later rendering. | ||
366 | glColor4fv(color.mV); | ||
367 | |||
368 | render_cone_bbox(manipulator_bbox); | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | |||
373 | |||
374 | void QToolAlign::render() | ||
375 | { | ||
376 | mBBox = get_selection_axis_aligned_bbox(); | ||
377 | |||
378 | // Draw bounding box | ||
379 | LLGLSUIDefault gls_ui; | ||
380 | LLGLEnable gl_blend(GL_BLEND); | ||
381 | LLGLEnable gls_alpha_test(GL_ALPHA_TEST); | ||
382 | LLGLDepthTest gls_depth(GL_FALSE); | ||
383 | gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); | ||
384 | |||
385 | // render box | ||
386 | LLColor4 default_normal_color( 0.7f, 0.7f, 0.7f, 0.1f ); | ||
387 | gGL.color4fv( default_normal_color.mV ); | ||
388 | |||
389 | render_bbox(mBBox); | ||
390 | renderManipulators(); | ||
391 | } | ||
392 | |||
393 | // only works for our specialized (AABB, position centered) bboxes | ||
394 | BOOL bbox_overlap(LLBBox bbox1, LLBBox bbox2) | ||
395 | { | ||
396 | const F32 FUDGE = 0.001; // because of stupid SL precision/rounding | ||
397 | |||
398 | LLVector3 delta = bbox1.getCenterAgent() - bbox2.getCenterAgent(); | ||
399 | |||
400 | LLVector3 half_extent = (bbox1.getExtentLocal() + bbox2.getExtentLocal()) / 2.0; | ||
401 | |||
402 | return ((fabs(delta.mV[VX]) < half_extent.mV[VX] - FUDGE) && | ||
403 | (fabs(delta.mV[VY]) < half_extent.mV[VY] - FUDGE) && | ||
404 | (fabs(delta.mV[VZ]) < half_extent.mV[VZ] - FUDGE)); | ||
405 | } | ||
406 | |||
407 | |||
408 | |||
409 | // used to sort bboxes before packing | ||
410 | class BBoxCompare | ||
411 | { | ||
412 | public: | ||
413 | BBoxCompare(S32 axis, F32 direction, std::map<LLPointer<LLViewerObject>, LLBBox >& bboxes) : | ||
414 | mAxis(axis), mDirection(direction), mBBoxes(bboxes) {} | ||
415 | |||
416 | BOOL operator() (LLViewerObject* object1, LLViewerObject* object2) | ||
417 | { | ||
418 | LLVector3 corner1 = mBBoxes[object1].getCenterAgent() - | ||
419 | mDirection * mBBoxes[object1].getExtentLocal()/2.0; | ||
420 | |||
421 | LLVector3 corner2 = mBBoxes[object2].getCenterAgent() - | ||
422 | mDirection * mBBoxes[object2].getExtentLocal()/2.0; | ||
423 | |||
424 | |||
425 | return mDirection * corner1.mV[mAxis] < mDirection * corner2.mV[mAxis]; | ||
426 | } | ||
427 | |||
428 | S32 mAxis; | ||
429 | F32 mDirection; | ||
430 | std::map<LLPointer<LLViewerObject>, LLBBox >& mBBoxes; | ||
431 | }; | ||
432 | |||
433 | |||
434 | void QToolAlign::align() | ||
435 | { | ||
436 | // no linkset parts, please | ||
437 | LLSelectMgr::getInstance()->promoteSelectionToRoot(); | ||
438 | |||
439 | std::vector<LLPointer<LLViewerObject> > objects; | ||
440 | std::map<LLPointer<LLViewerObject>, LLBBox > original_bboxes; | ||
441 | |||
442 | // cycle over the nodes in selection and collect them into an array | ||
443 | for (LLObjectSelection::root_iterator selection_iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); | ||
444 | selection_iter != LLSelectMgr::getInstance()->getSelection()->root_end(); | ||
445 | ++selection_iter) | ||
446 | { | ||
447 | LLSelectNode *select_node = *selection_iter; | ||
448 | if (select_node) | ||
449 | { | ||
450 | LLViewerObject* object = select_node->getObject(); | ||
451 | if (object) | ||
452 | { | ||
453 | LLVector3 position = object->getPositionAgent(); | ||
454 | |||
455 | LLBBox bbox = LLBBox(position, LLQuaternion(), LLVector3(), LLVector3()); | ||
456 | bbox.addPointLocal(LLVector3()); | ||
457 | |||
458 | // add the parent's bbox | ||
459 | bbox.addBBoxAgent(object->getBoundingBoxAgent()); | ||
460 | LLViewerObject::const_child_list_t& children = object->getChildren(); | ||
461 | |||
462 | for (LLViewerObject::const_child_list_t::const_iterator i = children.begin(); | ||
463 | i != children.end(); i++) | ||
464 | { | ||
465 | // add the child's bbox | ||
466 | LLViewerObject* child = *i; | ||
467 | bbox.addBBoxAgent(child->getBoundingBoxAgent()); | ||
468 | } | ||
469 | |||
470 | objects.push_back(object); | ||
471 | original_bboxes[object] = bbox; | ||
472 | } | ||
473 | } | ||
474 | } | ||
475 | |||
476 | S32 axis = mHighlightedAxis; | ||
477 | F32 direction = mHighlightedDirection; | ||
478 | |||
479 | // sort them into positional order for proper packing | ||
480 | BBoxCompare compare(axis, direction, original_bboxes); | ||
481 | sort(objects.begin(), objects.end(), compare); | ||
482 | |||
483 | // storage for their new position after alignment - start with original position first | ||
484 | std::map<LLPointer<LLViewerObject>, LLBBox > new_bboxes = original_bboxes; | ||
485 | |||
486 | // find new positions | ||
487 | for (S32 i = 0; i < objects.size(); i++) | ||
488 | { | ||
489 | LLBBox target_bbox = mBBox; | ||
490 | LLVector3 target_corner = target_bbox.getCenterAgent() - | ||
491 | direction * target_bbox.getExtentLocal() / 2.0; | ||
492 | |||
493 | LLViewerObject* object = objects[i]; | ||
494 | |||
495 | LLBBox this_bbox = original_bboxes[object]; | ||
496 | LLVector3 this_corner = this_bbox.getCenterAgent() - | ||
497 | direction * this_bbox.getExtentLocal() / 2.0; | ||
498 | |||
499 | // for packing, we cycle over several possible positions, taking the smallest that does not overlap | ||
500 | F32 smallest = direction * 9999999; // 999999 guarenteed not to be the smallest | ||
501 | for (S32 j = 0; j <= i; j++) | ||
502 | { | ||
503 | // how far must it move? | ||
504 | LLVector3 delta = target_corner - this_corner; | ||
505 | |||
506 | // new position moves only on one axis, please | ||
507 | LLVector3 delta_one_axis = LLVector3(0,0,0); | ||
508 | delta_one_axis.mV[axis] = delta.mV[axis]; | ||
509 | |||
510 | LLVector3 new_position = this_bbox.getCenterAgent() + delta_one_axis; | ||
511 | |||
512 | // construct the new bbox | ||
513 | LLBBox new_bbox = LLBBox(new_position, LLQuaternion(), LLVector3(), LLVector3()); | ||
514 | new_bbox.addPointLocal(this_bbox.getExtentLocal() / 2.0); | ||
515 | new_bbox.addPointLocal(-1.0 * this_bbox.getExtentLocal() / 2.0); | ||
516 | |||
517 | // check to see if it overlaps the previously placed objects | ||
518 | BOOL overlap = FALSE; | ||
519 | |||
520 | llwarns << "i=" << i << " j=" << j << llendl; | ||
521 | |||
522 | if (!mForce) // well, don't check if in force mode | ||
523 | { | ||
524 | for (S32 k = 0; k < i; k++) | ||
525 | { | ||
526 | LLViewerObject* other_object = objects[k]; | ||
527 | LLBBox other_bbox = new_bboxes[other_object]; | ||
528 | |||
529 | BOOL overlaps_this = bbox_overlap(other_bbox, new_bbox); | ||
530 | |||
531 | if (overlaps_this) | ||
532 | { | ||
533 | llwarns << "overlap" << new_bbox.getCenterAgent() << other_bbox.getCenterAgent() << llendl; | ||
534 | llwarns << "extent" << new_bbox.getExtentLocal() << other_bbox.getExtentLocal() << llendl; | ||
535 | } | ||
536 | |||
537 | overlap = (overlap || overlaps_this); | ||
538 | } | ||
539 | } | ||
540 | |||
541 | if (!overlap) | ||
542 | { | ||
543 | F32 this_value = (new_bbox.getCenterAgent() - | ||
544 | direction * new_bbox.getExtentLocal() / 2.0).mV[axis]; | ||
545 | |||
546 | if (direction * this_value < direction * smallest) | ||
547 | { | ||
548 | smallest = this_value; | ||
549 | // store it | ||
550 | new_bboxes[object] = new_bbox; | ||
551 | } | ||
552 | } | ||
553 | |||
554 | // update target for next time through the loop | ||
555 | if (j < objects.size()) | ||
556 | { | ||
557 | LLBBox next_bbox = new_bboxes[objects[j]]; | ||
558 | target_corner = next_bbox.getCenterAgent() + | ||
559 | direction * next_bbox.getExtentLocal() / 2.0; | ||
560 | } | ||
561 | } | ||
562 | } | ||
563 | |||
564 | |||
565 | // now move them | ||
566 | for (S32 i = 0; i < objects.size(); i++) | ||
567 | { | ||
568 | LLViewerObject* object = objects[i]; | ||
569 | |||
570 | LLBBox original_bbox = original_bboxes[object]; | ||
571 | LLBBox new_bbox = new_bboxes[object]; | ||
572 | |||
573 | LLVector3 delta = new_bbox.getCenterAgent() - original_bbox.getCenterAgent(); | ||
574 | |||
575 | LLVector3 original_position = object->getPositionAgent(); | ||
576 | LLVector3 new_position = original_position + delta; | ||
577 | |||
578 | object->setPosition(new_position); | ||
579 | } | ||
580 | |||
581 | |||
582 | LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_POSITION); | ||
583 | } | ||
584 | |||
585 | |||
diff --git a/linden/indra/newview/qtoolalign.h b/linden/indra/newview/qtoolalign.h new file mode 100644 index 0000000..b2c18b7 --- /dev/null +++ b/linden/indra/newview/qtoolalign.h | |||
@@ -0,0 +1,50 @@ | |||
1 | /** | ||
2 | * @file qtoolalign.h | ||
3 | * @brief A tool to align objects | ||
4 | * @author Karl Stiefvater (Qarl) | ||
5 | * | ||
6 | * Karl has given permission to use this code under the terms of | ||
7 | * the GNU GPL v2 plus FLOSS exception and/or the GNU LGPL v2.1. | ||
8 | * | ||
9 | * Backported for Viewer 1.X code base by Jacek Antonelli. | ||
10 | */ | ||
11 | |||
12 | #ifndef Q_QTOOLALIGN_H | ||
13 | #define Q_QTOOLALIGN_H | ||
14 | |||
15 | #include "lltool.h" | ||
16 | #include "llbbox.h" | ||
17 | |||
18 | class LLViewerObject; | ||
19 | class LLPickInfo; | ||
20 | class LLToolSelectRect; | ||
21 | |||
22 | class QToolAlign | ||
23 | : public LLTool, public LLSingleton<QToolAlign> | ||
24 | { | ||
25 | public: | ||
26 | QToolAlign(); | ||
27 | virtual ~QToolAlign(); | ||
28 | |||
29 | virtual void handleSelect(); | ||
30 | virtual void handleDeselect(); | ||
31 | virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); | ||
32 | virtual BOOL handleHover(S32 x, S32 y, MASK mask); | ||
33 | virtual void render(); | ||
34 | |||
35 | static void pickCallback(const LLPickInfo& pick_info); | ||
36 | |||
37 | private: | ||
38 | void align(); | ||
39 | void computeManipulatorSize(); | ||
40 | void renderManipulators(); | ||
41 | BOOL findSelectedManipulator(S32 x, S32 y); | ||
42 | |||
43 | LLBBox mBBox; | ||
44 | F32 mManipulatorSize; | ||
45 | S32 mHighlightedAxis; | ||
46 | F32 mHighlightedDirection; | ||
47 | BOOL mForce; | ||
48 | }; | ||
49 | |||
50 | #endif // Q_QTOOLALIGN_H | ||
diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_tools.xml b/linden/indra/newview/skins/default/xui/en-us/floater_tools.xml index 3e0a5fa..7438329 100644 --- a/linden/indra/newview/skins/default/xui/en-us/floater_tools.xml +++ b/linden/indra/newview/skins/default/xui/en-us/floater_tools.xml | |||
@@ -79,6 +79,9 @@ | |||
79 | <check_box bottom_delta="-15" follows="left|top" font="SansSerifSmall" height="16" | 79 | <check_box bottom_delta="-15" follows="left|top" font="SansSerifSmall" height="16" |
80 | initial_value="false" label="Select faces to texture" left="4" mouse_opaque="true" | 80 | initial_value="false" label="Select faces to texture" left="4" mouse_opaque="true" |
81 | name="radio select face" radio_style="true" width="114" /> | 81 | name="radio select face" radio_style="true" width="114" /> |
82 | <check_box bottom_delta="-15" follows="left|top" font="SansSerifSmall" height="16" | ||
83 | initial_value="false" label="Align" left="4" mouse_opaque="true" | ||
84 | name="radio align" radio_style="true" width="114" /> | ||
82 | <check_box bottom_delta="-19" control_name="EditLinkedParts" follows="left|top" | 85 | <check_box bottom_delta="-19" control_name="EditLinkedParts" follows="left|top" |
83 | font="SansSerifSmall" height="16" initial_value="false" | 86 | font="SansSerifSmall" height="16" initial_value="false" |
84 | label="Edit linked parts" left="4" mouse_opaque="true" | 87 | label="Edit linked parts" left="4" mouse_opaque="true" |