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