/**************************************************************************\
*
* This file is part of the Coin 3D visualization library.
* Copyright (C) 1998-2007 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 SoIndexedLineSet SoIndexedLineSet.h Inventor/nodes/SoIndexedLineSet.h
\brief The SoIndexedLineSet class is used to render and otherwise represent indexed lines.
\ingroup nodes
The indexed counterpart of SoLineSet. Lines can specified using
indices for coordinates, normals, materials and texture coordinates.
If no normals are supplied on the stack (or in the vertexProperty
field), the lines will be rendered with lighting disabled.
For more information about line sets, see documentation in
SoLineSet. For more information about indexed shapes, see
documentation in SoIndexedShape and SoIndexedFaceSet.
FILE FORMAT/DEFAULTS:
\code
IndexedLineSet {
vertexProperty NULL
coordIndex 0
materialIndex -1
normalIndex -1
textureCoordIndex -1
}
\endcode
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_CONFIG_H
#include
#endif // HAVE_CONFIG_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../misc/SoVertexArrayIndexer.h"
#include "../misc/SoVBO.h"
#include "../misc/PointerMap.h"
#if COIN_DEBUG
#include
#endif
SO_NODE_SOURCE(SoIndexedLineSet);
#define PRIVATE(obj) ((SoIndexedLineSetP*) PointerMap::get(obj))
#define LOCK_VAINDEXER(obj) SoBase::staticDataLock()
#define UNLOCK_VAINDEXER(obj) SoBase::staticDataUnlock()
class SoIndexedLineSetP {
public:
SoIndexedLineSetP() : vaindexer(NULL), nodeid(0) {}
~SoIndexedLineSetP() { delete this->vaindexer; }
SoVertexArrayIndexer * vaindexer;
uint32_t nodeid;
};
/*!
Constructor.
*/
SoIndexedLineSet::SoIndexedLineSet()
{
PointerMap::add(this, new SoIndexedLineSetP);
SO_NODE_INTERNAL_CONSTRUCTOR(SoIndexedLineSet);
}
/*!
Destructor.
*/
SoIndexedLineSet::~SoIndexedLineSet()
{
SoIndexedLineSetP * pimpl = PRIVATE(this);
PointerMap::remove(this);
delete pimpl;
}
// doc from parent
void
SoIndexedLineSet::initClass(void)
{
SO_NODE_INTERNAL_INIT_CLASS(SoIndexedLineSet, SO_FROM_INVENTOR_1|SoNode::VRML1);
}
//
// translates current normal binding into the internal Binding enum
//
SoIndexedLineSet::Binding
SoIndexedLineSet::findNormalBinding(SoState * state)
{
Binding binding = PER_VERTEX_INDEXED;
SoNormalBindingElement::Binding normbind =
(SoNormalBindingElement::Binding) SoNormalBindingElement::get(state);
switch (normbind) {
case SoNormalBindingElement::OVERALL:
binding = OVERALL;
break;
case SoNormalBindingElement::PER_VERTEX:
binding = PER_VERTEX;
break;
case SoNormalBindingElement::PER_VERTEX_INDEXED:
binding = PER_VERTEX_INDEXED;
break;
case SoNormalBindingElement::PER_PART:
binding = PER_SEGMENT;
break;
case SoNormalBindingElement::PER_PART_INDEXED:
binding = PER_SEGMENT_INDEXED;
break;
case SoNormalBindingElement::PER_FACE:
binding = PER_LINE;
break;
case SoNormalBindingElement::PER_FACE_INDEXED:
binding = PER_LINE_INDEXED;
break;
default:
#if COIN_DEBUG
SoDebugError::postWarning("SoIndexedLineSet::findNormalBinding",
"unknown normal binding setting");
#endif // COIN_DEBUG
break;
}
return binding;
}
//
// translates current material binding into the internal Binding enum
//
SoIndexedLineSet::Binding
SoIndexedLineSet::findMaterialBinding(SoState * state)
{
Binding binding = OVERALL;
SoMaterialBindingElement::Binding matbind =
(SoMaterialBindingElement::Binding) SoMaterialBindingElement::get(state);
switch (matbind) {
case SoNormalBindingElement::OVERALL:
binding = OVERALL;
break;
case SoNormalBindingElement::PER_VERTEX:
binding = PER_VERTEX;
break;
case SoNormalBindingElement::PER_VERTEX_INDEXED:
binding = PER_VERTEX_INDEXED;
break;
case SoNormalBindingElement::PER_PART:
binding = PER_SEGMENT;
break;
case SoNormalBindingElement::PER_PART_INDEXED:
binding = PER_SEGMENT_INDEXED;
break;
case SoNormalBindingElement::PER_FACE:
binding = PER_LINE;
break;
case SoNormalBindingElement::PER_FACE_INDEXED:
binding = PER_LINE_INDEXED;
break;
default:
#if COIN_DEBUG
SoDebugError::postWarning("SoIndexedFaceSet::findNormalBinding",
"unknown normal binding setting");
#endif // COIN_DEBUG
break;
}
return binding;
}
// doc from parent
void
SoIndexedLineSet::GLRender(SoGLRenderAction * action)
{
if (this->coordIndex.getNum() < 2) return;
SoState * state = action->getState();
if (!this->shouldGLRender(action)) return;
SoIndexedLineSetP * pimpl = PRIVATE(this);
SbBool didpush = FALSE;
if (this->vertexProperty.getValue()) {
state->push();
didpush = TRUE;
this->vertexProperty.getValue()->GLRender(action);
}
SoMaterialBundle mb(action);
SoTextureCoordinateBundle tb(action, TRUE, FALSE);
SbBool doTextures = tb.needCoordinates();
const SoCoordinateElement * coords;
const SbVec3f * normals;
const int32_t * cindices;
int numindices;
const int32_t * nindices;
const int32_t * tindices;
const int32_t * mindices;
SbBool normalCacheUsed;
SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
getVertexData(state, coords, normals, cindices,
nindices, tindices, mindices, numindices,
sendNormals, normalCacheUsed);
if (sendNormals && normals == NULL) {
if (!didpush) {
state->push();
didpush = TRUE;
}
sendNormals = FALSE;
SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR);
}
Binding mbind = this->findMaterialBinding(state);
Binding nbind = this->findNormalBinding(state);
if (this->getNodeType() == SoNode::VRML1) {
// For VRML1, PER_VERTEX means per vertex in shape, not PER_VERTEX
// on the state.
if (mbind == PER_VERTEX) {
mbind = PER_VERTEX_INDEXED;
mindices = cindices;
}
if (nbind == PER_VERTEX) {
nbind = PER_VERTEX_INDEXED;
nindices = cindices;
}
}
if (doTextures) {
if (SoTextureCoordinateBindingElement::get(state) ==
SoTextureCoordinateBindingElement::PER_VERTEX) {
tindices = NULL; // just in case
}
else if (tindices == NULL) {
tindices = cindices;
}
}
mb.sendFirst(); // make sure we have the correct material
if (!sendNormals) nbind = OVERALL;
else if (nbind == OVERALL) {
glNormal3fv((const GLfloat *)normals);
}
SbBool drawPoints =
SoDrawStyleElement::get(state) == SoDrawStyleElement::POINTS;
#ifdef COIN_NEXT_MINOR
SoGLLazyElement * lelem = NULL;
const uint32_t contextid = action->getCacheContext();
SbBool dova =
!drawPoints &&
SoVBO::shouldRenderAsVertexArrays(contextid, numindices) &&
((nbind == OVERALL) || ((nbind == PER_VERTEX_INDEXED) && ((nindices == cindices) || (nindices == NULL)))) &&
(!doTextures || (tindices == cindices)) &&
((mbind == OVERALL) || ((mbind == PER_VERTEX_INDEXED) && ((mindices == cindices) || (mindices == NULL)))) &&
cc_glglue_has_vertex_array(sogl_glue_instance(state));
const SoGLVBOElement * vboelem = SoGLVBOElement::getInstance(state);
SoVBO * colorvbo = NULL;
if (dova && (mbind != OVERALL)) {
dova = FALSE;
if ((mbind == PER_VERTEX_INDEXED) && ((mindices == cindices) || (mindices == NULL))) {
lelem = (SoGLLazyElement*) SoLazyElement::getInstance(state);
colorvbo = vboelem->getColorVBO();
if (colorvbo) dova = TRUE;
else {
// we might be able to do VA-rendering, but need to check the
// diffuse color type first.
if (!lelem->isPacked() && lelem->getNumTransparencies() <= 1) {
dova = TRUE;
}
}
}
}
if (dova) {
SbBool dovbo = this->startVertexArray(action,
coords,
nbind != OVERALL ? normals : NULL,
doTextures,
mbind != OVERALL);
LOCK_VAINDEXER(this);
if (pimpl->vaindexer == NULL || pimpl->nodeid != this->getNodeId()) {
delete pimpl->vaindexer;
pimpl->vaindexer = NULL;
pimpl->nodeid = this->getNodeId();
SoVertexArrayIndexer * indexer = new SoVertexArrayIndexer;
int i = 0;
while (i < numindices) {
int cnt = 0;
while (i + cnt < numindices && cindices[i+cnt] >= 0) cnt++;
if (cnt >= 2) {
for (int j = 1; j < cnt;j++) {
indexer->addLine(cindices[i+j-1],
cindices[i+j]);
}
}
i += cnt + 1;
}
indexer->close();
if (indexer->getNumVertices()) {
pimpl->vaindexer = indexer;
}
else {
delete indexer;
}
#if 0
fprintf(stderr,"XXX: ILS create VertexArrayIndexer: %d\n", indexer->getNumVertices());
#endif
}
if (pimpl->vaindexer) {
pimpl->vaindexer->render(sogl_glue_instance(state), dovbo, contextid);
}
UNLOCK_VAINDEXER(this);
this->finishVertexArray(action,
dovbo,
nbind != OVERALL,
doTextures,
mbind != OVERALL);
}
#else // COIN_NEXT_MINOR
if (0) { }
#endif // !COIN_NEXT_MINOR
else {
sogl_render_lineset((SoGLCoordinateElement*)coords,
cindices,
numindices,
normals,
nindices,
&mb,
mindices,
&tb,
tindices,
(int)nbind,
(int)mbind,
doTextures ? 1 : 0,
drawPoints ? 1 : 0);
}
if (didpush) {
state->pop();
}
// send approx number of lines for autocache handling
sogl_autocache_update(state, this->coordIndex.getNum() / 2);
}
// Documented in superclass.
SbBool
SoIndexedLineSet::generateDefaultNormals(SoState *, SoNormalBundle *)
{
return FALSE;
}
// Documented in superclass.
SbBool
SoIndexedLineSet::generateDefaultNormals(SoState * state, SoNormalCache * nc)
{
// not possible to generate normals for IndexedLineSet
nc->set(0, NULL);
return TRUE;
}
// doc from parent
void
SoIndexedLineSet::getBoundingBox(SoGetBoundingBoxAction * action)
{
inherited::getBoundingBox(action);
// notify open (if any) bbox caches about lines in this shape
SoBoundingBoxCache::setHasLinesOrPoints(action->getState());
}
// doc from parent
void
SoIndexedLineSet::getPrimitiveCount(SoGetPrimitiveCountAction *action)
{
if (!this->shouldPrimitiveCount(action)) return;
int n = this->coordIndex.getNum();
if (n < 2) return;
if (action->canApproximateCount()) {
action->addNumLines(n/3);
}
else {
const int32_t * ptr = coordIndex.getValues(0);
const int32_t * endptr = ptr + n;
int cnt = 0;
int add = 0;
while (ptr < endptr) {
if (*ptr++ >= 0) cnt++;
else {
add += cnt-1;
cnt = 0;
}
}
// in case index array wasn't terminated by a -1
if (cnt >= 2) add += cnt-1;
action->addNumLines(add);
}
}
// doc from parent
void
SoIndexedLineSet::generatePrimitives(SoAction *action)
{
if (this->coordIndex.getNum() < 2) return;
SoState * state = action->getState();
if (this->vertexProperty.getValue()) {
state->push();
this->vertexProperty.getValue()->doAction(action);
}
Binding mbind = this->findMaterialBinding(state);
Binding nbind = this->findNormalBinding(state);
const SoCoordinateElement * coords;
const SbVec3f * normals;
const int32_t * cindices;
int numindices;
const int32_t * normindices;
const int32_t * texindices;
const int32_t * matindices;
SbBool doTextures;
SbBool sendNormals = TRUE;
SbBool normalCacheUsed;
getVertexData(state, coords, normals, cindices,
normindices, texindices, matindices, numindices,
sendNormals, normalCacheUsed);
if (normals == NULL) {
sendNormals = FALSE;
nbind = OVERALL;
}
if (this->getNodeType() == SoNode::VRML1) {
// For VRML1, PER_VERTEX means per vertex in shape, not PER_VERTEX
// on the state.
if (mbind == PER_VERTEX) {
mbind = PER_VERTEX_INDEXED;
matindices = cindices;
}
if (nbind == PER_VERTEX) {
nbind = PER_VERTEX_INDEXED;
normindices = cindices;
}
}
SoTextureCoordinateBundle tb(action, FALSE, FALSE);
doTextures = tb.needCoordinates();
if (doTextures) {
if (SoTextureCoordinateBindingElement::get(state) ==
SoTextureCoordinateBindingElement::PER_VERTEX) {
texindices = NULL; // just in case
}
else if (texindices == NULL) {
texindices = cindices;
}
}
if (mbind == PER_VERTEX_INDEXED && matindices == NULL) {
matindices = cindices;
}
if (nbind == PER_VERTEX_INDEXED && normindices == NULL) {
normindices = cindices;
}
if (mbind == PER_VERTEX || mbind == PER_LINE || mbind == PER_SEGMENT) {
matindices = NULL;
}
if (nbind == PER_VERTEX || nbind == PER_LINE || nbind == PER_SEGMENT) {
normindices = NULL;
}
if (nbind == OVERALL) normindices = NULL;
if (mbind == OVERALL) matindices = NULL;
int matnr = 0;
int normnr = 0;
int texidx = 0;
int32_t i;
const int32_t *end = cindices + numindices;
SoPrimitiveVertex vertex;
SoPointDetail pointDetail;
SoLineDetail lineDetail;
vertex.setDetail(&pointDetail);
SbVec3f dummynormal(0.0f, 0.0f, 1.0f);
const SbVec3f *currnormal = &dummynormal;
if (normals) currnormal = normals;
if (nbind == OVERALL) {
vertex.setNormal(*currnormal);
}
if (mbind == PER_SEGMENT || mbind == PER_SEGMENT_INDEXED ||
nbind == PER_SEGMENT || nbind == PER_SEGMENT_INDEXED) {
int previ;
SbBool matPerPolyline = mbind == PER_LINE || mbind == PER_LINE_INDEXED;
SbBool normPerPolyline = nbind == PER_LINE || nbind == PER_LINE_INDEXED;
this->beginShape(action, SoShape::LINES, &lineDetail);
while (cindices + 1 < end) { // need at least two vertices
previ = *cindices++;
if (matPerPolyline || mbind >= PER_VERTEX) {
if (matindices) vertex.setMaterialIndex(*matindices++);
else vertex.setMaterialIndex(matnr++);
pointDetail.setMaterialIndex(vertex.getMaterialIndex());
}
if (normPerPolyline || nbind >= PER_VERTEX) {
if (normindices) {
pointDetail.setNormalIndex(*normindices);
currnormal = &normals[*normindices++];
}
else {
pointDetail.setNormalIndex(normnr);
currnormal = &normals[normnr++];
}
vertex.setNormal(*currnormal);
}
if (doTextures) {
if (tb.isFunction()) {
vertex.setTextureCoords(tb.get(coords->get3(previ), *currnormal));
}
else {
pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
}
}
while (cindices < end && (i = *cindices++) >= 0) {
if (mbind == PER_SEGMENT || mbind == PER_SEGMENT_INDEXED) {
if (matindices) vertex.setMaterialIndex(*matindices++);
else vertex.setMaterialIndex(matnr++);
pointDetail.setMaterialIndex(vertex.getMaterialIndex());
}
if (nbind == PER_SEGMENT || nbind == PER_SEGMENT_INDEXED) {
if (normindices) {
pointDetail.setNormalIndex(*normindices);
currnormal = &normals[*normindices++];
}
else {
pointDetail.setNormalIndex(normnr);
currnormal = &normals[normnr++];
}
vertex.setNormal(*currnormal);
}
pointDetail.setCoordinateIndex(previ);
vertex.setPoint(coords->get3(previ));
this->shapeVertex(&vertex);
if (mbind >= PER_VERTEX) {
if (matindices) vertex.setMaterialIndex(*matindices++);
else vertex.setMaterialIndex(matnr++);
pointDetail.setMaterialIndex(vertex.getMaterialIndex());
}
if (nbind >= PER_VERTEX) {
if (normindices) {
pointDetail.setNormalIndex(*normindices);
currnormal = &normals[*normindices++];
}
else {
pointDetail.setNormalIndex(normnr);
currnormal = &normals[normnr++];
}
vertex.setNormal(*currnormal);
}
if (doTextures) {
if (tb.isFunction()) {
vertex.setTextureCoords(tb.get(coords->get3(i), *currnormal));
}
else {
pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
}
}
pointDetail.setCoordinateIndex(i);
vertex.setPoint(coords->get3(i));
this->shapeVertex(&vertex);
lineDetail.incPartIndex();
previ = i;
}
lineDetail.incLineIndex();
if (mbind == PER_VERTEX_INDEXED) matindices++;
if (nbind == PER_VERTEX_INDEXED) normindices++;
if (doTextures && texindices) texindices++;
}
this->endShape();
return;
}
while (cindices + 1 < end) { // need at least two vertices
this->beginShape(action, LINE_STRIP, &lineDetail);
i = *cindices++;
assert(i >= 0);
if (matindices) {
pointDetail.setMaterialIndex(*matindices);
vertex.setMaterialIndex(*matindices++);
}
else if (mbind != OVERALL) {
pointDetail.setMaterialIndex(matnr);
vertex.setMaterialIndex(matnr++);
}
if (normindices) {
pointDetail.setNormalIndex(*normindices);
currnormal = &normals[*normindices++];
}
else if (nbind != OVERALL) {
pointDetail.setNormalIndex(normnr);
currnormal = &normals[normnr++];
}
vertex.setNormal(*currnormal);
if (doTextures) {
if (tb.isFunction()) {
vertex.setTextureCoords(tb.get(coords->get3(i), *currnormal));
}
else {
pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
}
}
pointDetail.setCoordinateIndex(i);
vertex.setPoint(coords->get3(i));
this->shapeVertex(&vertex);
i = *cindices++;
assert(i >= 0);
if (mbind >= PER_VERTEX) {
if (matindices) vertex.setMaterialIndex(*matindices++);
else vertex.setMaterialIndex(matnr++);
pointDetail.setMaterialIndex(vertex.getMaterialIndex());
}
if (nbind >= PER_VERTEX) {
if (normindices) {
pointDetail.setNormalIndex(*normindices);
currnormal = &normals[*normindices++];
}
else {
pointDetail.setNormalIndex(normnr);
currnormal = &normals[normnr++];
}
vertex.setNormal(*currnormal);
}
if (doTextures) {
if (tb.isFunction()) {
vertex.setTextureCoords(tb.get(coords->get3(i), *currnormal));
}
else {
pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
}
}
pointDetail.setCoordinateIndex(i);
vertex.setPoint(coords->get3(i));
this->shapeVertex(&vertex);
lineDetail.incPartIndex();
while (cindices < end && (i = *cindices++) >= 0) {
assert(cindices <= end);
if (mbind >= PER_VERTEX) {
if (matindices) vertex.setMaterialIndex(*matindices++);
else vertex.setMaterialIndex(matnr++);
pointDetail.setMaterialIndex(vertex.getMaterialIndex());
}
if (nbind >= PER_VERTEX) {
if (normindices) {
pointDetail.setNormalIndex(*normindices);
currnormal = &normals[*normindices++];
}
else {
pointDetail.setNormalIndex(normnr);
currnormal = &normals[normnr++];
}
vertex.setNormal(*currnormal);
}
if (doTextures) {
if (tb.isFunction()) {
vertex.setTextureCoords(tb.get(coords->get3(i), *currnormal));
}
else {
pointDetail.setTextureCoordIndex(texindices?*texindices:texidx);
vertex.setTextureCoords(tb.get(texindices?*texindices++:texidx++));
}
}
pointDetail.setCoordinateIndex(i);
vertex.setPoint(coords->get3(i));
this->shapeVertex(&vertex);
lineDetail.incPartIndex();
}
this->endShape(); // end of line strip
if (mbind == PER_VERTEX_INDEXED) matindices++;
if (nbind == PER_VERTEX_INDEXED) normindices++;
if (doTextures && texindices) texindices++;
lineDetail.incLineIndex();
}
if (this->vertexProperty.getValue()) {
state->pop();
}
}
#undef LOCK_VAINDEXER
#undef PRIVATE
#undef UNLOCK_VAINDEXER