// Doc in parent void SoVRMLCylinder::GLRender(SoGLRenderAction * action) { if (!shouldGLRender(action)) return; SoState * state = action->getState(); SoMaterialBundle mb(action); mb.sendFirst(); unsigned int flags = 0; SbBool sendNormals = !mb.isColorOnly() || (SoMultiTextureCoordinateElement::getType(state) == SoMultiTextureCoordinateElement::FUNCTION); if (sendNormals) flags |= SOGL_NEED_NORMALS; if ((SoGLMultiTextureEnabledElement::get(state)) && SoMultiTextureCoordinateElement::getType(state) != SoMultiTextureCoordinateElement::TEXGEN) flags |= SOGL_NEED_TEXCOORDS; if (this->side.getValue()) flags |= SOGL_RENDER_SIDE; if (this->top.getValue()) flags |= SOGL_RENDER_TOP; if (this->bottom.getValue()) flags |= SOGL_RENDER_BOTTOM; float complexity = this->getComplexityValue(action); // enable back face culling SoGLShapeHintsElement::forceSend(state, TRUE, TRUE); sogl_render_cylinder(this->radius.getValue(), this->height.getValue(), (int)(CYL_SIDE_NUMTRIS * complexity), &mb, flags, state); }
// Doc in parent. void SoCylinder::GLRender(SoGLRenderAction * action) { if (!shouldGLRender(action)) return; SoState * state = action->getState(); SoCylinder::Part p = (SoCylinder::Part) this->parts.getValue(); SoMaterialBundle mb(action); mb.sendFirst(); SbBool sendNormals = !mb.isColorOnly() || (SoMultiTextureCoordinateElement::getType(state) == SoMultiTextureCoordinateElement::FUNCTION); unsigned int flags = 0; if (sendNormals) flags |= SOGL_NEED_NORMALS; if (SoGLMultiTextureEnabledElement::get(state, 0)) { if (SoGLMultiTextureEnabledElement::getMode(state, 0) == SoMultiTextureEnabledElement::TEXTURE3D) { flags |= SOGL_NEED_3DTEXCOORDS; } else { flags |= SOGL_NEED_TEXCOORDS; } } if (p & SIDES) flags |= SOGL_RENDER_SIDE; if (p & TOP) flags |= SOGL_RENDER_TOP; if (p & BOTTOM) flags |= SOGL_RENDER_BOTTOM; SoMaterialBindingElement::Binding bind = SoMaterialBindingElement::get(state); if (bind == SoMaterialBindingElement::PER_PART || bind == SoMaterialBindingElement::PER_PART_INDEXED) flags |= SOGL_MATERIAL_PER_PART; float complexity = this->getComplexityValue(action); sogl_render_cylinder(this->radius.getValue(), this->height.getValue(), (int)(CYL_SIDE_NUMTRIS * complexity), &mb, flags, state); }
void SoCylinder::GLRender(SoGLRenderAction *action) // //////////////////////////////////////////////////////////////////////// { // First see if the object is visible and should be rendered now if (! shouldGLRender(action)) return; // See if texturing is enabled SbBool doTextures = SoGLTextureEnabledElement::get(action->getState()); // Render the cylinder. The GLRenderGeneric() method handles any // case. The GLRenderNvertTnone() handles the case where we are // outputting normals but no texture coordinates. This case is // handled separately since it occurs often and warrants its own // method. SbBool sendNormals = (SoLightModelElement::get(action->getState()) != SoLightModelElement::BASE_COLOR); if (! doTextures && sendNormals) GLRenderNvertTnone(action); else GLRenderGeneric(action, sendNormals, doTextures); }
void SoNurbsSurface::GLRender(SoGLRenderAction *action) // //////////////////////////////////////////////////////////////////////// { // First see if the object is visible and should be rendered now if (! shouldGLRender(action)) return; const SoCoordinateElement *ce = SoCoordinateElement::getInstance(action->getState()); GLfloat *sKnots, *tKnots, *dstCoords; GLenum type; float *fKnots; int32_t nCoords, uOffset, vOffset; int32_t nsKnots, ntKnots, nsCoords, ntCoords; int32_t nDstCoords; int32_t sOffset, tOffset; int i, j; // Check for 0 control points nCoords = ce->getNum(); if (nCoords == 0) return; // Make sure the first current material is sent to GL SoMaterialBundle mb(action); mb.sendFirst(); // // Find the number of steps required for object space tessellation and // the pixel tolerance used for screen space tessellation. // float val = SoComplexityElement::get(action->getState()); if (val < 0.0) val = 0.0; if (val > 1.0) val = 1.0; int steps; if (val < 0.10) steps = 2; else if (val < 0.25) steps = 3; else if (val < 0.40) steps = 4; else if (val < 0.55) steps = 5; else steps = (int)(powf(val, 3.32)*28) + 2; float pixTolerance = 104.0*val*val - 252.0*val + 150; // // If the surface is being cached, or if the tessellation is in object // space, use the software NURBS library. Create a software NURBS // rendering class and use it to make nurbs rendering calls. Since // the software NURBS library generates triangles, texture mapping // will happen automatically without having to render a separate // texture surface. // if (SoComplexityTypeElement::get(action->getState()) == SoComplexityTypeElement::OBJECT_SPACE) { _SoNurbsGLRender *GLRender = new _SoNurbsGLRender(); // // Set the sampling to be constant across the surface with the // tessellation to be 'steps' across the S and T parameters // GLRender->setnurbsproperty( N_T2D, N_SAMPLINGMETHOD, N_FIXEDRATE ); GLRender->setnurbsproperty( N_V3D, N_SAMPLINGMETHOD, N_FIXEDRATE ); GLRender->setnurbsproperty( N_V3DR, N_SAMPLINGMETHOD, N_FIXEDRATE ); GLRender->setnurbsproperty( N_T2D, N_S_STEPS, steps); GLRender->setnurbsproperty( N_T2D, N_T_STEPS, steps); GLRender->setnurbsproperty( N_V3D, N_S_STEPS, steps); GLRender->setnurbsproperty( N_V3D, N_T_STEPS, steps); GLRender->setnurbsproperty( N_V3DR, N_S_STEPS, steps); GLRender->setnurbsproperty( N_V3DR, N_T_STEPS, steps); // Determine whether a texture coordinate surface must be generated SbBool doTextures = SoGLTextureEnabledElement::get(action->getState()); // Draw the surface drawNURBS (GLRender, action->getState(), doTextures); delete GLRender; return; } if (SoDrawStyleElement::get(action->getState()) == SoDrawStyleElement::POINTS) { // // Render the control points of the surface. Rendering the points // of the surface would be very slow, as the Software NURBS library // would have to be used, and because of the view dependent // tessellation, points would not necessarily remain visible. // glBegin(GL_POINTS); if (ce->is3D()) { for (i=0; i<nCoords; i++) { const SbVec3f & coords3 = ce->get3((int)i); glVertex3f ((GLfloat)(coords3[0]), (GLfloat)(coords3[1]), (GLfloat)(coords3[2])); } } else { for (i=0; i<nCoords; i++) { const SbVec4f & coords4 = ce->get4((int)i); glVertex4f ((GLfloat)(coords4[0]), (GLfloat)(coords4[1]), (GLfloat)(coords4[2]), (GLfloat)(coords4[3])); } } glEnd(); return; } // // Render the NURBS surface using the GLU. // GLUnurbsObj *nurbsObj = gluNewNurbsRenderer(); switch (SoDrawStyleElement::get(action->getState())) { case SoDrawStyleElement::FILLED: gluNurbsProperty (nurbsObj, (GLenum)GLU_DISPLAY_MODE, GLU_FILL); break; case SoDrawStyleElement::LINES: gluNurbsProperty (nurbsObj, (GLenum)GLU_DISPLAY_MODE, GLU_OUTLINE_POLYGON); break; } gluNurbsProperty (nurbsObj, (GLenum)GLU_SAMPLING_TOLERANCE, (GLfloat)pixTolerance); // // Collect the control points and knot vectors into an array suitable // for sending to the GL. The control points and knot vectors must be // converted to double precision so that they can be passed to the // GL NURBS routines. // GLfloat *dCoords, *duKnots, *dvKnots; if (ce->is3D()) { dCoords = (GLfloat *)new GLfloat[3*nCoords]; for (i=0; i<nCoords; i++) { const SbVec3f &c3 = ce->get3((int)i); dCoords[3*i] = (GLfloat)c3[0]; dCoords[3*i+1] = (GLfloat)c3[1]; dCoords[3*i+2] = (GLfloat)c3[2]; } uOffset = 3; type = GL_MAP2_VERTEX_3; } else { dCoords = (GLfloat *)new GLfloat[4*nCoords]; for (i=0; i<nCoords; i++) { const SbVec4f &c4 = ce->get4((int)i); dCoords[4*i] = (GLfloat)c4[0]; dCoords[4*i+1] = (GLfloat)c4[1]; dCoords[4*i+2] = (GLfloat)c4[2]; dCoords[4*i+3] = (GLfloat)c4[3]; } uOffset = 4; type = GL_MAP2_VERTEX_4; } vOffset = uOffset * numUControlPoints.getValue(); fKnots = (float *)uKnotVector.getValues(0); duKnots = (GLfloat *)new GLfloat[uKnotVector.getNum()]; for (i=0; i<uKnotVector.getNum(); i++) duKnots[i] = (GLfloat)fKnots[i]; fKnots = (GLfloat *)vKnotVector.getValues(0); dvKnots = (GLfloat *)new GLfloat[vKnotVector.getNum()]; for (i=0; i<vKnotVector.getNum(); i++) dvKnots[i] = (GLfloat)fKnots[i]; // Texture mapping. If doTextures == TRUE // we are drawing textures. If the textureCoordinateBinding is // DEFAULT, we have to build a default NURBS surface for the texture // coordinates, otherwise we use the texture coordinates in the texture // element. // If there is a software texture function defined, then we have to // create a texture nurb surface with the same number of points and // knots as the original surface, and call the texture coordinate function // at each vertex. SbBool doTextures = SoGLTextureEnabledElement::get(action->getState()); if(doTextures) { switch (SoTextureCoordinateElement::getType(action->getState())) { // software texture functions case SoTextureCoordinateElement::FUNCTION: { // generate S and T coords from U and V coords SbVec3f coord; SbVec2f stCoord; int offset; SoTextureCoordinateBundle tb(action, TRUE); nsCoords = numUControlPoints.getValue(); ntCoords = numVControlPoints.getValue(); sKnots = duKnots; tKnots = dvKnots; nsKnots = uKnotVector.getNum(); ntKnots = vKnotVector.getNum(); nDstCoords = nsCoords * ntCoords; dstCoords = (GLfloat *)new GLfloat[nDstCoords * 2]; for(int v = 0; v < ntCoords; v++) { for(int u = 0; u < nsCoords; u++) { if (ce->is3D()) { offset = 3 * (v * (int)nsCoords + u); coord[0] = dCoords[offset + 0]; coord[1] = dCoords[offset + 1]; coord[2] = dCoords[offset + 2]; } else { offset = 4 * (v * (int)nsCoords + u); coord[0] = dCoords[offset + 0] / dCoords[offset + 3]; coord[1] = dCoords[offset + 1] / dCoords[offset + 3]; coord[2] = dCoords[offset + 2] / dCoords[offset + 3]; } const SbVec4f &tc = tb.get(coord, SbVec3f(0.0, 1.0, 0.0)); dstCoords[(v * (int)nsCoords + u) * 2 + 0] = tc[0]; dstCoords[(v * (int)nsCoords + u) * 2 + 1] = tc[1]; } } break; } // texture coordinates defined from texture node case SoTextureCoordinateElement::EXPLICIT: // get texture coordinates from texture node const SoTextureCoordinateElement *te = SoTextureCoordinateElement::getInstance(action->getState()); int32_t nstCoords = te->getNum(); if (nstCoords < 1) { // Default texture coordinates are computed by defining // a bezier surface that is defined in the same valid // parameter space as the geometric surface. The valid // parameter space is defined based on the order and knot // vector. The coordinates go from 0 to one and the knot // vectors span the valid range of the geometric surface. // The knot vectors default to 0 and 1 in the event of bogus // input data. int uOrder, vOrder; GLfloat sKnotVal1, sKnotVal2, tKnotVal1, tKnotVal2; uOrder = uKnotVector.getNum() - numUControlPoints.getValue(); vOrder = vKnotVector.getNum() - numVControlPoints.getValue(); if ((uOrder > 0) && (uOrder < uKnotVector.getNum())) sKnotVal1 = duKnots[uOrder-1]; else sKnotVal1 = 0; if ((uOrder > 0) && (uOrder < uKnotVector.getNum())) sKnotVal2 = duKnots[uKnotVector.getNum()-uOrder]; else sKnotVal2 = 1; if ((vOrder > 0) && (vOrder < vKnotVector.getNum())) tKnotVal1 = dvKnots[vOrder-1]; else tKnotVal1 = 0; if ((vOrder > 0) && (vOrder < vKnotVector.getNum())) tKnotVal2 = dvKnots[vKnotVector.getNum()-vOrder]; else tKnotVal2 = 1; // do a linear 2x2 array nsKnots = 4; ntKnots = 4; sKnots = (GLfloat *)new GLfloat[4]; tKnots = (GLfloat *)new GLfloat[4]; sKnots[0] = sKnots[1] = sKnotVal1; tKnots[0] = tKnots[1] = tKnotVal1; sKnots[2] = sKnots[3] = sKnotVal2; tKnots[2] = tKnots[3] = tKnotVal2; // allocate a 2 x 2 array of GLfloat[2]'s nsCoords = 2; ntCoords = 2; nDstCoords = nsCoords * ntCoords * 2; dstCoords = (GLfloat *)new GLfloat[nDstCoords]; for(i = 0; i < 2; i++) { for(j = 0; j < 2; j++) { dstCoords[(i * 2 + j) * 2 + 0] = j; dstCoords[(i * 2 + j) * 2 + 1] = i; } } } else { // get knot vectors from this node nsKnots = sKnotVector.getNum(); fKnots = (float *)sKnotVector.getValues(0); sKnots = (GLfloat *)new GLfloat[nsKnots]; for (i=0; i < nsKnots; i++) sKnots[i] = (GLfloat)fKnots[i]; ntKnots = tKnotVector.getNum(); fKnots = (float *)tKnotVector.getValues(0); tKnots = (GLfloat *)new GLfloat[ntKnots]; for (i=0; i < ntKnots; i++) tKnots[i] = (GLfloat)fKnots[i]; nsCoords = numSControlPoints.getValue(); ntCoords = numTControlPoints.getValue(); nDstCoords = 2 * nstCoords; dstCoords = (GLfloat *)new GLfloat[nDstCoords]; for(i = 0; i < nstCoords; i++) { const SbVec2f &tc2 = te->get2(i); dstCoords[2*i] = (GLfloat)tc2[0]; dstCoords[2*i+1] = (GLfloat)tc2[1]; } } break; } sOffset = 2; tOffset = sOffset * nsCoords; } // // Draw the NURBS surface. Begin the surface. Then load the texture // map as a nurbs surface. Then, draw the geometric surface followed // by all of its trim curves. Then, end the surface. // glEnable(GL_AUTO_NORMAL); // Get one camera based element so that this node will be registered // with the cache. If the camera changes, this element will cause // the cache to be blown for this node and the nurbs surface will be // regenerated. SbMatrix vMat = SoViewingMatrixElement::get (action->getState()); SbMatrix mMat = SoModelMatrixElement::get (action->getState()); // Begin the surface. gluBeginSurface(nurbsObj); // Draw the texture surface if(doTextures) { // send down nurbs surface, then free memory gluNurbsSurface(nurbsObj, (GLint)nsKnots, sKnots, (GLint)ntKnots, tKnots, (GLint)sOffset, (GLint)tOffset, dstCoords, (GLint)(nsKnots - nsCoords), (GLint)(ntKnots - ntCoords), GL_MAP2_TEXTURE_COORD_2); // delete knots if not sharing them with the surface description // (in the case of software texture coordinates only) if(sKnots != duKnots) { delete [] sKnots; delete [] tKnots; } delete [] dstCoords; } gluNurbsSurface (nurbsObj, (GLint)(uKnotVector.getNum()), duKnots, (GLint)(vKnotVector.getNum()), dvKnots, (GLint)uOffset, (GLint)vOffset, dCoords, (GLint)(uKnotVector.getNum() - numUControlPoints.getValue()), (GLint)(vKnotVector.getNum() - numVControlPoints.getValue()), type); // // Get all of the trim curves and use them to trim the surface. // SoProfile *profile; const SoNodeList &trimNodes = SoProfileElement::get(action->getState()); SbBool haveTrim = FALSE; float *trimCoords, *trimKnots; int32_t numTrimCoords, numKnots, offset; int numTrims = trimNodes.getLength(); int floatsPerVec; // // For each trim curve, check its linkage to find out if it should be // continued on to the previous trim curve or if it should begin a // new trim curve. Then, send the trim to the NURBS library. // for (i=0; i<numTrims; i++) { GLfloat *dTrimCoords; GLfloat *dtmp; float *ftmp; // Get the trim curve. profile = (SoProfile *)trimNodes[(int) i]; profile->getTrimCurve (action->getState(), numTrimCoords, trimCoords, floatsPerVec, numKnots, trimKnots); // Check for degenerate trim curves if (numTrimCoords == 0) continue; // Check the linkage. if ((profile->linkage.getValue() == SoProfileElement::START_FIRST) || (profile->linkage.getValue() == SoProfileElement::START_NEW)) { if (haveTrim) gluEndTrim(nurbsObj); gluBeginTrim(nurbsObj); haveTrim = TRUE; } // Set the data type of the control points to non-rational or rational if (floatsPerVec == 2) type = (GLenum)GLU_MAP1_TRIM_2; else type = (GLenum)GLU_MAP1_TRIM_3; offset = floatsPerVec; dTrimCoords = new GLfloat[numTrimCoords*floatsPerVec]; dtmp = dTrimCoords; ftmp = trimCoords; for (j=0; j<floatsPerVec*numTrimCoords; j++) *dtmp++ = (GLfloat)(*ftmp++); if (numKnots == 0) { // Send down a Piecewise Linear Trim Curve gluPwlCurve (nurbsObj, (GLint)numTrimCoords, dTrimCoords, (GLint)offset, type); } else { // Send down a NURBS Trim Curve GLfloat *dTrimKnots = new GLfloat[numKnots]; dtmp = dTrimKnots; ftmp = trimKnots; for (j=0; j<numKnots; j++) *dtmp++ = (GLfloat)(*ftmp++); gluNurbsCurve (nurbsObj, (GLint)numKnots, dTrimKnots, (GLint)offset, dTrimCoords, (GLint)(numKnots - numTrimCoords), type); delete[] dTrimKnots; delete[] trimKnots; } delete[] dTrimCoords; delete[] trimCoords; } if (haveTrim) gluEndTrim(nurbsObj); gluEndSurface(nurbsObj); gluDeleteNurbsRenderer(nurbsObj); glDisable(GL_AUTO_NORMAL); delete[] dvKnots; delete[] duKnots; delete[] dCoords; }