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