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 SoAntiSquish SoAntiSquish.h Inventor/nodes/SoAntiSquish.h
35 \brief The SoAntiSquish class is used to reset to uniform scaling.
36
37 \ingroup nodes
38
39 When traversed, this node replaces the scale vector of the matrix
40 with uniform values, based on one of the SoAntiSquish::Sizing
41 strategies.
42
43 This node is for instance used by manipulators to retain the shape
44 of their dragger geometries when set up within the influence of an
45 SoSurroundScale node (which can yield nonuniform scale-vectors in
46 the current state transformation matrix).
47
48 <b>FILE FORMAT/DEFAULTS:</b>
49 \code
50 AntiSquish {
51 recalcAlways TRUE
52 sizing AVERAGE_DIMENSION
53 }
54 \endcode
55
56 \sa SoSurroundScale for a usage example.
57 */
58
59 #include <Inventor/nodes/SoAntiSquish.h>
60
61 #include <Inventor/actions/SoGetMatrixAction.h>
62 #include <Inventor/actions/SoGetBoundingBoxAction.h>
63 #include <Inventor/elements/SoModelMatrixElement.h>
64
65 #include "nodes/SoSubNodeP.h"
66
67 /*!
68 \enum SoAntiSquish::Sizing
69
70 Different strategies for "unsquishing" a scale. Values are used by
71 the SoAntiSquish::sizing field.
72 */
73
74
75 /*!
76 \var SoSFEnum SoAntiSquish::sizing
77
78 The current "unsquish" strategy. Default value is
79 SoAntiSquish::AVERAGE_DIMENSION.
80 */
81 /*!
82 \var SoSFBool SoAntiSquish::recalcAlways
83
84 Whether to automatically have the unsquishing parameters
85 recalculated for every traversal. Default value is \c TRUE.
86
87 You can set this to \c FALSE and manually invoke
88 SoAntiSquish::recalc() if you need closer control of the geometry
89 influenced by this node.
90 */
91
92
93 // *************************************************************************
94
95 SO_NODE_SOURCE(SoAntiSquish);
96
97 /*!
98 Constructor.
99 */
SoAntiSquish(void)100 SoAntiSquish::SoAntiSquish(void)
101 {
102 SO_NODE_INTERNAL_CONSTRUCTOR(SoAntiSquish);
103
104 SO_NODE_ADD_FIELD(recalcAlways, (TRUE));
105 SO_NODE_ADD_FIELD(sizing, (SoAntiSquish::AVERAGE_DIMENSION));
106
107 SO_NODE_DEFINE_ENUM_VALUE(Sizing, X);
108 SO_NODE_DEFINE_ENUM_VALUE(Sizing, Y);
109 SO_NODE_DEFINE_ENUM_VALUE(Sizing, Z);
110 SO_NODE_DEFINE_ENUM_VALUE(Sizing, AVERAGE_DIMENSION);
111 SO_NODE_DEFINE_ENUM_VALUE(Sizing, BIGGEST_DIMENSION);
112 SO_NODE_DEFINE_ENUM_VALUE(Sizing, SMALLEST_DIMENSION);
113 SO_NODE_DEFINE_ENUM_VALUE(Sizing, LONGEST_DIAGONAL);
114 SO_NODE_SET_SF_ENUM_TYPE(sizing, Sizing);
115
116 this->matrixvalid = FALSE;
117 this->inversevalid = FALSE;
118 }
119
120 /*!
121 Destructor.
122 */
~SoAntiSquish()123 SoAntiSquish::~SoAntiSquish()
124 {
125 }
126
127 // Doc from superclass.
128 void
initClass(void)129 SoAntiSquish::initClass(void)
130 {
131 SO_NODE_INTERNAL_INIT_CLASS(SoAntiSquish, SO_FROM_INVENTOR_1);
132 }
133
134 // Doc from superclass.
135 void
getBoundingBox(SoGetBoundingBoxAction * action)136 SoAntiSquish::getBoundingBox(SoGetBoundingBoxAction * action)
137 {
138 SbBool matrixwasvalid = this->matrixvalid;
139 SoAntiSquish::doAction(action);
140 if (this->recalcAlways.getValue() == FALSE) {
141 // Usually when recalcAlways is FALSE, SoAntiSquish::recalc() is
142 // called by manipulators at the same time as
143 // SoSurroundScale::invalidate() is called. This means that the
144 // first time we get here is often because SoSurrondScale applied
145 // an SoGetBoundingBoxAction to the scene graph it is going to
146 // calculate the surround scale for. This is _not_ the scene graph
147 // we want to anti-squish so we should recalculate it the next time
148 // we get here.
149 if (matrixwasvalid == FALSE) {
150 this->matrixvalid = FALSE;
151 }
152 }
153 }
154
155 /*!
156 If SoAntiSquish::recalcAlways has been set to \c FALSE, you must
157 call this method whenever the transformations before this node in
158 the graph has changed.
159
160 \sa SoAntiSquish::recalcAlways
161 */
162 void
recalc(void)163 SoAntiSquish::recalc(void)
164 {
165 this->matrixvalid = FALSE;
166 }
167
168 /*!
169 Accumulates an "unsquishing" matrix on top of the current model
170 matrix.
171 */
172 void
doAction(SoAction * action)173 SoAntiSquish::doAction(SoAction * action)
174 {
175 SoState * state = action->getState();
176 if (!this->matrixvalid || this->recalcAlways.getValue()) {
177 this->matrixvalid = TRUE;
178 this->inversevalid = FALSE;
179 this->unsquishedmatrix =
180 this->getUnsquishingMatrix(SoModelMatrixElement::get(state),
181 FALSE, this->inversematrix);
182 }
183 SoModelMatrixElement::mult(action->getState(), this, this->unsquishedmatrix);
184 }
185
186 // Doc from superclass.
187 void
callback(SoCallbackAction * action)188 SoAntiSquish::callback(SoCallbackAction * action)
189 {
190 SoAntiSquish::doAction((SoAction *)action);
191 }
192
193 // Doc from superclass.
194 void
GLRender(SoGLRenderAction * action)195 SoAntiSquish::GLRender(SoGLRenderAction * action)
196 {
197 SoAntiSquish::doAction((SoAction *) action);
198 }
199
200 // Doc from superclass.
201 void
getMatrix(SoGetMatrixAction * action)202 SoAntiSquish::getMatrix(SoGetMatrixAction * action)
203 {
204 if (!this->matrixvalid || !this->inversevalid ||
205 this->recalcAlways.getValue()) {
206 this->matrixvalid = TRUE;
207 this->inversevalid = TRUE;
208 this->unsquishedmatrix = this->getUnsquishingMatrix(action->getMatrix(),
209 TRUE,
210 this->inversematrix);
211 }
212
213 SbMatrix & m = action->getMatrix();
214 SbMatrix & i = action->getInverse();
215 m.multLeft(this->unsquishedmatrix);
216 i.multRight(this->inversematrix);
217 }
218
219 // Doc from superclass.
220 void
pick(SoPickAction * action)221 SoAntiSquish::pick(SoPickAction * action)
222 {
223 SoAntiSquish::doAction((SoAction *) action);
224 }
225
226 /*!
227 Calculate and return the matrix needed to "unsquish" the \a
228 squishedmatrix.
229
230 If \a calcinverse is \c TRUE, store the inverse of the
231 unsquishmatrix in \a getinverse.
232 */
233 SbMatrix
getUnsquishingMatrix(const SbMatrix & squishedmatrix,const SbBool calcinverse,SbMatrix & getinverse)234 SoAntiSquish::getUnsquishingMatrix(const SbMatrix & squishedmatrix,
235 const SbBool calcinverse,
236 SbMatrix & getinverse)
237 {
238 SbRotation r, so;
239 SbVec3f t, scale;
240
241 float val = 1.0f;
242
243 squishedmatrix.getTransform(t, r, scale, so);
244 switch (this->sizing.getValue()) {
245 case X:
246 val = scale[0];
247 break;
248 case Y:
249 val = scale[1];
250 break;
251 case Z:
252 val = scale[2];
253 break;
254 case AVERAGE_DIMENSION:
255 val = (scale[0] + scale[1] + scale[2]) / 3.0f;
256 break;
257 case BIGGEST_DIMENSION:
258 val = scale[0];
259 if (scale[1] > val) val = scale[1];
260 if (scale[2] > val) val = scale[2];
261 break;
262 case SMALLEST_DIMENSION:
263 val = scale[0];
264 if (scale[1] < val) val = scale[1];
265 if (scale[2] < val) val = scale[2];
266 break;
267 case LONGEST_DIAGONAL:
268 {
269 SbVec3f unitcube[8];
270 for (int i = 0; i < 8; i++) {
271 unitcube[i][0] = i & 1 ? 1.0f : -1.0f;
272 unitcube[i][1] = i & 2 ? 1.0f : -1.0f;
273 unitcube[i][2] = i & 4 ? 1.0f : -1.0f;
274 squishedmatrix.multVecMatrix(unitcube[i], unitcube[i]);
275 }
276
277 val = (unitcube[1] - unitcube[6]).sqrLength();
278 float tmp = (unitcube[5] - unitcube[2]).sqrLength();
279 if (tmp > val) val = tmp;
280 tmp = (unitcube[3] - unitcube[4]).sqrLength();
281 if (tmp > val) val = tmp;
282 tmp = (unitcube[0] - unitcube[7]).sqrLength();
283 if (tmp > val) val = tmp;
284
285 val = (float) sqrt(val);
286 val *= 0.5f;
287 break;
288 }
289 default:
290 assert(0 && "unknown sizing parameter");
291 val = (scale[0] + scale[1] + scale[2]) / 3.0f; // use avarage
292 break;
293 }
294 scale[0] = scale[1] = scale[2] = val;
295 SbMatrix matrix;
296 matrix.setTransform(t, r, scale, so);
297 matrix.multRight(squishedmatrix.inverse());
298 if (calcinverse) getinverse = matrix.inverse();
299 return matrix;
300 }
301