/**************************************************************************\ * * 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 SoIndexedNurbsSurface SoIndexedNurbsSurface.h Inventor/nodes/SoIndexedNurbsSurface.h \brief The SoIndexedNurbsSurface class can be used to render NURBS surfaces. \ingroup nodes It is very similar to the SoNurbsSurface class, but controlpoints can be specified using indices. FILE FORMAT/DEFAULTS: \code IndexedNurbsSurface { numUControlPoints 0 numVControlPoints 0 coordIndex 0 uKnotVector 0 vKnotVector 0 numSControlPoints 0 numTControlPoints 0 textureCoordIndex -1 sKnotVector 0 tKnotVector 0 } \endcode */ // FIXME: more class doc. Usage example! 20011220 mortene. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif // HAVE_CONFIG_H #include #include #include // COIN_OBSOLETED() /*! \var SoSFInt32 SoIndexedNurbsSurface::numUControlPoints Number of control points in the U direction. */ /*! \var SoSFInt32 SoIndexedNurbsSurface::numVControlPoints Number of control points in the V direction. */ /*! \var SoSFInt32 SoIndexedNurbsSurface::numSControlPoints Number of control points in the S direction. */ /*! \var SoSFInt32 SoIndexedNurbsSurface::numTControlPoints Number of control points in the T direction. */ /*! \var SoMFFloat SoIndexedNurbsSurface::uKnotVector The Bezier knot vector for the U direction. */ /*! \var SoMFFloat SoIndexedNurbsSurface::vKnotVector The Bezier knot vector for the V direction. */ /*! \var SoMFFloat SoIndexedNurbsSurface::sKnotVector The Bezier knot vector for the S direction. */ /*! \var SoMFFloat SoIndexedNurbsSurface::tKnotVector The Bezier knot vector for the T direction. */ /*! \var SoMFInt32 SoIndexedNurbsSurface::coordIndex The coordinate control point indices. */ /*! \var SoMFInt32 SoIndexedNurbsSurface::textureCoordIndex The texture coordinate control point indices. */ // ************************************************************************* class SoIndexedNurbsSurfaceP { public: SoIndexedNurbsSurfaceP(SoIndexedNurbsSurface * m) { this->owner = m; this->nurbsrenderer = NULL; this->offscreenctx = NULL; } ~SoIndexedNurbsSurfaceP() { if (this->offscreenctx) { cc_glglue_context_destruct(this->offscreenctx); } if (this->nurbsrenderer) { GLUWrapper()->gluDeleteNurbsRenderer(this->nurbsrenderer); } } void * offscreenctx; void * nurbsrenderer; void doNurbs(SoAction * action, const SbBool glrender); static void APIENTRY tessBegin(int , void * data); static void APIENTRY tessTexCoord(float * texcoord, void * data); static void APIENTRY tessNormal(float * normal, void * data); static void APIENTRY tessVertex(float * vertex, void * data); static void APIENTRY tessEnd(void * data); private: SoIndexedNurbsSurface * owner; }; #undef PRIVATE #define PRIVATE(p) (p->pimpl) #undef PUBLIC #define PUBLIC(p) (p->owner) // ************************************************************************* SO_NODE_SOURCE(SoIndexedNurbsSurface); /*! Constructor. */ SoIndexedNurbsSurface::SoIndexedNurbsSurface() { SO_NODE_INTERNAL_CONSTRUCTOR(SoIndexedNurbsSurface); SO_NODE_ADD_FIELD(numUControlPoints, (0)); SO_NODE_ADD_FIELD(numVControlPoints, (0)); SO_NODE_ADD_FIELD(coordIndex, (0)); SO_NODE_ADD_FIELD(uKnotVector, (0)); SO_NODE_ADD_FIELD(vKnotVector, (0)); SO_NODE_ADD_FIELD(numSControlPoints, (0)); SO_NODE_ADD_FIELD(numTControlPoints, (0)); SO_NODE_ADD_FIELD(textureCoordIndex, (-1)); SO_NODE_ADD_FIELD(sKnotVector, (0)); SO_NODE_ADD_FIELD(tKnotVector, (0)); PRIVATE(this) = new SoIndexedNurbsSurfaceP(this); } /*! Destructor. */ SoIndexedNurbsSurface::~SoIndexedNurbsSurface() { delete PRIVATE(this); } // doc from parent void SoIndexedNurbsSurface::initClass(void) { SO_NODE_INTERNAL_INIT_CLASS(SoIndexedNurbsSurface, SO_FROM_INVENTOR_1); } /*! Calculates the bounding box of all control points and sets the center to the average of these points. */ void SoIndexedNurbsSurface::computeBBox(SoAction * action, SbBox3f & box, SbVec3f & center) { SoState * state = action->getState(); const SoCoordinateElement * coordelem = SoCoordinateElement::getInstance(state); const int num = this->coordIndex.getNum(); const int32_t * idxptr = this->coordIndex.getValues(0); box.makeEmpty(); SbVec3f acccenter(0.0f, 0.0f, 0.0f); SbVec3f tmp3D; if (coordelem->is3D()) { const SbVec3f * coords = coordelem->getArrayPtr3(); assert(coords); for (int i = 0; i < num; i++) { tmp3D = coords[idxptr[i]]; box.extendBy(tmp3D); acccenter += tmp3D; } } else { const SbVec4f * coords = coordelem->getArrayPtr4(); assert(coords); for (int i = 0; i< num; i++) { coords[idxptr[i]].getReal(tmp3D); box.extendBy(tmp3D); acccenter += tmp3D; } } if (num) center = acccenter / float(num); } // Doc in superclass. void SoIndexedNurbsSurface::GLRender(SoGLRenderAction * action) { if (!this->shouldGLRender(action)) return; // initialize current material SoMaterialBundle mb(action); mb.sendFirst(); glEnable(GL_AUTO_NORMAL); PRIVATE(this)->doNurbs(action, TRUE); glDisable(GL_AUTO_NORMAL); SoState * state = action->getState(); if (SoComplexityTypeElement::get(state) == SoComplexityTypeElement::OBJECT_SPACE) { SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DO_AUTO_CACHE); } } // Doc in superclass. void SoIndexedNurbsSurface::rayPick(SoRayPickAction * action) { if (GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) { SoShape::rayPick(action); // do normal generatePrimitives() pick } else { if (!this->shouldRayPick(action)) return; static SbBool firstpick = TRUE; if (firstpick) { firstpick = FALSE; SoDebugError::postWarning("SoIndexedNurbsSurface::rayPick", "Proper NURBS picking requires\n" "GLU version 1.3. Picking is done on bounding box."); } SoState * state = action->getState(); state->push(); SoPickStyleElement::set(state, this, SoPickStyleElement::BOUNDING_BOX); (void)this->shouldRayPick(action); // this will cause a pick on bbox state->pop(); } } // // used only for GLU callbacks // typedef struct { SoAction * action; SoIndexedNurbsSurface * thisp; SoPrimitiveVertex vertex; } coin_ins_cbdata; // Documented in superclass. void SoIndexedNurbsSurface::getPrimitiveCount(SoGetPrimitiveCountAction * action) { // for now, just generate primitives to count. Very slow, of course. SoShape::getPrimitiveCount(action); } /*! This method is part of the original SGI Inventor API, but not implemented in Coin, as it looks like a method that should probably have been private in Open Inventor. */ void SoIndexedNurbsSurface::sendPrimitive(SoAction * , SoPrimitiveVertex *) { COIN_OBSOLETED(); } // Documented in superclass. void SoIndexedNurbsSurface::generatePrimitives(SoAction * action) { if (GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) { // We've found that the SGI GLU NURBS renderer makes some OpenGL // calls even when in tessellate mode. So we need to set up an // offscreen context to be guaranteed to have a valid GL context // before making the GLU calls. if (PRIVATE(this)->offscreenctx == NULL) { PRIVATE(this)->offscreenctx = cc_glglue_context_create_offscreen(32, 32); } if (PRIVATE(this)->offscreenctx && cc_glglue_context_make_current(PRIVATE(this)->offscreenctx)) { PRIVATE(this)->doNurbs(action, FALSE); cc_glglue_context_reinstate_previous(PRIVATE(this)->offscreenctx); } } } // Documented in superclass. SoDetail * SoIndexedNurbsSurface::createTriangleDetail(SoRayPickAction * /* action */, const SoPrimitiveVertex * /*v1*/, const SoPrimitiveVertex * /*v2*/, const SoPrimitiveVertex * /*v3*/, SoPickedPoint * /* pp */) { return NULL; } void SoIndexedNurbsSurfaceP::doNurbs(SoAction * action, const SbBool glrender) { if (GLUWrapper()->available == 0 || !GLUWrapper()->gluNewNurbsRenderer) { #if COIN_DEBUG static int first = 1; if (first) { SoDebugError::postInfo("SoIndexedNurbsCurveP::doNurbs", "Looks like your GLU library doesn't have NURBS " "functionality"); first = 0; } #endif // COIN_DEBUG return; } if (!PUBLIC(this)->coordIndex.getNum()) return; if (this->nurbsrenderer == NULL) { this->nurbsrenderer = GLUWrapper()->gluNewNurbsRenderer(); if (GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) { GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_BEGIN_DATA, (gluNurbsCallback_cb_t)SoIndexedNurbsSurfaceP::tessBegin); GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_TEXTURE_COORD_DATA, (gluNurbsCallback_cb_t)SoIndexedNurbsSurfaceP::tessTexCoord); GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_NORMAL_DATA, (gluNurbsCallback_cb_t)SoIndexedNurbsSurfaceP::tessNormal); GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_VERTEX_DATA, (gluNurbsCallback_cb_t)SoIndexedNurbsSurfaceP::tessVertex); GLUWrapper()->gluNurbsCallback(this->nurbsrenderer, (GLenum) GLU_NURBS_END_DATA, (gluNurbsCallback_cb_t)SoIndexedNurbsSurfaceP::tessEnd); } } // NB, don't move this structure inside the if-statement. It needs // to be here so that the callbacks from sogl_render_nurbs_surface() // have a valid pointer to the structure. coin_ins_cbdata cbdata; if (GLUWrapper()->versionMatchesAtLeast(1, 3, 0)) { if (!glrender) { GLUWrapper()->gluNurbsCallbackData(this->nurbsrenderer, &cbdata); cbdata.action = action; cbdata.thisp = PUBLIC(this); cbdata.vertex.setNormal(SbVec3f(0.0f, 0.0f, 1.0f)); cbdata.vertex.setMaterialIndex(0); cbdata.vertex.setTextureCoords(SbVec4f(0.0f, 0.0f, 0.0f, 1.0f)); cbdata.vertex.setPoint(SbVec3f(0.0f, 0.0f, 0.0f)); cbdata.vertex.setDetail(NULL); } } SbBool texindex = PUBLIC(this)->textureCoordIndex.getNum() && PUBLIC(this)->textureCoordIndex[0] >= 0; int displaymode = (int) GLU_FILL; if (glrender) { switch (SoDrawStyleElement::get(action->getState())) { case SoDrawStyleElement::LINES: displaymode = (int) GLU_OUTLINE_POLYGON; break; case SoDrawStyleElement::POINTS: // not possible to draw NURBS as points using GLU... displaymode = (int) GLU_OUTLINE_PATCH; break; default: break; } } GLUWrapper()->gluNurbsProperty(this->nurbsrenderer, (GLenum) GLU_DISPLAY_MODE, (GLfloat) displaymode); sogl_render_nurbs_surface(action, PUBLIC(this), this->nurbsrenderer, PUBLIC(this)->numUControlPoints.getValue(), PUBLIC(this)->numVControlPoints.getValue(), PUBLIC(this)->uKnotVector.getValues(0), PUBLIC(this)->vKnotVector.getValues(0), PUBLIC(this)->uKnotVector.getNum(), PUBLIC(this)->vKnotVector.getNum(), PUBLIC(this)->numSControlPoints.getValue(), PUBLIC(this)->numTControlPoints.getValue(), PUBLIC(this)->sKnotVector.getValues(0), PUBLIC(this)->tKnotVector.getValues(0), PUBLIC(this)->sKnotVector.getNum(), PUBLIC(this)->tKnotVector.getNum(), glrender, PUBLIC(this)->coordIndex.getNum(), PUBLIC(this)->coordIndex.getValues(0), texindex ? PUBLIC(this)->textureCoordIndex.getNum() : 0, texindex ? PUBLIC(this)->textureCoordIndex.getValues(0) : NULL); } void APIENTRY SoIndexedNurbsSurfaceP::tessBegin(int type, void * data) { coin_ins_cbdata * cbdata = (coin_ins_cbdata*) data; SoIndexedNurbsSurface::TriangleShape shapetype; switch ((int)type) { case GL_TRIANGLES: shapetype = SoShape::TRIANGLES; break; case GL_TRIANGLE_STRIP: shapetype = SoShape::TRIANGLE_STRIP; break; case GL_TRIANGLE_FAN: shapetype = SoShape::TRIANGLE_FAN; break; case GL_QUADS: shapetype = SoShape::QUADS; break; case GL_QUAD_STRIP: shapetype = SoShape::QUAD_STRIP; break; case GL_POLYGON: shapetype = SoShape::POLYGON; break; default: shapetype = SoShape::POINTS; // illegal value #if COIN_DEBUG SoDebugError::postInfo("SoIndexedNurbsSurfaceP::tessBegin", "unsupported GL enum: 0x%x", type); #endif // COIN_DEBUG break; } if (shapetype != SoShape::POINTS) { cbdata->thisp->beginShape(cbdata->action, shapetype, NULL); } } void APIENTRY SoIndexedNurbsSurfaceP::tessTexCoord(float * texcoord, void * data) { coin_ins_cbdata * cbdata = (coin_ins_cbdata*) data; cbdata->vertex.setTextureCoords(SbVec2f(texcoord[0], texcoord[1])); } void APIENTRY SoIndexedNurbsSurfaceP::tessNormal(float * normal, void * data) { coin_ins_cbdata * cbdata = (coin_ins_cbdata*) data; cbdata->vertex.setNormal(SbVec3f(normal[0], normal[1], normal[2])); } void APIENTRY SoIndexedNurbsSurfaceP::tessVertex(float * vertex, void * data) { coin_ins_cbdata * cbdata = (coin_ins_cbdata*) data; cbdata->vertex.setPoint(SbVec3f(vertex[0], vertex[1], vertex[2])); cbdata->thisp->shapeVertex(&cbdata->vertex); } void APIENTRY SoIndexedNurbsSurfaceP::tessEnd(void * data) { coin_ins_cbdata * cbdata = (coin_ins_cbdata*) data; cbdata->thisp->endShape(); }