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 SoIndexedLineSet SoIndexedLineSet.h Inventor/nodes/SoIndexedLineSet.h
35   \brief The SoIndexedLineSet class is used to render and otherwise represent indexed lines.
36 
37   \ingroup nodes
38 
39   The indexed counterpart of SoLineSet. Lines can specified using
40   indices for coordinates, normals, materials and texture coordinates.
41 
42   If no normals are supplied on the stack (or in the vertexProperty
43   field), the lines will be rendered with lighting disabled.
44 
45   For more information about line sets, see documentation in
46   SoLineSet.  For more information about indexed shapes, see
47   documentation in SoIndexedShape and SoIndexedFaceSet.
48 
49   <b>FILE FORMAT/DEFAULTS:</b>
50   \code
51     IndexedLineSet {
52         vertexProperty NULL
53         coordIndex 0
54         materialIndex -1
55         normalIndex -1
56         textureCoordIndex -1
57     }
58   \endcode
59 
60 */
61 
62 #include <Inventor/nodes/SoIndexedLineSet.h>
63 
64 #include <cassert>
65 
66 #ifdef HAVE_CONFIG_H
67 #include <config.h>
68 #endif // HAVE_CONFIG_H
69 
70 #include <Inventor/SoPrimitiveVertex.h>
71 #include <Inventor/caches/SoNormalCache.h>
72 #include <Inventor/misc/SoState.h>
73 #include <Inventor/bundles/SoMaterialBundle.h>
74 #include <Inventor/actions/SoGLRenderAction.h>
75 #include <Inventor/actions/SoGetBoundingBoxAction.h>
76 #include <Inventor/system/gl.h>
77 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
78 #include <Inventor/elements/SoNormalBindingElement.h>
79 #include <Inventor/elements/SoMaterialBindingElement.h>
80 #include <Inventor/elements/SoCoordinateElement.h>
81 #include <Inventor/elements/SoGLShapeHintsElement.h>
82 #include <Inventor/elements/SoTextureCoordinateBindingElement.h>
83 #include <Inventor/elements/SoDrawStyleElement.h>
84 #include <Inventor/elements/SoGLCoordinateElement.h>
85 #include <Inventor/elements/SoGLLazyElement.h>
86 #include <Inventor/elements/SoGLCacheContextElement.h>
87 #include <Inventor/elements/SoGLVBOElement.h>
88 #include <Inventor/elements/SoMultiTextureCoordinateElement.h>
89 #include <Inventor/elements/SoMultiTextureEnabledElement.h>
90 #include <Inventor/bundles/SoTextureCoordinateBundle.h>
91 #include <Inventor/details/SoLineDetail.h>
92 #include <Inventor/caches/SoBoundingBoxCache.h>
93 #include <Inventor/misc/SoGLDriverDatabase.h>
94 
95 #if COIN_DEBUG
96 #include <Inventor/errors/SoDebugError.h>
97 #endif // COIN_DEBUG
98 
99 #include "nodes/SoSubNodeP.h"
100 #include "rendering/SoGL.h"
101 #include "rendering/SoVertexArrayIndexer.h"
102 #include "rendering/SoVBO.h"
103 
104 
105 SO_NODE_SOURCE(SoIndexedLineSet);
106 
107 
108 #define PRIVATE(obj) obj->pimpl
109 #define LOCK_VAINDEXER(obj) SoBase::staticDataLock()
110 #define UNLOCK_VAINDEXER(obj) SoBase::staticDataUnlock()
111 
112 class SoIndexedLineSetP {
113 public:
114   SoVertexArrayIndexer * vaindexer;
115 };
116 
117 /*!
118   Constructor.
119 */
SoIndexedLineSet()120 SoIndexedLineSet::SoIndexedLineSet()
121 {
122   PRIVATE(this) = new SoIndexedLineSetP;
123   PRIVATE(this)->vaindexer = NULL;
124 
125   SO_NODE_INTERNAL_CONSTRUCTOR(SoIndexedLineSet);
126 }
127 
128 /*!
129   Destructor.
130 */
~SoIndexedLineSet()131 SoIndexedLineSet::~SoIndexedLineSet()
132 {
133   delete PRIVATE(this)->vaindexer;
134   delete PRIVATE(this);
135 }
136 
137 // doc from parent
138 void
initClass(void)139 SoIndexedLineSet::initClass(void)
140 {
141   SO_NODE_INTERNAL_INIT_CLASS(SoIndexedLineSet, SO_FROM_INVENTOR_1|SoNode::VRML1);
142 }
143 
144 //
145 // translates current normal binding into the internal Binding enum
146 //
147 SoIndexedLineSet::Binding
findNormalBinding(SoState * state)148 SoIndexedLineSet::findNormalBinding(SoState * state)
149 {
150   Binding binding = PER_VERTEX_INDEXED;
151 
152   SoNormalBindingElement::Binding normbind =
153     (SoNormalBindingElement::Binding) SoNormalBindingElement::get(state);
154 
155   switch (normbind) {
156   case SoNormalBindingElement::OVERALL:
157     binding = OVERALL;
158     break;
159   case SoNormalBindingElement::PER_VERTEX:
160     binding = PER_VERTEX;
161     break;
162   case SoNormalBindingElement::PER_VERTEX_INDEXED:
163     binding = PER_VERTEX_INDEXED;
164     break;
165   case SoNormalBindingElement::PER_PART:
166     binding = PER_SEGMENT;
167     break;
168   case SoNormalBindingElement::PER_PART_INDEXED:
169     binding = PER_SEGMENT_INDEXED;
170     break;
171   case SoNormalBindingElement::PER_FACE:
172     binding = PER_LINE;
173     break;
174   case SoNormalBindingElement::PER_FACE_INDEXED:
175     binding = PER_LINE_INDEXED;
176     break;
177   default:
178 #if COIN_DEBUG
179     SoDebugError::postWarning("SoIndexedLineSet::findNormalBinding",
180                               "unknown normal binding setting");
181 #endif // COIN_DEBUG
182     break;
183   }
184 
185   return binding;
186 }
187 
188 //
189 // translates current material binding into the internal Binding enum
190 //
191 SoIndexedLineSet::Binding
findMaterialBinding(SoState * state)192 SoIndexedLineSet::findMaterialBinding(SoState * state)
193 {
194   Binding binding = OVERALL;
195 
196   SoMaterialBindingElement::Binding matbind =
197     (SoMaterialBindingElement::Binding) SoMaterialBindingElement::get(state);
198 
199   switch (matbind) {
200   case SoNormalBindingElement::OVERALL:
201     binding = OVERALL;
202     break;
203   case SoNormalBindingElement::PER_VERTEX:
204     binding = PER_VERTEX;
205     break;
206   case SoNormalBindingElement::PER_VERTEX_INDEXED:
207     binding = PER_VERTEX_INDEXED;
208     break;
209   case SoNormalBindingElement::PER_PART:
210     binding = PER_SEGMENT;
211     break;
212   case SoNormalBindingElement::PER_PART_INDEXED:
213     binding = PER_SEGMENT_INDEXED;
214     break;
215   case SoNormalBindingElement::PER_FACE:
216     binding = PER_LINE;
217     break;
218   case SoNormalBindingElement::PER_FACE_INDEXED:
219     binding = PER_LINE_INDEXED;
220     break;
221   default:
222 #if COIN_DEBUG
223     SoDebugError::postWarning("SoIndexedFaceSet::findNormalBinding",
224                               "unknown normal binding setting");
225 #endif // COIN_DEBUG
226     break;
227   }
228 
229   return binding;
230 }
231 
232 
233 // doc from parent
234 void
GLRender(SoGLRenderAction * action)235 SoIndexedLineSet::GLRender(SoGLRenderAction * action)
236 {
237   if (this->coordIndex.getNum() < 2) return;
238   SoState * state = action->getState();
239 
240   if (!this->shouldGLRender(action)) return;
241 
242   SbBool didpush = FALSE;
243 
244   if (this->vertexProperty.getValue()) {
245     state->push();
246     didpush = TRUE;
247     this->vertexProperty.getValue()->GLRender(action);
248   }
249 
250   SoMaterialBundle mb(action);
251   SoTextureCoordinateBundle tb(action, TRUE, FALSE);
252   SbBool doTextures = tb.needCoordinates();
253 
254   const SoCoordinateElement * coords;
255   const SbVec3f * normals;
256   const int32_t * cindices;
257   int numindices;
258   const int32_t * nindices;
259   const int32_t * tindices;
260   const int32_t * mindices;
261   SbBool normalCacheUsed;
262 
263   SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
264 
265   getVertexData(state, coords, normals, cindices,
266                 nindices, tindices, mindices, numindices,
267                 sendNormals, normalCacheUsed);
268 
269   if (sendNormals && normals == NULL) {
270     if (!didpush) {
271       state->push();
272       didpush = TRUE;
273     }
274     sendNormals = FALSE;
275     SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR);
276   }
277 
278   Binding mbind = this->findMaterialBinding(state);
279   Binding nbind = this->findNormalBinding(state);
280 
281   if (this->getNodeType() == SoNode::VRML1) {
282     // For VRML1, PER_VERTEX means per vertex in shape, not PER_VERTEX
283     // on the state.
284     if (mbind == PER_VERTEX) {
285       mbind = PER_VERTEX_INDEXED;
286       mindices = cindices;
287     }
288     if (nbind == PER_VERTEX) {
289       nbind = PER_VERTEX_INDEXED;
290       nindices = cindices;
291     }
292   }
293 
294   if (doTextures) {
295     if (SoTextureCoordinateBindingElement::get(state) ==
296         SoTextureCoordinateBindingElement::PER_VERTEX) {
297       tindices = NULL; // just in case
298     }
299     else if (tindices == NULL) {
300       tindices = cindices;
301     }
302   }
303 
304   mb.sendFirst(); // make sure we have the correct material
305 
306   if (!sendNormals) nbind = OVERALL;
307   else if (nbind == OVERALL) {
308     glNormal3fv((const GLfloat *)normals);
309   }
310 
311   SbBool drawPoints =
312     SoDrawStyleElement::get(state) == SoDrawStyleElement::POINTS;
313 
314   const uint32_t contextid = action->getCacheContext();
315   SoGLLazyElement * lelem = NULL;
316 
317   SbBool dova =
318     !drawPoints &&
319     SoVBO::shouldRenderAsVertexArrays(state, contextid, numindices) &&
320     ((nbind == OVERALL) || ((nbind == PER_VERTEX_INDEXED) && ((nindices == cindices) || (nindices == NULL)))) &&
321     (!doTextures || (tindices == cindices)) &&
322     ((mbind == OVERALL) || ((mbind == PER_VERTEX_INDEXED) && ((mindices == cindices) || (mindices == NULL)))) &&
323     SoGLDriverDatabase::isSupported(sogl_glue_instance(state), SO_GL_VERTEX_ARRAY);
324 
325   const SoGLVBOElement * vboelem = SoGLVBOElement::getInstance(state);
326   SoVBO * colorvbo = NULL;
327 
328   if (dova && (mbind != OVERALL)) {
329     dova = FALSE;
330     if ((mbind == PER_VERTEX_INDEXED) && ((mindices == cindices) || (mindices == NULL))) {
331       lelem = (SoGLLazyElement*) SoLazyElement::getInstance(state);
332       colorvbo = vboelem->getColorVBO();
333       if (colorvbo) dova = TRUE;
334       else {
335         // we might be able to do VA-rendering, but need to check the
336         // diffuse color type first.
337         if (!lelem->isPacked() && lelem->getNumTransparencies() <= 1) {
338           dova = TRUE;
339         }
340       }
341     }
342   }
343   SbBool didrenderasvbo = FALSE;
344   if (dova) {
345     SbBool dovbo = this->startVertexArray(action,
346                                           coords,
347                                           nbind != OVERALL ? normals : NULL,
348                                           doTextures,
349                                           mbind != OVERALL);
350     didrenderasvbo = dovbo;
351     LOCK_VAINDEXER(this);
352     if (PRIVATE(this)->vaindexer == NULL) {
353       SoVertexArrayIndexer * indexer = new SoVertexArrayIndexer;
354 
355       int i = 0;
356       while (i < numindices) {
357         int cnt = 0;
358         while (i + cnt < numindices && cindices[i+cnt] >= 0) cnt++;
359         if (cnt >= 2) {
360           for (int j = 1; j < cnt;j++) {
361             indexer->addLine(cindices[i+j-1],
362                              cindices[i+j]);
363           }
364         }
365         i += cnt + 1;
366       }
367       indexer->close();
368       if (indexer->getNumVertices()) {
369         PRIVATE(this)->vaindexer = indexer;
370       }
371       else {
372         delete indexer;
373       }
374 #if 0
375       fprintf(stderr,"XXX: ILS create VertexArrayIndexer: %d\n", indexer->getNumVertices());
376 #endif
377     }
378 
379     if (PRIVATE(this)->vaindexer) {
380       PRIVATE(this)->vaindexer->render(sogl_glue_instance(state), dovbo, contextid);
381     }
382     UNLOCK_VAINDEXER(this);
383 
384     this->finishVertexArray(action,
385                             dovbo,
386                             nbind != OVERALL,
387                             doTextures,
388                             mbind != OVERALL);
389   }
390   else {
391     sogl_render_lineset((SoGLCoordinateElement*)coords,
392                         cindices,
393                         numindices,
394                         normals,
395                         nindices,
396                         &mb,
397                         mindices,
398                         &tb,
399                         tindices,
400                         (int)nbind,
401                         (int)mbind,
402                         doTextures ? 1 : 0,
403                         drawPoints ? 1 : 0);
404   }
405 
406   if (didpush) {
407     state->pop();
408   }
409   // send approx number of lines for autocache handling
410   sogl_autocache_update(state, this->coordIndex.getNum() / 2, didrenderasvbo);
411 }
412 
413 // Documented in superclass.
414 SbBool
generateDefaultNormals(SoState *,SoNormalBundle *)415 SoIndexedLineSet::generateDefaultNormals(SoState *, SoNormalBundle *)
416 {
417   return FALSE;
418 }
419 
420 // Documented in superclass.
421 SbBool
generateDefaultNormals(SoState * COIN_UNUSED_ARG (state),SoNormalCache * nc)422 SoIndexedLineSet::generateDefaultNormals(SoState * COIN_UNUSED_ARG(state), SoNormalCache * nc)
423 {
424   // not possible to generate normals for IndexedLineSet
425   nc->set(0, NULL);
426   return TRUE;
427 }
428 
429 // doc from parent
430 void
getBoundingBox(SoGetBoundingBoxAction * action)431 SoIndexedLineSet::getBoundingBox(SoGetBoundingBoxAction * action)
432 {
433   inherited::getBoundingBox(action);
434   // notify open (if any) bbox caches about lines in this shape
435   SoBoundingBoxCache::setHasLinesOrPoints(action->getState());
436 }
437 
438 // doc from parent
439 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)440 SoIndexedLineSet::getPrimitiveCount(SoGetPrimitiveCountAction *action)
441 {
442   if (!this->shouldPrimitiveCount(action)) return;
443 
444   int n = this->coordIndex.getNum();
445   if (n < 2) return;
446 
447   if (action->canApproximateCount()) {
448     action->addNumLines(n/3);
449   }
450   else {
451     const int32_t * ptr = coordIndex.getValues(0);
452     const int32_t * endptr = ptr + n;
453     int cnt = 0;
454     int add = 0;
455     while (ptr < endptr) {
456       if (*ptr++ >= 0) cnt++;
457       else {
458         add += cnt-1;
459         cnt = 0;
460       }
461     }
462     // in case index array wasn't terminated by a -1
463     if (cnt >= 2) add += cnt-1;
464     action->addNumLines(add);
465   }
466 }
467 
468 // doc from parent
469 void
generatePrimitives(SoAction * action)470 SoIndexedLineSet::generatePrimitives(SoAction *action)
471 {
472   if (this->coordIndex.getNum() < 2) return;
473 
474   SoState * state = action->getState();
475 
476   if (this->vertexProperty.getValue()) {
477     state->push();
478     this->vertexProperty.getValue()->doAction(action);
479   }
480 
481   Binding mbind = this->findMaterialBinding(state);
482   Binding nbind = this->findNormalBinding(state);
483 
484   const SoCoordinateElement * coords;
485   const SbVec3f * normals;
486   const int32_t * cindices;
487   int numindices;
488   const int32_t * normindices;
489   const int32_t * texindices;
490   const int32_t * matindices;
491   SbBool doTextures;
492   SbBool sendNormals = TRUE;
493   SbBool normalCacheUsed;
494 
495   getVertexData(state, coords, normals, cindices,
496                 normindices, texindices, matindices, numindices,
497                 sendNormals, normalCacheUsed);
498 
499   if (normals == NULL) {
500     sendNormals = FALSE;
501     nbind = OVERALL;
502   }
503 
504   if (this->getNodeType() == SoNode::VRML1) {
505     // For VRML1, PER_VERTEX means per vertex in shape, not PER_VERTEX
506     // on the state.
507     if (mbind == PER_VERTEX) {
508       mbind = PER_VERTEX_INDEXED;
509       matindices = cindices;
510     }
511     if (nbind == PER_VERTEX) {
512       nbind = PER_VERTEX_INDEXED;
513       normindices = cindices;
514     }
515   }
516 
517   SoTextureCoordinateBundle tb(action, FALSE, FALSE);
518   doTextures = tb.needCoordinates();
519 
520   if (doTextures) {
521     if (SoTextureCoordinateBindingElement::get(state) ==
522         SoTextureCoordinateBindingElement::PER_VERTEX) {
523       texindices = NULL; // just in case
524     }
525     else if (texindices == NULL) {
526       texindices = cindices;
527     }
528   }
529 
530   if (mbind == PER_VERTEX_INDEXED && matindices == NULL) {
531     matindices = cindices;
532   }
533   if (nbind == PER_VERTEX_INDEXED && normindices == NULL) {
534     normindices = cindices;
535   }
536   if (mbind == PER_VERTEX || mbind == PER_LINE || mbind == PER_SEGMENT) {
537     matindices = NULL;
538   }
539   if (nbind == PER_VERTEX || nbind == PER_LINE || nbind == PER_SEGMENT) {
540     normindices = NULL;
541   }
542 
543   if (nbind == OVERALL) normindices = NULL;
544   if (mbind == OVERALL) matindices = NULL;
545 
546   int matnr = 0;
547   int normnr = 0;
548   int texidx = 0;
549   int32_t i;
550   const int32_t *end = cindices + numindices;
551 
552   SoPrimitiveVertex vertex;
553   SoPointDetail pointDetail;
554   SoLineDetail lineDetail;
555 
556   vertex.setDetail(&pointDetail);
557 
558   SbVec3f dummynormal(0.0f, 0.0f, 1.0f);
559   const SbVec3f *currnormal = &dummynormal;
560   if (normals) currnormal = normals;
561 
562   if (nbind == OVERALL) {
563     vertex.setNormal(*currnormal);
564   }
565 
566   if (mbind == PER_SEGMENT || mbind == PER_SEGMENT_INDEXED ||
567       nbind == PER_SEGMENT || nbind == PER_SEGMENT_INDEXED) {
568     int previ;
569     SbBool matPerPolyline = mbind == PER_LINE || mbind == PER_LINE_INDEXED;
570     SbBool normPerPolyline = nbind == PER_LINE || nbind == PER_LINE_INDEXED;
571 
572     this->beginShape(action, SoShape::LINES, &lineDetail);
573 
574     while (cindices + 1 < end) { // need at least two vertices
575       previ = *cindices++;
576 
577       if (matPerPolyline || mbind >= PER_VERTEX) {
578         if (matindices) vertex.setMaterialIndex(*matindices++);
579         else vertex.setMaterialIndex(matnr++);
580         pointDetail.setMaterialIndex(vertex.getMaterialIndex());
581       }
582       if (normPerPolyline || nbind >= PER_VERTEX) {
583         if (normindices) {
584           pointDetail.setNormalIndex(*normindices);
585           currnormal = &normals[*normindices++];
586         }
587         else {
588           pointDetail.setNormalIndex(normnr);
589           currnormal = &normals[normnr++];
590         }
591         vertex.setNormal(*currnormal);
592       }
593       if (doTextures) {
594         if (tb.isFunction()) {
595           vertex.setTextureCoords(tb.get(coords->get3(previ), *currnormal));
596         }
597         else {
598           pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
599           vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
600         }
601       }
602 
603       while (cindices < end && (i = *cindices++) >= 0) {
604         if (mbind == PER_SEGMENT || mbind == PER_SEGMENT_INDEXED) {
605           if (matindices) vertex.setMaterialIndex(*matindices++);
606           else vertex.setMaterialIndex(matnr++);
607           pointDetail.setMaterialIndex(vertex.getMaterialIndex());
608         }
609         if (nbind == PER_SEGMENT || nbind == PER_SEGMENT_INDEXED) {
610           if (normindices) {
611             pointDetail.setNormalIndex(*normindices);
612             currnormal = &normals[*normindices++];
613           }
614           else {
615             pointDetail.setNormalIndex(normnr);
616             currnormal = &normals[normnr++];
617           }
618           vertex.setNormal(*currnormal);
619         }
620         pointDetail.setCoordinateIndex(previ);
621         vertex.setPoint(coords->get3(previ));
622         this->shapeVertex(&vertex);
623 
624         if (mbind >= PER_VERTEX) {
625           if (matindices) vertex.setMaterialIndex(*matindices++);
626           else vertex.setMaterialIndex(matnr++);
627           pointDetail.setMaterialIndex(vertex.getMaterialIndex());
628         }
629         if (nbind >= PER_VERTEX) {
630           if (normindices) {
631             pointDetail.setNormalIndex(*normindices);
632             currnormal = &normals[*normindices++];
633           }
634           else {
635             pointDetail.setNormalIndex(normnr);
636             currnormal = &normals[normnr++];
637           }
638           vertex.setNormal(*currnormal);
639         }
640         if (doTextures) {
641           if (tb.isFunction()) {
642             vertex.setTextureCoords(tb.get(coords->get3(i), *currnormal));
643           }
644           else {
645             pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
646             vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
647           }
648         }
649         pointDetail.setCoordinateIndex(i);
650         vertex.setPoint(coords->get3(i));
651         this->shapeVertex(&vertex);
652         lineDetail.incPartIndex();
653         previ = i;
654       }
655       lineDetail.incLineIndex();
656       if (mbind == PER_VERTEX_INDEXED) matindices++;
657       if (nbind == PER_VERTEX_INDEXED) normindices++;
658       if (doTextures && texindices) texindices++;
659     }
660     this->endShape();
661     return;
662   }
663 
664   while (cindices + 1 < end) { // need at least two vertices
665     this->beginShape(action, LINE_STRIP, &lineDetail);
666     i = *cindices++;
667     assert(i >= 0);
668     if (matindices) {
669       pointDetail.setMaterialIndex(*matindices);
670       vertex.setMaterialIndex(*matindices++);
671     }
672     else if (mbind != OVERALL) {
673       pointDetail.setMaterialIndex(matnr);
674       vertex.setMaterialIndex(matnr++);
675     }
676     if (normindices) {
677       pointDetail.setNormalIndex(*normindices);
678       currnormal = &normals[*normindices++];
679     }
680     else if (nbind != OVERALL) {
681       pointDetail.setNormalIndex(normnr);
682       currnormal = &normals[normnr++];
683     }
684     vertex.setNormal(*currnormal);
685     if (doTextures) {
686       if (tb.isFunction()) {
687         vertex.setTextureCoords(tb.get(coords->get3(i), *currnormal));
688       }
689       else {
690         pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
691         vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
692       }
693     }
694     pointDetail.setCoordinateIndex(i);
695     vertex.setPoint(coords->get3(i));
696     this->shapeVertex(&vertex);
697 
698     i = *cindices++;
699     assert(i >= 0);
700     if (mbind >= PER_VERTEX) {
701       if (matindices) vertex.setMaterialIndex(*matindices++);
702       else vertex.setMaterialIndex(matnr++);
703       pointDetail.setMaterialIndex(vertex.getMaterialIndex());
704     }
705     if (nbind >= PER_VERTEX) {
706       if (normindices) {
707         pointDetail.setNormalIndex(*normindices);
708         currnormal = &normals[*normindices++];
709       }
710       else {
711         pointDetail.setNormalIndex(normnr);
712         currnormal = &normals[normnr++];
713       }
714       vertex.setNormal(*currnormal);
715     }
716     if (doTextures) {
717       if (tb.isFunction()) {
718         vertex.setTextureCoords(tb.get(coords->get3(i), *currnormal));
719       }
720       else {
721         pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
722         vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
723       }
724     }
725     pointDetail.setCoordinateIndex(i);
726     vertex.setPoint(coords->get3(i));
727     this->shapeVertex(&vertex);
728     lineDetail.incPartIndex();
729 
730     while (cindices < end && (i = *cindices++) >= 0) {
731       assert(cindices <= end);
732       if (mbind >= PER_VERTEX) {
733         if (matindices) vertex.setMaterialIndex(*matindices++);
734         else vertex.setMaterialIndex(matnr++);
735         pointDetail.setMaterialIndex(vertex.getMaterialIndex());
736       }
737       if (nbind >= PER_VERTEX) {
738         if (normindices) {
739           pointDetail.setNormalIndex(*normindices);
740           currnormal = &normals[*normindices++];
741         }
742         else {
743           pointDetail.setNormalIndex(normnr);
744           currnormal = &normals[normnr++];
745         }
746         vertex.setNormal(*currnormal);
747       }
748       if (doTextures) {
749         if (tb.isFunction()) {
750           vertex.setTextureCoords(tb.get(coords->get3(i), *currnormal));
751         }
752         else {
753           pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
754           vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
755         }
756       }
757       pointDetail.setCoordinateIndex(i);
758       vertex.setPoint(coords->get3(i));
759       this->shapeVertex(&vertex);
760       lineDetail.incPartIndex();
761     }
762     this->endShape(); // end of line strip
763     if (mbind == PER_VERTEX_INDEXED) matindices++;
764     if (nbind == PER_VERTEX_INDEXED) normindices++;
765     if (doTextures && texindices) texindices++;
766     lineDetail.incLineIndex();
767   }
768 
769   if (this->vertexProperty.getValue()) {
770     state->pop();
771   }
772 }
773 
774 void
notify(SoNotList * list)775 SoIndexedLineSet::notify(SoNotList * list)
776 {
777   SoField *f = list->getLastField();
778   if (f == &this->coordIndex) {
779     LOCK_VAINDEXER(this);
780     delete PRIVATE(this)->vaindexer;
781     PRIVATE(this)->vaindexer = NULL;
782     UNLOCK_VAINDEXER(this);
783   }
784   inherited::notify(list);
785 }
786 
787 
788 #undef LOCK_VAINDEXER
789 #undef PRIVATE
790 #undef UNLOCK_VAINDEXER
791