/* * NodeNurbsSurface.cpp * * Copyright (C) 1999 Stephen F. White * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file "COPYING" for details); if * not, write to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. */ #include #include #ifndef _WIN32 # include "stdlib.h" #endif #include "stdafx.h" #include "NodeNurbsSurface.h" #include "Scene.h" #include "FieldValue.h" #include "SFInt32.h" #include "Mesh.h" #include "MFFloat.h" #include "MFInt32.h" #include "MFVec2f.h" #include "MFVec3f.h" #include "SFNode.h" #include "SFBool.h" #include "SFVec3f.h" #include "Vec2f.h" #include "Vec3f.h" #include "RenderState.h" #include "DuneApp.h" #include "NodeCoordinate.h" #include "NodeNormal.h" #include "NodeTextureCoordinate.h" #include "NodeIndexedFaceSet.h" #include "NodeNurbsGroup.h" #include "NurbsSurfaceDegreeElevate.h" #include "Util.h" #include "Field.h" #include "ExposedField.h" ProtoNurbsSurface::ProtoNurbsSurface(Scene *scene) : Proto(scene, "NurbsSurface") { uDimension.set( addField(SFINT32, "uDimension", new SFInt32(0), new SFInt32(0))); vDimension.set( addField(SFINT32, "vDimension", new SFInt32(0), new SFInt32(0))); uKnot.set( addField(MFFLOAT, "uKnot", new MFFloat())); vKnot.set( addField(MFFLOAT, "vKnot", new MFFloat())); uOrder.set( addField(SFINT32, "uOrder", new SFInt32(3), new SFInt32(2))); vOrder.set( addField(SFINT32, "vOrder", new SFInt32(3), new SFInt32(2))); ExposedField* cpoint = new ExposedField(MFVEC3F, "controlPoint", new MFVec3f()); cpoint->setFlags(EIF_RECOMMENDED); controlPoint.set(addExposedField(cpoint)); weight.set( addExposedField(MFFLOAT, "weight", new MFFloat(), new SFFloat(0.0f))); uTessellation.set( addExposedField(SFINT32, "uTessellation", new SFInt32(0))); vTessellation.set( addExposedField(SFINT32, "vTessellation", new SFInt32(0))); texCoord.set( addExposedField(SFNODE, "texCoord", new SFNode(NULL), NURBS_TEXTURE_SURFACE_NODE | TEXTURE_COORDINATE_NODE)); ccw.set( addField(SFBOOL, "ccw", new SFBool(true))); solid.set( addField(SFBOOL, "solid", new SFBool(true))); } Node * ProtoNurbsSurface::create(Scene *scene) { return new NodeNurbsSurface(scene, this); } NodeNurbsSurface::NodeNurbsSurface(Scene *scene, Proto *proto) : MeshBasedNode(scene, proto) { } NodeNurbsSurface::~NodeNurbsSurface() { delete _mesh; } void NodeNurbsSurface::createMesh() { int iuDimension = uDimension()->getValue(); int ivDimension = vDimension()->getValue(); int iuTess = uTessellation()->getValue(); int ivTess = vTessellation()->getValue(); bool bccw = ccw()->getValue(); float *weights = NULL; int i, j; if (iuDimension == 0 || ivDimension == 0) return; if (uKnot()->getSize() != uOrder()->getValue() + iuDimension || vKnot()->getSize() != vOrder()->getValue() + ivDimension) return; if (controlPoint()->getSize() != iuDimension * ivDimension * 3) return; if (weight()->getSize() == 0) { weights = new float[iuDimension * ivDimension]; for (i = 0; i < iuDimension * ivDimension; i++) { weights[i] = 1.0f; } } else if (weight()->getSize() != iuDimension * ivDimension) { return; } MFVec2f *texCoords = NULL; if (texCoord()->getValue()) if (texCoord()->getValue()->getType() == NODE_TEXTURE_COORDINATE) texCoords = ((NodeTextureCoordinate *)(texCoord()->getValue())) ->point(); if (iuTess <= 0) iuTess = 32; if (ivTess <= 0) ivTess = 32; int size = (iuTess + 1) * (ivTess + 1); Vec3f *tess = new Vec3f[size]; Vec3f *normals = new Vec3f[size]; const float *uKnots = uKnot()->getValues(); const float *vKnots = vKnot()->getValues(); float uInc = (uKnots[uKnot()->getSize()-1] - uKnots[0]) / iuTess; float vInc = (vKnots[vKnot()->getSize()-1] - vKnots[0]) / ivTess; int index = 0; float u, v; const float *w = weights ? weights : weight()->getValues(); for (j = 0, v = vKnots[0]; j <= ivTess; j++, v += vInc) { for (i = 0, u = uKnots[0]; i <= iuTess; i++, u += uInc) { tess[index] = surfacePoint( iuDimension, uOrder()->getValue(), uKnots, ivDimension, vOrder()->getValue(), vKnots, (const Vec3f *) controlPoint()->getValues(), w, u, v, normals[index]); if (!ccw()->getValue()) normals[index] = -normals[index]; index++; } } index = 0; Vec2f *tc = new Vec2f[size]; float uinv = 1.0f / iuTess; float vinv = 1.0f / ivTess; for (j = 0; j <= ivTess; j++) { for (i = 0; i <= iuTess; i++) { tc[index++] = Vec2f(i * uinv, j * vinv); } } int *ci = new int[size * 5]; index = 0; for (j = 0; j < ivTess; j++) { for (i = 0; i < iuTess; i++) { ci[index++] = j * (iuTess+1) + i; ci[index++] = j * (iuTess+1) + (i+1); ci[index++] = (j+1) * (iuTess+1) + (i+1); ci[index++] = (j+1) * (iuTess+1) + i; ci[index++] = -1; } } index = cleanDoubleVertices(ci, tess, normals, index, ccw()->getValue()); MFVec3f *vertices = new MFVec3f((float *) tess, size * 3); MFVec3f *normal = new MFVec3f((float *) normals, size * 3); MFInt32 *coordIndex = new MFInt32(ci, index); if (!texCoords) texCoords = new MFVec2f((float *) tc, size * 2); int meshFlags = 0; if (ccw()->getValue()) meshFlags |= MESH_CCW; if (solid()->getValue()) meshFlags |= MESH_SOLID; if (_mesh) delete _mesh; _mesh = new Mesh(vertices, coordIndex, normal, NULL, NULL, NULL, texCoords, NULL, 0, meshFlags); delete [] weights; } void NodeNurbsSurface::drawHandles() { int iuDimension = uDimension()->getValue(); int ivDimension = vDimension()->getValue(); RenderState state; if (controlPoint()->getSize() != iuDimension * ivDimension * 3) return; if (weight()->getSize() != iuDimension * ivDimension) { return; } glPushName(iuDimension * ivDimension + 1); glDisable(GL_LIGHTING); Util::myGlColor3f(1.0f, 1.0f, 1.0f); if (TheApp->GetHandleMeshAlways() || (_scene->getSelectedHandle() == -1)) { for (int i = 0; i < iuDimension; i++) { glBegin(GL_LINE_STRIP); for (int j = 0; j < ivDimension; j++) { const float *v = controlPoint()->getValue(i + j*iuDimension); float w = weight()->getValue(i + j*iuDimension); glVertex3f(v[0] / w, v[1] / w, v[2] / w); } glEnd(); } for (int j = 0; j < ivDimension; j++) { glBegin(GL_LINE_STRIP); for (int i = 0; i < iuDimension; i++) { const float *v = controlPoint()->getValue(i + j*iuDimension); float w = weight()->getValue(i + j*iuDimension); glVertex3f(v[0] / w, v[1] / w, v[2] / w); } glEnd(); } } state.startDrawHandles(); for (int ci = 0; ci < iuDimension * ivDimension; ci++) { glLoadName(ci); state.drawHandle( Vec3f(controlPoint()->getValue(ci)) / weight()->getValue(ci)); } state.endDrawHandles(); glPopName(); glEnable(GL_LIGHTING); } Vec3f NodeNurbsSurface::getHandle(int handle, int *constraint, int *field) { *constraint = CONSTRAIN_NONE; *field = controlPoint_Index() ; if (handle >= 0 && handle < controlPoint()->getSize() / 3) { Vec3f ret((Vec3f)controlPoint()->getValue(handle) / weight()->getValue(handle)); return ret; } else { return Vec3f(0.0f, 0.0f, 0.0f); } } void NodeNurbsSurface::setHandle(MFVec3f *value, int handle, float newWeight, const Vec3f &newV, const Vec3f &oldV, bool already_changed) { bool changed = false; MFVec3f *newValue = (MFVec3f *) value->copy(); if (_scene->getXSymetricNurbsMode()) { float epsilon = TheApp->GetEpsilon(); int numPoints = newValue->getSize() / 3; for (int i = 0; i < numPoints; i++) { if (i != handle) { Vec3f vPoint = controlPoint()->getValue(i); float wPoint = weight()->getValue(i); float w = wPoint; if (wPoint != 0) vPoint = vPoint / wPoint; if (newWeight != 0) w = w / newWeight; if ( (fabs(vPoint.z - oldV.z) < epsilon) && (fabs(vPoint.y - oldV.y) < epsilon)) { if (fabs(vPoint.x + oldV.x) < epsilon) { changed = true; if (fabs(oldV.x) < epsilon) newValue->setValue(i * 3, 0); else newValue->setValue(i * 3, - newV.x * w); newValue->setValue(i * 3+1, newV.y * w); newValue->setValue(i * 3+2, newV.z * w); } else if (fabs(vPoint.x - oldV.x) < epsilon) { changed = true; if (fabs(oldV.x) < epsilon) newValue->setValue(i * 3, 0); else newValue->setValue(i * 3, newV.x * w); newValue->setValue(i * 3+1, newV.y * w); newValue->setValue(i * 3+2, newV.z * w); } } } } } if (already_changed) changed = true; if (changed) { _meshDirty = true; _scene->setField(this, controlPoint_Index(), newValue); } } void NodeNurbsSurface::setHandle(float newWeight, const Vec3f &newV, const Vec3f &oldV) { setHandle(controlPoint(), -1, newWeight, newV, oldV); } NodeNurbsGroup * NodeNurbsSurface::findNurbsGroup() { if (hasParent()) { Node* parent = getParent(); if (parent->getType() == NODE_NURBS_GROUP) { return (NodeNurbsGroup *) parent; } else if (parent->getType() == NODE_SHAPE) if (parent->hasParent()) if (parent->getParent()->getType() == NODE_NURBS_GROUP) { return (NodeNurbsGroup *) parent->getParent(); } } return NULL; } void NodeNurbsSurface::setHandle(int handle, const Vec3f &v) { MFVec3f *newValue = new MFVec3f(*controlPoint()); float epsilon = TheApp->GetEpsilon(); int numPoints = controlPoint()->getSize() / 3; if (handle >= 0 && handle < numPoints) { float w = weight()->getValue(handle); Vec3f oldV = controlPoint()->getValue(handle); if (w != 0) oldV = oldV / w; Vec3f newV = v * w; if (_scene->getXSymetricNurbsMode() && (fabs(oldV.x) < epsilon)) newValue->setValue(handle * 3, 0); else newValue->setValue(handle * 3, newV.x); newValue->setValue(handle * 3+1, newV.y); newValue->setValue(handle * 3+2, newV.z); // set other handles for symetric modelling // which also snap handles at the same place setHandle(newValue, handle, w, newV, oldV, true); // search for NurbsGroup nodes and set handles if (_scene->getXSymetricNurbsMode()) { NodeNurbsGroup *nurbsGroup = findNurbsGroup(); if (nurbsGroup) nurbsGroup->setHandle(this, w, newV, oldV); } } } void NodeNurbsSurface::setField(int index, FieldValue *value) { _meshDirty = true; Node::setField(index, value); } int NodeNurbsSurface::findSpan(int dimension, int order, float u, const float knots[]) { int low, mid, high; int n = dimension + order - 1; if (u >= knots[n]) { return n - order; } low = order - 1; high = n - order + 1; mid = (low + high) / 2; int oldLow = low; int oldHigh = high; int oldMid = mid; while (u < knots[mid] || u >= knots[mid+1]) { if (u < knots[mid]) { high = mid; } else { low = mid; } mid = (low+high)/2; // emergency abort of loop, otherwise a endless loop can occure if ((low == oldLow) && (high == oldHigh) && (mid == oldMid)) break; oldLow = low; oldHigh = high; oldMid = mid; } return mid; } void NodeNurbsSurface::basisFuns(int span, float u, int order, const float knots[], float basis[], float deriv[]) { float *left = (float *) malloc(order * sizeof(float)); float *right = (float *) malloc(order * sizeof(float)); if ((left==NULL) || (right==NULL)) return; basis[0] = 1.0f; for (int j = 1; j < order; j++) { left[j] = u - knots[span+1-j]; right[j] = knots[span+j]-u; float saved = 0.0f, dsaved = 0.0f; for (int r = 0; r < j; r++) { if ((right[r+1] + left[j-r]) == 0) return; float temp = basis[r] / (right[r+1] + left[j-r]); basis[r] = saved + right[r+1] * temp; deriv[r] = dsaved - j * temp; saved = left[j-r] * temp; dsaved = j * temp; } basis[j] = saved; deriv[j] = dsaved; } free(left); free(right); } Vec3f NodeNurbsSurface::surfacePoint(int uDimension, int uOrder, const float uKnots[], int vDimension, int vOrder, const float vKnots[], const Vec3f controlPoints[], const float weight[], float u, float v, Vec3f &normal) { float *uBasis = (float *) malloc(uOrder * sizeof(float)); float *vBasis = (float *) malloc(vOrder * sizeof(float)); float *uDeriv = (float *) malloc(uOrder * sizeof(float)); float *vDeriv = (float *) malloc(vOrder * sizeof(float)); if ((uBasis==NULL) || (vBasis==NULL) || (uDeriv==NULL) || (vDeriv==NULL)) return NULL; int uSpan = findSpan(uDimension, uOrder, u, uKnots); int vSpan = findSpan(vDimension, vOrder, v, vKnots); basisFuns(uSpan, u, uOrder, uKnots, uBasis, uDeriv); basisFuns(vSpan, v, vOrder, vKnots, vBasis, vDeriv); int uBase = uSpan-uOrder+1; int vBase = vSpan-vOrder+1; int index = vBase*uDimension + uBase; Vec3f S(0.0f, 0.0f, 0.0f), du(0.0f, 0.0f, 0.0f), dv(0.0f, 0.0f, 0.0f); float w = 0.0f, duw = 0.0f, dvw = 0.0f; for (int j = 0; j < vOrder; j++) { for (int i = 0; i < uOrder; i++) { float gain = uBasis[i] * vBasis[j]; float dugain = uDeriv[i] * vBasis[j]; float dvgain = uBasis[i] * vDeriv[j]; S += controlPoints[index] * gain; w += weight[index] * gain; du += controlPoints[index] * dugain; dv += controlPoints[index] * dvgain; duw += weight[index] * dugain; dvw += weight[index] * dvgain; index++; } index += uDimension - uOrder; } S = S / w; Vec3f un = (du - S * duw) / w; Vec3f vn = (dv - S * dvw) / w; normal = un.cross(vn); normal.normalize(); free(uBasis); free(vBasis); free(uDeriv); free(vDeriv); return S; } bool NodeNurbsSurface::writeEXTERNPROTO(int f) { RET_ONERROR( mywritestr(f ,"EXTERNPROTO NurbsSurface[\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," field SFInt32 uDimension\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," field SFInt32 vDimension\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," field MFFloat uKnot\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," field MFFloat vKnot\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," field SFInt32 uOrder\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," field SFInt32 vOrder\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," exposedField MFVec3f controlPoint\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," exposedField MFFloat weight\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," exposedField SFInt32 uTessellation\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," exposedField SFInt32 vTessellation\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," exposedField SFNode texCoord\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," field SFBool ccw\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," field SFBool solid\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," ]\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ,"[\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," \"urn:web3d:vrml97:node:NurbsSurface\",\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," \"urn:inet:blaxxun.com:node:NurbsSurface\",\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ," \"urn:ParaGraph:NurbsSurface\",\n") ) TheApp->incSelectionLinenumber(); #ifdef HAVE_VRML97_AMENDMENT1_PROTO_URL RET_ONERROR( mywritestr(f ," \"") ) RET_ONERROR( mywritestr(f ,HAVE_VRML97_AMENDMENT1_PROTO_URL) ) RET_ONERROR( mywritestr(f ,"/NurbsSurfacePROTO.wrl") ) RET_ONERROR( mywritestr(f ,"\"\n") ) TheApp->incSelectionLinenumber(); #else RET_ONERROR( mywritestr(f ," \"NurbsSurfacePROTO.wrl\",\n") ) TheApp->incSelectionLinenumber(); #endif RET_ONERROR( mywritestr(f ," \"http://www.csv.ica.uni-stuttgart.de/vrml/dune/docs/vrml97Amendment1/NurbsSurfacePROTO.wrl\"\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( mywritestr(f ,"]\n") ) TheApp->incSelectionLinenumber(); return true; } int NodeNurbsSurface::write(int filedes, int indent) { if (_scene->isPureVRML97()) { Node * node = toIndexedFaceSet(); RET_ONERROR( node->write(filedes, indent) ) node->unref(); } else RET_ONERROR( NodeData::write(filedes, indent) ) return 0; } void NodeNurbsSurface::rotate(SFRotation rot) { int numPoints = controlPoint()->getSize() / 3; for (int i = 0; i < numPoints; i++) { Vec3f vPoint = controlPoint()->getValue(i); vPoint = rot.getQuat() * vPoint; controlPoint()->setSFValue(i, new SFVec3f(vPoint)); } } void NodeNurbsSurface::flip(int index) { if (controlPoint()) controlPoint()->flip(index); SFBool *bccw = new SFBool(!(ccw()->getValue())); ccw(bccw); _meshDirty = true; } Node* NodeNurbsSurface::degreeElevate(int newUDegree, int newVDegree) { //degree elevate a nurbs surface, if(newUDegree < ((uOrder()->getValue())-1)){ return NULL; } if(newVDegree < ((vOrder()->getValue())-1)){ return NULL; } NodeNurbsSurface *node = (NodeNurbsSurface *) _scene->createNode("NurbsSurface"); if((newUDegree >= ((uOrder()->getValue())-1)) && (newVDegree >= ((vOrder()->getValue())-1))){ //load old values int i; int tuDimension = uDimension()->getValue(); int tvDimension = vDimension()->getValue(); Vec3f *tPoints = new Vec3f[tuDimension * tvDimension]; float *tWeights = new float[tuDimension * tvDimension]; int tuOrder = uOrder()->getValue(); int tvOrder = vOrder()->getValue(); int uKnotSize = uKnot()->getSize(); int vKnotSize = vKnot()->getSize(); Array tuKnots(uKnotSize); Array tvKnots(vKnotSize); int tuDegree = tuOrder - 1; int tvDegree = tvOrder - 1; int tuUpDegree = newUDegree - tuDegree; int tvUpDegree = newVDegree - tvDegree; for (i=0; i<(tuDimension*tvDimension); i++){ tPoints[i] = controlPoint()->getValue(i); tWeights[i] =weight()->getValue(i); } for (i=0; igetValue(i); } for (i=0; igetValue(i); } //elevate surface NurbsSurfaceDegreeElevate elevatedSurface(tPoints, tWeights, tuKnots, tvKnots, tuDimension, tvDimension, tuDegree, tvDegree, tuUpDegree, tvUpDegree); //load new node int newUDimension = elevatedSurface.getUDimension(); int newVDimension = elevatedSurface.getVDimension(); float *newControlPoints = new float[newUDimension * newVDimension *3]; float *newWeights = new float[newUDimension * newVDimension]; float *newUKnots = new float[elevatedSurface.getUKnotSize()]; float *newVKnots = new float[elevatedSurface.getVKnotSize()]; int newUOrder = newUDegree + 1; int newVOrder = newVDegree + 1; for(i=0; i<(newUDimension * newVDimension); i++){ newControlPoints[(i*3)] = elevatedSurface.getControlPoints(i).x; newControlPoints[(i*3)+1] = elevatedSurface.getControlPoints(i).y; newControlPoints[(i*3)+2] = elevatedSurface.getControlPoints(i).z; newWeights[i] = elevatedSurface.getWeights(i); } for(i=0; i<(elevatedSurface.getUKnotSize()); i++){ newUKnots[i] = elevatedSurface.getUKnots(i); } for(i=0; i<(elevatedSurface.getVKnotSize()); i++){ newVKnots[i] = elevatedSurface.getVKnots(i); } node->setField(node->uDimension_Index(), new SFInt32(newUDimension)); node->setField(node->vDimension_Index(), new SFInt32(newVDimension)); node->uKnot(new MFFloat(newUKnots, newUDimension + newUOrder)); node->vKnot(new MFFloat(newVKnots, newVDimension + newVOrder)); node->setField(node->uOrder_Index(), new SFInt32(newUOrder)); node->setField(node->vOrder_Index(), new SFInt32(newVOrder)); node->controlPoint(new MFVec3f(newControlPoints, newUDimension * newVDimension * 3)); node->weight(new MFFloat(newWeights, newUDimension * newVDimension)); node->uTessellation(new SFInt32(uTessellation()->getValue())); node->vTessellation(new SFInt32(vTessellation()->getValue())); node->texCoord(new SFNode(texCoord()->getValue())); node->solid(new SFBool(solid()->getValue())); node->ccw(new SFBool(ccw()->getValue())); return node; } return NULL; }