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 SoNurbsProfile SoNurbsProfile.h Inventor/nodes/SoNurbsProfile.h
35 \brief The SoNurbsProfile class is a node for specifying smooth profile curves.
36
37 \ingroup nodes
38
39 Use nodes of this type if you want to set up profiles that are
40 smooth curves.
41
42 Use ProfileCoordinate2 for nonrational profile where weight is 1.0 (default),
43 and ProfileCoordinate3 for rational profile curves to specify the weight.
44 Weight is analogous to having magnets pulling on the curve.
45
46 A typical usage case for SoNurbsProfile is to specify NURBS trimming
47 curves. For example:
48
49 \code
50 #Inventor V2.1 ascii
51
52 ShapeHints {
53 vertexOrdering COUNTERCLOCKWISE
54 }
55
56 Coordinate3 {
57 point [
58 -3 -3 -3, -3 -1 -3, -3 1 -3, -3 3 -3,
59 -1 -3 -3, -1 -1 3, -1 1 3, -1 3 -3,
60 1 -3 -3, 1 -1 3, 1 1 3, 1 3 -3,
61 3 -3 -3, 3 -1 -3, 3 1 -3, 3 3 -3
62 ]
63 }
64
65 ProfileCoordinate2 {
66 point [ 0.0 0.0 ,
67 0.75 0.0,
68 0.75 0.75 ,
69 0.25 0.75 ,
70 0.0 0.0 ]
71 }
72
73 NurbsProfile {
74 index [ 0 , 1 , 2 , 3, 4 ]
75 linkage START_NEW
76 knotVector [ 0, 0, 0, 0, 0.5, 1, 1, 1, 1 ]
77 }
78
79 NurbsSurface {
80 numUControlPoints 4
81 numVControlPoints 4
82 uKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]
83 vKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]
84 }
85 \endcode
86
87 <center>
88 \image html nurbsprofile.png "Rendering of Example Scenegraph"
89 </center>
90
91 Note that the coordinates of the NurbsProfile live in the parametric
92 space of the trimmed SoNurbsSurface, and that the same complexity
93 setting (which is calculated based on the dimensions of the bounding
94 box of the nurbs surface) is used to determine the sampling
95 tolerance both for the SoNurbsSurface and the SoNurbsProfile.
96
97 This means that if you want to change the tessellation of the
98 trimming curve <i>itself</i> (i.e. increase or decrease the
99 resolution of the boundaries of the "cut-out"), you should not
100 change the SoComplexity setting but rather adapt the parametric
101 scale in relation to the trimmed surface.
102
103 As an example, to increase the resolution of the curve in the above
104 example, replace...
105
106 \code
107 ProfileCoordinate2 {
108 point [ 0.0 0.0, 0.75 0.0, 0.75 0.75, 0.25 0.75, 0.0 0.0 ]
109 }
110 \endcode
111
112 .. with...
113
114 \code
115 ProfileCoordinate2 {
116 point [ 0.0 0.0, 7.5 0.0, 7.5 7.5, 2.5 7.5, 0.0 0.0 ]
117 }
118 \endcode
119
120 and change the uKnotVector and vKnotVector of the NurbsSurface to be
121
122 \code
123 uKnotVector [ 0.0, 0.0, 0.0, 0.0, 10, 10, 10, 10 ]
124 vKnotVector [ 0.0, 0.0, 0.0, 0.0, 10, 10, 10, 10 ]
125 \endcode
126
127 However, keep in mind that increasing the accuracy of the trimming
128 curve results in a much more complex tesselation of the trimmed
129 surface. As a general rule of thumb, the extent of the trimming
130 curve coordinates should never be greater than its "real" extents in
131 relation to the trimmed surface, and often can be much lower.
132
133 If you find the above confusing, you probably do not want to use
134 NURBS without reading up on the general concepts first. An
135 explanation of NURBS is beyond the scope of the Coin documentation;
136 for detailed information, refer to the specialized literature on the
137 topic (for example "An Introduction to NURBS: With Historical
138 Perspective" by David F. Rogers). A basic overview of curve and
139 surface rendering using NURBS can also be found in chapter 8 of "The
140 Inventor Mentor".
141
142 <b>FILE FORMAT/DEFAULTS:</b>
143 \code
144 NurbsProfile {
145 index 0
146 linkage START_FIRST
147 knotVector 0
148 }
149 \endcode
150 */
151
152 // *************************************************************************
153
154 #include <Inventor/nodes/SoNurbsProfile.h>
155
156 #include <cstdlib>
157
158 #ifdef HAVE_CONFIG_H
159 #include "config.h"
160 #endif // HAVE_CONFIG_H
161
162 #include <Inventor/SbMatrix.h>
163 #include <Inventor/SbViewVolume.h>
164 #include <Inventor/elements/SoComplexityElement.h>
165 #include <Inventor/elements/SoProfileCoordinateElement.h>
166 #include <Inventor/lists/SbList.h>
167 #include <Inventor/errors/SoDebugError.h>
168 #include <Inventor/system/gl.h>
169 #include <Inventor/threads/SbStorage.h>
170
171 #include "nodes/SoSubNodeP.h"
172 #include "glue/GLUWrapper.h"
173 #include "tidbitsp.h"
174
175 // *************************************************************************
176
177 /*!
178 \var SoMFFloat SoNurbsProfile::knotVector
179 Knot values for the nurbs curve.
180 */
181
182 // *************************************************************************
183
184 typedef struct {
185 SbList <float> * coordlist;
186 SbList <float> * tmplist;
187 } so_nurbsprofile_data;
188
189 static void
so_nurbsprofile_construct_data(void * closure)190 so_nurbsprofile_construct_data(void * closure)
191 {
192 so_nurbsprofile_data * data = (so_nurbsprofile_data*) closure;
193 data->coordlist = NULL;
194 data->tmplist = NULL;
195 }
196
197 static void
so_nurbsprofile_destruct_data(void * closure)198 so_nurbsprofile_destruct_data(void * closure)
199 {
200 so_nurbsprofile_data * data = (so_nurbsprofile_data*) closure;
201 delete data->coordlist;
202 delete data->tmplist;
203 }
204
205 static SbStorage * so_nurbsprofile_storage;
206
207 static void
so_nurbsprofile_cleanup(void)208 so_nurbsprofile_cleanup(void)
209 {
210 delete so_nurbsprofile_storage;
211 }
212
213 static SbList <float> *
so_nurbsprofile_get_coordlist(const SbBool tmplist)214 so_nurbsprofile_get_coordlist(const SbBool tmplist)
215 {
216 so_nurbsprofile_data * data = NULL;
217 data = (so_nurbsprofile_data*) so_nurbsprofile_storage->get();
218
219 if (tmplist) {
220 if (data->tmplist == NULL) {
221 data->tmplist = new SbList<float>;
222 }
223 return data->tmplist;
224 }
225 else {
226 if (data->coordlist == NULL) {
227 data->coordlist = new SbList<float>;
228 }
229 return data->coordlist;
230 }
231 }
232
233 // *************************************************************************
234
235 SO_NODE_SOURCE(SoNurbsProfile);
236
237 /*!
238 Constructor.
239 */
SoNurbsProfile(void)240 SoNurbsProfile::SoNurbsProfile(void)
241 {
242 SO_NODE_INTERNAL_CONSTRUCTOR(SoNurbsProfile);
243
244 SO_NODE_ADD_FIELD(knotVector, (0.0f));
245 this->nurbsrenderer = NULL;
246 }
247
248 /*!
249 Destructor.
250 */
~SoNurbsProfile()251 SoNurbsProfile::~SoNurbsProfile()
252 {
253 if (this->nurbsrenderer) {
254 GLUWrapper()->gluDeleteNurbsRenderer(this->nurbsrenderer);
255 }
256 }
257
258 // Doc from superclass.
259 void
initClass(void)260 SoNurbsProfile::initClass(void)
261 {
262 SO_NODE_INTERNAL_INIT_CLASS(SoNurbsProfile, SO_FROM_INVENTOR_1);
263 so_nurbsprofile_storage = new SbStorage(sizeof(so_nurbsprofile_data),
264 so_nurbsprofile_construct_data,
265 so_nurbsprofile_destruct_data);
266 coin_atexit((coin_atexit_f*) so_nurbsprofile_cleanup, CC_ATEXIT_NORMAL);
267 }
268
269 // Doc from superclass.
270 void
getTrimCurve(SoState * state,int32_t & numpoints,float * & points,int & floatspervec,int32_t & numknots,float * & knotvector)271 SoNurbsProfile::getTrimCurve(SoState * state, int32_t & numpoints,
272 float *& points, int & floatspervec,
273 int32_t & numknots, float *& knotvector)
274 {
275 SbList <float> * coordListNurbsProfile =
276 so_nurbsprofile_get_coordlist(FALSE);
277
278 numknots = this->knotVector.getNum();
279 if (numknots) knotvector = (float *)(this->knotVector.getValues(0));
280
281 const SoProfileCoordinateElement * elem = (const SoProfileCoordinateElement*)
282 SoProfileCoordinateElement::getInstance(state);
283
284 coordListNurbsProfile->truncate(0);
285
286 // Get the number of SoProfileCoordinate2/3 points
287 int32_t numcoords = elem->getNum();
288 // Get the number of profile coordinate indices
289 int n = this->index.getNum();
290
291 if (numcoords) {
292 // Both 2D or 3D profile coordinates might have been specified, so
293 // get the appropriate coordinates and save the number of floats
294 // per vector for later usage.
295 if (elem->is2D()) {
296 points = (float*) elem->getArrayPtr2();
297 floatspervec = 2;
298 }
299 else {
300 points = (float*) elem->getArrayPtr3();
301 floatspervec = 3;
302 }
303
304 assert(points);
305 }
306
307 // Append the coordinates to a list over the profile coordinates.
308 for (int i = 0; i < n; i++) {
309 int idx = this->index[i];
310
311 // If valid profile coordinates have been specified
312 if (idx >= 0 && idx < numcoords) {
313 for (int j = 0; j < floatspervec; j++) {
314 coordListNurbsProfile->append(points[(idx * floatspervec) + j]);
315 }
316 }
317 // If invalid profile coordinates have been specified
318 else {
319 // Add dummy coordinate for robustness
320 for (int j = 0; j < floatspervec; j++) {
321 coordListNurbsProfile->append(0.0f);
322 }
323
324 // Print errormessage
325 static uint32_t current_errors = 0;
326 if (current_errors < 1) {
327 SoDebugError::postWarning("SoNurbsProfile::getTrimCurve", "Illegal profile "
328 "coordinate index specified: %d. Should be within "
329 "[0, %d]", idx, numcoords - 1);
330 }
331 current_errors++;
332 }
333 }
334
335 points = (float*) coordListNurbsProfile->getArrayPtr();
336 numpoints = n;
337 }
338
339
340 static void APIENTRY
nurbsprofile_tess_vertex(float * vertex)341 nurbsprofile_tess_vertex(float * vertex)
342 {
343 SbList <float> * coordListNurbsProfile =
344 so_nurbsprofile_get_coordlist(FALSE);
345
346 coordListNurbsProfile->append(vertex[0]);
347 coordListNurbsProfile->append(vertex[1]);
348 }
349
350 // doc from superclass.
351 void
getVertices(SoState * state,int32_t & numvertices,SbVec2f * & vertices)352 SoNurbsProfile::getVertices(SoState * state, int32_t & numvertices,
353 SbVec2f * & vertices)
354 {
355 // FIXME: optimize by detecting when the previously calculated
356 // vertices can be returned. pederb, 20000922
357 int32_t numpoints;
358 float * points;
359 int floatspervec;
360 int32_t numknots;
361 float * knotvector;
362 this->getTrimCurve(state, numpoints, points, floatspervec, numknots, knotvector);
363 if (numpoints == 0 || numknots == 0) {
364 numvertices = 0;
365 vertices = NULL;
366 return;
367 }
368
369 SbList <float> * coordListNurbsProfile =
370 so_nurbsprofile_get_coordlist(FALSE);
371
372 SbList <float> * nurbsProfileTempList =
373 so_nurbsprofile_get_coordlist(TRUE);
374
375 nurbsProfileTempList->truncate(0);
376 for (int i = 0; i < numpoints; i++) {
377 nurbsProfileTempList->append(points[i*floatspervec]);
378 nurbsProfileTempList->append(points[i*floatspervec+1]);
379 if (GLUWrapper()->available &&
380 GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) {
381 nurbsProfileTempList->append(0.0f); // gluNurbs needs 3D coordinates
382 }
383 }
384 if (GLUWrapper()->available &&
385 GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) {
386 // we will write into this array in the GLU callback
387 coordListNurbsProfile->truncate(0);
388
389 if (this->nurbsrenderer == NULL) {
390 this->nurbsrenderer = GLUWrapper()->gluNewNurbsRenderer();
391 GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_VERTEX,
392 (gluNurbsCallback_cb_t)nurbsprofile_tess_vertex);
393 GLUWrapper()->gluNurbsProperty(this->nurbsrenderer, (GLenum) GLU_NURBS_MODE, GLU_NURBS_TESSELLATOR);
394 GLUWrapper()->gluNurbsProperty(this->nurbsrenderer, (GLenum) GLU_AUTO_LOAD_MATRIX, FALSE);
395 GLUWrapper()->gluNurbsProperty(this->nurbsrenderer, (GLenum) GLU_DISPLAY_MODE, GLU_POINT);
396 GLUWrapper()->gluNurbsProperty(this->nurbsrenderer, (GLenum) GLU_SAMPLING_METHOD, GLU_DOMAIN_DISTANCE);
397 }
398
399 // this looks pretty good
400 float cmplx = SoComplexityElement::get(state);
401 cmplx += 1.0f;
402 cmplx = cmplx * cmplx * cmplx;
403 GLUWrapper()->gluNurbsProperty(this->nurbsrenderer, (GLenum) GLU_U_STEP, float(numpoints)*cmplx);
404
405 // these values are not important as we're not using screen-space
406 // complexity (yet)
407 SbMatrix modelmatrix = SbMatrix::identity();
408 SbMatrix affine, proj;
409 SbViewVolume vv;
410 vv.ortho(0.0f, 1.0f,
411 0.0f, 1.0f,
412 -1.0f, 1.0f);
413 vv.getMatrices(affine, proj);
414 GLint viewport[4];
415 viewport[0] = 0;
416 viewport[1] = 0;
417 viewport[2] = 256;
418 viewport[3] = 256;
419 GLUWrapper()->gluLoadSamplingMatrices(this->nurbsrenderer,
420 modelmatrix[0],
421 proj[0],
422 viewport);
423
424 // generate curve
425 GLUWrapper()->gluBeginCurve(this->nurbsrenderer);
426 GLUWrapper()->gluNurbsCurve(this->nurbsrenderer,
427 numknots,
428 (float*)knotvector,
429 3,
430 (float*)nurbsProfileTempList->getArrayPtr(),
431 numknots - numpoints,
432 GL_MAP1_VERTEX_3);
433 GLUWrapper()->gluEndCurve(this->nurbsrenderer);
434
435 // when we get here, the GLU callback should have added the
436 // points to the list
437 numvertices = coordListNurbsProfile->getLength() / 2;
438 vertices = (SbVec2f*) coordListNurbsProfile->getArrayPtr();
439 }
440 else {
441 // just send the control points when GLU v1.3 is not available
442 numvertices = numpoints;
443 vertices = (SbVec2f*) nurbsProfileTempList->getArrayPtr();
444 }
445 }
446