1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 /*!
34 \class SoBoxHighlightRenderAction SoBoxHighlightRenderAction.h Inventor/actions/SoBoxHighlightRenderAction.h
35 \brief The SoBoxHighlightRenderAction class renders the scene with highlighted boxes around selections.
36
37 \ingroup actions
38
39 This action performs the same tasks as its parent class,
40 SoGLRenderAction, with the added ability to render highlighted
41 bounding boxes around geometry in selected nodes. This is a simple
42 but convenient way of giving feedback to the user upon interaction
43 with the scene graph.
44
45 To have the highlighting actually happen (and to be able to
46 automatically "select" nodes by picking with the mouse cursor), you
47 need to use SoSelection nodes in place of group nodes.
48
49 \sa SoLineHighlightRenderAction, SoSelection
50 */
51
52 #include <Inventor/actions/SoBoxHighlightRenderAction.h>
53 #include <Inventor/actions/SoSearchAction.h>
54 #include <Inventor/actions/SoGetBoundingBoxAction.h>
55 #include <Inventor/nodes/SoSelection.h>
56 #include <Inventor/nodes/SoCube.h>
57 #include <Inventor/nodes/SoCamera.h>
58 #include <Inventor/nodes/SoMatrixTransform.h>
59 #include <Inventor/nodes/SoDrawStyle.h>
60 #include <Inventor/nodes/SoComplexity.h>
61 #include <Inventor/nodes/SoLightModel.h>
62 #include <Inventor/nodes/SoBaseColor.h>
63 #include <cassert>
64
65 #include "actions/SoSubActionP.h"
66 #include "SbBasicP.h"
67
68 #ifdef HAVE_CONFIG_H
69 #include "config.h"
70 #endif // HAVE_CONFIG_H
71
72 /*!
73 \var SoBoxHighlightRenderAction::hlVisible
74
75 Boolean which decides whether or not the highlights for selected
76 nodes should be visible.
77 */
78
79 #ifndef DOXYGEN_SKIP_THIS
80
81 class SoBoxHighlightRenderActionP {
82 public:
SoBoxHighlightRenderActionP(void)83 SoBoxHighlightRenderActionP(void) : master(NULL) { }
84
85 SoBoxHighlightRenderAction * master;
86 SoSearchAction * searchaction;
87 SoSearchAction * camerasearch;
88 SoGetBoundingBoxAction * bboxaction;
89 SoBaseColor * basecolor;
90 SoTempPath * postprocpath;
91 SoSeparator * bboxseparator;
92 SoMatrixTransform * bboxtransform;
93 SoCube * bboxcube;
94 SoDrawStyle * drawstyle;
95
96 void initBoxGraph();
97 void drawHighlightBox(const SoPath * path);
98 };
99
100 #define PRIVATE(obj) ((obj)->pimpl)
101 #define PUBLIC(obj) ((obj)->master)
102
103 // used to initialize the internal storage class with variables
104 void
initBoxGraph()105 SoBoxHighlightRenderActionP::initBoxGraph()
106 {
107 this->bboxseparator = new SoSeparator;
108 this->bboxseparator->ref();
109 this->bboxseparator->renderCaching = SoSeparator::OFF;
110 this->bboxseparator->boundingBoxCaching = SoSeparator::OFF;
111
112 this->bboxtransform = new SoMatrixTransform;
113 this->bboxcube = new SoCube;
114
115 this->drawstyle = new SoDrawStyle;
116 this->drawstyle->style = SoDrawStyleElement::LINES;
117 this->basecolor = new SoBaseColor;
118
119 SoLightModel * lightmodel = new SoLightModel;
120 lightmodel->model = SoLazyElement::BASE_COLOR;
121
122 SoComplexity * complexity = new SoComplexity;
123 complexity->textureQuality = 0.0f;
124 complexity->type = SoComplexityTypeElement::BOUNDING_BOX;
125
126 this->bboxseparator->addChild(this->drawstyle);
127 this->bboxseparator->addChild(this->basecolor);
128
129 this->bboxseparator->addChild(lightmodel);
130 this->bboxseparator->addChild(complexity);
131
132 this->bboxseparator->addChild(this->bboxtransform);
133 this->bboxseparator->addChild(this->bboxcube);
134 }
135
136
137 // used to render shape and non-shape nodes (usually SoGroup or SoSeparator).
138 void
drawHighlightBox(const SoPath * path)139 SoBoxHighlightRenderActionP::drawHighlightBox(const SoPath * path)
140 {
141 if (this->camerasearch == NULL) {
142 this->camerasearch = new SoSearchAction;
143 }
144
145 // find camera used to render node
146 this->camerasearch->setFind(SoSearchAction::TYPE);
147 this->camerasearch->setInterest(SoSearchAction::FIRST); // find first camera to break out asap
148 this->camerasearch->setType(SoCamera::getClassTypeId());
149 this->camerasearch->apply(const_cast<SoPath*>(path));
150
151 if (this->camerasearch->getPath()) {
152 this->bboxseparator->insertChild(this->camerasearch->getPath()->getTail(), 0);
153 }
154 this->camerasearch->reset();
155
156 if (this->bboxaction == NULL) {
157 this->bboxaction = new SoGetBoundingBoxAction(SbViewportRegion(100, 100));
158 }
159 this->bboxaction->setViewportRegion(PUBLIC(this)->getViewportRegion());
160 this->bboxaction->apply(const_cast<SoPath*>(path));
161
162 SbXfBox3f & box = this->bboxaction->getXfBoundingBox();
163
164 if (!box.isEmpty()) {
165 // set cube size
166 float x, y, z;
167 box.getSize(x, y, z);
168 this->bboxcube->width = x;
169 this->bboxcube->height = y;
170 this->bboxcube->depth = z;
171
172 SbMatrix transform = box.getTransform();
173
174 // get center (in the local bbox coordinate system)
175 SbVec3f center = box.SbBox3f::getCenter();
176
177 // if center != (0,0,0), move the cube
178 if (center != SbVec3f(0.0f, 0.0f, 0.0f)) {
179 SbMatrix t;
180 t.setTranslate(center);
181 transform.multLeft(t);
182 }
183 this->bboxtransform->matrix = transform;
184
185 PUBLIC(this)->SoGLRenderAction::apply(this->bboxseparator);
186 }
187 // remove camera
188 this->bboxseparator->removeChild(0);
189 }
190
191 #endif // DOXYGEN_SKIP_THIS
192
193 SO_ACTION_SOURCE(SoBoxHighlightRenderAction);
194
195 // Overridden from parent class.
196 void
initClass(void)197 SoBoxHighlightRenderAction::initClass(void)
198 {
199 SO_ACTION_INTERNAL_INIT_CLASS(SoBoxHighlightRenderAction, SoGLRenderAction);
200 }
201
202
203 /*!
204 Default constructor. Note: passes a default SbViewportRegion to the
205 parent constructor.
206 */
SoBoxHighlightRenderAction(void)207 SoBoxHighlightRenderAction::SoBoxHighlightRenderAction(void)
208 : inherited(SbViewportRegion())
209 {
210 this->init();
211 }
212
213 /*!
214 Constructor, taking an explicit \a viewportregion to render.
215 */
SoBoxHighlightRenderAction(const SbViewportRegion & viewportregion)216 SoBoxHighlightRenderAction::SoBoxHighlightRenderAction(const SbViewportRegion & viewportregion)
217 : inherited(viewportregion)
218 {
219 this->init();
220 }
221
222 //
223 // private. called by both constructors
224 //
225 void
init(void)226 SoBoxHighlightRenderAction::init(void)
227 {
228 SO_ACTION_CONSTRUCTOR(SoBoxHighlightRenderAction);
229
230 PRIVATE(this)->master = this;
231
232 // Initialize local variables
233 PRIVATE(this)->initBoxGraph();
234
235 this->hlVisible = TRUE;
236
237 PRIVATE(this)->basecolor->rgb.setValue(1.0f, 0.0f, 0.0f);
238 PRIVATE(this)->drawstyle->linePattern = 0xffff;
239 PRIVATE(this)->drawstyle->lineWidth = 3.0f;
240 PRIVATE(this)->searchaction = NULL;
241 PRIVATE(this)->camerasearch = NULL;
242 PRIVATE(this)->bboxaction = NULL;
243
244 // SoBase-derived objects should be dynamically allocated.
245 PRIVATE(this)->postprocpath = new SoTempPath(32);
246 PRIVATE(this)->postprocpath->ref();
247 }
248
249
250 /*!
251 Destructor.
252 */
~SoBoxHighlightRenderAction(void)253 SoBoxHighlightRenderAction::~SoBoxHighlightRenderAction(void)
254 {
255 PRIVATE(this)->postprocpath->unref();
256 PRIVATE(this)->bboxseparator->unref();
257
258 delete PRIVATE(this)->searchaction;
259 delete PRIVATE(this)->camerasearch;
260 delete PRIVATE(this)->bboxaction;
261 }
262
263 // Documented in superclass. Overridden to add highlighting after the
264 // "ordinary" rendering.
265 void
apply(SoNode * node)266 SoBoxHighlightRenderAction::apply(SoNode * node)
267 {
268 SoGLRenderAction::apply(node);
269 if (this->hlVisible) {
270 if (PRIVATE(this)->searchaction == NULL) {
271 PRIVATE(this)->searchaction = new SoSearchAction;
272 }
273 const SbBool searchall = FALSE;
274 PRIVATE(this)->searchaction->setType(SoSelection::getClassTypeId());
275 PRIVATE(this)->searchaction->setInterest(searchall ? SoSearchAction::ALL : SoSearchAction::FIRST);
276 PRIVATE(this)->searchaction->apply(node);
277
278 if (searchall) {
279 const SoPathList & pathlist = PRIVATE(this)->searchaction->getPaths();
280 if (pathlist.getLength() > 0) {
281 int i;
282 for (i = 0; i < pathlist.getLength(); i++) {
283 SoFullPath * path = static_cast<SoFullPath *>(pathlist[i]);
284 assert(path);
285 SoSelection * selection = static_cast<SoSelection *>(path->getTail());
286 if (selection->getNumSelected() > 0)
287 this->drawBoxes(path, selection->getList());
288 }
289 }
290 }
291 else {
292 SoFullPath * path =
293 static_cast<SoFullPath *>(PRIVATE(this)->searchaction->getPath());
294 if (path) {
295 SoSelection * selection = static_cast<SoSelection *>(path->getTail());
296 if (selection->getNumSelected()) {
297 this->drawBoxes(path, selection->getList());
298 }
299 }
300 }
301 PRIVATE(this)->searchaction->reset();
302 }
303 }
304
305 // Documented in superclass. This method will just call the
306 // SoGLRenderAction::apply() method (so no highlighting will be done).
307 //
308 // It has been overridden to avoid confusing the compiler, which
309 // typically want to see either all or none of the apply() methods
310 // overridden.
311 void
apply(SoPath * path)312 SoBoxHighlightRenderAction::apply(SoPath * path)
313 {
314 SoGLRenderAction::apply(path);
315 }
316
317 // Documented in superclass. This method will just call the
318 // SoGLRenderAction::apply() method (so no highlighting will be done).
319 //
320 // It has been overridden to avoid confusing the compiler, which
321 // typically want to see either all or none of the apply() methods
322 // overridden.
323 void
apply(const SoPathList & pathlist,SbBool obeysrules)324 SoBoxHighlightRenderAction::apply(const SoPathList & pathlist,
325 SbBool obeysrules)
326 {
327 SoGLRenderAction::apply(pathlist, obeysrules);
328 }
329
330 /*!
331 Sets if highlighted boxes should be \a visible when
332 rendering. Defaults to \c TRUE.
333 */
334 void
setVisible(const SbBool visible)335 SoBoxHighlightRenderAction::setVisible(const SbBool visible)
336 {
337 this->hlVisible = visible;
338 }
339
340 /*!
341 Return if highlighted boxes are to be visible.
342 */
343 SbBool
isVisible(void) const344 SoBoxHighlightRenderAction::isVisible(void) const
345 {
346 return this->hlVisible;
347 }
348
349 /*!
350 Sets the \a color for the highlighted boxes. Defaults to completely
351 red.
352 */
353 void
setColor(const SbColor & color)354 SoBoxHighlightRenderAction::setColor(const SbColor & color)
355 {
356 PRIVATE(this)->basecolor->rgb = color;
357 }
358
359 /*!
360 Returns rendering color of the highlighted boxes.
361 */
362 const SbColor &
getColor(void)363 SoBoxHighlightRenderAction::getColor(void)
364 {
365 return PRIVATE(this)->basecolor->rgb[0];
366 }
367
368 /*!
369 Sets the line \a pattern used for the highlighted boxes. Defaults to
370 \c 0xffff (i.e. drawn with no stipples).
371 */
372 void
setLinePattern(unsigned short pattern)373 SoBoxHighlightRenderAction::setLinePattern(unsigned short pattern)
374 {
375 PRIVATE(this)->drawstyle->linePattern = pattern;
376 }
377
378 /*!
379 Returns line pattern used when drawing boxes.
380 */
381 unsigned short
getLinePattern(void) const382 SoBoxHighlightRenderAction::getLinePattern(void) const
383 {
384 return PRIVATE(this)->drawstyle->linePattern.getValue();
385 }
386
387 /*!
388 Sets the line \a width used when drawing boxes, in screen pixels (as
389 for all OpenGL rendering). Defaults to 3.
390 */
391 void
setLineWidth(const float width)392 SoBoxHighlightRenderAction::setLineWidth(const float width)
393 {
394 PRIVATE(this)->drawstyle->lineWidth = width;
395 }
396
397 /*!
398 Returns the line width used when drawing highlight boxes.
399 */
400 float
getLineWidth(void) const401 SoBoxHighlightRenderAction::getLineWidth(void) const
402 {
403 return PRIVATE(this)->drawstyle->lineWidth.getValue();
404 }
405
406 void
drawBoxes(SoPath * pathtothis,const SoPathList * pathlist)407 SoBoxHighlightRenderAction::drawBoxes(SoPath * pathtothis, const SoPathList * pathlist)
408 {
409 int i;
410 int thispos = reclassify_cast<SoFullPath *>(pathtothis)->getLength()-1;
411 assert(thispos >= 0);
412 PRIVATE(this)->postprocpath->setHead(pathtothis->getHead()); // reset
413
414 for (i = 1; i < thispos; i++) {
415 PRIVATE(this)->postprocpath->append(pathtothis->getIndex(i));
416 }
417
418 // we need to disable accumulation buffer antialiasing while
419 // rendering selected objects
420 int oldnumpasses = this->getNumPasses();
421 this->setNumPasses(1);
422
423 SoState * thestate = this->getState();
424 thestate->push();
425
426 for (i = 0; i < pathlist->getLength(); i++) {
427 SoFullPath * path = reclassify_cast<SoFullPath *>((*pathlist)[i]);
428 PRIVATE(this)->postprocpath->append(path->getHead());
429 for (int j = 1; j < path->getLength(); j++) {
430 PRIVATE(this)->postprocpath->append(path->getIndex(j));
431 }
432
433 // Previously SoGLRenderAction was used to draw the bounding boxes
434 // of shapes in selection paths, by overriding renderstyle state
435 // elements to lines drawstyle and simply doing:
436 //
437 // SoGLRenderAction::apply(PRIVATE(this)->postprocpath); // Bug
438 //
439 // This could have the unwanted side effect of rendering
440 // non-selected shapes, as they could be part of the path (due to
441 // being placed below SoGroup nodes (instead of SoSeparator
442 // nodes)) up to the selected shape.
443 //
444 //
445 // A better approach turned out to be to soup up and draw only the
446 // bounding boxes of the selected shapes:
447 PRIVATE(this)->drawHighlightBox(PRIVATE(this)->postprocpath);
448
449 // Remove temporary path from path buffer
450 PRIVATE(this)->postprocpath->truncate(thispos);
451 }
452
453 this->setNumPasses(oldnumpasses);
454 thestate->pop();
455 }
456
457
458 #undef PRIVATE
459 #undef PUBLIC
460