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 SoGLLineWidthElement Inventor/elements/SoGLLineWidthElement.h
35   \brief The SoGLLineWidthElement class changes the linewidth setting of the OpenGL render state.
36 
37   \ingroup elements
38 
39   Requests from the scenegraph to change the linewidth when rendering
40   OpenGL line primitives will be made through this element, which
41   forwards it to the appropriate native OpenGL call.
42 
43   The Coin library does not place any bounds on the values of the
44   linewidths, but be aware that the range and granularity of what is
45   valid linewidths depends on the underlying OpenGL
46   implementation. Application programmers using line primitives
47   (typically through the SoLineSet or SoIndexedLineSet nodes) should
48   heed these boundary values. They can be acquired by running the
49   following code from within a valid OpenGL context:
50 
51   \code
52     GLfloat bounds[2];
53     glGetFloatv(GL_LINE_WIDTH_RANGE, bounds);
54     GLfloat granularity[1];
55     glGetFloatv(GL_LINE_WIDTH_GRANULARITY, granularity);
56   \endcode
57 
58   Another, perhaps more convenient, way of acquiring the OpenGL
59   implementation limits with regard to pointsizes is to use the
60   So\@Gui\@GLWidget\::getPointSizeLimits() method in the GUI "glue" interface
61   library you are using (SoQt, SoXt, SoGtk, SoWin, ...).
62 */
63 
64 #include <Inventor/elements/SoGLLineWidthElement.h>
65 #include "coindefs.h"
66 
67 #include <cfloat>
68 #ifdef HAVE_CONFIG_H
69 #include <config.h>
70 #endif // HAVE_CONFIG_H
71 
72 #include <Inventor/system/gl.h>
73 
74 #include <cassert>
75 #include <Inventor/errors/SoDebugError.h>
76 
77 // Important note: do _not_ use a "static const" variable instead, as
78 // it is then not given that it will be initialized before the static
79 // "sizerange" class variable array below.
80 #define RANGE_NOT_CHECKED FLT_MAX
81 
82 float SoGLLineWidthElement::sizerange[2] = { RANGE_NOT_CHECKED, -1.0f};
83 
84 
85 SO_ELEMENT_SOURCE(SoGLLineWidthElement);
86 
87 // doc in super
88 void
initClass(void)89 SoGLLineWidthElement::initClass(void)
90 {
91   SO_ELEMENT_INIT_CLASS(SoGLLineWidthElement, inherited);
92 }
93 
94 /*!
95   Destructor.
96 */
~SoGLLineWidthElement(void)97 SoGLLineWidthElement::~SoGLLineWidthElement(void)
98 {
99 }
100 
101 // doc in super
102 void
init(SoState * stateptr)103 SoGLLineWidthElement::init(SoState * stateptr)
104 {
105   inherited::init(stateptr);
106 }
107 
108 // doc in super
109 void
push(SoState * stateptr)110 SoGLLineWidthElement::push(SoState * stateptr)
111 {
112   SoGLLineWidthElement * prev = (SoGLLineWidthElement*)this->getNextInStack();
113   this->data = prev->data;
114   // capture previous element since we might or might not change the
115   // GL state in set/pop
116   prev->capture(stateptr);
117 }
118 
119 // doc in super
120 void
pop(SoState * COIN_UNUSED_ARG (stateptr),const SoElement * prevTopElement)121 SoGLLineWidthElement::pop(SoState * COIN_UNUSED_ARG(stateptr), const SoElement * prevTopElement)
122 {
123   SoGLLineWidthElement * prev = (SoGLLineWidthElement*)prevTopElement;
124   if (this->data != prev->data) {
125     this->updategl();
126   }
127 }
128 
129 // doc in super
130 void
setElt(float width)131 SoGLLineWidthElement::setElt(float width)
132 {
133   if (width != this->data) {
134     this->data = width;
135     this->updategl();
136   }
137 }
138 
139 // private
140 void
updategl(void)141 SoGLLineWidthElement::updategl(void)
142 {
143   if (SoGLLineWidthElement::sizerange[0] == RANGE_NOT_CHECKED) {
144     GLfloat vals[2];
145     glGetFloatv(GL_LINE_WIDTH_RANGE, vals);
146 
147     // Matthias Koenig reported on coin-discuss that the OpenGL
148     // implementation on SGI Onyx 2 InfiniteReality returns 0 for the
149     // lowest linewidth, but it will still set the return value of
150     // glGetError() to GL_INVALID_VALUE if this size is attempted
151     // used. This is a workaround for what looks like an OpenGL bug.
152 
153     if (vals[0] <= 0.0f) { vals[0] = SbMin(1.0f, vals[1]); }
154 
155     SoGLLineWidthElement::sizerange[0] = vals[0];
156     SoGLLineWidthElement::sizerange[1] = vals[1];
157   }
158 
159   float useval = this->data;
160 
161   // 0.0f is used as a "dummy" default value by our superclass and by
162   // SoDrawStyle::lineWidth, so handle that case outside of the
163   // rangecheck below.
164 
165   if (this->data == 0.0f) { useval = 1.0f; }
166 
167   // Range checks.
168 
169   if (useval < SoGLLineWidthElement::sizerange[0]) {
170     useval = SoGLLineWidthElement::sizerange[0];
171   }
172   if (useval > SoGLLineWidthElement::sizerange[1]) {
173     useval = SoGLLineWidthElement::sizerange[1];
174   }
175 
176 
177   if (COIN_DEBUG) {
178     // Detect invalid values and warn the application programmer.
179     // (0.0f is used as a "dummy" default value by our superclass and
180     // by SoDrawStyle::lineWidth, so ignore that case.)
181     if ((useval != this->data) && (this->data != 0.0f)) {
182       // Only warn once for each case.
183       static SbBool warn_below = TRUE;
184       static SbBool warn_above = TRUE;
185       if ((warn_below && (useval > this->data)) ||
186           (warn_above && (useval < this->data))) {
187         if (useval > this->data) { warn_below = FALSE; }
188         if (useval < this->data) { warn_above = FALSE; }
189         SoDebugError::postWarning("SoGLLineWidthElement::updategl",
190                                   "%f is outside the legal range of [%f, %f] "
191                                   "for this OpenGL implementation's "
192                                   "glLineWidth() settings. It was now clamped.\n\n"
193                                   "See the documentation of SoGLLineWidthElement for "
194                                   "information on how the application programmer should "
195                                   "acquire the boundary values for the legal "
196                                   "range.",
197                                   this->data,
198                                   SoGLLineWidthElement::sizerange[0],
199                                   SoGLLineWidthElement::sizerange[1]);
200       }
201     }
202   }
203 
204   // Forward to OpenGL state.
205 
206   glLineWidth(useval);
207 }
208 
209 #undef RANGE_NOT_CHECKED
210