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