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 SoPolygonOffset SoPolygonOffset.h Inventor/nodes/SoPolygonOffset.h
35 \brief The SoPolygonOffset class is a node type for "layering" rendering primitives.
36
37 \ingroup nodes
38
39 A common problem with realtime 3D rendering systems is that rendered
40 primitives which are at approximately the same depth with regard to
41 the camera viewpoint will appear to flicker. I.e.: from one angle
42 one primitive will appear to be closer, while at another angle,
43 another primitive will appear closer. When this happens, the
44 rendered graphics at that part of the scene will of course look a
45 lot less visually pleasing.
46
47 One common situation where this problem often occurs is when you
48 attempt to put a wireframe grid as an outline on top of filled
49 polygons.
50
51 The cause of the problem described above is that the Z-buffer of any
52 render system has a limited resolution, often at 16, 24 or 32
53 bits. Because of this, primitives which are close will sometimes get
54 the \e same depth value in the Z-buffer, even though they are \a not
55 actually at the same depth-coordinate.
56
57 To rectify the flickering problem, this node can be inserted in the
58 scene graph at the proper place(s) to explicitly define how
59 polygons, lines and/or points should be offset with regard to other
60 primitives.
61
62 As for the details of how the SoPolygonOffset::factor and
63 SoPolygonOffset::units should be set, we quote the OpenGL
64 documentation:
65
66 \verbatim
67
68 The value of the offset is
69
70 factor * DZ + r * units
71
72 where DZ is a measurement of the change in depth relative to the
73 screen area of the polygon, and r is the smallest value that is
74 guaranteed to produce a resolvable offset for a given
75 implementation. The offset is added before the depth test is
76 performed and before the value is written into the depth buffer.
77
78 \endverbatim
79
80 One word of notice with regard to the above quote from the OpenGL
81 documentation: it doesn't really make sense to set "factor" and
82 "units" to values with different signs, i.e. "factor" to a negative
83 value and "units" to a positive value, or vice versa.
84
85 The pixels would then be "pushed back" in z-order by one part of the
86 equation, but at the same time be "pushed forward" by the other part
87 of the equation. This would most likely give very inconsistent
88 results, but which may at first look ok.
89
90 We mention this potential for making a mistake, as it seems to be a
91 quite common error.
92
93
94 Below is a simple, correct usage example:
95
96 \verbatim
97 #Inventor V2.1 ascii
98
99 Separator {
100 Coordinate3 { point [ -1 -1 0, 1 -1 0, 1 1 0, -1 1 0 ] }
101
102 Separator {
103 BaseColor { rgb 1 1 0 }
104 # needs to draw polygons-as-line, and not "real" lines -- see
105 # documentation below on why this is so:
106 DrawStyle { style LINES }
107 # draw two triangles, to get a line crossing the face of the
108 # polygon:
109 IndexedFaceSet { coordIndex [ 0, 1, 2, -1, 0, 2, 3 -1 ] }
110 }
111
112 PolygonOffset {
113 styles FILLED
114 factor 1.0
115 units 1.0
116 }
117
118 BaseColor { rgb 0 0.5 0 }
119 FaceSet { numVertices [ 4 ] }
120 }
121 \endverbatim
122
123 Without the polygonoffset node in the above example, the lines may
124 look irregularly stippled with some graphics card drivers, as parts
125 of it will show through the faceset, others not. This happen on
126 seemingly random parts, as the z-buffer floating point calculations
127 will be fickle with regard to whether or not the polygon or the line
128 will be closer to the camera.
129
130 See the API documentation of the SoPolygonOffset::styles field below
131 for a discussion of one important limitation of OpenGL's Z-buffer
132 offset mechanism: it only works with polygons or polygons rendered
133 in line or point mode, using the SoDrawStyle::style field.
134
135 <b>FILE FORMAT/DEFAULTS:</b>
136 \code
137 PolygonOffset {
138 factor 1
139 units 1
140 styles FILLED
141 on TRUE
142 }
143 \endcode
144
145 \since TGS Inventor 2.5
146 \since Coin 1.0
147 */
148
149 // *************************************************************************
150
151 #include <Inventor/nodes/SoPolygonOffset.h>
152
153 #include <Inventor/actions/SoCallbackAction.h>
154 #include <Inventor/actions/SoGLRenderAction.h>
155 #include <Inventor/elements/SoGLPolygonOffsetElement.h>
156 #include <Inventor/elements/SoOverrideElement.h>
157
158 #include "nodes/SoSubNodeP.h"
159
160 // *************************************************************************
161
162 /*!
163 \enum SoPolygonOffset::Style
164
165 Enumeration of the rendering primitives which can be influenced by
166 an SoPolygonOffset node.
167 */
168
169 /*!
170 \var SoSFFloat SoPolygonOffset::factor
171
172 Offset multiplication factor. Scales the variable depth in the
173 z-buffer of the rendered primitives.
174
175 See SoPolygonOffset's main class documentation above for detailed
176 information on how the factor value is used.
177
178 Default value is 1.0.
179 */
180 /*!
181 \var SoSFFloat SoPolygonOffset::units
182
183 Offset translation multiplication factor. Will be multiplied with
184 the value which represents the smallest discrete step that can be
185 distinguished with the underlying Z-buffer resolution.
186
187 See SoPolygonOffset's main class documentation above for detailed
188 information on how the units value is used.
189
190 Note that positive values will push geometry "away" into the
191 Z-buffer, while negative values will "move" geometry closer.
192
193 Default value is 1.0.
194 */
195 /*!
196 \var SoSFBitMask SoPolygonOffset::styles
197
198 The rendering primitive type to be influenced by this node. This is
199 a bitmask variable, so you can select several primitive types (out
200 of filled polygons, lines and points) be influenced by the offset at
201 the same time.
202
203 There is one very important OpenGL limitation to know about in this
204 regard: Z-buffer offsetting can \e only be done for either polygons,
205 or for \e polygons rendered \e as \e lines or \e as \e points.
206
207 So attempts at using this node to offset e.g. SoLineSet /
208 SoIndexedLineSet or SoPointSet primitives will \e not work.
209
210 See the comments in the scene graph below for a detailed example on
211 what SoPolygonOffset can and can not do:
212
213 \code
214 #Inventor V2.1 ascii
215
216 Separator {
217 # render polygon:
218
219 Coordinate3 { point [ -1.1 -1.1 0, 1.1 -1.1 0, 1.1 1.1 0, -1.1 1.1 0 ] }
220 BaseColor { rgb 0 0.5 0 }
221 FaceSet { numVertices [ 4 ] }
222
223 # offset polygon-as-lines to be in front of above polygon:
224
225 PolygonOffset {
226 styles LINES
227 factor -2.0
228 units 1.0
229 }
230
231 # render lines:
232
233 Coordinate3 { point [ -1 -1 0, 1 -1 0, 1 1 0, -1 1 0 ] }
234 BaseColor { rgb 1 1 0 }
235
236 Switch {
237 # change this to '0' to see how glPolygonOffset() does *not* work
238 # with "true" lines
239 whichChild 1
240
241 DEF child0 Group {
242 # can *not* be offset
243 IndexedLineSet { coordIndex [ 0, 1, 2, 3, 0, 2, -1, 1, 3 -1 ]
244 }
245 }
246
247 DEF child1 Group {
248 # will be offset
249 DrawStyle { style LINES }
250 FaceSet { numVertices [ 4 ] }
251 }
252 }
253 }
254 \endcode
255
256 Field default value is SoPolygonOffset::FILLED.
257 */
258
259 /*!
260 \var SoSFBool SoPolygonOffset::on
261
262 Whether the offset is on or off. Default is for SoPolygonOffset::on
263 to be \c TRUE.
264 */
265
266
267 // *************************************************************************
268
269 SO_NODE_SOURCE(SoPolygonOffset);
270
271 /*!
272 Constructor.
273 */
SoPolygonOffset(void)274 SoPolygonOffset::SoPolygonOffset(void)
275 {
276 SO_NODE_INTERNAL_CONSTRUCTOR(SoPolygonOffset);
277
278 SO_NODE_ADD_FIELD(factor, (1.0f));
279 SO_NODE_ADD_FIELD(units, (1.0f));
280 SO_NODE_ADD_FIELD(styles, (SoPolygonOffset::FILLED));
281 SO_NODE_ADD_FIELD(on, (TRUE));
282
283 SO_NODE_DEFINE_ENUM_VALUE(Style, FILLED);
284 SO_NODE_DEFINE_ENUM_VALUE(Style, LINES);
285 SO_NODE_DEFINE_ENUM_VALUE(Style, POINTS);
286 SO_NODE_SET_SF_ENUM_TYPE(styles, Style);
287 }
288
289 /*!
290 Destructor.
291 */
~SoPolygonOffset()292 SoPolygonOffset::~SoPolygonOffset()
293 {
294 }
295
296 // doc in super
297 void
initClass(void)298 SoPolygonOffset::initClass(void)
299 {
300 SO_NODE_INTERNAL_INIT_CLASS(SoPolygonOffset, SO_FROM_INVENTOR_2_5|SO_FROM_COIN_1_0);
301
302 SO_ENABLE(SoCallbackAction, SoPolygonOffsetElement);
303 SO_ENABLE(SoGLRenderAction, SoGLPolygonOffsetElement);
304 }
305
306
307 void
doAction(SoAction * action)308 SoPolygonOffset::doAction(SoAction * action)
309 {
310 SoState * state = action->getState();
311
312 if (SoOverrideElement::getPolygonOffsetOverride(state)) return;
313
314 float factorval, units_val;
315 SoPolygonOffsetElement::Style styles_val;
316 SbBool offset_on;
317
318 factorval = this->factor.getValue();
319 units_val = this->units.getValue();
320 styles_val = (SoPolygonOffsetElement::Style)this->styles.getValue();
321 offset_on = this->on.getValue();
322
323 SoPolygonOffsetElement::set(action->getState(),
324 this,
325 factorval,
326 units_val,
327 styles_val,
328 offset_on);
329
330 if (this->isOverride()) {
331 SoOverrideElement::setPolygonOffsetOverride(state, this, TRUE);
332 }
333 }
334
335 void
callback(SoCallbackAction * action)336 SoPolygonOffset::callback(SoCallbackAction * action)
337 {
338 SoPolygonOffset::doAction((SoAction *)action);
339 }
340
341 void
GLRender(SoGLRenderAction * action)342 SoPolygonOffset::GLRender(SoGLRenderAction * action)
343 {
344 SoPolygonOffset::doAction((SoAction *)action);
345 }
346