/**************************************************************************\
 *
 *  This file is part of the Coin 3D visualization library.
 *  Copyright (C) 1998-2006 by Systems in Motion.  All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  ("GPL") version 2 as published by the Free Software Foundation.
 *  See the file LICENSE.GPL at the root directory of this source
 *  distribution for additional information about the GNU GPL.
 *
 *  For using Coin with software that can not be combined with the GNU
 *  GPL, and for taking advantage of the additional benefits of our
 *  support services, please contact Systems in Motion about acquiring
 *  a Coin Professional Edition License.
 *
 *  See http://www.coin3d.org/ for more information.
 *
 *  Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY.
 *  http://www.sim.no/  sales@sim.no  coin-support@coin3d.org
 *
\**************************************************************************/

/*!
  \class SoTriangleStripSet SoTriangleStripSet.h Inventor/nodes/SoTriangleStripSet.h
  \brief The SoTriangleStripSet class is used to render and control non-indexed triangle strips.
  \ingroup nodes

  Triangle strips are specified using the numVertices field.

  Coordinates, normals, materials and texture coordinates are all
  fetched in order from the current state or from the vertexProperty
  node if set. (To render triangle strips from vertex indices, use the
  SoIndexedTriangleStripSet node.)

  The numVertices field may be used for multiple strips, coordinates
  will be fetched with a monotonically increasing index from the
  coordinates on the traversal state stack (i.e. typically from the
  last SoCoordinate3 node encountered during traversal).

  For example, if numVertices is set to [3, 4, 5, 3], this node would
  specify a triangle from coordinates 0, 1 and 2, a triangle strip
  from coordinates 3, 4, 5 and 6, a triangle strip from coordinates 7,
  8, 9, 10 and 11 and finally a triangle from coordinates 12, 13, 14.

  Or to put it another way: in a tristrip there will always be two
  vertices more than there are triangles.  Realize that you are
  handling data on the vertex level (not polygon-level), and that the
  triangles are laid out like this, given 5 vertices:

  \verbatim

  1-----3-----5
   \   / \   /
    \ /   \ /
     2-----4

  \endverbatim

  The above figure in scene graph file format:

  \verbatim
  #Inventor V2.1 ascii

  Separator {
    Coordinate3 {
      point [ -2 1 0, -1 -1 0, 0 1 0, 1 -1 0, 2 1 0 ]
    }

    TriangleStripSet {
      numVertices [ 5 ]
    }
  }
  \endverbatim

  The scene graph above in a viewer:

  <center><img src="http://doc.coin3d.org/images/Coin/nodes/trianglestripset.png"></center>


  Strips are converted into triangles the way OpenGL does it, of
  course, so for the dirty details, check out the documentation of
  OpenGL's \c GL_TRIANGLE_STRIP primitive rendering type.

  Binding PER_PART (per strip), PER_VERTEX, PER_FACE or OVERALL can be
  set for material, and normals. The default material binding is
  OVERALL. The default normal binding is PER_VERTEX.

  <b>FILE FORMAT/DEFAULTS:</b>
  \code
    TriangleStripSet {
        vertexProperty NULL
        startIndex 0
        numVertices -1
    }
  \endcode

  \sa SoIndexedTriangleStripSet
*/

#include <Inventor/nodes/SoTriangleStripSet.h>
#include <Inventor/nodes/SoSubNodeP.h>
#include <Inventor/misc/SoState.h>
#include <Inventor/SoPrimitiveVertex.h>
#include <Inventor/bundles/SoTextureCoordinateBundle.h>

#include <Inventor/actions/SoGLRenderAction.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif // HAVE_CONFIG_H
#include <Inventor/system/gl.h>

#include <Inventor/actions/SoGetPrimitiveCountAction.h>

#include <Inventor/elements/SoGLLazyElement.h>
#include <Inventor/elements/SoGLCoordinateElement.h>
#include <Inventor/elements/SoNormalBindingElement.h>
#include <Inventor/elements/SoMaterialBindingElement.h>
#include <Inventor/errors/SoDebugError.h>
#include <Inventor/bundles/SoMaterialBundle.h>
#include <Inventor/elements/SoShapeHintsElement.h>
#include <Inventor/elements/SoCreaseAngleElement.h>

#include <Inventor/caches/SoNormalCache.h>
#include <Inventor/misc/SoNormalGenerator.h>
#include <Inventor/details/SoFaceDetail.h>
#include <Inventor/details/SoPointDetail.h>
#include <Inventor/misc/SoGL.h>

/*!
  \var SoMFInt32 SoTriangleStripSet::numVertices

  Specifies the number of vertices in each triangle strip. The
  vertices are fetched from the current coordinate node, or from the
  vertexProperty field if present.
*/

SO_NODE_SOURCE(SoTriangleStripSet);

/*!
  Constructor.
*/
SoTriangleStripSet::SoTriangleStripSet()
{
  SO_NODE_INTERNAL_CONSTRUCTOR(SoTriangleStripSet);

  SO_NODE_ADD_FIELD(numVertices, (-1));
}

/*!
  Destructor.
*/
SoTriangleStripSet::~SoTriangleStripSet()
{
}

// doc from parent
void
SoTriangleStripSet::computeBBox(SoAction * action,
                                SbBox3f & box, SbVec3f & center)
{
  int32_t numvertices = 0;
  for (int i=0; i < this->numVertices.getNum(); i++)
    numvertices += this->numVertices[i];

  inherited::computeCoordBBox(action, numvertices, box, center);
}

/*!
  \COININTERNAL
*/
SoTriangleStripSet::Binding
SoTriangleStripSet::findMaterialBinding(SoState * const state) const
{
  SoMaterialBindingElement::Binding matbind =
    SoMaterialBindingElement::get(state);

  Binding binding;
  switch (matbind) {
  case SoMaterialBindingElement::OVERALL:
    binding = OVERALL;
    break;
  case SoMaterialBindingElement::PER_VERTEX:
  case SoMaterialBindingElement::PER_VERTEX_INDEXED:
    binding = PER_VERTEX;
    break;
  case SoMaterialBindingElement::PER_PART:
  case SoMaterialBindingElement::PER_PART_INDEXED:
    binding = PER_STRIP;
    break;
  case SoMaterialBindingElement::PER_FACE:
  case SoMaterialBindingElement::PER_FACE_INDEXED:
    binding = PER_FACE;
    break;
  default:
    binding = OVERALL;
#if COIN_DEBUG
    SoDebugError::postWarning("SoTriangleStripSet::findMaterialBinding",
                              "unknown material binding setting");
#endif // COIN_DEBUG
    break;
  }
  return binding;
}

/*!
  \COININTERNAL
*/
SoTriangleStripSet::Binding
SoTriangleStripSet::findNormalBinding(SoState * const state) const
{
  SoNormalBindingElement::Binding normbind =
    SoNormalBindingElement::get(state);

  Binding binding;
  switch (normbind) {
  case SoMaterialBindingElement::OVERALL:
    binding = OVERALL;
    break;
  case SoMaterialBindingElement::PER_VERTEX:
  case SoMaterialBindingElement::PER_VERTEX_INDEXED:
    binding = PER_VERTEX;
    break;
  case SoMaterialBindingElement::PER_PART:
  case SoMaterialBindingElement::PER_PART_INDEXED:
    binding = PER_STRIP;
    break;
  case SoMaterialBindingElement::PER_FACE:
  case SoMaterialBindingElement::PER_FACE_INDEXED:
    binding = PER_FACE;
    break;
  default:
    binding = PER_VERTEX;
#if COIN_DEBUG
    SoDebugError::postWarning("SoTriangleStripSet::findNormalBinding",
                              "unknown normal binding setting");
#endif // COIN_DEBUG
    break;
  }
  return binding;
}

typedef void sogl_render_ni_tristripset_func( const SoGLCoordinateElement * coords,
    const SbVec3f *normals,
    SoMaterialBundle * mb,
    const SoTextureCoordinateBundle * tb,
    int nbind,
    int mbind,
    int doTextures,
    int32_t idx,
    const int32_t *ptr,
    const int32_t *end,
    SbBool needNormals);

static sogl_render_ni_tristripset_func *sotristripset_ni_render_funcs[ 32 ];

#define OVERALL       0
#define PER_STRIP     1
#define PER_FACE      2
#define PER_VERTEX    3

#define MBINDING OVERALL
#define NBINDING OVERALL
#define TEXTURES FALSE
static void sogl_nits_m0_n0_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_STRIP
#define NBINDING OVERALL
#define TEXTURES FALSE
static void sogl_nits_m1_n0_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_FACE
#define NBINDING OVERALL
#define TEXTURES FALSE
static void sogl_nits_m2_n0_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_VERTEX
#define NBINDING OVERALL
#define TEXTURES FALSE
static void sogl_nits_m3_n0_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING OVERALL
#define NBINDING PER_STRIP
#define TEXTURES FALSE
static void sogl_nits_m0_n1_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_STRIP
#define NBINDING PER_STRIP
#define TEXTURES FALSE
static void sogl_nits_m1_n1_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_FACE
#define NBINDING PER_STRIP
#define TEXTURES FALSE
static void sogl_nits_m2_n1_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_VERTEX
#define NBINDING PER_STRIP
#define TEXTURES FALSE
static void sogl_nits_m3_n1_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING OVERALL
#define NBINDING PER_FACE
#define TEXTURES FALSE
static void sogl_nits_m0_n2_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_STRIP
#define NBINDING PER_FACE
#define TEXTURES FALSE
static void sogl_nits_m1_n2_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_FACE
#define NBINDING PER_FACE
#define TEXTURES FALSE
static void sogl_nits_m2_n2_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_VERTEX
#define NBINDING PER_FACE
#define TEXTURES FALSE
static void sogl_nits_m3_n2_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING OVERALL
#define NBINDING PER_VERTEX
#define TEXTURES FALSE
static void sogl_nits_m0_n3_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_STRIP
#define NBINDING PER_VERTEX
#define TEXTURES FALSE
static void sogl_nits_m1_n3_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_FACE
#define NBINDING PER_VERTEX
#define TEXTURES FALSE
static void sogl_nits_m2_n3_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_VERTEX
#define NBINDING PER_VERTEX
#define TEXTURES FALSE
static void sogl_nits_m3_n3_t0
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING OVERALL
#define NBINDING OVERALL
#define TEXTURES TRUE
static void sogl_nits_m0_n0_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_STRIP
#define NBINDING OVERALL
#define TEXTURES TRUE
static void sogl_nits_m1_n0_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_FACE
#define NBINDING OVERALL
#define TEXTURES TRUE
static void sogl_nits_m2_n0_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_VERTEX
#define NBINDING OVERALL
#define TEXTURES TRUE
static void sogl_nits_m3_n0_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING OVERALL
#define NBINDING PER_STRIP
#define TEXTURES TRUE
static void sogl_nits_m0_n1_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_STRIP
#define NBINDING PER_STRIP
#define TEXTURES TRUE
static void sogl_nits_m1_n1_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_FACE
#define NBINDING PER_STRIP
#define TEXTURES TRUE
static void sogl_nits_m2_n1_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_VERTEX
#define NBINDING PER_STRIP
#define TEXTURES TRUE
static void sogl_nits_m3_n1_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING OVERALL
#define NBINDING PER_FACE
#define TEXTURES TRUE
static void sogl_nits_m0_n2_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_STRIP
#define NBINDING PER_FACE
#define TEXTURES TRUE
static void sogl_nits_m1_n2_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_FACE
#define NBINDING PER_FACE
#define TEXTURES TRUE
static void sogl_nits_m2_n2_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_VERTEX
#define NBINDING PER_FACE
#define TEXTURES TRUE
static void sogl_nits_m3_n2_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING OVERALL
#define NBINDING PER_VERTEX
#define TEXTURES TRUE
static void sogl_nits_m0_n3_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_STRIP
#define NBINDING PER_VERTEX
#define TEXTURES TRUE
static void sogl_nits_m1_n3_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_FACE
#define NBINDING PER_VERTEX
#define TEXTURES TRUE
static void sogl_nits_m2_n3_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#define MBINDING PER_VERTEX
#define NBINDING PER_VERTEX
#define TEXTURES TRUE
static void sogl_nits_m3_n3_t1
#include "../misc/SoGLnonindexedTristripSetTemplate.icc"
#undef MBINDING
#undef NBINDING
#undef TEXTURES

#undef OVERALL
#undef PER_STRIP
#undef PER_FACE
#undef PER_VERTEX

// doc from parent
void
SoTriangleStripSet::initClass(void)
{
  SO_NODE_INTERNAL_INIT_CLASS(SoTriangleStripSet, SO_FROM_INVENTOR_1);

  sotristripset_ni_render_funcs[ 0] = sogl_nits_m0_n0_t0;
  sotristripset_ni_render_funcs[ 1] = sogl_nits_m0_n0_t1;
  sotristripset_ni_render_funcs[ 2] = sogl_nits_m0_n1_t0;
  sotristripset_ni_render_funcs[ 3] = sogl_nits_m0_n1_t1;
  sotristripset_ni_render_funcs[ 4] = sogl_nits_m0_n2_t0;
  sotristripset_ni_render_funcs[ 5] = sogl_nits_m0_n2_t1;
  sotristripset_ni_render_funcs[ 6] = sogl_nits_m0_n3_t0;
  sotristripset_ni_render_funcs[ 7] = sogl_nits_m0_n3_t1;

  sotristripset_ni_render_funcs[ 8] = sogl_nits_m1_n0_t0;
  sotristripset_ni_render_funcs[ 9] = sogl_nits_m1_n0_t1;
  sotristripset_ni_render_funcs[10] = sogl_nits_m1_n1_t0;
  sotristripset_ni_render_funcs[11] = sogl_nits_m1_n1_t1;
  sotristripset_ni_render_funcs[12] = sogl_nits_m1_n2_t0;
  sotristripset_ni_render_funcs[13] = sogl_nits_m1_n2_t1;
  sotristripset_ni_render_funcs[14] = sogl_nits_m1_n3_t0;
  sotristripset_ni_render_funcs[15] = sogl_nits_m1_n3_t1;
  
  sotristripset_ni_render_funcs[16] = sogl_nits_m2_n0_t0;
  sotristripset_ni_render_funcs[17] = sogl_nits_m2_n0_t1;
  sotristripset_ni_render_funcs[18] = sogl_nits_m2_n1_t0;
  sotristripset_ni_render_funcs[19] = sogl_nits_m2_n1_t1;
  sotristripset_ni_render_funcs[20] = sogl_nits_m2_n2_t0;
  sotristripset_ni_render_funcs[21] = sogl_nits_m2_n2_t1;
  sotristripset_ni_render_funcs[22] = sogl_nits_m2_n3_t0;
  sotristripset_ni_render_funcs[23] = sogl_nits_m2_n3_t1;
  
  sotristripset_ni_render_funcs[24] = sogl_nits_m3_n0_t0;
  sotristripset_ni_render_funcs[25] = sogl_nits_m3_n0_t1;
  sotristripset_ni_render_funcs[26] = sogl_nits_m3_n1_t0;
  sotristripset_ni_render_funcs[27] = sogl_nits_m3_n1_t1;
  sotristripset_ni_render_funcs[28] = sogl_nits_m3_n2_t0;
  sotristripset_ni_render_funcs[29] = sogl_nits_m3_n2_t1;
  sotristripset_ni_render_funcs[30] = sogl_nits_m3_n3_t0;
  sotristripset_ni_render_funcs[31] = sogl_nits_m3_n3_t1;
}

// doc from parent
void
SoTriangleStripSet::GLRender(SoGLRenderAction * action)
{
  int32_t idx = this->startIndex.getValue();
  int32_t dummyarray[1];
  const int32_t * ptr = this->numVertices.getValues(0);
  const int32_t * end = ptr + this->numVertices.getNum();
  if ((end-ptr == 1) && ptr[0] == 0) return; // nothing to render
  
  SoState * state = action->getState();
  this->fixNumVerticesPointers(state, ptr, end, dummyarray);

  SbBool didpush = FALSE;

  if (this->vertexProperty.getValue()) {
    state->push();
    didpush = TRUE;
    this->vertexProperty.getValue()->GLRender(action);
  }

  Binding mbind = this->findMaterialBinding(action->getState());
  Binding nbind = this->findNormalBinding(action->getState());

  if ((nbind == PER_FACE) || (mbind == PER_FACE)) {
    if (!didpush) {
      didpush = TRUE;
      state->push();
    }
    SoLazyElement::setShadeModel(state, TRUE);
  }

  if (!this->shouldGLRender(action)) {
    if (didpush)
      state->pop();
    return;
  }

  const SoCoordinateElement * tmp;
  const SbVec3f * normals;
  SbBool doTextures;

  SoTextureCoordinateBundle tb(action, TRUE, FALSE);
  doTextures = tb.needCoordinates();

  SoMaterialBundle mb(action);
  SbBool needNormals = !mb.isColorOnly() || tb.isFunction();

  SoVertexShape::getVertexData(action->getState(), tmp, normals,
                               needNormals);

  const SoGLCoordinateElement * coords = (SoGLCoordinateElement *)tmp;

  if (!needNormals) nbind = OVERALL;


  SoNormalCache * nc = NULL;
  if (needNormals && normals == NULL) {
    nc = this->generateAndReadLockNormalCache(state);
    normals = nc->getNormals();
  }

  mb.sendFirst(); // make sure we have the correct material
  
  sotristripset_ni_render_funcs[ (mbind << 3) | (nbind << 1) | doTextures ]
    ( coords,
      normals,
      &mb,
      &tb,
      nbind,
      mbind,
      doTextures,
      idx,
      ptr,
      end,
      needNormals);
  
  if (nc) {
    this->readUnlockNormalCache();
  }
  
  if (didpush)
    state->pop();

  int numv = this->numVertices.getNum();
  // send approx number of triangles for autocache handling
  sogl_autocache_update(state, numv ? 
                        (this->numVertices[0]-2)*numv : 0);
}

// doc from parent
SbBool
SoTriangleStripSet::generateDefaultNormals(SoState * state, SoNormalCache * nc)
{
  SbBool ccw = TRUE;
  if (SoShapeHintsElement::getVertexOrdering(state) ==
      SoShapeHintsElement::CLOCKWISE) ccw = FALSE;

  const SoCoordinateElement * coords =
    SoCoordinateElement::getInstance(state);
  assert(coords);

  SoNormalGenerator * gen =
    new SoNormalGenerator(ccw, this->numVertices.getNum()*3);

  int32_t idx = startIndex.getValue();
  int32_t dummyarray[1];
  const int32_t * ptr = numVertices.getValues(0);
  const int32_t * start = ptr;
  const int32_t * end = ptr + numVertices.getNum();
  this->fixNumVerticesPointers(state, ptr, end, dummyarray);

  while (ptr < end) {
    int num = *ptr++ - 3;
    assert(num >= 0);
    SbVec3f striptri[3];
    striptri[0] = coords->get3(idx++);
    striptri[1] = coords->get3(idx++);
    striptri[2] = coords->get3(idx++);
    gen->triangle(striptri[0], striptri[1], striptri[2]);
    SbBool flag = FALSE;
    while (num--) {
      if (flag) striptri[1] = striptri[2];
      else striptri[0] = striptri[2];
      flag = !flag;
      striptri[2] = coords->get3(idx++);
      gen->triangle(striptri[0], striptri[1], striptri[2]);
    }
  }

  const ptrdiff_t range = end - start;
  switch (this->findNormalBinding(state)) {
  case OVERALL:
    gen->generateOverall();
    break;
  case PER_STRIP:
    gen->generatePerStrip(start, (int)range);
    break;
  case PER_FACE:
    gen->generatePerFace();
    break;
  case PER_VERTEX:
    gen->generate(SoCreaseAngleElement::get(state), start, (int)range);
    break;
  }
  nc->set(gen);
  return TRUE;
}

// doc from parent
void
SoTriangleStripSet::getPrimitiveCount(SoGetPrimitiveCountAction *action)
{
  if (!this->shouldPrimitiveCount(action)) return;

  int32_t dummyarray[1];
  const int32_t * ptr = numVertices.getValues(0);
  const int32_t * end = ptr + numVertices.getNum();
  const ptrdiff_t range = end - ptr;
  if ((range == 1) && ptr[0] == 0) return;
  
  this->fixNumVerticesPointers(action->getState(), ptr, end, dummyarray);
  
  if (action->canApproximateCount()) {
    // this is a wild guess, disable? pederb, 20000131
    action->addNumTriangles((int)(range * 8));
  }
  else {
    int cnt = 0;
    while (ptr < end) {
      cnt += *ptr++ - 2;
    }
    action->addNumTriangles(cnt);
  }
}

// Documented in superclass.
SbBool
SoTriangleStripSet::generateDefaultNormals(SoState * /* state */,
                                           SoNormalBundle * /* nb */)
{
  // Normals are generated in normal cache.
  return FALSE;
}

// Documented in superclass.
void
SoTriangleStripSet::generatePrimitives(SoAction *action)
{
  if (this->numVertices.getNum() == 1 &&
      this->numVertices[0] == 0) return;

  SoState * state = action->getState();

  if (this->vertexProperty.getValue()) {
    state->push();
    this->vertexProperty.getValue()->doAction(action);
  }

  const SoCoordinateElement * coords;
  const SbVec3f * normals;
  SbBool doTextures;
  SbBool needNormals = TRUE;

  SoVertexShape::getVertexData(action->getState(), coords, normals,
                               needNormals);

  SoTextureCoordinateBundle tb(action, FALSE, FALSE);
  doTextures = tb.needCoordinates();

  Binding mbind = this->findMaterialBinding(action->getState());
  Binding nbind = this->findNormalBinding(action->getState());

  SoNormalCache * nc = NULL;

  if (needNormals && normals == NULL) {
    nc = this->generateAndReadLockNormalCache(state);
    normals = nc->getNormals();
  }

  int32_t idx = startIndex.getValue();
  int32_t dummyarray[1];
  const int32_t * ptr = numVertices.getValues(0);
  const int32_t * end = ptr + numVertices.getNum();
  this->fixNumVerticesPointers(action->getState(), ptr, end, dummyarray);

  int matnr = 0;
  int texnr = 0;
  int normnr = 0;
  int n;

  SbVec3f dummynormal(0.0f, 0.0f, 1.0f);
  const SbVec3f * currnormal = &dummynormal;
  if (normals) currnormal = normals;

  SoPrimitiveVertex vertex;
  SoFaceDetail faceDetail;
  SoPointDetail pointDetail;

  vertex.setNormal(*currnormal);
  vertex.setDetail(&pointDetail);

  while (ptr < end) {
    n = *ptr++ - 3;
    if (n < 0) continue; // triangle with < 3 vertices, try next one

    faceDetail.setFaceIndex(0);
    this->beginShape(action, TRIANGLE_STRIP, &faceDetail);

    // first vertex
    if (nbind != OVERALL) {
      pointDetail.setNormalIndex(normnr);
      currnormal = &normals[normnr++];
      vertex.setNormal(*currnormal);
    }
    if (mbind != OVERALL) {
      pointDetail.setMaterialIndex(matnr);
      vertex.setMaterialIndex(matnr++);
    }
    if (doTextures) {
      if (tb.isFunction()) {
        vertex.setTextureCoords(tb.get(coords->get3(idx), *currnormal));
        if (tb.needIndices()) pointDetail.setTextureCoordIndex(texnr++);
      }
      else {
        pointDetail.setTextureCoordIndex(texnr);
        vertex.setTextureCoords(tb.get(texnr++));
      }
    }
    pointDetail.setCoordinateIndex(idx);
    vertex.setPoint(coords->get3(idx++));
    this->shapeVertex(&vertex);

    // second vertex
    if (nbind == PER_VERTEX) {
      pointDetail.setNormalIndex(normnr);
      currnormal = &normals[normnr++];
      vertex.setNormal(*currnormal);
    }
    if (mbind == PER_VERTEX) {
      pointDetail.setMaterialIndex(matnr);
      vertex.setMaterialIndex(matnr++);
    }
    if (doTextures) {
      if (tb.isFunction()) {
        vertex.setTextureCoords(tb.get(coords->get3(idx), *currnormal));
        if (tb.needIndices()) pointDetail.setTextureCoordIndex(texnr++);
      }
      else {
        pointDetail.setTextureCoordIndex(texnr);
        vertex.setTextureCoords(tb.get(texnr++));
      }
    }
    pointDetail.setCoordinateIndex(idx);
    vertex.setPoint(coords->get3(idx++));
    this->shapeVertex(&vertex);

    // third vertex
    if (nbind == PER_VERTEX) {
      pointDetail.setNormalIndex(normnr);
      currnormal = &normals[normnr++];
      vertex.setNormal(*currnormal);
    }
    if (mbind == PER_VERTEX) {
      pointDetail.setMaterialIndex(matnr);
      vertex.setMaterialIndex(matnr++);
    }
    if (doTextures) {
      if (tb.isFunction()) {
        vertex.setTextureCoords(tb.get(coords->get3(idx), *currnormal));
        if (tb.needIndices()) pointDetail.setTextureCoordIndex(texnr++);
      }
      else {
        pointDetail.setTextureCoordIndex(texnr);
        vertex.setTextureCoords(tb.get(texnr++));
      }
    }
    pointDetail.setCoordinateIndex(idx);
    vertex.setPoint(coords->get3(idx++));
    this->shapeVertex(&vertex);

    // loop for vertices 4-n
    while (n--) {
      if (nbind >= PER_FACE) {
        pointDetail.setNormalIndex(normnr);
        currnormal = &normals[normnr++];
        vertex.setNormal(*currnormal);
      }
      if (mbind >= PER_FACE) {
        pointDetail.setMaterialIndex(matnr);
        vertex.setMaterialIndex(matnr++);
      }
      if (doTextures) {
        if (tb.isFunction()) {
          vertex.setTextureCoords(tb.get(coords->get3(idx), *currnormal));
          if (tb.needIndices()) pointDetail.setTextureCoordIndex(texnr++);
        }
        else {
          pointDetail.setCoordinateIndex(texnr);
          vertex.setTextureCoords(tb.get(texnr++));
        }
      }
      pointDetail.setCoordinateIndex(idx);
      vertex.setPoint(coords->get3(idx++));
      this->shapeVertex(&vertex);
      faceDetail.incFaceIndex();
    }
    this->endShape();
    faceDetail.incPartIndex();
  }

  if (nc) {
    this->readUnlockNormalCache();
  }

  if (this->vertexProperty.getValue())
    state->pop();
}
