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