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 SoQuadMesh SoQuadMesh.h Inventor/nodes/SoQuadMesh.h
35 \brief The SoQuadMesh class is used to render and optimize a quadrilateral mesh.
36
37 \ingroup nodes
38
39 This node uses the coordinates in order, either from the state or
40 from the SoQuadMesh::vertexProperty node, to construct a
41 quadrilateral mesh.
42
43 The quads are generated in row major order, using the two fields
44 SoQuadMesh::verticesPerColumn and SoQuadMesh::verticesPerRow to
45 specify the mesh. E.g. if SoQuadMesh::verticesPerColumn is 3 and
46 SoQuadMesh::verticesPerRow is 2, two quads will be generated with
47 the first one using (in order) coordinates 0, 1, 3 and 2, the second
48 one using coordinates 2, 3, 5 and 4 (you get three rows of vertices;
49 the first row uses vertices 0 and 1, the second row 2 and 3, and the
50 third row 4 and 5).
51
52 Here's a quick and simple usage example code snippet:
53
54 \code
55 // Vertices for the Quad mesh.
56 static float vertices[25][3] = {
57 // Row 1
58 {-11, 0, 1}, {0, 11, 1}, {11, 0, 1}, {0, -11, 1}, {-11, 0, 1},
59 // Row 2
60 {-9, 0, 1}, {0, 9, 1}, {9, 0, 1}, {0, -9, 1}, {-9, 0, 1},
61 // Row 3
62 {-9, 0, -1}, {0, 9, -1}, {9, 0, -1}, {0, -9, -1}, {-9, 0, -1},
63 // Row 4
64 {-11, 0, -1}, {0, 11, -1}, {11, 0, -1}, {0, -11, -1}, {-11, 0, -1},
65 // Row 5
66 {-11, 0, 1}, {0, 11, 1}, {11, 0, 1}, {0, -11, 1}, {-11, 0, 1}
67 };
68
69 // This function generate an object by using the SoQuadMesh node
70 // Return:
71 // SoSeparator *
72 static SoSeparator *
73 quadMesh(void)
74 {
75 SoSeparator * qm = new SoSeparator;
76
77 // Define coordinates
78 SoCoordinate3 * coords = new SoCoordinate3;
79 coords->point.setValues(0, 30, vertices);
80 qm->addChild(coords);
81
82 // QuadMesh
83 SoQuadMesh * mesh = new SoQuadMesh;
84 mesh->verticesPerRow = 5;
85 mesh->verticesPerColumn = 5;
86 qm->addChild(mesh);
87
88 return qm;
89 }
90 \endcode
91
92 The quadmesh geometry resulting from this code looks like this:<br>
93
94 <center>
95 \image html quadmesh.png "Rendering of Example Scenegraph"
96 </center>
97
98 Here is another example, this time making a 2x2 grid, with a
99 texture:
100
101 \verbatim
102 #Inventor V2.1 ascii
103
104 Separator {
105 Complexity { textureQuality 0.01 }
106 Texture2 {
107 image 2 2 4 0xff0000ff 0x00ff00ff 0xffff00ff 0xff00ffff
108 }
109 Coordinate3 {
110 point [
111 0 2 0,
112 1 2 0,
113 2 2 0,
114 0 1 0,
115 1 1 0,
116 2 1 0,
117 0 0 0,
118 1 0 0,
119 2 0 0
120 ]
121 }
122 QuadMesh {
123 verticesPerRow 3
124 verticesPerColumn 3
125 }
126 }
127 \endverbatim
128
129
130 For SoQuadMesh, normals and materials can be bound PER_PART (per
131 row), PER_FACE, PER_VERTEX and OVERALL. The default material binding
132 is OVERALL. The default normal binding is PER_VERTEX.
133
134
135 A note about SoQuadMesh shading: the quads in the mesh are just
136 passed on to OpenGL's GL_QUAD primitive rendering. Under certain
137 circumstances, this can lead to artifacts in how your meshes are
138 shaded. This is an inherent problem with drawing quads in meshes.
139
140 There is a work-around solution for the above mentioned problem that
141 can be applied with Coin: by setting the global environment variable
142 \c COIN_QUADMESH_PRECISE_LIGHTING to "1", the quads will be broken
143 up in triangles before rendered, and shading will likely look much
144 better. Be aware that this technique causes rendering of the
145 SoQuadMesh to slow down by an approximate factor of 6.
146
147 The "precise lighting" technique is currently limited to work only
148 when SoQuadMesh rendering is parameterized with 3D coordinates, a
149 materialbinding that is \e not per vertex, and if texture mapping is
150 done is must be without using any of the SoTextureCoordinateFunction
151 subclass nodes.
152
153 <b>FILE FORMAT/DEFAULTS:</b>
154 \code
155 QuadMesh {
156 vertexProperty NULL
157 startIndex 0
158 verticesPerColumn 1
159 verticesPerRow 1
160 }
161 \endcode
162
163 \sa SoTriangleStripSet SoIndexedTriangleStripSet
164 */
165
166 #include <Inventor/nodes/SoQuadMesh.h>
167
168 #include <cmath> // ilogb
169 #include <cfloat> // _logb
170
171 #ifdef HAVE_CONFIG_H
172 #include <config.h>
173 #endif // HAVE_CONFIG_H
174
175 #include <Inventor/C/tidbits.h>
176 #include <Inventor/SbPlane.h>
177 #include <Inventor/SoPrimitiveVertex.h>
178 #include <Inventor/actions/SoGLRenderAction.h>
179 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
180 #include <Inventor/bundles/SoMaterialBundle.h>
181 #include <Inventor/bundles/SoTextureCoordinateBundle.h>
182 #include <Inventor/caches/SoNormalCache.h>
183 #include <Inventor/details/SoFaceDetail.h>
184 #include <Inventor/elements/SoGLCoordinateElement.h>
185 #include <Inventor/elements/SoGLLazyElement.h>
186 #include <Inventor/elements/SoMaterialBindingElement.h>
187 #include <Inventor/elements/SoNormalBindingElement.h>
188 #include <Inventor/elements/SoShapeHintsElement.h>
189 #include <Inventor/errors/SoDebugError.h>
190 #include <Inventor/misc/SoState.h>
191 #include <Inventor/system/gl.h>
192
193 #include "rendering/SoGL.h"
194 #include "nodes/SoSubNodeP.h"
195
196 /*!
197 \var SoSFInt32 SoQuadMesh::verticesPerColumn
198 Specifies to number of vertices in each column.
199 */
200 /*!
201 \var SoSFInt32 SoQuadMesh::verticesPerRow
202 Specifies the number of vertices in each row.
203 */
204
205 SO_NODE_SOURCE(SoQuadMesh);
206
207 /*!
208 Constructor.
209 */
SoQuadMesh()210 SoQuadMesh::SoQuadMesh()
211 {
212 SO_NODE_INTERNAL_CONSTRUCTOR(SoQuadMesh);
213
214 SO_NODE_ADD_FIELD(verticesPerColumn, (1));
215 SO_NODE_ADD_FIELD(verticesPerRow, (1));
216 }
217
218 /*!
219 Destructor.
220 */
~SoQuadMesh()221 SoQuadMesh::~SoQuadMesh()
222 {
223 }
224
225 // Documented in superclass.
226 void
computeBBox(SoAction * action,SbBox3f & box,SbVec3f & center)227 SoQuadMesh::computeBBox(SoAction * action, SbBox3f & box, SbVec3f & center)
228 {
229 inherited::computeCoordBBox(action,
230 this->verticesPerRow.getValue() *
231 this->verticesPerColumn.getValue(),
232 box, center);
233 }
234
235 //
236 // translates current material binding to the internal Binding enum.
237 //
238 SoQuadMesh::Binding
findMaterialBinding(SoState * const state) const239 SoQuadMesh::findMaterialBinding(SoState * const state) const
240 {
241 SoMaterialBindingElement::Binding matbind =
242 SoMaterialBindingElement::get(state);
243
244 Binding binding;
245 switch (matbind) {
246 case SoMaterialBindingElement::OVERALL:
247 binding = OVERALL;
248 break;
249 case SoMaterialBindingElement::PER_VERTEX:
250 case SoMaterialBindingElement::PER_VERTEX_INDEXED:
251 binding = PER_VERTEX;
252 break;
253 case SoMaterialBindingElement::PER_PART:
254 case SoMaterialBindingElement::PER_PART_INDEXED:
255 binding = PER_ROW;
256 break;
257 case SoMaterialBindingElement::PER_FACE:
258 case SoMaterialBindingElement::PER_FACE_INDEXED:
259 binding = PER_FACE;
260 break;
261 default:
262 binding = OVERALL;
263 #if COIN_DEBUG
264 SoDebugError::postWarning("SoQuadMesh::findMaterialBinding",
265 "unknown material binding setting");
266 #endif // COIN_DEBUG
267 break;
268 }
269 return binding;
270 }
271
272
273 //
274 // translates current normal binding to the internal Binding enum.
275 //
276 SoQuadMesh::Binding
findNormalBinding(SoState * const state) const277 SoQuadMesh::findNormalBinding(SoState * const state) const
278 {
279 SoNormalBindingElement::Binding normbind =
280 SoNormalBindingElement::get(state);
281
282 Binding binding;
283 switch (normbind) {
284 case SoMaterialBindingElement::OVERALL:
285 binding = OVERALL;
286 break;
287 case SoMaterialBindingElement::PER_VERTEX:
288 case SoMaterialBindingElement::PER_VERTEX_INDEXED:
289 binding = PER_VERTEX;
290 break;
291 case SoMaterialBindingElement::PER_PART:
292 case SoMaterialBindingElement::PER_PART_INDEXED:
293 binding = PER_ROW;
294 break;
295 case SoMaterialBindingElement::PER_FACE:
296 case SoMaterialBindingElement::PER_FACE_INDEXED:
297 binding = PER_FACE;
298 break;
299 default:
300 binding = PER_VERTEX;
301 #if COIN_DEBUG
302 SoDebugError::postWarning("SoQuadMesh::findNormalBinding",
303 "unknown normal binding setting");
304 #endif // COIN_DEBUG
305 break;
306 }
307 return binding;
308 }
309
310
311 #define QUADMESH_WEIGHTS_NR 32
312 static float precompWeights[QUADMESH_WEIGHTS_NR];
precalculateWeight(int i)313 static float precalculateWeight(int i)
314 {
315 int exponent = i - (QUADMESH_WEIGHTS_NR / 2);
316 double p2 = ldexp(0.75, exponent);
317 double p = sqrt(p2);
318 return float(p / (1.0 + p));
319 }
qmeshGetWeight(float value)320 static float qmeshGetWeight(float value)
321 {
322 #if defined(HAVE_ILOGB)
323 int exponent = ilogb(value) + (QUADMESH_WEIGHTS_NR / 2);
324 #elif defined(HAVE__LOGB)
325 int exponent = ((int) _logb(value)) + (QUADMESH_WEIGHTS_NR / 2);
326 #else // HAVE__LOGB
327 // FIXME: implement coin_ilogb(double). pederb, 2002-12-19
328 int exponent = QUADMESH_WEIGHTS_NR / 2;
329 #endif // !HAVE_ILOGB && ! HAVE__LOGB
330 if (exponent < 0) return 0.f;
331 if (exponent >= QUADMESH_WEIGHTS_NR) return 1.f;
332 return precompWeights[exponent];
333 }
334
335 // qmeshNormalize
336 // v.length() must be in 0..sqrt(toLength2)/4 range
qmeshNormalize(SbVec3f & v,float toLength2)337 static SbBool qmeshNormalize(SbVec3f & v, float toLength2)
338 {
339 // FIXME: this function should be optimized by look-up table
340 // (sqrt and divisions are expensive operations)
341 float vl2 = v.sqrLength();
342 if (vl2 > 0.f) {
343 v *= (float) sqrt(toLength2 / (vl2 * 4.0));
344 return TRUE;
345 }
346 return FALSE;
347 }
348
349 namespace { namespace SoGL { namespace QuadMesh {
350
351 #define IDX(r,c) ((r)*rowsize+(c))
352
353 enum AttributeBinding {
354 OVERALL = 0,
355 PER_ROW = 1,
356 PER_FACE = 2,
357 PER_VERTEX = 3
358 };
359
360 template < int NormalBinding,
361 int MaterialBinding,
362 int TexturingEnabled >
GLRender(const SoGLCoordinateElement * coords,const SbVec3f * normals,SoMaterialBundle * mb,const SoTextureCoordinateBundle * tb,SbBool needNormals,int rowsize,int colsize,int start,SbBool preciseLighting)363 static void GLRender(const SoGLCoordinateElement * coords,
364 const SbVec3f *normals,
365 SoMaterialBundle * mb,
366 const SoTextureCoordinateBundle * tb,
367 SbBool needNormals,
368 int rowsize,
369 int colsize,
370 int start,
371 SbBool preciseLighting)
372 {
373 assert(rowsize >= 0 && colsize >= 0 && start >= 0);
374 assert(coords->getNum() - start >= rowsize * colsize);
375
376 const SbBool is3d = coords->is3D();
377 const SbVec3f * coords3d = is3d ? coords->getArrayPtr3() : NULL;
378 const SbVec4f * coords4d = is3d ? NULL : coords->getArrayPtr4();
379
380 if (preciseLighting == FALSE) {
381
382 // This is the same code as in SoGLCoordinateElement::send().
383 // It is inlined here for speed (~15% speed increase).
384 #define SEND_VERTEX(_idx_) \
385 if (is3d) glVertex3fv((const GLfloat*) (coords3d + (_idx_))); \
386 else glVertex4fv((const GLfloat*) (coords4d + (_idx_)));
387
388 int midx = 0;
389
390 SbVec3f dummynormal(0.0f, 0.0f, 1.0f);
391 const SbVec3f * currnormal = &dummynormal;
392 if (normals) currnormal = normals;
393
394 if ((AttributeBinding)NormalBinding == OVERALL) {
395 if (needNormals) {
396 glNormal3fv((const GLfloat *)currnormal);
397 }
398 }
399
400 int curridx; // for optimization only
401
402 for (int i = 0; i < colsize-1; i++) {
403 int j = 0;
404 glBegin(GL_QUAD_STRIP);
405 if ((AttributeBinding)NormalBinding == PER_ROW) {
406 currnormal = normals++;
407 glNormal3fv((const GLfloat *)currnormal);
408 }
409 if ((AttributeBinding)MaterialBinding == PER_ROW) {
410 mb->send(midx++,TRUE);
411 }
412
413 for (j = 0; j < rowsize; j++) {
414 curridx = IDX(i,j);
415 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
416 currnormal = &normals[curridx];
417 glNormal3fv((const GLfloat *)currnormal);
418 }
419 if ((AttributeBinding)NormalBinding == PER_FACE) {
420 // j != 1, since we send four vertices for the first quad, then two
421 // vertices for all other quads
422 if (j != 1) {
423 currnormal = normals++;
424 glNormal3fv((const GLfloat *)currnormal);
425 }
426 }
427 if ((AttributeBinding)MaterialBinding == PER_VERTEX) {
428 mb->send(curridx, TRUE);
429 }
430 if ((AttributeBinding)MaterialBinding == PER_FACE) {
431 // j != 1, since we send four vertices for the first quad, then two
432 // vertices for all other quads
433 if (j != 1) mb->send(midx++, TRUE);
434 }
435 if (TexturingEnabled == TRUE) {
436 tb->send(curridx, coords->get3(start + curridx),
437 *currnormal);
438 }
439 SEND_VERTEX(start + curridx);
440 curridx = IDX(i+1,j);
441 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
442 currnormal = &normals[curridx];
443 glNormal3fv((const GLfloat *)currnormal);
444 }
445 if ((AttributeBinding)MaterialBinding == PER_VERTEX) {
446 mb->send(curridx, TRUE);
447 }
448 if (TexturingEnabled == TRUE) {
449 tb->send(curridx, coords->get3(start + curridx), *currnormal);
450 }
451 SEND_VERTEX(start + curridx);
452 }
453 glEnd(); // end of strip/row
454 }
455 #undef SEND_VERTEX
456
457 } else { // PreciseLighting == TRUE
458
459 // Alternative code for rendering with more precise lighting by
460 // placing an extra vertice in the middle of the quad, and
461 // thereby splitting it to 2 triangles
462
463 // Developed and contributed by PC John (Jan Peciva).
464
465 int midx = 0;
466 SbVec3f dummynormal(0.0f, 0.0f, 1.0f);
467 const SbVec3f * currnormal = &dummynormal;
468 if (normals) currnormal = normals;
469
470 if ((AttributeBinding)NormalBinding == OVERALL) {
471 if (needNormals) {
472 glNormal3fv((const GLfloat *)currnormal);
473 }
474 }
475
476 const SbVec3f *c1d3 = NULL,*c2d3 = NULL,*c3d3 = NULL,*c4d3 = NULL;
477 SbVec3f ccd3;
478 const SbVec4f *c1d4 = NULL,*c2d4 = NULL,*c3d4 = NULL,*c4d4 = NULL;
479 SbVec4f ccd4;
480 SbVec4f sum234d4,sum134d4,sum124d4,sum123d4;
481 SbVec4f vec1d4,vec2d4,vec3d4,vec4d4;
482 float s1,s2,s3,s4;
483 float w1,w2,w3,w4;
484 const SbVec3f *n1,*n2,*n3,*n4;
485 SbVec3f nc;
486 const SbVec4f *t1,*t2, * t3 = NULL, * t4 = NULL;
487 SbVec4f tc;
488
489 int curridx1 = 0;
490 int curridx2 = rowsize;
491 for (int i = 0; i < colsize-1; i++) {
492 int j = 0;
493 if ((AttributeBinding)NormalBinding == PER_ROW) {
494 currnormal = normals++;
495 glNormal3fv((const GLfloat *)currnormal);
496 }
497 if ((AttributeBinding)MaterialBinding == PER_ROW) {
498 mb->send(midx++, TRUE);
499 }
500
501 if (is3d) {
502 c3d3 = &coords3d[start+curridx1];
503 c4d3 = &coords3d[start+curridx2];
504 } else {
505 c3d4 = &coords4d[start+curridx1];
506 c4d4 = &coords4d[start+curridx2];
507 }
508 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
509 n3 = &normals[curridx1];
510 n4 = &normals[curridx2];
511 }
512 curridx1++;
513 curridx2++;
514
515 for (j = 1; j < rowsize; j++) {
516 if (is3d) {
517 c1d3 = c3d3;
518 c2d3 = c4d3;
519 c3d3 = &coords3d[start+curridx1];
520 c4d3 = &coords3d[start+curridx2];
521 ccd3 = ((*c1d3)+(*c2d3)+(*c3d3)+(*c4d3)) * 0.25f;
522 } else {
523 c1d4 = c3d4;
524 c2d4 = c4d4;
525 c3d4 = &coords4d[start+curridx1];
526 c4d4 = &coords4d[start+curridx2];
527 assert(!"4d coordinates handling unimplemented yet");
528 }
529
530 if ((AttributeBinding)NormalBinding == PER_VERTEX ||
531 TexturingEnabled == TRUE) {
532 if (is3d) {
533 s1 = ((*c1d3) - ccd3).sqrLength();
534 s2 = ((*c2d3) - ccd3).sqrLength();
535 s3 = ((*c3d3) - ccd3).sqrLength();
536 s4 = ((*c4d3) - ccd3).sqrLength();
537 } else {
538 // FIXME: 4D coordinates are not currently implemented
539 // for HQ rendering - following code is never used
540 //sum234d4 = qmeshAddVec4f(c2, sum34d4);
541 //vec1d4 = qmeshAddSpec4f(c1, sum234d4);
542 //sum134d4 = qmeshAddVec4f(c1, sum34d4);
543 //vec2d4 = qmeshAddSpec4f(c2, sum134d4);
544 //sum124d4 = qmeshAddVec4f(c4, sum12d4);
545 //vec3d4 = qmeshAddSpec4f(c3, sum124d4);
546 //sum123d4 = qmeshAddVec4f(c3, sum12d4);
547 //vec4d4 = qmeshAddSpec4f(c4, sum123d4);
548 //s1 = qmeshSqrLen(vec1d4);
549 //s2 = qmeshSqrLen(vec2d4);
550 //s3 = qmeshSqrLen(vec3d4);
551 //s4 = qmeshSqrLen(vec4d4);
552 }
553
554 if ((AttributeBinding)NormalBinding == PER_VERTEX ||
555 TexturingEnabled == TRUE) {
556 w1 = qmeshGetWeight(s1/s4) * 0.5f;
557 w2 = qmeshGetWeight(s2/s3) * 0.5f;
558 w3 = 0.5f - w2;
559 w4 = 0.5f - w1;
560 }
561 }
562
563 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
564 n1 = n3;
565 n2 = n4;
566 n3 = &normals[curridx1];
567 n4 = &normals[curridx2];
568 nc = ((*n1)*w1 + (*n2)*w2 + (*n3)*w3 + (*n4)*w4);
569 if (!qmeshNormalize(nc, n1->sqrLength() + n2->sqrLength() +
570 n3->sqrLength() + n4->sqrLength())) {
571 if (is3d) {
572 SbPlane p1(*c1d3,*c2d3,*c4d3);
573 SbPlane p2(*c1d3,*c4d3,*c3d3);
574 SbVec3f n = p1.getNormal() + p2.getNormal();
575 SbBool quadok = qmeshNormalize(n, n1->sqrLength() + n2->sqrLength() +
576 n3->sqrLength() + n4->sqrLength());
577 #if COIN_DEBUG
578 if ( !quadok )
579 SoDebugError::postWarning("SoQuadMesh::GLRender",
580 "Can not compute normal because of "
581 "wrong quad coordinates.");
582 #endif // COIN_DEBUG
583 } else {
584 // FIXME
585 }
586 }
587 }
588
589 if ((AttributeBinding)MaterialBinding == PER_VERTEX) {
590 assert(FALSE && "yet unimplemented");
591 }
592
593 if (TexturingEnabled == TRUE) {
594 t1 = t3;
595 t2 = t4;
596 if (!tb->isFunction()) {
597 t3 = &((SoTextureCoordinateBundle*)tb)->get(curridx1);
598 t4 = &((SoTextureCoordinateBundle*)tb)->get(curridx2);
599 } else {
600 assert(FALSE && "unimplemented");
601 }
602 tc = ((*t1)*w1 + (*t2)*w2 + (*t3)*w3 + (*t4)*w4);
603 }
604
605 glBegin(GL_TRIANGLE_FAN);
606
607 if ((AttributeBinding)NormalBinding == PER_FACE) {
608 currnormal = normals++;
609 glNormal3fv((const GLfloat *)currnormal);
610 }
611 if ((AttributeBinding)MaterialBinding == PER_FACE) {
612 mb->send(curridx1, TRUE);
613 }
614
615 // CENTER vertex
616 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
617 glNormal3fv(nc.getValue());
618 }
619 if ((AttributeBinding)MaterialBinding == PER_VERTEX) {
620 assert(FALSE && "unimplemented");
621 }
622 if (TexturingEnabled == TRUE) {
623 // tb->send(?curridx?, cc, nc) was replaced by
624 // glTexCoord for center vertex
625 glTexCoord4fv((const GLfloat*)&tc);
626 }
627 if (is3d) {
628 glVertex3fv((const GLfloat*)&ccd3);
629 } else {
630 glVertex4fv((const GLfloat*)&ccd4);
631 }
632
633 // FIRST vertex
634 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
635 glNormal3fv(n1->getValue());
636 }
637 if ((AttributeBinding)MaterialBinding == PER_VERTEX) {
638 assert(FALSE && "unimplemented");
639 }
640 if (TexturingEnabled == TRUE) {
641 if (is3d) {
642 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
643 tb->send(curridx1-1, *c1d3, *n1);
644 } else {
645 tb->send(curridx1-1, *c1d3, *currnormal);
646 }
647 } else {
648 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
649 //tb->send(curridx1-1, *c1d4, *n1);
650 } else {
651 //tb->send(curridx1-1, *c1d4, *currnormal);
652 }
653 }
654 }
655 if (is3d) {
656 glVertex3fv((const GLfloat*)c1d3);
657 } else {
658 glVertex4fv((const GLfloat*)c1d4);
659 }
660
661 // SECOND vertex
662 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
663 glNormal3fv(n2->getValue());
664 }
665 if ((AttributeBinding)MaterialBinding == PER_VERTEX) {
666 assert(!"unimplemented");
667 }
668 if (TexturingEnabled == TRUE) {
669 if (is3d) {
670 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
671 tb->send(curridx2-1, *c2d3, *n2);
672 } else {
673 tb->send(curridx2-1, *c2d3, *currnormal);
674 }
675 } else {
676 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
677 //tb->send(curridx2-1, *c2d4, *n2);
678 } else {
679 //tb->send(curridx2-1, *c2d4, *currnormal);
680 }
681 }
682 }
683 if (is3d) {
684 glVertex3fv((const GLfloat*)c2d3);
685 } else {
686 glVertex4fv((const GLfloat*)c2d4);
687 }
688
689 // FOURTH vertex
690 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
691 glNormal3fv(n4->getValue());
692 }
693 if ((AttributeBinding)MaterialBinding == PER_VERTEX) {
694 assert(FALSE && "unimplemented");
695 }
696 if (TexturingEnabled == TRUE) {
697 if (is3d) {
698 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
699 tb->send(curridx2, *c4d3, *n4);
700 } else {
701 tb->send(curridx2, *c4d3, *currnormal);
702 }
703 } else {
704 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
705 //tb->send(curridx2, *c4d4, *n4);
706 } else {
707 //tb->send(curridx2, *c4d4, *currnormal);
708 }
709 }
710 }
711 if (is3d) {
712 glVertex3fv((const GLfloat*)c4d3);
713 } else {
714 glVertex4fv((const GLfloat*)c4d4);
715 }
716
717 // THIRD vertex
718 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
719 glNormal3fv(n3->getValue());
720 }
721 if ((AttributeBinding)MaterialBinding == PER_VERTEX) {
722 assert(!"unimplemented");
723 }
724 if (TexturingEnabled == TRUE) {
725 if (is3d) {
726 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
727 tb->send(curridx1, *c3d3, *n3);
728 } else {
729 tb->send(curridx1, *c3d3, *currnormal);
730 }
731 } else {
732 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
733 //tb->send(curridx1, *c3d4, *n3);
734 } else {
735 //tb->send(curridx1, *c3d4, *currnormal);
736 }
737 }
738 }
739 if (is3d) {
740 glVertex3fv((const GLfloat*)c3d3);
741 } else {
742 glVertex4fv((const GLfloat*)c3d4);
743 }
744
745 // again FIRST vertex
746 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
747 glNormal3fv(n1->getValue());
748 }
749 if ((AttributeBinding)MaterialBinding == PER_VERTEX) {
750 assert(!"unimplemented");
751 }
752 if (TexturingEnabled == TRUE) {
753 if (is3d) {
754 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
755 tb->send(curridx1-1, *c1d3, *n1);
756 } else {
757 tb->send(curridx1-1, *c1d3, *currnormal);
758 }
759 } else {
760 if ((AttributeBinding)NormalBinding == PER_VERTEX) {
761 //tb->send(curridx1-1, *c1d4, *n1);
762 } else {
763 //tb->send(curridx1-1, *c1d4, *currnormal);
764 }
765 }
766 }
767 if (is3d) {
768 glVertex3fv((const GLfloat*)c1d3);
769 } else {
770 glVertex4fv((const GLfloat*)c1d4);
771 }
772
773 glEnd();
774
775 curridx1++;
776 curridx2++;
777 }
778 }
779 }
780 }
781
782 #undef IDX
783
784 } } } // namespace
785
786 // Documented in superclass.
787 void
initClass(void)788 SoQuadMesh::initClass(void)
789 {
790 SO_NODE_INTERNAL_INIT_CLASS(SoQuadMesh, SO_FROM_INVENTOR_1);
791
792 for (int pc = 0; pc < QUADMESH_WEIGHTS_NR; pc++)
793 precompWeights[pc] = precalculateWeight(pc);
794 }
795
796 // -----
797
798 #define SOGL_QUADMESH_GLRENDER_CALL_FUNC(normalbinding, materialbinding, texturing, args) \
799 SoGL::QuadMesh::GLRender<normalbinding, materialbinding, texturing> args
800
801 #define SOGL_QUADMESH_GLRENDER_RESOLVE_ARG3(normalbinding, materialbinding, texturing, args) \
802 if (texturing) { \
803 SOGL_QUADMESH_GLRENDER_CALL_FUNC(normalbinding, materialbinding, TRUE, args); \
804 } else { \
805 SOGL_QUADMESH_GLRENDER_CALL_FUNC(normalbinding, materialbinding, FALSE, args); \
806 }
807
808 #define SOGL_QUADMESH_GLRENDER_RESOLVE_ARG2(normalbinding, materialbinding, texturing, args) \
809 switch (materialbinding) { \
810 case SoGL::QuadMesh::OVERALL: \
811 SOGL_QUADMESH_GLRENDER_RESOLVE_ARG3(normalbinding, SoGL::QuadMesh::OVERALL, texturing, args); \
812 break; \
813 case SoGL::QuadMesh::PER_ROW: \
814 SOGL_QUADMESH_GLRENDER_RESOLVE_ARG3(normalbinding, SoGL::QuadMesh::PER_ROW, texturing, args); \
815 break; \
816 case SoGL::QuadMesh::PER_FACE: \
817 SOGL_QUADMESH_GLRENDER_RESOLVE_ARG3(normalbinding, SoGL::QuadMesh::PER_FACE, texturing, args); \
818 break; \
819 case SoGL::QuadMesh::PER_VERTEX: \
820 SOGL_QUADMESH_GLRENDER_RESOLVE_ARG3(normalbinding, SoGL::QuadMesh::PER_VERTEX, texturing, args); \
821 break; \
822 default: \
823 assert(!"invalid materialbinding argument"); \
824 break; \
825 }
826
827 #define SOGL_QUADMESH_GLRENDER_RESOLVE_ARG1(normalbinding, materialbinding, texturing, args) \
828 switch (normalbinding) { \
829 case SoGL::QuadMesh::OVERALL: \
830 SOGL_QUADMESH_GLRENDER_RESOLVE_ARG2(SoGL::QuadMesh::OVERALL, materialbinding, texturing, args); \
831 break; \
832 case SoGL::QuadMesh::PER_ROW: \
833 SOGL_QUADMESH_GLRENDER_RESOLVE_ARG2(SoGL::QuadMesh::PER_ROW, materialbinding, texturing, args); \
834 break; \
835 case SoGL::QuadMesh::PER_FACE: \
836 SOGL_QUADMESH_GLRENDER_RESOLVE_ARG2(SoGL::QuadMesh::PER_FACE, materialbinding, texturing, args); \
837 break; \
838 case SoGL::QuadMesh::PER_VERTEX: \
839 SOGL_QUADMESH_GLRENDER_RESOLVE_ARG2(SoGL::QuadMesh::PER_VERTEX, materialbinding, texturing, args); \
840 break; \
841 default: \
842 assert(!"invalid normalbinding argument"); \
843 break; \
844 }
845
846 #define SOGL_QUADMESH_GLRENDER(normalbinding, materialbinding, texturing, args) \
847 SOGL_QUADMESH_GLRENDER_RESOLVE_ARG1(normalbinding, materialbinding, texturing, args)
848
849 // Documented in superclass.
850 void
GLRender(SoGLRenderAction * action)851 SoQuadMesh::GLRender(SoGLRenderAction * action)
852 {
853 SoState * state = action->getState();
854 SbBool didpush = FALSE;
855
856 if (this->vertexProperty.getValue()) {
857 state->push();
858 didpush = TRUE;
859 this->vertexProperty.getValue()->GLRender(action);
860 }
861
862 if (!this->shouldGLRender(action)) {
863 if (didpush) state->pop();
864 return;
865 }
866
867 const int rowsize = this->verticesPerRow.getValue();
868 const int colsize = this->verticesPerColumn.getValue();
869
870 // send approx number of triangles for autocache handling
871 sogl_autocache_update(state, (rowsize-1)*(colsize-1)*2, FALSE);
872
873 const SoCoordinateElement * tmp;
874 const SbVec3f * normals;
875 SbBool doTextures;
876
877 SoMaterialBundle mb(action);
878 SoTextureCoordinateBundle tb(action, TRUE, FALSE);
879 doTextures = tb.needCoordinates();
880 SbBool needNormals = !mb.isColorOnly() || tb.isFunction();
881
882 SoVertexShape::getVertexData(action->getState(), tmp, normals,
883 needNormals);
884
885 const SoGLCoordinateElement * coords = (SoGLCoordinateElement *)tmp;
886
887 const int start = this->startIndex.getValue();
888
889 Binding mbind = findMaterialBinding(action->getState());
890 Binding nbind = findNormalBinding(action->getState());
891 if (!needNormals) nbind = OVERALL;
892
893 SoNormalCache * nc = NULL;
894
895 if (needNormals && normals == NULL) {
896 nc = this->generateAndReadLockNormalCache(state);
897 normals = nc->getNormals();
898 }
899
900 mb.sendFirst(); // make sure we have the correct material
901
902 // this is needed to get correct per-face material/normal
903 // rendering. It must be after the SoMaterialBundle::sendFirst()
904 // call.
905 if ((nbind == PER_FACE) || (mbind == PER_FACE)) {
906 SoGLLazyElement::sendFlatshading(state, TRUE);
907 }
908
909 // Check if precise lighting rendering is requested.
910 static int preciselighting = -1;
911 if (preciselighting == -1) {
912 const char * env = coin_getenv("COIN_QUADMESH_PRECISE_LIGHTING");
913 preciselighting = env && (atoi(env) > 0);
914 }
915
916 // Even if precise lighting rendering is requested, we need to check
917 // that the rendering path is supported.
918 SbBool pl = preciselighting &&
919 coords->is3D() && (mbind != PER_VERTEX) && !tb.isFunction();
920
921 // Robustness test to make sure the start index is positive
922 if (start < 0) {
923 static uint32_t current_errors = 0;
924 if (current_errors < 1) {
925 SoDebugError::postWarning("SoQuadMesh::GLRender", "Erroneous "
926 "startIndex: %d. Should be >= 0. This message will only "
927 "be shown once, but there might be more errors", start);
928 }
929 current_errors++;
930 goto glrender_done;
931 }
932
933 // Robustness test to make sure rowsize and colsize are valid
934 if (rowsize < 2 || colsize < 2) {
935 static uint32_t current_errors = 0;
936 if (current_errors < 1) {
937 SoDebugError::postWarning("SoQuadMesh::GLRender", "Erroneous quadmesh "
938 "dimension [%d %d] with %d coordinates available. "
939 "Must specify >= 2 rows and columns. "
940 "Aborting rendering. This message will only be shown "
941 "once, but there might be more errors.",
942 rowsize, colsize, coords->getNum() - start);
943 }
944 current_errors++;
945 goto glrender_done;
946 }
947
948 // Robustness test to make sure rowsize and colsize specify
949 // coordinates that really exist.
950 if (coords->getNum() - start < rowsize * colsize) {
951 static uint32_t current_errors = 0;
952 if (current_errors < 1) {
953 SoDebugError::postWarning("SoQuadMesh::GLRender", "Erroneous quadmesh "
954 "dimension [%d %d] with %d coordinates available. "
955 "Ignoring. This message will only be shown once, but "
956 "there might be more errors.",
957 rowsize, colsize, coords->getNum() - start);
958 }
959 current_errors++;
960 goto glrender_done;
961 }
962
963 SOGL_QUADMESH_GLRENDER(nbind, mbind, doTextures, (coords,
964 normals,
965 &mb,
966 &tb,
967 needNormals,
968 rowsize,
969 colsize,
970 start,
971 pl));
972
973 glrender_done:
974
975 if (nc) {
976 this->readUnlockNormalCache();
977 }
978
979 if (didpush) state->pop();
980 }
981
982 #undef SOGL_QUADMESH_GLRENDER_CALL_FUNC
983 #undef SOGL_QUADMESH_GLRENDER_RESOLVE_ARG3
984 #undef SOGL_QUADMESH_GLRENDER_RESOLVE_ARG2
985 #undef SOGL_QUADMESH_GLRENDER_RESOLVE_ARG1
986 #undef SOGL_QUADMESH_GLRENDER
987
988 // Documented in superclass.
989 SbBool
generateDefaultNormals(SoState * state,SoNormalCache * nc)990 SoQuadMesh::generateDefaultNormals(SoState * state, SoNormalCache * nc)
991 {
992 // FIXME: consider creaseAngle? pederb, 20000809
993
994 if (verticesPerRow.getValue() < 2 || verticesPerColumn.getValue() < 2)
995 return TRUE; // nothing to generate
996
997 SbBool ccw = TRUE;
998 if (SoShapeHintsElement::getVertexOrdering(state) ==
999 SoShapeHintsElement::CLOCKWISE) ccw = FALSE;
1000
1001 const SbVec3f * coords = SoCoordinateElement::getInstance(state)->getArrayPtr3();
1002 assert(coords);
1003
1004 int numcoords = SoCoordinateElement::getInstance(state)->getNum() - startIndex.getValue();
1005
1006 Binding binding = findNormalBinding(state);
1007
1008 switch (binding) {
1009 case PER_VERTEX:
1010 nc->generatePerVertexQuad(coords + startIndex.getValue(),
1011 numcoords,
1012 verticesPerRow.getValue(),
1013 verticesPerColumn.getValue(),
1014 ccw);
1015 break;
1016 case PER_FACE:
1017 nc->generatePerFaceQuad(coords + startIndex.getValue(),
1018 numcoords,
1019 verticesPerRow.getValue(),
1020 verticesPerColumn.getValue(),
1021 ccw);
1022 break;
1023 case PER_ROW:
1024 nc->generatePerRowQuad(coords + startIndex.getValue(),
1025 numcoords,
1026 verticesPerRow.getValue(),
1027 verticesPerColumn.getValue(),
1028 ccw);
1029 break;
1030 case OVERALL:
1031 break;
1032 default:
1033 assert(0);
1034 return FALSE;
1035 break;
1036 }
1037 return TRUE;
1038 }
1039
1040 // Documented in superclass.
1041 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)1042 SoQuadMesh::getPrimitiveCount(SoGetPrimitiveCountAction *action)
1043 {
1044 if (!this->shouldPrimitiveCount(action)) return;
1045
1046 action->addNumTriangles(2 * this->verticesPerRow.getValue() *
1047 this->verticesPerColumn.getValue());
1048 }
1049
1050 // Documented in superclass. Overridden to return FALSE. Normals are
1051 // generated in normal cache.
1052 SbBool
generateDefaultNormals(SoState *,SoNormalBundle *)1053 SoQuadMesh::generateDefaultNormals(SoState * /* state */, SoNormalBundle * /* nb */)
1054 {
1055 return FALSE;
1056 }
1057
1058 // Documented in superclass.
1059 void
generatePrimitives(SoAction * action)1060 SoQuadMesh::generatePrimitives(SoAction *action)
1061 {
1062 SoState * state = action->getState();
1063
1064 if (this->vertexProperty.getValue()) {
1065 state->push();
1066 this->vertexProperty.getValue()->doAction(action);
1067 }
1068
1069 const int rowsize = this->verticesPerRow.getValue();
1070 const int colsize = this->verticesPerColumn.getValue();
1071
1072 if (rowsize < 2 || colsize < 2) {
1073 if (vertexProperty.getValue()) state->pop();
1074 return;
1075 }
1076 const SoCoordinateElement *coords;
1077 const SbVec3f * normals;
1078 SbBool doTextures;
1079 SbBool needNormals = TRUE;
1080
1081 SoVertexShape::getVertexData(action->getState(), coords, normals,
1082 needNormals);
1083
1084 SoTextureCoordinateBundle tb(action, FALSE, FALSE);
1085 doTextures = tb.needCoordinates();
1086
1087 int start = this->startIndex.getValue();
1088
1089 Binding mbind = findMaterialBinding(action->getState());
1090 Binding nbind = findNormalBinding(action->getState());
1091
1092 SoNormalCache * nc = NULL;
1093
1094 if (needNormals && normals == NULL) {
1095 nc = this->generateAndReadLockNormalCache(state);
1096 normals = nc->getNormals();
1097 }
1098
1099 SbVec3f dummynormal(0.0f, 0.0f, 1.0f);
1100 const SbVec3f * currnormal = &dummynormal;
1101 if (normals) currnormal = normals;
1102
1103 SoPrimitiveVertex vertex;
1104 SoFaceDetail faceDetail;
1105 SoPointDetail pointDetail;
1106 vertex.setDetail(&pointDetail);
1107
1108 if (nbind == OVERALL && needNormals) {
1109 vertex.setNormal(*currnormal);
1110 }
1111
1112 int curridx; // for optimization only
1113
1114 #define IDX(r,c) ((r)*rowsize+(c))
1115
1116 int normnr = 0;
1117 int midx = 0;
1118 for (int i = 0; i < colsize-1; i++) {
1119 int j = 0;
1120 this->beginShape(action, QUAD_STRIP, &faceDetail);
1121 if (nbind == PER_ROW) {
1122 pointDetail.setNormalIndex(normnr);
1123 currnormal = &normals[normnr++];
1124 vertex.setNormal(*currnormal);
1125 }
1126 if (mbind == PER_ROW) {
1127 pointDetail.setMaterialIndex(midx);
1128 vertex.setMaterialIndex(midx++);
1129 }
1130 SbBool first = TRUE;
1131 faceDetail.setFaceIndex(0);
1132 for (j = 0; j < rowsize; j++) {
1133 curridx = IDX(i,j);
1134 if (nbind == PER_VERTEX) {
1135 pointDetail.setNormalIndex(curridx);
1136 currnormal = &normals[curridx];
1137 vertex.setNormal(*currnormal);
1138 }
1139 else if (nbind == PER_FACE) {
1140 pointDetail.setNormalIndex(normnr);
1141 currnormal = &normals[normnr++];
1142 vertex.setNormal(*currnormal);
1143 }
1144 if (mbind == PER_VERTEX) {
1145 pointDetail.setMaterialIndex(curridx);
1146 vertex.setMaterialIndex(curridx);
1147 }
1148 else if (mbind == PER_FACE) {
1149 pointDetail.setMaterialIndex(midx);
1150 vertex.setMaterialIndex(midx++);
1151 }
1152 if (doTextures) {
1153 if (tb.isFunction()) {
1154 vertex.setTextureCoords(tb.get(coords->get3(start+curridx), *currnormal));
1155 if (tb.needIndices()) pointDetail.setTextureCoordIndex(curridx);
1156 }
1157 else {
1158 pointDetail.setTextureCoordIndex(curridx);
1159 vertex.setTextureCoords(tb.get(curridx));
1160 }
1161 }
1162 pointDetail.setCoordinateIndex(start + curridx);
1163 vertex.setPoint(coords->get3(start + curridx));
1164 this->shapeVertex(&vertex);
1165
1166 curridx = IDX(i+1,j);
1167 if (nbind == PER_VERTEX) {
1168 pointDetail.setNormalIndex(curridx);
1169 currnormal = &normals[curridx];
1170 vertex.setNormal(*currnormal);
1171 }
1172 if (mbind == PER_VERTEX) {
1173 pointDetail.setMaterialIndex(curridx);
1174 vertex.setMaterialIndex(curridx);
1175 }
1176 if (doTextures) {
1177 if (tb.isFunction()) {
1178 vertex.setTextureCoords(tb.get(coords->get3(start+curridx), *currnormal));
1179 if (tb.needIndices()) pointDetail.setTextureCoordIndex(curridx);
1180 }
1181 else {
1182 pointDetail.setTextureCoordIndex(curridx);
1183 vertex.setTextureCoords(tb.get(curridx));
1184 }
1185 }
1186 pointDetail.setCoordinateIndex(start + curridx);
1187 vertex.setPoint(coords->get3(start + curridx));
1188 this->shapeVertex(&vertex);
1189 if (first) first = FALSE;
1190 else faceDetail.incFaceIndex();
1191 }
1192 this->endShape();
1193 faceDetail.incPartIndex();
1194 }
1195 #undef IDX
1196
1197 if (nc) {
1198 this->readUnlockNormalCache();
1199 }
1200
1201 if (this->vertexProperty.getValue())
1202 state->pop();
1203
1204 }
1205
1206 #undef QUADMESH_WEIGHTS_NR
1207