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 // Note: the class documentation for the basic primitive shapes
34 // SoSphere, SoCylinder, SoCone and SoCube have many common, or at
35 // least close to common, paragraphs. If you make any changes, check
36 // those other shapes too, to see if your updates / fixes should be
37 // migrated. <mortene@sim.no>.
38 /*!
39 \class SoCylinder SoCylinder.h Inventor/nodes/SoCylinder.h
40 \brief The SoCylinder class is for rendering cylinder shapes.
41
42 \ingroup nodes
43
44 Insert a cylinder shape into the scenegraph. The cylinder is
45 rendered with the current material, texture and drawstyle settings
46 (if any, otherwise the default settings are used).
47
48 The SoCylinder node class is provided as a convenient abstraction
49 for the application programmer to use "complex" shapes of this type
50 without having to do the tessellation to polygons and other low-level
51 programming herself.
52
53 A cylinder is visualized by the underlying rendering system by first
54 tessellating the conceptual cylinder into a set of polygons. To
55 control the trade-off between an as much as possible correct visual
56 appearance of the cylinder versus fast rendering, use an
57 SoComplexity node to influence the number of polygons generated from
58 the tessellation process. (The higher the complexity value, the more
59 polygons will be generated, the more \e rounded the sides of the
60 cylinder will look.) Set the SoComplexity::value field to what you
61 believe would be a good trade-off between correctness and speed for
62 your particular application.
63
64 A nice trick for rendering a disc is to render an SoCylinder with
65 SoCylinder::height set to zero:
66
67 \code
68 #Inventor V2.1 ascii
69
70 ShapeHints { # to get two-sided lighting on the disc
71 vertexOrdering COUNTERCLOCKWISE
72 shapeType UNKNOWN_SHAPE_TYPE
73 }
74
75 Cylinder {
76 height 0
77 parts TOP
78 }
79 \endcode
80
81 <b>FILE FORMAT/DEFAULTS:</b>
82 \code
83 Cylinder {
84 radius 1
85 height 2
86 parts (SIDES | TOP | BOTTOM)
87 }
88 \endcode
89
90 \sa SoCone, SoSphere, SoCube
91 */
92
93 #include <Inventor/nodes/SoCylinder.h>
94 #include "coindefs.h"
95
96 #include <cmath>
97
98 #if COIN_DEBUG
99 #include <Inventor/errors/SoDebugError.h>
100 #endif // COIN_DEBUG
101 #include <Inventor/SbCylinder.h>
102 #include <Inventor/SbPlane.h>
103 #include <Inventor/SoPickedPoint.h>
104 #include <Inventor/actions/SoGLRenderAction.h>
105 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
106 #include <Inventor/actions/SoRayPickAction.h>
107 #include <Inventor/bundles/SoMaterialBundle.h>
108 #include <Inventor/details/SoCylinderDetail.h>
109 #include <Inventor/elements/SoComplexityTypeElement.h>
110 #include <Inventor/elements/SoMaterialBindingElement.h>
111 #include <Inventor/elements/SoGLMultiTextureEnabledElement.h>
112 #include <Inventor/elements/SoMultiTextureCoordinateElement.h>
113 #include <Inventor/misc/SoState.h>
114
115 #include "nodes/SoSubNodeP.h"
116 #include "rendering/SoGL.h"
117 #include "misc/SoGenerate.h"
118 #include "misc/SoPick.h"
119
120 #define CYL_SIDE_NUMTRIS 40.0f
121
122 /*!
123 \enum SoCylinder::Part
124 The parts of a cylinder shape.
125 */
126
127
128 /*!
129 \var SoSFFloat SoCylinder::radius
130 Radius of cylinder. Default value 1.0.
131 */
132 /*!
133 \var SoSFFloat SoCylinder::height
134 Height of cylinder. Default is 2.0.
135 */
136 /*!
137 \var SoSFBitMask SoCylinder::parts
138 Which parts to use for rendering, picking, etc. Defaults to
139 SoCylinder::ALL.
140 */
141
142
143 // *************************************************************************
144
145 SO_NODE_SOURCE(SoCylinder);
146
147 /*!
148 Constructor.
149 */
SoCylinder(void)150 SoCylinder::SoCylinder(void)
151 {
152 SO_NODE_INTERNAL_CONSTRUCTOR(SoCylinder);
153
154 SO_NODE_ADD_FIELD(radius, (1.0f));
155 SO_NODE_ADD_FIELD(height, (2.0f));
156 SO_NODE_ADD_FIELD(parts, (SoCylinder::ALL));
157
158 SO_NODE_DEFINE_ENUM_VALUE(Part, SIDES);
159 SO_NODE_DEFINE_ENUM_VALUE(Part, TOP);
160 SO_NODE_DEFINE_ENUM_VALUE(Part, BOTTOM);
161 SO_NODE_DEFINE_ENUM_VALUE(Part, ALL);
162 SO_NODE_SET_SF_ENUM_TYPE(parts, Part);
163 }
164
165 /*!
166 Destructor.
167 */
~SoCylinder()168 SoCylinder::~SoCylinder()
169 {
170 }
171
172 // Doc in parent.
173 void
initClass(void)174 SoCylinder::initClass(void)
175 {
176 SO_NODE_INTERNAL_INIT_CLASS(SoCylinder, SO_FROM_INVENTOR_1|SoNode::VRML1);
177 }
178
179 // Doc in parent.
180 void
computeBBox(SoAction * COIN_UNUSED_ARG (action),SbBox3f & box,SbVec3f & center)181 SoCylinder::computeBBox(SoAction * COIN_UNUSED_ARG(action), SbBox3f & box, SbVec3f & center)
182 {
183
184 float r = this->radius.getValue();
185 float h = this->height.getValue();
186
187 // Allow negative values.
188 if (r < 0.0f) r = -r;
189 if (h < 0.0f) h = -h;
190
191 // Either the SIDES are present, or we've at least got both the TOP
192 // and BOTTOM caps -- so just find the middle point and enclose
193 // everything.
194 if (this->parts.getValue() & SoCylinder::SIDES ||
195 (this->parts.getValue() & SoCylinder::BOTTOM &&
196 this->parts.getValue() & SoCylinder::TOP)) {
197 center.setValue(0.0f, 0.0f, 0.0f);
198 box.setBounds(SbVec3f(-r, -h/2.0f, -r), SbVec3f(r, h/2.0f, r));
199 }
200 // ..not a "full" cylinder, but we've got the BOTTOM cap.
201 else if (this->parts.getValue() & SoCylinder::BOTTOM) {
202 center.setValue(0.0f, -h/2.0f, 0.0f);
203 box.setBounds(SbVec3f(-r, -h/2.0f, -r), SbVec3f(r, -h/2.0f, r));
204 }
205 // ..not a "full" cylinder, but we've got the TOP cap.
206 else if (this->parts.getValue() & SoCylinder::TOP) {
207 center.setValue(0.0f, h/2.0f, 0.0f);
208 box.setBounds(SbVec3f(-r, h/2.0f, -r), SbVec3f(r, h/2.0f, r));
209 }
210 // ..no parts present. My confidence is shot -- I feel very small.
211 else {
212 center.setValue(0.0f, 0.0f, 0.0f);
213 box.setBounds(SbVec3f(0.0f, 0.0f, 0.0f), SbVec3f(0.0f, 0.0f, 0.0f));
214 }
215 }
216
217 // Doc in parent.
218 void
GLRender(SoGLRenderAction * action)219 SoCylinder::GLRender(SoGLRenderAction * action)
220 {
221 if (!shouldGLRender(action)) return;
222
223 SoState * state = action->getState();
224
225 SoCylinder::Part p = (SoCylinder::Part) this->parts.getValue();
226 SoMaterialBundle mb(action);
227 mb.sendFirst();
228
229 SbBool sendNormals = !mb.isColorOnly() ||
230 (SoMultiTextureCoordinateElement::getType(state) == SoMultiTextureCoordinateElement::FUNCTION);
231
232 unsigned int flags = 0;
233 if (sendNormals)
234 flags |= SOGL_NEED_NORMALS;
235 if (SoGLMultiTextureEnabledElement::get(state, 0)) {
236 if (SoGLMultiTextureEnabledElement::getMode(state, 0) ==
237 SoMultiTextureEnabledElement::TEXTURE3D) {
238 flags |= SOGL_NEED_3DTEXCOORDS;
239 }
240 else {
241 flags |= SOGL_NEED_TEXCOORDS;
242 }
243 }
244 if (p & SIDES) flags |= SOGL_RENDER_SIDE;
245 if (p & TOP) flags |= SOGL_RENDER_TOP;
246 if (p & BOTTOM) flags |= SOGL_RENDER_BOTTOM;
247
248 SoMaterialBindingElement::Binding bind =
249 SoMaterialBindingElement::get(state);
250 if (bind == SoMaterialBindingElement::PER_PART ||
251 bind == SoMaterialBindingElement::PER_PART_INDEXED)
252 flags |= SOGL_MATERIAL_PER_PART;
253
254 float complexity = this->getComplexityValue(action);
255
256 sogl_render_cylinder(this->radius.getValue(),
257 this->height.getValue(),
258 (int)(CYL_SIDE_NUMTRIS * complexity),
259 &mb,
260 flags, state);
261 }
262
263 /*!
264 Add a \a part to the cylinder.
265
266 \sa removePart(), hasPart()
267 */
268 void
addPart(SoCylinder::Part part)269 SoCylinder::addPart(SoCylinder::Part part)
270 {
271 if (this->hasPart(part)) {
272 #if COIN_DEBUG
273 SoDebugError::postWarning("SoCylinder::addPart", "part already set");
274 #endif // COIN_DEBUG
275 return;
276 }
277
278 this->parts.setValue(this->parts.getValue() | part);
279 }
280
281 /*!
282 Remove a \a part from the cylinder.
283
284 \sa addPart(), hasPart()
285 */
286 void
removePart(SoCylinder::Part part)287 SoCylinder::removePart(SoCylinder::Part part)
288 {
289 if (!this->hasPart(part)) {
290 #if COIN_DEBUG
291 SoDebugError::postWarning("SoCylinder::removePart", "part was not set");
292 #endif // COIN_DEBUG
293 return;
294 }
295
296 this->parts.setValue(this->parts.getValue() & ~part);
297 }
298
299 /*!
300 Returns \c TRUE if rendering of the given \a part is currently
301 turned on.
302
303 \sa addPart(), removePart()
304 */
305 SbBool
hasPart(SoCylinder::Part part) const306 SoCylinder::hasPart(SoCylinder::Part part) const
307 {
308 return (this->parts.getValue() & part) ? TRUE : FALSE;
309 }
310
311 // Doc in parent.
312 void
rayPick(SoRayPickAction * action)313 SoCylinder::rayPick(SoRayPickAction * action)
314 {
315 if (!shouldRayPick(action)) return;
316
317
318 unsigned int flags = 0;
319 SoCylinder::Part p = (SoCylinder::Part) this->parts.getValue();
320 if (p & SIDES) flags |= SOPICK_SIDES;
321 if (p & TOP) flags |= SOPICK_TOP;
322 if (p & BOTTOM) flags |= SOPICK_BOTTOM;
323
324 SoMaterialBindingElement::Binding bind =
325 SoMaterialBindingElement::get(action->getState());
326 if (bind == SoMaterialBindingElement::PER_PART ||
327 bind == SoMaterialBindingElement::PER_PART_INDEXED)
328 flags |= SOPICK_MATERIAL_PER_PART;
329
330 sopick_pick_cylinder(this->radius.getValue(),
331 this->height.getValue(),
332 flags,
333 this, action);
334 }
335
336 // Doc in parent.
337 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)338 SoCylinder::getPrimitiveCount(SoGetPrimitiveCountAction * action)
339 {
340 if (!this->shouldPrimitiveCount(action)) return;
341
342 float complexity = this->getComplexityValue(action);
343 int numtris = (int)(complexity * CYL_SIDE_NUMTRIS);
344
345 if (this->parts.getValue() & SoCylinder::BOTTOM) {
346 action->addNumTriangles(numtris-2);
347 }
348 if (this->parts.getValue() & SoCylinder::TOP) {
349 action->addNumTriangles(numtris-2);
350 }
351 if (this->parts.getValue() & SoCylinder::SIDES) {
352 action->addNumTriangles(numtris * 2);
353 }
354 }
355
356 // Doc in parent.
357 void
generatePrimitives(SoAction * action)358 SoCylinder::generatePrimitives(SoAction * action)
359 {
360 SoCylinder::Part p = (SoCylinder::Part) this->parts.getValue();
361 unsigned int flags = 0;
362 if (p & SoCylinder::SIDES) flags |= SOGEN_GENERATE_SIDE;
363 if (p & SoCylinder::BOTTOM) flags |= SOGEN_GENERATE_BOTTOM;
364 if (p & SoCylinder::TOP) flags |= SOGEN_GENERATE_TOP;
365
366 SoMaterialBindingElement::Binding bind =
367 SoMaterialBindingElement::get(action->getState());
368 if (bind == SoMaterialBindingElement::PER_PART ||
369 bind == SoMaterialBindingElement::PER_PART_INDEXED)
370 flags |= SOGL_MATERIAL_PER_PART;
371
372 float complexity = this->getComplexityValue(action);
373
374 sogen_generate_cylinder(this->radius.getValue(),
375 this->height.getValue(),
376 (int)(CYL_SIDE_NUMTRIS * complexity),
377 flags,
378 this,
379 action);
380 }
381
382 #undef CYL_SIDE_NUMTRIS
383