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