/**************************************************************************\ * * 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 * \**************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif // HAVE_CONFIG_H #ifdef HAVE_VRML97 /*! \class SoVRMLIndexedLineSet SoVRMLIndexedLineSet.h Inventor/VRMLnodes/SoVRMLIndexedLineSet.h \brief The SoVRMLIndexedLineSet class is used to represent a generic 3D line shape. \ingroup VRMLnodes \WEB3DCOPYRIGHT \verbatim IndexedLineSet { eventIn MFInt32 set_colorIndex eventIn MFInt32 set_coordIndex exposedField SFNode color NULL exposedField SFNode coord NULL field MFInt32 colorIndex [] # [-1, inf) field SFBool colorPerVertex TRUE field MFInt32 coordIndex [] # [-1, inf) } \endverbatim The IndexedLineSet node represents a 3D geometry formed by constructing polylines from 3D vertices specified in the coord field. IndexedLineSet uses the indices in its coordIndex field to specify the polylines by connecting vertices from the coord field. An index of "- 1" indicates that the current polyline has ended and the next one begins. The last polyline may be (but does not have to be) followed by a "- 1". IndexedLineSet is specified in the local coordinate system and is affected by the transformations of its ancestors. The coord field specifies the 3D vertices of the line set and contains a Coordinate node. Lines are not lit, are not texture-mapped, and do not participate in collision detection. The width of lines is implementation dependent and each line segment is solid (i.e., not dashed). If the color field is not NULL, it shall contain a Color node. The colours are applied to the line(s) as follows: - If colorPerVertex is FALSE: - If the colorIndex field is not empty, one colour is used for each polyline of the IndexedLineSet. There shall be at least as many indices in the colorIndex field as there are polylines in the IndexedLineSet. If the greatest index in the colorIndex field is N, there shall be N+1 colours in the Color node. The colorIndex field shall not contain any negative entries. - If the colorIndex field is empty, the colours from the Color node are applied to each polyline of the IndexedLineSet in order. There shall be at least as many colours in the Color node as there are polylines. - If colorPerVertex is TRUE: - If the colorIndex field is not empty, colours are applied to each vertex of the IndexedLineSet in exactly the same manner that the coordIndex field is used to supply coordinates for each vertex from the Coordinate node. The colorIndex field shall contain at least as many indices as the coordIndex field and shall contain end-of-polyline markers (-1) in exactly the same places as the coordIndex field. If the greatest index in the colorIndex field is N, there shall be N+1 colours in the Color node. - If the colorIndex field is empty, the coordIndex field is used to choose colours from the Color node. If the greatest index in the coordIndex field is N, there shall be N+1 colours in the Color node. If the color field is NULL and there is a Material defined for the Appearance affecting this IndexedLineSet, the emissiveColor of the Material shall be used to draw the lines. Details on lighting equations as they affect IndexedLineSet nodes are described in 4.14, Lighting model (). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 // COIN_DEBUG class SoVRMLIndexedLineSetP { public: SoVRMLIndexedLineSetP() : vaindexer(NULL), nodeid(0) { } ~SoVRMLIndexedLineSetP() { delete this->vaindexer; } enum Binding { // Needs to be these specific values to match the rendering code // in SoGL.cpp. FIXME: bad dependency. It looks like the same // dependency also exists many other places in the Coin // sources. Should grep around and fix this. 20020805 mortene. OVERALL = 0, PER_LINE = 3, PER_LINE_INDEXED = 4, PER_VERTEX = 5 , PER_VERTEX_INDEXED = 6 }; static Binding findMaterialBinding(SoVRMLIndexedLineSet * node, SoState * state); SoVertexArrayIndexer * vaindexer; uint32_t nodeid; }; #define PRIVATE(obj) ((SoVRMLIndexedLineSetP*) PointerMap::get(this)) #define LOCK_VAINDEXER(obj) SoBase::staticDataLock() #define UNLOCK_VAINDEXER(obj) SoBase::staticDataUnlock() SO_NODE_SOURCE(SoVRMLIndexedLineSet); void SoVRMLIndexedLineSet::initClass(void) // static { SO_NODE_INTERNAL_INIT_CLASS(SoVRMLIndexedLineSet, SO_VRML97_NODE_TYPE); } SoVRMLIndexedLineSet::SoVRMLIndexedLineSet(void) { PointerMap::add(this, new SoVRMLIndexedLineSetP); SO_VRMLNODE_INTERNAL_CONSTRUCTOR(SoVRMLIndexedLineSet); } SoVRMLIndexedLineSet::~SoVRMLIndexedLineSet() { SoVRMLIndexedLineSetP * pimpl = PRIVATE(this); PointerMap::remove(this); delete pimpl; } SoVRMLIndexedLineSetP::Binding SoVRMLIndexedLineSetP::findMaterialBinding(SoVRMLIndexedLineSet * node, SoState * state) { Binding binding = OVERALL; if (SoOverrideElement::getMaterialBindingOverride(state)) { switch (SoMaterialBindingElement::get(state)) { case SoMaterialBindingElement::OVERALL: binding = OVERALL; break; case SoMaterialBindingElement::PER_VERTEX: binding = PER_VERTEX; break; case SoMaterialBindingElement::PER_VERTEX_INDEXED: binding = PER_VERTEX_INDEXED; break; case SoMaterialBindingElement::PER_PART: case SoMaterialBindingElement::PER_FACE: binding = PER_LINE; break; case SoMaterialBindingElement::PER_FACE_INDEXED: case SoMaterialBindingElement::PER_PART_INDEXED: binding = PER_LINE_INDEXED; break; default: #if COIN_DEBUG SoDebugError::postWarning("SoVRMLIndexedLineSetP::findMaterialBinding", "unknown material binding setting"); #endif // COIN_DEBUG break; } } else { if (node->color.getValue()) { if (node->colorPerVertex.getValue()) { binding = PER_VERTEX_INDEXED; if (!node->colorIndex.getNum()) binding = PER_VERTEX; } else { binding = PER_LINE; if (node->colorIndex.getNum()) binding = PER_LINE_INDEXED; } } } return binding; } void SoVRMLIndexedLineSet::GLRender(SoGLRenderAction * action) { if (this->coordIndex.getNum() < 2) return; SoState * state = action->getState(); state->push(); SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); SoGLTextureEnabledElement::set(state, this, FALSE); SoGLTexture3EnabledElement::set(state, this, FALSE); SoVRMLVertexLine::GLRender(action); if (!this->shouldGLRender(action)) { state->pop(); return; } SoVRMLIndexedLineSetP * pimpl = PRIVATE(this); // If the coordIndex field is invalid by not including the // terminating -1, fix the field by adding it. // // (FIXME: this is a bit naughty, as we change a field without // warning from within the library code. Should really see if we // could find a better solution -- which also goes for the other // nodes using coordinate index fields, of course. 20010104 // mortene.) const SoCoordinateElement * coords; const int32_t * cindices; int32_t numindices; const int32_t * mindices; coords = SoCoordinateElement::getInstance(state); cindices = this->coordIndex.getValues(0); numindices = this->coordIndex.getNum(); mindices = this->colorIndex.getNum() ? this->colorIndex.getValues(0) : NULL; SoVRMLIndexedLineSetP::Binding mbind = SoVRMLIndexedLineSetP::findMaterialBinding(this, state); if (mbind == SoVRMLIndexedLineSetP::PER_VERTEX) { mbind = SoVRMLIndexedLineSetP::PER_VERTEX_INDEXED; mindices = cindices; } SbBool drawPoints = SoDrawStyleElement::get(state) == SoDrawStyleElement::POINTS; // place it here so that it will stay in stack scope uint32_t packedcolor; if (this->color.getValue() == NULL) { // FIXME: the vrml97 spec states that the emissiveColor should be // used when no color node is found, but this doesn't work for // some Hydro models, since diffuseColor is used to set the color // of lines. It might be a bug in the VRML2 exporter, but for now // I just add emissive and diffuse to support this. pederb, // 2001-11-21 SbColor col = SoLazyElement::getEmissive(state); const SbColor & col2 = SoLazyElement::getDiffuse(state, 0); col += col2; SbColor4f c(SbClamp(col[0], 0.0f, 1.0f), SbClamp(col[1], 0.0f, 1.0f), SbClamp(col[2], 0.0f, 1.0f), 1.0f); packedcolor = col.getPackedValue(); SoLazyElement::setPacked(state, this, 1, &packedcolor); } SoMaterialBundle mb(action); mb.sendFirst(); // make sure we have the correct material #ifdef COIN_NEXT_MINOR SoGLLazyElement * lelem = NULL; const uint32_t contextid = action->getCacheContext(); SbBool dova = !drawPoints && SoVBO::shouldRenderAsVertexArrays(contextid, numindices) && cc_glglue_has_vertex_array(sogl_glue_instance(state)); const SoGLVBOElement * vboelem = SoGLVBOElement::getInstance(state); SoVBO * colorvbo = NULL; if (dova && (mbind != SoVRMLIndexedLineSetP::OVERALL)) { dova = FALSE; if ((mbind == SoVRMLIndexedLineSetP::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, FALSE, FALSE, mbind != SoVRMLIndexedLineSetP::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, FALSE, FALSE, mbind != SoVRMLIndexedLineSetP::OVERALL); } #else // COIN_NEXT_MINOR if (0) {} #endif // !COIN_NEXT_MINOR else { sogl_render_lineset((SoGLCoordinateElement*)coords, cindices, numindices, NULL, NULL, &mb, mindices, NULL, 0, 0, (int)mbind, 0, drawPoints ? 1 : 0); } // send approx number of triangles for autocache handling sogl_autocache_update(state, this->coordIndex.getNum() / 2); state->pop(); } void SoVRMLIndexedLineSet::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); } } void SoVRMLIndexedLineSet::getBoundingBox(SoGetBoundingBoxAction * action) { inherited::getBoundingBox(action); // notify open (if any) bbox caches about lines in this shape SoBoundingBoxCache::setHasLinesOrPoints(action->getState()); } void SoVRMLIndexedLineSet::generatePrimitives(SoAction * action) { if (this->coordIndex.getNum() < 2) return; SoState * state = action->getState(); state->push(); SoVRMLVertexLine::doAction(action); const SoCoordinateElement * coords; int32_t numindices; const int32_t * cindices; const int32_t * matindices; coords = SoCoordinateElement::getInstance(state); cindices = this->coordIndex.getValues(0); numindices = this->coordIndex.getNum(); matindices = this->colorIndex.getNum() ? this->colorIndex.getValues(0) : NULL; SoVRMLIndexedLineSetP::Binding mbind = SoVRMLIndexedLineSetP::findMaterialBinding(this, state); if (mbind == SoVRMLIndexedLineSetP::PER_VERTEX) { mbind = SoVRMLIndexedLineSetP::PER_VERTEX_INDEXED; matindices = cindices; } if (mbind == SoVRMLIndexedLineSetP::PER_LINE || mbind == SoVRMLIndexedLineSetP::OVERALL) { matindices = NULL; } int matnr = 0; int32_t i; const int32_t *end = cindices + numindices; SoPrimitiveVertex vertex; SoPointDetail pointDetail; SoLineDetail lineDetail; vertex.setDetail(&pointDetail); while (cindices + 1 < end) { this->beginShape(action, LINE_STRIP, &lineDetail); i = *cindices++; assert(i >= 0); if (matindices) { pointDetail.setMaterialIndex(*matindices); vertex.setMaterialIndex(*matindices++); } else if (mbind != SoVRMLIndexedLineSetP::OVERALL) { pointDetail.setMaterialIndex(matnr); vertex.setMaterialIndex(matnr++); } pointDetail.setCoordinateIndex(i); vertex.setPoint(coords->get3(i)); this->shapeVertex(&vertex); i = *cindices++; assert(i >= 0); if (mbind >= SoVRMLIndexedLineSetP::PER_VERTEX) { if (matindices) vertex.setMaterialIndex(*matindices++); else vertex.setMaterialIndex(matnr++); pointDetail.setMaterialIndex(vertex.getMaterialIndex()); } pointDetail.setCoordinateIndex(i); vertex.setPoint(coords->get3(i)); this->shapeVertex(&vertex); lineDetail.incPartIndex(); i = cindices < end ? *cindices++ : -1; while (i >= 0) { if (mbind >= SoVRMLIndexedLineSetP::PER_VERTEX) { if (matindices) vertex.setMaterialIndex(*matindices++); else vertex.setMaterialIndex(matnr++); pointDetail.setMaterialIndex(vertex.getMaterialIndex()); } pointDetail.setCoordinateIndex(i); vertex.setPoint(coords->get3(i)); this->shapeVertex(&vertex); lineDetail.incPartIndex(); i = cindices < end ? *cindices++ : -1; } this->endShape(); // end of line strip if (mbind == SoVRMLIndexedLineSetP::PER_VERTEX_INDEXED) matindices++; lineDetail.incLineIndex(); } state->pop(); } #undef LOCK_VAINDEXER #undef PRIVATE #undef UNLOCK_VAINDEXER #endif // HAVE_VRML97