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 SoNurbsSurface SoNurbsSurface.h Inventor/nodes/SoNurbsSurface.h
35   \brief The SoNurbsSurface class is used to render smooth surfaces.
36 
37   \ingroup nodes
38 
39   A general explanation of NURBS is beyond the scope of the Coin
40   documentation. For detailed information, refer to the specialized
41   literature on the topic (for example "An Introduction to NURBS: With
42   Historical Perspective" by David F. Rogers). A basic overview of
43   curve and surface rendering using NURBS can be found in chapter 8 of
44   "The Inventor Mentor".
45 
46   Note that knot values should be specified as [0, 1, 2,..., a] rather
47   than [0, 1/a, 2/a,..., 1] to avoid tesselation errors due to
48   floating point precision problems. (Even if the rendered surface
49   <i>looks</i> correct, such issues might surface when e.g. doing
50   picking, since the tesselated representation used internally is not
51   the same as the one you see rendered by OpenGL on-screen.)
52 
53   Each control point has a weight that changes the shape of its basis function.
54   Weight is analogous to having magnets pulling on the curve.
55   Coordinate3 sets control points to have an equal weight of 1.0 (nonrational).
56   Use Coordinate4 to specify x, y, z and weight values (rational).
57 
58   A basic usage example:
59 
60   \code
61   #Inventor V2.1 ascii
62 
63   ShapeHints {
64     vertexOrdering COUNTERCLOCKWISE
65   }
66 
67   Coordinate3 {
68      point [
69        -3 -3 -3, -3 -1 -3, -3  1 -3, -3  3 -3,
70        -1 -3 -3, -1 -1  3, -1  1  3, -1  3 -3,
71         1 -3 -3,  1 -1  3,  1  1  3,  1  3 -3,
72         3 -3 -3,  3 -1 -3,  3  1 -3,  3  3 -3
73       ]
74   }
75 
76   NurbsSurface {
77      numUControlPoints 4
78      numVControlPoints 4
79      uKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]
80      vKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]
81   }
82   \endcode
83 
84   <center>
85   \image html nurbssurface.png "Rendering of Example Scenegraph"
86   </center>
87 
88   <b>FILE FORMAT/DEFAULTS:</b>
89   \code
90     NurbsSurface {
91         numUControlPoints 0
92         numVControlPoints 0
93         numSControlPoints 0
94         numTControlPoints 0
95         uKnotVector 0
96         vKnotVector 0
97         sKnotVector 0
98         tKnotVector 0
99     }
100   \endcode
101 
102 */
103 
104 #include <Inventor/nodes/SoNurbsSurface.h>
105 #include "SoNurbsP.h"
106 
107 #ifdef HAVE_CONFIG_H
108 #include <config.h>
109 #endif // HAVE_CONFIG_H
110 
111 #include <Inventor/bundles/SoMaterialBundle.h>
112 #include <Inventor/elements/SoCoordinateElement.h>
113 #include <Inventor/elements/SoPickStyleElement.h>
114 #include <Inventor/elements/SoDrawStyleElement.h>
115 #include <Inventor/actions/SoGLRenderAction.h>
116 #include <Inventor/actions/SoRayPickAction.h>
117 #include <Inventor/misc/SoState.h>
118 #include <Inventor/nodes/SoCallback.h>
119 #include <Inventor/SoPrimitiveVertex.h>
120 #include <Inventor/errors/SoDebugError.h>
121 #include <Inventor/elements/SoGLCacheContextElement.h>
122 #include <Inventor/elements/SoComplexityTypeElement.h>
123 #include <Inventor/system/gl.h>
124 
125 #include "glue/GLUWrapper.h"
126 #include "nodes/SoSubNodeP.h"
127 #include "rendering/SoGLNurbs.h"
128 #include "rendering/SoGL.h"
129 #include "coindefs.h" // COIN_OBSOLETED()
130 
131 /*!
132   \var SoSFInt32 SoNurbsSurface::numUControlPoints
133   Number of control points in the U direction.
134 */
135 /*!
136   \var SoSFInt32 SoNurbsSurface::numVControlPoints
137   Number of control points in the V direction.
138 */
139 /*!
140   \var SoSFInt32 SoNurbsSurface::numSControlPoints
141   Number of control points in the S direction.
142 */
143 /*!
144   \var SoSFInt32 SoNurbsSurface::numTControlPoints
145   Number of control points in the T direction.
146 */
147 /*!
148   \var SoMFFloat SoNurbsSurface::uKnotVector
149   The Bezier knot vector for the U direction.
150 */
151 /*!
152   \var SoMFFloat SoNurbsSurface::vKnotVector
153   The Bezier knot vector for the V direction.
154 */
155 /*!
156   \var SoMFFloat SoNurbsSurface::sKnotVector
157   The Bezier knot vector for the S direction.
158 */
159 /*!
160   \var SoMFFloat SoNurbsSurface::tKnotVector
161   The Bezier knot vector for the T direction.
162 */
163 
164 // *************************************************************************
165 
166 class SoNurbsSurfaceP {
167 public:
SoNurbsSurfaceP(SoNurbsSurface * m)168   SoNurbsSurfaceP(SoNurbsSurface * m)
169   {
170     this->owner = m;
171     this->nurbsrenderer = NULL;
172     this->offscreenctx = NULL;
173   }
174 
~SoNurbsSurfaceP()175   ~SoNurbsSurfaceP()
176   {
177     if (this->nurbsrenderer) {
178       GLUWrapper()->gluDeleteNurbsRenderer(this->nurbsrenderer);
179     }
180     if (this->offscreenctx) { cc_glglue_context_destruct(this->offscreenctx); }
181   }
182 
183   void * offscreenctx;
184   void * nurbsrenderer;
185 
186   void doNurbs(SoAction * action, const SbBool glrender);
187 
188 private:
189   SoNurbsSurface * owner;
190 };
191 
192 #define PRIVATE(p) (p->pimpl)
193 #define PUBLIC(p) (p->owner)
194 
195 // *************************************************************************
196 
197 SO_NODE_SOURCE(SoNurbsSurface);
198 
199 /*!
200   Constructor.
201 */
SoNurbsSurface()202 SoNurbsSurface::SoNurbsSurface()
203 {
204   SO_NODE_INTERNAL_CONSTRUCTOR(SoNurbsSurface);
205 
206   SO_NODE_ADD_FIELD(numUControlPoints, (0));
207   SO_NODE_ADD_FIELD(numVControlPoints, (0));
208   SO_NODE_ADD_FIELD(numSControlPoints, (0));
209   SO_NODE_ADD_FIELD(numTControlPoints, (0));
210   SO_NODE_ADD_FIELD(uKnotVector, (0));
211   SO_NODE_ADD_FIELD(vKnotVector, (0));
212   SO_NODE_ADD_FIELD(sKnotVector, (0));
213   SO_NODE_ADD_FIELD(tKnotVector, (0));
214 
215   PRIVATE(this) = new SoNurbsSurfaceP(this);
216 }
217 
218 /*!
219   Destructor.
220 */
~SoNurbsSurface()221 SoNurbsSurface::~SoNurbsSurface()
222 {
223   delete PRIVATE(this);
224 }
225 
226 // Documented in superclass.
227 void
initClass(void)228 SoNurbsSurface::initClass(void)
229 {
230   SO_NODE_INTERNAL_INIT_CLASS(SoNurbsSurface, SO_FROM_INVENTOR_1);
231 }
232 
233 // Documented in superclass.
234 void
GLRender(SoGLRenderAction * action)235 SoNurbsSurface::GLRender(SoGLRenderAction * action)
236 {
237   if (!this->shouldGLRender(action)) return;
238 
239   // initialize current material
240   SoMaterialBundle mb(action);
241   mb.sendFirst();
242 
243   SbBool calcnormals = sogl_calculate_nurbs_normals();
244 
245   if (!calcnormals) {
246     glEnable(GL_AUTO_NORMAL);
247   }
248   PRIVATE(this)->doNurbs(action, TRUE);
249   if (!calcnormals) {
250     glDisable(GL_AUTO_NORMAL);
251   }
252 
253   SoState * state = action->getState();
254   if (SoComplexityTypeElement::get(state) == SoComplexityTypeElement::OBJECT_SPACE) {
255     SoGLCacheContextElement::shouldAutoCache(state,
256                                              SoGLCacheContextElement::DO_AUTO_CACHE);
257   }
258 }
259 
260 /*!
261   Calculates the bounding box of all control points, and sets the center to
262   the average of these points.
263 */
264 void
computeBBox(SoAction * action,SbBox3f & box,SbVec3f & center)265 SoNurbsSurface::computeBBox(SoAction * action, SbBox3f & box, SbVec3f & center)
266 {
267   SoState * state = action->getState();
268   const SoCoordinateElement * coordelem =
269     SoCoordinateElement::getInstance(state);
270 
271   int num =
272     this->numUControlPoints.getValue() * this->numVControlPoints.getValue();
273   assert(num <= coordelem->getNum());
274 
275   SbVec3f acccenter(0.0f, 0.0f, 0.0f);
276   box.makeEmpty();
277 
278   if (coordelem->is3D()) {
279     const SbVec3f * coords = coordelem->getArrayPtr3();
280     assert(coords);
281     for (int i = 0; i < num; i++) {
282       box.extendBy(coords[i]);
283       acccenter += coords[i];
284     }
285   }
286   else {
287     const SbVec4f * coords = coordelem->getArrayPtr4();
288     assert(coords);
289     for (int i = 0; i< num; i++) {
290       SbVec4f tmp = coords[i];
291       if (tmp[3] != 0.0f) {
292         float mul = 1.0f / tmp[3];
293         tmp[0] *= mul;
294         tmp[1] *= mul;
295         tmp[2] *= mul;
296       }
297       acccenter += SbVec3f(tmp[0], tmp[1], tmp[2]);
298       box.extendBy(SbVec3f(tmp[0], tmp[1], tmp[2]));
299     }
300   }
301   if (num > 0) center = acccenter / float(num);
302 }
303 
304 // Documented in superclass.
305 void
rayPick(SoRayPickAction * action)306 SoNurbsSurface::rayPick(SoRayPickAction * action)
307 {
308   if (!this->shouldRayPick(action)) return;
309 
310   if (GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) {
311     SoShape::rayPick(action); // do normal generatePrimitives() pick
312   }
313   else {
314     static SbBool firstpick = TRUE;
315     if (firstpick) {
316       firstpick = FALSE;
317       SoDebugError::postWarning("SoNurbsSurface::rayPick",
318                                 "Proper NURBS picking requires\n"
319                                 "GLU version 1.3. Picking is done on bounding box.");
320     }
321     SoState * state = action->getState();
322     state->push();
323     SoPickStyleElement::set(state, this, SoPickStyleElement::BOUNDING_BOX);
324     (void)this->shouldRayPick(action); // this will cause a pick on bbox
325     state->pop();
326   }
327 }
328 
329 // Doc in superclass.
330 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)331 SoNurbsSurface::getPrimitiveCount(SoGetPrimitiveCountAction * action)
332 {
333   // for now, just generate primitives to count. Very slow, of course.
334   SoShape::getPrimitiveCount(action);
335 }
336 
337 /*!
338   This method is part of the original SGI Inventor API, but not
339   implemented in Coin, as it looks like a method that should probably
340   have been private in Open Inventor.
341 */
342 void
sendPrimitive(SoAction *,SoPrimitiveVertex *)343 SoNurbsSurface::sendPrimitive(SoAction *,  SoPrimitiveVertex *)
344 {
345   COIN_OBSOLETED();
346 }
347 
348 // Documented in superclass.
349 void
generatePrimitives(SoAction * action)350 SoNurbsSurface::generatePrimitives(SoAction * action)
351 {
352   if (GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) {
353 
354     // We've found that the SGI GLU NURBS renderer makes some OpenGL
355     // calls even when in tessellate mode. So we need to set up an
356     // offscreen context to be guaranteed to have a valid GL context
357     // before making the GLU calls.
358 
359     if (PRIVATE(this)->offscreenctx == NULL) {
360       PRIVATE(this)->offscreenctx = cc_glglue_context_create_offscreen(32, 32);
361     }
362 
363     if (PRIVATE(this)->offscreenctx &&
364         cc_glglue_context_make_current(PRIVATE(this)->offscreenctx)) {
365       PRIVATE(this)->doNurbs(action, FALSE);
366       cc_glglue_context_reinstate_previous(PRIVATE(this)->offscreenctx);
367     }
368   }
369 }
370 
371 // Documented in superclass.
372 SoDetail *
createTriangleDetail(SoRayPickAction *,const SoPrimitiveVertex *,const SoPrimitiveVertex *,const SoPrimitiveVertex *,SoPickedPoint *)373 SoNurbsSurface::createTriangleDetail(SoRayPickAction * /* action */,
374                                      const SoPrimitiveVertex * /*v1*/,
375                                      const SoPrimitiveVertex * /*v2*/,
376                                      const SoPrimitiveVertex * /*v3*/,
377                                      SoPickedPoint * /* pp */)
378 {
379   return NULL;
380 }
381 
382 typedef SoNurbsP<SoNurbsSurface>::coin_nurbs_cbdata coin_ns_cbdata;
383 
384 //
385 // render or generate the NURBS surface
386 //
387 void
doNurbs(SoAction * action,const SbBool glrender)388 SoNurbsSurfaceP::doNurbs(SoAction * action, const SbBool glrender)
389 {
390   if (GLUWrapper()->available == 0 || !GLUWrapper()->gluNewNurbsRenderer) {
391 #if COIN_DEBUG
392     static int first = 1;
393     if (first) {
394       SoDebugError::postInfo("SoIndexedNurbsCurveP::doNurbs",
395                              "Looks like your GLU library doesn't have NURBS "
396                              "functionality");
397       first = 0;
398     }
399 #endif // COIN_DEBUG
400     return;
401   }
402 
403   if (this->nurbsrenderer == NULL) {
404     this->nurbsrenderer = GLUWrapper()->gluNewNurbsRenderer();
405 
406     if (GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) {
407       GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_BEGIN_DATA, (gluNurbsCallback_cb_t)SoNurbsP<SoNurbsSurface>::tessBegin);
408       GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_TEXTURE_COORD_DATA, (gluNurbsCallback_cb_t)SoNurbsP<SoNurbsSurface>::tessTexCoord);
409       GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_NORMAL_DATA,  (gluNurbsCallback_cb_t)SoNurbsP<SoNurbsSurface>::tessNormal);
410       GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_VERTEX_DATA,  (gluNurbsCallback_cb_t)SoNurbsP<SoNurbsSurface>::tessVertex);
411       GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_END_DATA,  (gluNurbsCallback_cb_t)SoNurbsP<SoNurbsSurface>::tessEnd);
412     }
413   }
414 
415   // NB, don't move this structure inside the if-statement. It needs
416   // to be here so that the callbacks from sogl_render_nurbs_surface()
417   // have a valid pointer to the structure.
418   coin_ns_cbdata cbdata(action, PUBLIC(this),
419                         !SoCoordinateElement::getInstance(action->getState())->is3D());
420 
421   if (GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) {
422     if (!glrender) {
423       GLUWrapper()->gluNurbsCallbackData(this->nurbsrenderer, &cbdata);
424       cbdata.vertex.setNormal(SbVec3f(0.0f, 0.0f, 1.0f));
425       cbdata.vertex.setMaterialIndex(0);
426       cbdata.vertex.setTextureCoords(SbVec4f(0.0f, 0.0f, 0.0f, 1.0f));
427       cbdata.vertex.setPoint(SbVec3f(0.0f, 0.0f, 0.0f));
428       cbdata.vertex.setDetail(NULL);
429     }
430   }
431 
432   int displaymode = (int) GLU_FILL;
433   if (glrender) {
434     switch (SoDrawStyleElement::get(action->getState())) {
435     case SoDrawStyleElement::LINES:
436       displaymode = (int) GLU_OUTLINE_POLYGON;
437       break;
438     case SoDrawStyleElement::POINTS:
439       // not possible to draw NURBS as points using GLU...
440       displaymode = (int) GLU_OUTLINE_PATCH;
441       break;
442     default:
443       break;
444     }
445   }
446   GLUWrapper()->gluNurbsProperty(this->nurbsrenderer, (GLenum) GLU_DISPLAY_MODE, (GLfloat) displaymode);
447 
448   sogl_render_nurbs_surface(action, PUBLIC(this), this->nurbsrenderer,
449                             PUBLIC(this)->numUControlPoints.getValue(),
450                             PUBLIC(this)->numVControlPoints.getValue(),
451                             PUBLIC(this)->uKnotVector.getValues(0),
452                             PUBLIC(this)->vKnotVector.getValues(0),
453                             PUBLIC(this)->uKnotVector.getNum(),
454                             PUBLIC(this)->vKnotVector.getNum(),
455                             PUBLIC(this)->numSControlPoints.getValue(),
456                             PUBLIC(this)->numTControlPoints.getValue(),
457                             PUBLIC(this)->sKnotVector.getValues(0),
458                             PUBLIC(this)->tKnotVector.getValues(0),
459                             PUBLIC(this)->sKnotVector.getNum(),
460                             PUBLIC(this)->tKnotVector.getNum(),
461                             glrender);
462 }
463 
464 
465 #undef PRIVATE
466 #undef PUBLIC
467