File: [Development] / inventor / apps / nodes / GeneralizedCylinder / GeneralizedCylinder.c++ (download)
Revision 1.2, Sat Oct 14 10:46:06 2000 UTC (17 years ago) by jlim
Branch: MAIN
CVS Tags: release-2_1_5-9, release-2_1_5-8, release-2_1_5-10, HEAD Changes since 1.1: +2 -1
lines
Fixed Bug 22, removed dependence on POSIX_SOURCE and _XOPEN_SOURCE, conform to
ANSI 'for' scoping rules (Bug 7), added proper type casts, replaced bcopy()
with memcpy(), and eliminated warnings about implicit function definitions.
|
/*
*
* Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/*
| Classes:
| GeneralizedCylinder
|
| Author(s) : Paul Isaacs
|
*/
#include <stdio.h>
#include <Inventor/SbLinear.h>
#include <Inventor/SoDB.h>
#include <Inventor/nodekits/SoAppearanceKit.h>
#include <Inventor/nodes/SoGroup.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoShapeHints.h>
#include <Inventor/nodes/SoCoordinate3.h>
#include <Inventor/nodes/SoFaceSet.h>
#include <Inventor/nodes/SoIndexedFaceSet.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoQuadMesh.h>
#include <Inventor/nodes/SoSwitch.h>
#include <Inventor/nodes/SoTextureCoordinateBinding.h>
#include <Inventor/nodes/SoTextureCoordinate2.h>
#include <Inventor/nodes/SoTexture2Transform.h>
#include <Inventor/nodes/SoTexture2.h>
#include <Inventor/nodes/SoTransform.h>
#include <Inventor/nodes/SoTriangleStripSet.h>
#include <Inventor/manips/SoTransformManip.h>
#include <Inventor/draggers/SoDragger.h>
#include <Inventor/nodes/SoSurroundScale.h>
#include <Inventor/actions/SoWriteAction.h>
#include <Inventor/sensors/SoNodeSensor.h>
#include "GeneralizedCylinder.h"
#include "Triangulator.h"
#include "NurbMaker.h"
#include <Inventor/nodes/SoVertexShape.h>
SO_KIT_SOURCE(GeneralizedCylinder);
////////////////////////////////////////////////////////////////////////
//
// Description:
// This initializes the GeneralizedCylinder class.
//
// Use: internal
void
GeneralizedCylinder::initClass()
//
////////////////////////////////////////////////////////////////////////
{
SO__KIT_INIT_CLASS(GeneralizedCylinder, "GeneralizedCylinder",
SoSeparatorKit);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor
//
// Use: public
GeneralizedCylinder::GeneralizedCylinder()
//
////////////////////////////////////////////////////////////////////////
{
SO_KIT_CONSTRUCTOR(GeneralizedCylinder);
SO_KIT_ADD_FIELD(renderShapeType, (FACE_SET));
SO_KIT_ADD_FIELD(normsFlipped, (FALSE));
SO_KIT_ADD_FIELD(profileClosed, (FALSE));
SO_KIT_ADD_FIELD(crossSectionClosed, (FALSE));
SO_KIT_ADD_FIELD(spineClosed, (FALSE));
SO_KIT_ADD_FIELD(twistClosed, (FALSE));
SO_KIT_ADD_FIELD(minNumRows, (1));
SO_KIT_ADD_FIELD(minNumCols, (1));
SO_KIT_ADD_FIELD(withSides, (TRUE));
SO_KIT_ADD_FIELD(withTopCap, (TRUE));
SO_KIT_ADD_FIELD(withBottomCap, (TRUE));
SO_KIT_ADD_FIELD(withTextureCoords, (FALSE));
SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType, FACE_SET);
SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType, TRIANGLE_STRIP_SET);
SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType, QUAD_MESH);
SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType, BEZIER_SURFACE);
SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType, CUBIC_SPLINE_SURFACE);
SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType, CUBIC_TO_EDGE_SURFACE);
SO_KIT_SET_SF_ENUM_TYPE(renderShapeType, RenderShapeType);
// The input half of the scene graph
SO_KIT_ADD_CATALOG_ENTRY(inputSwitch,SoSwitch,TRUE,topSeparator,childList,FALSE );
SO_KIT_ADD_CATALOG_ENTRY(profileCoords,SoCoordinate3,TRUE,inputSwitch,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(crossSectionCoords,SoCoordinate3,TRUE,inputSwitch,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(spineCoords,SoCoordinate3,TRUE,inputSwitch,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(twistCoords,SoCoordinate3,TRUE,inputSwitch,,TRUE );
// The output half of the scene graph
// ( the shape produced during 'updateSurface' based on the contents
// of the input half of the scene graph
SO_KIT_ADD_CATALOG_ENTRY(outputGrp,SoGroup,TRUE,topSeparator,childList,FALSE );
SO_KIT_ADD_CATALOG_ENTRY(shapeHints,SoShapeHints,TRUE,outputGrp,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(textureBinding,SoTextureCoordinateBinding,TRUE,outputGrp,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(textureCoords,SoTextureCoordinate2,TRUE,outputGrp,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(texture2,SoTexture2,TRUE,outputGrp,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(coords,SoCoordinate3,TRUE,outputGrp,,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(faceSet,SoFaceSet,TRUE,outputGrp,,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(quadMesh,SoQuadMesh,TRUE,outputGrp,,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(nurbsSurfaceGroup,SoGroup,TRUE,outputGrp,,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(triangleStripSet,SoTriangleStripSet,TRUE,outputGrp,,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(capTextureBinding,SoTextureCoordinateBinding,TRUE,outputGrp,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(capTextureCoords,SoTextureCoordinate2,TRUE,outputGrp,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(topCapCoords,SoCoordinate3,TRUE,outputGrp,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(topCapFaces,SoIndexedFaceSet,TRUE,outputGrp,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(bottomCapCoords,SoCoordinate3,TRUE,outputGrp,,TRUE );
SO_KIT_ADD_CATALOG_ENTRY(bottomCapFaces,SoIndexedFaceSet,TRUE,outputGrp,,TRUE );
SO_KIT_INIT_INSTANCE();
//////////////////////////////////////////
// Set up the input half of the subgraph
//////////////////////////////////////////
SoSwitch *crvSwitch = new SoSwitch;
crvSwitch->whichChild = SO_SWITCH_NONE;
setAnyPart( "inputSwitch",crvSwitch,TRUE );
setPart( "profileCoords",new SoCoordinate3 );
setPart( "crossSectionCoords",new SoCoordinate3 );
setPart( "spineCoords", new SoCoordinate3 );
setPart( "twistCoords", new SoCoordinate3 );
// Set the default values for these curves...
set("profileCoords { point [ 1 -1 0, 1 1 0 ] }" );
set("crossSectionCoords { point [ 1 0 1, 1 0 -1, -1 0 -1, -1 0 1 ] }" );
set("spineCoords { point [ 0 -1 0, 0 1 0 ] }" );
set("twistCoords { point [ 0 -1 0, 0 1 0 ] }" );
//////////////////////////////////////////
// Set up the flags and parameters
//////////////////////////////////////////
fullProfile = new SoCoordinate3;
fullCrossSection = new SoCoordinate3;
fullProfile->ref();
fullCrossSection->ref();
profileAlreadyClosed = FALSE;
crossSectionAlreadyClosed = FALSE;
spineAlreadyClosed = FALSE;
twistAlreadyClosed = FALSE;
alreadyWithTextureCoords = FALSE;
//////////////////////////////////////////
// Set up the output half of the subgraph
// that needs to be ready ahead of time.
//////////////////////////////////////////
//
// This combination of shape hints will turn on two-sided lighting
// for us (if it is available)
//
SoShapeHints *hints = new SoShapeHints;
hints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE;
hints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE;
hints->faceType = SoShapeHints::CONVEX;
hints->creaseAngle = M_PI/6.0;
setPart( "shapeHints", hints );
topCapScratchFace = new SoIndexedFaceSet;
topCapScratchFace->ref();
bottomCapScratchFace = new SoIndexedFaceSet;
bottomCapScratchFace->ref();
numAllocedSpineKeyRots = 0;
spineKeyPointRotations = NULL;
spineParamDistances = NULL;
// Initialize our nurbMaker
myNurbMaker = NULL;
selfSensor = new SoNodeSensor( GeneralizedCylinder::fieldsChangedCB, this);
selfSensor->setPriority(0);
inputSensor = new SoNodeSensor( GeneralizedCylinder::inputChangedCB, this);
inputSensor->setPriority(0);
setUpConnections(TRUE,TRUE);
}
// detach/attach any sensors, callbacks, and/or field connections.
// Called by: start/end of SoBaseKit::readInstance
// and on new copy by: start/end of SoBaseKit::copy.
// Classes that redefine must call setUpConnections(TRUE,TRUE)
// at end of constructor.
// Returns the state of the node when this was called.
SbBool
GeneralizedCylinder::setUpConnections( SbBool onOff, SbBool doItAlways )
{
if ( !doItAlways && connectionsSetUp == onOff)
return onOff;
if ( onOff ) {
// We connect AFTER base class.
SoSeparatorKit::setUpConnections( onOff, doItAlways );
// Turn sensors on...
SoNode *iSw = inputSwitch.getValue();
if (iSw && inputSensor->getAttachedNode() != iSw)
inputSensor->attach(iSw);
if (selfSensor->getAttachedNode() != this)
selfSensor->attach(this);
updateSurface();
}
else {
// We disconnect BEFORE base class.
if (selfSensor->getAttachedNode())
selfSensor->detach();
if (inputSensor->getAttachedNode())
inputSensor->detach();
SoSeparatorKit::setUpConnections( onOff, doItAlways );
}
return !(connectionsSetUp = onOff);
}
void
GeneralizedCylinder::setDefaultOnNonWritingFields()
{
// Before writing, call setDefault(TRUE) on all parts
// under the output group.
outputGrp.setDefault(TRUE);
shapeHints.setDefault(TRUE);
textureBinding.setDefault(TRUE);
textureCoords.setDefault(TRUE);
texture2Transform.setDefault(TRUE);
coords.setDefault(TRUE);
faceSet.setDefault(TRUE);
quadMesh.setDefault(TRUE);
nurbsSurfaceGroup.setDefault(TRUE);
triangleStripSet.setDefault(TRUE);
capTextureBinding.setDefault(TRUE);
capTextureCoords.setDefault(TRUE);
topCapCoords.setDefault(TRUE);
topCapFaces.setDefault(TRUE);
bottomCapCoords.setDefault(TRUE);
bottomCapFaces.setDefault(TRUE);
// Call base class...
SoSeparatorKit::setDefaultOnNonWritingFields();
}
//
// Destructor. Must unref everything reffed in the constructor.
//
GeneralizedCylinder::~GeneralizedCylinder()
{
if ( topCapScratchFace != NULL) {
topCapScratchFace->unref();
topCapScratchFace = NULL;
}
if ( bottomCapScratchFace != NULL) {
bottomCapScratchFace->unref();
bottomCapScratchFace = NULL;
}
if ( fullProfile != NULL ) {
fullProfile->unref();
fullProfile = NULL;
}
if ( fullCrossSection != NULL ) {
fullCrossSection->unref();
fullProfile = NULL;
}
if ( selfSensor != NULL ) {
delete selfSensor;
selfSensor = NULL;
}
if ( inputSensor != NULL ) {
delete inputSensor;
inputSensor = NULL;
}
if (myNurbMaker != NULL)
delete myNurbMaker;
}
void
GeneralizedCylinder::loadRow( int rowNum, SbVec3f *newCoords )
{
float scale, paramDist;
SbVec3f center;
SbRotation rot;
SbRotation twistRot;
scale = fullProfile->point[rowNum][0];
paramDist = (fullProfile->point[rowNum][1] - profileMinY)
* profileOverHeight;
getSpineInfo( paramDist, center, rot );
getTwistInfo( paramDist, twistRot );
SbMatrix changer, temp;
changer.setRotate( twistRot );
temp.setScale( scale );
changer.multRight( temp );
temp.setRotate( rot );
changer.multRight( temp );
temp.setTranslate( center );
changer.multRight( temp );
int numCrossPts = fullCrossSection->point.getNum();
for ( int i = 0; i < numCrossPts; i++ )
changer.multVecMatrix( fullCrossSection->point[i], newCoords[i] );
}
void
GeneralizedCylinder::loadTextureRow( int rowNum, SbVec2f *newCoords )
{
float uVal, vVal;
SoMFVec3f *cSC = &fullCrossSection->point;
if ( rowNum == -1 ) {
// special case. This is for the caps of the object.
for ( int i = 0; i < cSC->getNum(); i++ ) {
uVal = ( (*cSC)[i][0] - crossSectionMinX ) / crossSectionWidth;
vVal = 1 - (( (*cSC)[i][2] - crossSectionMinZ) / crossSectionDepth);
newCoords[i].setValue( uVal, vVal );
}
return;
}
// vVal is distance parametrically along the spine...
vVal = (fullProfile->point[rowNum][1] - profileMinY)
* profileOverHeight;
SbVec3f diff;
int numCrossPts = cSC->getNum();
float thisLength = 0.0;
for ( int i = 0; i < numCrossPts; i++ ) {
if ( i > 0 ) {
diff = (*cSC)[i] - (*cSC)[i-1];
thisLength += diff.length();
}
uVal = thisLength / crossSectionLength;
newCoords[i].setValue( uVal, vVal );
}
}
void
GeneralizedCylinder::getSpineInfo( float paramDist, SbVec3f ¢er,
SbRotation &rot )
{
if ( spineLength == 0.0 ) {
center = SbVec3f( 0,paramDist * profileHeight,0);
rot = SbRotation::identity();
return;
}
float desiredDist = paramDist * spineLength;
float lengthSoFar = 0.0;
float nextLength = 0.0;
SbVec3f p1, p2, diff, norm1, norm2, norm;
SbVec3f zAxis( 0, 0, 1 );
SoCoordinate3 *spinC = SO_GET_PART( this, "spineCoords", SoCoordinate3 );
if ( paramDist > 1.0 ) {
int last = spinC->point.getNum() - 1;
center = spinC->point[ last ];
rot = getSpinePointRotation( last, last, 0.0 );
return;
}
else if ( paramDist < 0.0 ) {
center = spinC->point[ 0 ];
rot = getSpinePointRotation( 0, 0, 0.0 );
return;
}
for ( int i = 0; i < spinC->point.getNum() - 1; i++ ) {
p1 = spinC->point[i];
p2 = spinC->point[i+1];
diff = p2 - p1;
nextLength = lengthSoFar + diff.length();
if ( desiredDist >= lengthSoFar && desiredDist <= nextLength ) {
float fraction = (desiredDist - lengthSoFar)
/ (nextLength - lengthSoFar);
center = p1 + fraction * ( p2 - p1 );
rot = getSpinePointRotation( i, i+1, fraction );
return;
}
lengthSoFar = nextLength;
}
}
SbRotation
GeneralizedCylinder::getSpinePointRotation(int ind0, int ind1, float paramDist )
{
SbRotation rot0, rot1, interpedRot;
if ( ind0 == ind1 )
return( spineKeyPointRotations[ind0]);
else {
rot0 = spineKeyPointRotations[ind0];
rot1 = spineKeyPointRotations[ind1];
interpedRot = SbRotation::slerp( rot0, rot1, paramDist );
return interpedRot;
}
}
void
GeneralizedCylinder::initSpineKeyPointRotations()
{
SoCoordinate3 *spinC = SO_GET_PART( this, "spineCoords", SoCoordinate3 );
if ( spinC->point.getNum() > numAllocedSpineKeyRots ) {
if ( spineKeyPointRotations != NULL )
delete [] spineKeyPointRotations;
spineKeyPointRotations = new SbRotation[spinC->point.getNum()];
numAllocedSpineKeyRots = spinC->point.getNum();
}
if (spinC->point.getNum() <= 0)
return;
if (spinC->point.getNum() == 1) {
spineKeyPointRotations[0] = SbRotation::identity();
return;
}
SbVec3f xAxis( 1, 0, 0 );
SbVec3f yAxis( 0, 1, 0 );
SbVec3f zAxis( 0, 0, 1 );
SbVec3f zeroVec( 0, 0, 0 );
SbVec3f diff1, diff2;
if (spinC->point.getNum() == 2) {
diff1 = spinC->point[1] - spinC->point[0];
diff1.normalize();
spineKeyPointRotations[0].setValue( yAxis, diff1 );
spineKeyPointRotations[1] = spineKeyPointRotations[0];
return;
}
// More than two points...
SbVec3f rotatedX, rotatedY, rotatedZ, prevRotatedZ;
SbVec3f p0, p1, p2;
int lastInd = spinC->point.getNum() - 1;
// Find rotation for First point...
// First, treat case where spine is a closed polygon
if ( spineClosed.getValue() ) {
// Z axis is perp to plane formed by three points around p[0]
diff1 = spinC->point[lastInd-1] - spinC->point[0];
diff2 = spinC->point[1] - spinC->point[0];
rotatedZ = diff2.cross( diff1 );
// If 3 pts colinear, don't do this, but go on to other algorithm...
if ( rotatedZ != zeroVec ) {
rotatedZ.normalize();
// But make it lean towards the Z axis of the previous point.
if ( rotatedZ.dot( zAxis ) < 0 )
rotatedZ *= -1.0;
// X axis is cross product of (nextPt-prevPt) with Z axis.
diff1 = spinC->point[1] - spinC->point[lastInd-1];
rotatedX = diff1.cross( rotatedZ );
rotatedX.normalize();
rotatedY = rotatedZ.cross( rotatedX );
}
}
// Spine not closed, or it was closed but couldn't get rotation...
if ( rotatedZ == zeroVec || !spineClosed.getValue() ) {
// Y axis just points from point 0 to first point that differs.
int i = 0;
p0 = p1 = spinC->point[i++];
while (p0 == p1 && i <= lastInd )
p1 = spinC->point[i++];
if ( p0 == p1 ) {
// all points were the same. Make everyone have no rotation.
for ( int j = 0; j <= lastInd; j++ ) {
spineKeyPointRotations[j] = SbRotation::identity();
}
return;
}
rotatedY = p1 - p0;
rotatedY.normalize();
// Z axis is perpendicular to plane formed by 1st three points.
// Repeat as long as the two vectors are colinear.
diff1 = p0 - p1;
diff1.normalize();
rotatedZ = zeroVec;
while (rotatedZ == zeroVec && i <= lastInd ) {
p2 = spinC->point[i++];
diff2 = p2 - p1;
diff2.normalize();
rotatedZ = diff2.cross( diff1 );
}
if ( rotatedZ == zeroVec ) {
// all points coplanar. Make everyone have the same rotation.
for ( int j = 0; j <= lastInd; j++ ) {
spineKeyPointRotations[j].setValue( yAxis, rotatedY );
}
return;
}
rotatedZ.normalize();
// But make it lean towards positive Z each time.
if ( rotatedZ.dot( zAxis ) < 0 )
rotatedZ *= -1.0;
// X axis is calculated based on Y and Z
rotatedX = rotatedY.cross( rotatedZ );
}
SbMatrix firstAnswerMat(rotatedX[0],rotatedX[1],rotatedX[2],0,
rotatedY[0],rotatedY[1],rotatedY[2],0,
rotatedZ[0],rotatedZ[1],rotatedZ[2],0,
0 , 0 , 0 ,1);
spineKeyPointRotations[0].setValue(firstAnswerMat);
prevRotatedZ = rotatedZ;
// Find rotation for all but first and last points...
for ( int ind = 1; ind < lastInd; ind++ ) {
// Z axis is perpendicular to plane formed by three points surrounding
// This one.
diff1 = spinC->point[ind-1] - spinC->point[ind];
diff2 = spinC->point[ind+1] - spinC->point[ind];
diff1.normalize();
diff2.normalize();
rotatedZ = diff2.cross( diff1 );
if (rotatedZ == zeroVec) // if colinear, use previous rotatedZ
rotatedZ = prevRotatedZ;
rotatedZ.normalize();
// But make it lean towards the Z axis of the previous point.
if ( rotatedZ.dot( prevRotatedZ ) < 0.0 )
rotatedZ *= -1.0;
// X axis is cross product of (nextPt-prevPt) with Z axis.
diff1 = spinC->point[ind+1] - spinC->point[ind-1];
if ( diff1 != zeroVec ) {
diff1.normalize();
rotatedX = diff1.cross( rotatedZ );
rotatedX.normalize();
}
// if diff1 = 0, just use previous value for rotatedX
// Y axis is calculated based on X and Z
rotatedY = rotatedZ.cross( rotatedX );
SbMatrix answerMat(rotatedX[0],rotatedX[1],rotatedX[2],0,
rotatedY[0],rotatedY[1],rotatedY[2],0,
rotatedZ[0],rotatedZ[1],rotatedZ[2],0,
0 , 0 , 0 ,1);
spineKeyPointRotations[ind].setValue(answerMat);
prevRotatedZ = rotatedZ;
}
// Find rotation for Last point...
if ( spineClosed.getValue() ) {
spineKeyPointRotations[lastInd] = spineKeyPointRotations[0];
}
else {
// Y axis just points from next-to-last point to last point
diff1 = spinC->point[lastInd] - spinC->point[lastInd - 1];
if (diff1 != zeroVec )
rotatedY = diff1;
// otherwise use previous value...
rotatedY.normalize();
// Z axis is same as previous value.
rotatedZ = prevRotatedZ;
// X axis is calculated based on Y and Z
rotatedX = rotatedY.cross( rotatedZ );
SbMatrix lastAnswerMat(rotatedX[0],rotatedX[1],rotatedX[2],0,
rotatedY[0],rotatedY[1],rotatedY[2],0,
rotatedZ[0],rotatedZ[1],rotatedZ[2],0,
0 , 0 , 0 ,1);
spineKeyPointRotations[lastInd].setValue(lastAnswerMat);
}
}
void
GeneralizedCylinder::getTwistInfo( float paramDist, SbRotation &twistRot )
{
// X coordinate determines amount of twist
float desiredHeight;
desiredHeight = paramDist * (twistMaxY - twistMinY) + twistMinY;
if (desiredHeight < twistMinY)
desiredHeight = twistMinY;
else if (desiredHeight > twistMaxY)
desiredHeight = twistMaxY;
float thisHeight, nextHeight, thisAngle, nextAngle;
float angle, fraction;
SbVec3f yAxis( 0, 1, 0 );
SoCoordinate3 *twisC = SO_GET_PART( this, "twistCoords", SoCoordinate3 );
for ( int i = 0; i < twisC->point.getNum() - 1; i++ ) {
thisHeight = twisC->point[i][1];
nextHeight = twisC->point[i+1][1];
if ( desiredHeight >= thisHeight && desiredHeight <= nextHeight ) {
fraction = (desiredHeight - thisHeight) / (nextHeight - thisHeight);
thisAngle = twisC->point[i][0];
nextAngle = twisC->point[i+1][0];
angle = thisAngle + fraction * ( nextAngle - thisAngle );
twistRot = SbRotation( yAxis, angle );
return;
}
if ( desiredHeight >= nextHeight && desiredHeight <= thisHeight ) {
fraction = (desiredHeight - nextHeight) / (thisHeight - nextHeight);
thisAngle = twisC->point[i][0];
nextAngle = twisC->point[i+1][0];
angle = nextAngle + fraction * ( thisAngle - nextAngle );
twistRot = SbRotation( yAxis, angle );
return;
}
}
twistRot = SbRotation::identity();
return;
}
void
GeneralizedCylinder::initUpdateInfo()
{
profileMinY = 100000000;
float maxY = -100000000;
float val;
SoCoordinate3 *profC = SO_GET_PART( this, "profileCoords", SoCoordinate3 );
SoCoordinate3 *crosC = SO_GET_PART( this, "crossSectionCoords", SoCoordinate3 );
SoCoordinate3 *spinC = SO_GET_PART( this, "spineCoords", SoCoordinate3 );
SoCoordinate3 *twisC = SO_GET_PART( this, "twistCoords", SoCoordinate3 );
int i;
for ( i = 0; i < profC->point.getNum(); i++ ) {
val = profC->point[i][1];
if ( profileMinY > val )
profileMinY = val;
if ( maxY < val )
maxY = val;
}
if ( maxY == profileMinY )
maxY += 1.0;
profileHeight = maxY - profileMinY;
profileOverHeight = 1.0 / profileHeight;
//Spine length, and parametric spine distance of each point on spine.
if ( spineParamDistances != NULL )
delete [] spineParamDistances;
spineParamDistances = new float[spinC->point.getNum()];
spineLength = 0.0;
spineParamDistances[0] = 0;
SbVec3f diff;
for ( i = 0; i < spinC->point.getNum() - 1; i++ ) {
diff = spinC->point[i+1] - spinC->point[i];
spineLength += diff.length();
spineParamDistances[i+1] = spineLength; // absolute distance
}
if ( spineLength != 0.0 ) {
// convert absolute distance into parametric distance.
for ( i = 0; i < spinC->point.getNum(); i++ )
spineParamDistances[i] = spineParamDistances[i] / spineLength;
}
// Rotations at key points of the spine...
initSpineKeyPointRotations();
//Profile length
profileLength = 0.0;
for ( i = 0; i < profC->point.getNum() - 1; i++ ) {
diff = profC->point[i+1] - profC->point[i];
profileLength += diff.length();
}
//CrossSection length
crossSectionLength = 0.0;
for ( i = 0; i < crosC->point.getNum() - 1; i++ ) {
diff = crosC->point[i+1] - crosC->point[i];
crossSectionLength += diff.length();
}
float xVal, zVal;
float maxX = -100000000;
float maxZ = -100000000;
crossSectionMinX = crossSectionMinZ = 100000000;
for ( i = 0; i < crosC->point.getNum(); i++ ) {
xVal = crosC->point[i][0];
if ( crossSectionMinX > xVal )
crossSectionMinX = xVal;
if ( maxX < xVal )
maxX = xVal;
zVal = crosC->point[i][2];
if ( maxZ < zVal )
maxZ = zVal;
if ( crossSectionMinZ > zVal )
crossSectionMinZ = zVal;
}
if ( maxX == crossSectionMinX )
maxX += 1.0;
if ( maxZ == crossSectionMinZ )
maxZ += 1.0;
crossSectionWidth = maxX - crossSectionMinX;
crossSectionDepth = maxZ - crossSectionMinZ;
twistMinY = 100000000;
twistMaxY = -100000000;
for ( i = 0; i < twisC->point.getNum(); i++ ) {
val = twisC->point[i][1];
if ( twistMinY > val )
twistMinY = val;
if ( twistMaxY < val )
twistMaxY = val;
val = twisC->point[i][0];
}
calculateFullProfile();
calculateFullCrossSection();
}
//
// Given a set of 2D profile coordinates (the z-coordinate of this
// Vec3f field is ignored), this creates a generalized cylinder
// If passed NULL, this routine will use the last coordinates passed
// (this is used when the number of sides in the generalized cylinder is
// changed).
//
void
GeneralizedCylinder::updateSurface()
{
initUpdateInfo();
const SoMFVec3f *prof = &fullProfile->point;
const SoMFVec3f *cross = &fullCrossSection->point;
int numRows = prof->getNum();
int numCols = cross->getNum();
SoFaceSet *myFaceSet = NULL;
SoTriangleStripSet *myTStripSet = NULL;
SoQuadMesh *myQMesh = NULL;
SoGroup *myNurbsSurfs = NULL;
GeneralizedCylinder::RenderShapeType sType
= (GeneralizedCylinder::RenderShapeType)renderShapeType.getValue();
// DEAL WITH SHAPE HINTS
SoShapeHints *hints = (SoShapeHints *) getPart("shapeHints", FALSE);
if (hints) {
switch(sType) {
case BEZIER_SURFACE:
case CUBIC_SPLINE_SURFACE:
case CUBIC_TO_EDGE_SURFACE:
hints->vertexOrdering = SoShapeHints::UNKNOWN_ORDERING;
break;
case FACE_SET:
case TRIANGLE_STRIP_SET:
case QUAD_MESH:
hints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE;
break;
}
}
SoCoordinate3 *coords = NULL;
// DELETE AND GET THE CORRECT NODES.
if (withSides.getValue() == FALSE ) {
setPart( "faceSet", NULL );
setPart( "triangleStripSet", NULL );
setPart( "quadMesh", NULL );
setPart( "nurbsSurfaceGroup", NULL );
setPart( "coords", NULL );
}
else {
switch(sType) {
case FACE_SET:
myFaceSet = SO_GET_PART( this, "faceSet", SoFaceSet );
setPart( "triangleStripSet", NULL );
setPart( "quadMesh", NULL );
setPart( "nurbsSurfaceGroup", NULL );
break;
case TRIANGLE_STRIP_SET:
myTStripSet
= SO_GET_PART(this,"triangleStripSet", SoTriangleStripSet );
setPart( "faceSet", NULL );
setPart( "quadMesh", NULL );
setPart( "nurbsSurfaceGroup", NULL );
break;
case QUAD_MESH:
myQMesh = SO_GET_PART( this, "quadMesh", SoQuadMesh );
setPart( "faceSet", NULL );
setPart( "triangleStripSet", NULL );
setPart( "nurbsSurfaceGroup", NULL );
break;
case BEZIER_SURFACE:
case CUBIC_SPLINE_SURFACE:
case CUBIC_TO_EDGE_SURFACE:
myNurbsSurfs = SO_GET_PART(this, "nurbsSurfaceGroup", SoGroup );
setPart( "faceSet", NULL );
setPart( "triangleStripSet", NULL );
setPart( "quadMesh", NULL );
break;
}
coords = SO_GET_PART( this, "coords", SoCoordinate3 );
}
// FORGET IT IF TOO FEW POINTS
if ( numRows < 2 || numCols < 2 ) {
setPart("faceSet", NULL);
setPart("triangleStripSet", NULL );
setPart("quadMesh", NULL );
setPart("nurbsSurfaceGroup", NULL );
setPart("topCapFaces", NULL);
setPart("bottomCapFaces", NULL);
if (withSides.getValue())
coords->point.deleteValues( 0, -1 );
updateSurroundingManip();
return;
}
// SET UP TOPOLOGICAL INFORMATION
int numCoordsTotal;
SoMFInt32 *nV;
if ( withSides.getValue() ) {
switch (sType ) {
case QUAD_MESH:
// There will be (numRows * numCols) coordinates
numCoordsTotal = numRows * numCols;
myQMesh->verticesPerColumn = numRows;
myQMesh->verticesPerRow = numCols;
if (myQMesh->startIndex.getValue() != 0)
myQMesh->startIndex = 0;
break;
case TRIANGLE_STRIP_SET:
// Each strip (there are numRows-1 in all) has 2*numCols coordinates
numCoordsTotal = (numRows - 1) * (numCols * 2);
nV = &myTStripSet->numVertices;
if (nV->getNum() != (numRows-1)) {
nV->setNum(numRows-1);
for (int k = 0; k < (numRows-1); k++ )
nV->set1Value( k, (numCols*2) );
}
if (myTStripSet->startIndex.getValue() != 0)
myTStripSet->startIndex = 0;
break;
case FACE_SET:
// Each square is made of 2 tris, drawn with 3 coords.
{
int numTris = 2 * (numRows - 1) * (numCols - 1);
numCoordsTotal = numTris * 3;
nV = &myFaceSet->numVertices;
if (nV->getNum() != numTris) {
nV->setNum(numTris);
for (int k = 0; k < numTris; k++ )
nV->set1Value( k, 3 );
}
if (myFaceSet->startIndex.getValue() != 0)
myFaceSet->startIndex = 0;
}
break;
case BEZIER_SURFACE:
case CUBIC_SPLINE_SURFACE:
case CUBIC_TO_EDGE_SURFACE:
{
// There will be (numRows * numCols) coordinates
numCoordsTotal = numRows * numCols;
// Enlist a NurbMaker to create the surface
// patch nodes.
if (myNurbMaker == NULL)
myNurbMaker = new NurbMaker;
if (sType == BEZIER_SURFACE)
myNurbMaker->setPatchType( NurbMaker::BEZIER );
else if (sType == CUBIC_SPLINE_SURFACE)
myNurbMaker->setPatchType( NurbMaker::CUBIC );
else
myNurbMaker->setPatchType( NurbMaker::CUBIC_TO_EDGE );
myNurbMaker->setFlipNormals( normsFlipped.getValue() );
SbVec2s closure( spineClosed.getValue(),
crossSectionClosed.getValue());
myNurbMaker->setWraparound(closure);
SbVec2s numMeshDivs(numCols, numRows);
SoGroup *surfGroup;
surfGroup = myNurbMaker->createNurbsGroup( numMeshDivs, closure );
setPart("nurbsSurfaceGroup", surfGroup );
}
break;
}
if (coords->point.getNum() < numCoordsTotal)
coords->point.insertSpace(0, numCoordsTotal - coords->point.getNum());
else if ( coords->point.getNum() > numCoordsTotal )
coords->point.deleteValues(0, coords->point.getNum() - numCoordsTotal );
}
int topRow, botRow;
static SbVec3f *topPoints = NULL;
static SbVec3f *botPoints = NULL;
static SbVec2f *topTex = NULL;
static SbVec2f *botTex = NULL;
static int p_alloc = 0;
if ( p_alloc != numCols ) {
if ( topPoints != NULL ) {
delete [] topPoints;
topPoints = NULL;
}
if ( botPoints != NULL ) {
delete [] botPoints;
botPoints = NULL;
}
if ( topTex != NULL ) {
delete [] topTex;
topTex = NULL;
}
if ( botTex != NULL ) {
delete [] botTex;
botTex = NULL;
}
p_alloc = 0;
}
if ( p_alloc == 0 ) {
p_alloc = numCols;
topPoints = new SbVec3f[p_alloc];
botPoints = new SbVec3f[p_alloc];
topTex = new SbVec2f[p_alloc];
botTex = new SbVec2f[p_alloc];
}
// If either cap is being drawn, and texture is on...
if ( withTextureCoords.getValue()
&& ( withTopCap.getValue() || withBottomCap.getValue() )
&& crossSectionClosed.getValue() && numCols >= 4 ) {
loadTextureRow( -1, topTex );
SoTextureCoordinate2 *cTC
= SO_GET_PART( this, "capTextureCoords", SoTextureCoordinate2 );
cTC->point.setValues( 0, numCols, topTex );
}
else {
setPart("capTextureCoords", NULL);
}
SoCoordinate3 *topCapC = SO_GET_PART(this,"topCapCoords",SoCoordinate3);
SoIndexedFaceSet *topCapF = SO_GET_PART(this,"topCapFaces", SoIndexedFaceSet);
SoCoordinate3 *botCapC = SO_GET_PART(this,"bottomCapCoords",SoCoordinate3);
SoIndexedFaceSet *botCapF =SO_GET_PART(this,"bottomCapFaces", SoIndexedFaceSet);
if ( withTopCap.getValue() && crossSectionClosed.getValue()
&& numCols >= 4 ) {
SoMFVec3f *topC = &topCapC->point;
SoMFInt32 *topF = &topCapF->coordIndex;
SoMFInt32 *topS = &topCapScratchFace->coordIndex;
// Load the values of the section points into topCapC.
// Leave out the last point, since it is a repeat of the first
// (remember, crossSectionClosed == TRUE !)
if ( numCols - 1 < topC->getNum() )
topC->deleteValues( 0, topC->getNum() - (numCols - 1) );
topC->setValues( 0, numCols - 1, cross->getValues(0) );
// The triangulator likes the y values to be 0.0. So flatten the
// sucker out.
SbVec3f *bunchaTopVecs = topC->startEditing();
SbBool topVecsChanged = FALSE;
for (int topCount = 0; topCount < topC->getNum(); topCount++ ) {
SbVec3f theVec = (*topC)[topCount];
if (bunchaTopVecs[topCount][1] != 0.0) {
bunchaTopVecs[topCount][1] = 0.0;
topVecsChanged = TRUE;
}
}
bunchaTopVecs = NULL;
if (topVecsChanged)
topC->finishEditing();
// Load the indices to draw the section as a single polygon into
// topCapScratchFace
if ( (topC->getNum() + 1) < topS->getNum() )
topS->deleteValues( 0, topS->getNum() - (topC->getNum() + 1) );
else if ( (topC->getNum() + 1) > topS->getNum() )
topS->insertSpace( 0, (topC->getNum() + 1) - topS->getNum() );
int32_t *scratchVals = topS->startEditing();
for ( int i = 0; i < topC->getNum(); i++ ) {
scratchVals[i] = i;
}
scratchVals[ topC->getNum() ] = -1; // to close the polygon
topS->finishEditing();
// Before calling loadRow (i.e., while the points still lay in the
// y=0 plane), and before triangulating (i.e., while we still have
// a record of a polygon which follows the entire cross section)
// determine if the cross section is clockwise ordered. This will
// affect the normal vector we select.
SbBool isSectionClockWise;
isSectionClockWise = Triangulator::clockWiseTest(
(*topC), (*topS), 0, topC->getNum());
// Triangulate, putting result into topCapF
Triangulator::triangulate( *topC, *topS, *topF );
// Triangulation makes everything clockwise ordered.
// Assure that normals are in the right direction...
if ( normsFlipped.getValue() != isSectionClockWise ) {
// reverse the order of the points in topF.
// Each polygon in topF should be a triangle at this point,
// so reversing the order is simple.
int32_t *vals = topF->startEditing();
int32_t swapTemp;
for ( int ind = 0; ind < topF->getNum(); ind += 4 ) {
// Switch ind and ind+2 (ind+3 is a -1 to signal end of poly)
swapTemp = vals[ind];
vals[ind] = vals[ind+2];
vals[ind+2] = swapTemp;
}
topF->finishEditing();
}
// Check that, after triangulation, there are still polygons left...
if ( topF->getNum() > 0 ) {
// Transform the values for the points in the section as they
// appear in the final row
topC->insertSpace( 0, 1 ); // Add room for an extra point at the
// end since loadRow will put it there...
SbVec3f *topCapPoints = topC->startEditing();
loadRow( 0, topCapPoints );
topC->finishEditing();
}
}
else {
// Clear out the coord and face set for top cap
topCapC->point.deleteValues( 0, -1 );
topCapF->coordIndex.deleteValues( 0, -1 );
}
if ( withBottomCap.getValue() && crossSectionClosed.getValue()
&& numCols >= 4 ) {
SoMFVec3f *botC = &botCapC->point;
SoMFInt32 *botF = &botCapF->coordIndex;
SoMFInt32 *botS = &bottomCapScratchFace->coordIndex;
// Load the values of the section points into botCapC.
// Leave out the last point, since it is a repeat of the first
// (remember, crossSectionClosed == TRUE !)
if ( numCols - 1 < botC->getNum() )
botC->deleteValues( 0, botC->getNum() - (numCols - 1) );
botC->setValues( 0, numCols - 1, cross->getValues(0) );
// The triangulator likes the y values to be 0.0. So flatten the
// sucker out.
SbVec3f *bunchaBotVecs = botC->startEditing();
SbBool botVecsChanged = FALSE;
for (int botCount = 0; botCount < botC->getNum(); botCount++ ) {
SbVec3f theVec = (*botC)[botCount];
if (bunchaBotVecs[botCount][1] != 0.0) {
bunchaBotVecs[botCount][1] = 0.0;
botVecsChanged = TRUE;
}
}
bunchaBotVecs = NULL;
if (botVecsChanged)
botC->finishEditing();
// Load the indices to draw the section as a single polygon into
// bottomCapScratchFace
if ( (botC->getNum() + 1) < botS->getNum() )
botS->deleteValues( 0, botS->getNum() - (botC->getNum() + 1) );
else if ( (botC->getNum() + 1) > botS->getNum() )
botS->insertSpace( 0, (botC->getNum() + 1) - botS->getNum() );
int32_t *scratchVals = botS->startEditing();
for ( int i = 0; i < botC->getNum(); i++ ) {
scratchVals[i] = i;
}
scratchVals[ botC->getNum() ] = -1; // to close the polygon
botS->finishEditing();
// Before calling loadRow (i.e., while the points still lay in the
// y=0 plane), and before triangulating (i.e., while we still have
// a record of a polygon which follows the entire cross section)
// determine if the cross section is clockwise ordered. This will
// affect the normal vector we select.
SbBool isSectionClockWise;
isSectionClockWise = Triangulator::clockWiseTest(
(*botC), (*botS), 0, botC->getNum());
// Triangulate, putting result into bottomCapFaces
Triangulator::triangulate( *botC, *botS, *botF );
// Triangulation makes everything clockwise ordered.
// Assure that normals are in the right direction...
if ( normsFlipped.getValue() == isSectionClockWise ) {
// reverse the order of the points in botF.
// Each polygon in topF should be a triangle at this point,
// so reversing the order is simple.
int32_t *vals = botF->startEditing();
int32_t swapTemp;
for ( int ind = 0; ind < botF->getNum(); ind += 4 ) {
// Switch ind and ind+2 (ind+3 is a -1 to signal end of poly)
swapTemp = vals[ind];
vals[ind] = vals[ind+2];
vals[ind+2] = swapTemp;
}
botF->finishEditing();
}
// Check that, after triangulation, there are still polygons left...
if ( botF->getNum() > 0 ) {
// Transform the values for the points in the section as they
// appear in the final row
botC->insertSpace( 0, 1 ); // Add room for an extra point at the
// end, since loadRow will put it there.
SbVec3f *bottomCapPoints = botC->startEditing();
loadRow( numRows - 1, bottomCapPoints );
botC->finishEditing();
}
}
else {
// Clear out the coord and face set for bottom cap
botCapC->point.deleteValues( 0, -1 );
botCapF->coordIndex.deleteValues( 0, -1 );
}
if ( withSides.getValue() ) {
SbVec2f *tCEdit;
SoTextureCoordinate2 *tCN;
if ( withTextureCoords.getValue() ) {
tCN = SO_GET_PART(this, "textureCoords", SoTextureCoordinate2 );
if (tCN->point.getNum() < numCoordsTotal)
tCN->point.insertSpace(0, numCoordsTotal - tCN->point.getNum());
else if ( tCN->point.getNum() > numCoordsTotal )
tCN->point.deleteValues(0, tCN->point.getNum() - numCoordsTotal );
tCEdit = tCN->point.startEditing();
}
else {
setPart("textureCoords", NULL);
}
SbVec3f *coordsEdit = coords->point.startEditing();
int curCoord = 0;
int curTexCoord = 0;
for (topRow = 0, botRow = 1; topRow < numRows - 1; topRow++ , botRow++ ){
if ( topRow == 0 ) {
// calculate topRow
loadRow( topRow, topPoints );
if ( withTextureCoords.getValue() )
loadTextureRow( topRow, topTex );
}
else {
// switch rows, so old botRow becomes new topRow
SbVec3f *temp = topPoints;
topPoints = botPoints;
botPoints = temp;
if ( withTextureCoords.getValue() ) {
SbVec2f *temp2 = topTex;
topTex = botTex;
botTex = temp2;
}
}
// calculate botRow
loadRow( botRow, botPoints );
if ( withTextureCoords.getValue() )
loadTextureRow( botRow, botTex );
// put the coordinates for this row triangles into the node...
SbBool flipped = normsFlipped.getValue();
switch(sType) {
case QUAD_MESH:
case BEZIER_SURFACE:
case CUBIC_SPLINE_SURFACE:
case CUBIC_TO_EDGE_SURFACE:
// For these two, load only coords for the bottom row.
// However load the top row also the first time through
{
int curCol;
if (topRow == 0) {
if ( flipped )
for (curCol = 0; curCol < numCols; curCol++ )
coordsEdit[curCoord++] = topPoints[curCol];
else
for (curCol = numCols-1; curCol >= 0; curCol-- )
coordsEdit[curCoord++] = topPoints[curCol];
}
// load bottom row
if ( flipped )
for (curCol = 0; curCol < numCols; curCol++ )
coordsEdit[curCoord++] = botPoints[curCol];
else
for (curCol = numCols-1; curCol >= 0; curCol-- )
coordsEdit[curCoord++] = botPoints[curCol];
}
break;
case TRIANGLE_STRIP_SET:
{
for( int curCol = 0; curCol < numCols; curCol++ ) {
if ( ! flipped ) {
coordsEdit[curCoord++] = topPoints[curCol];
coordsEdit[curCoord++] = botPoints[curCol];
}
else {
coordsEdit[curCoord++] = botPoints[curCol];
coordsEdit[curCoord++] = topPoints[curCol];
}
}
}
break;
case FACE_SET:
int leftCol, rightCol;
for ( leftCol = 0, rightCol = 1; leftCol < numCols - 1;
leftCol++, rightCol++ ) {
if ( ! flipped ) {
// first draw the top left triangle
coordsEdit[curCoord++] = topPoints[leftCol];
coordsEdit[curCoord++] = topPoints[rightCol];
coordsEdit[curCoord++] = botPoints[leftCol];
// then draw the bottom right triangle
coordsEdit[curCoord++] = botPoints[leftCol];
coordsEdit[curCoord++] = topPoints[rightCol];
coordsEdit[curCoord++] = botPoints[rightCol];
}
else {
// first draw the top left triangle
coordsEdit[curCoord++] = botPoints[leftCol];
coordsEdit[curCoord++] = topPoints[rightCol];
coordsEdit[curCoord++] = topPoints[leftCol];
// then draw the bottom right triangle
coordsEdit[curCoord++] = botPoints[rightCol];
coordsEdit[curCoord++] = topPoints[rightCol];
coordsEdit[curCoord++] = botPoints[leftCol];
}
}
break;
}
if ( withTextureCoords.getValue() ) {
switch(sType) {
case QUAD_MESH:
case BEZIER_SURFACE:
case CUBIC_SPLINE_SURFACE:
case CUBIC_TO_EDGE_SURFACE:
// For these two, load only coords for the bottom row.
// However load the top row also the first time through
{
int curCol;
if (topRow == 0) {
if ( flipped )
for (curCol = 0; curCol < numCols; curCol++ )
tCEdit[curTexCoord++] = topTex[curCol];
else
for (curCol = numCols-1; curCol >= 0; curCol-- )
tCEdit[curTexCoord++] = topTex[curCol];
}
// load bottom row
if ( flipped )
for (curCol = 0; curCol < numCols; curCol++ )
tCEdit[curTexCoord++] = botTex[curCol];
else
for (curCol = numCols-1; curCol >= 0; curCol-- )
tCEdit[curTexCoord++] = botTex[curCol];
}
break;
case TRIANGLE_STRIP_SET:
{
for (int curCol=0; curCol < numCols;curCol++){
if ( ! flipped ) {
tCEdit[ curTexCoord++] = topTex[curCol];
tCEdit[ curTexCoord++] = botTex[curCol];
}
else {
tCEdit[ curTexCoord++] = botTex[curCol];
tCEdit[ curTexCoord++] = topTex[curCol];
}
}
}
break;
case FACE_SET:
int leftCol, rightCol;
// set the texture coordinates for these triangles...
for ( leftCol = 0, rightCol = 1; leftCol < numCols - 1;
leftCol++, rightCol++ ) {
if ( ! flipped ) {
// first draw the top left triangle
tCEdit[ curTexCoord++] = topTex[leftCol];
tCEdit[ curTexCoord++] = topTex[rightCol];
tCEdit[ curTexCoord++] = botTex[leftCol];
// then draw the bottom right triangle
tCEdit[ curTexCoord++] = botTex[leftCol];
tCEdit[ curTexCoord++] = topTex[rightCol];
tCEdit[ curTexCoord++] = botTex[rightCol];
}
else {
// first draw the top left triangle
tCEdit[ curTexCoord++] = botTex[leftCol];
tCEdit[ curTexCoord++] = topTex[rightCol];
tCEdit[ curTexCoord++] = topTex[leftCol];
// then draw the bottom right triangle
tCEdit[ curTexCoord++] = botTex[rightCol];
tCEdit[ curTexCoord++] = topTex[rightCol];
tCEdit[ curTexCoord++] = botTex[leftCol];
}
}
break;
}
}
}
coords->point.finishEditing();
if ( withTextureCoords.getValue() ) {
tCN->point.finishEditing();
}
}
// If there's a manip as our transform, tell it to change size.
updateSurroundingManip();
}
SoSeparator *
GeneralizedCylinder::makeVanillaVersion()
{
// Start with a nice update!
updateSurface();
SoSeparator *answer = new SoSeparator;
answer->ref();
SoNode *newKid;
int numCols = fullCrossSection->point.getNum();
// Go through all the relevant parts. If you find them, add them as
// a child to the answer.
if ( newKid = getPart( "callbackList", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "pickStyle", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "appearance.lightModel", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "appearance.environment", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "appearance.drawStyle", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "appearance.material", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "appearance.complexity", FALSE ))
answer->addChild( newKid );
if ( withTextureCoords.getValue() ) {
if ( newKid = getPart( "appearance.texture2", FALSE ))
answer->addChild( newKid );
}
if ( newKid = getPart( "appearance.font", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "units", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "transform", FALSE ))
answer->addChild( newKid );
if ( withTextureCoords.getValue() ) {
if ( newKid = getPart( "texture2Transform", FALSE ))
answer->addChild( newKid );
}
if ( newKid = getPart( "shapeHints", FALSE ))
answer->addChild( newKid );
if ( withTextureCoords.getValue() ) {
if ( newKid = getPart( "textureBinding", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "textureCoords", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "texture2", FALSE ))
answer->addChild( newKid );
}
if ( newKid = getPart( "coords", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "faceSet", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "quadMesh", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "nurbsSurfaceGroup", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "triangleStripSet", FALSE ))
answer->addChild( newKid );
if ( withTextureCoords.getValue()
&& ( withTopCap.getValue() || withBottomCap.getValue() )
&& crossSectionClosed.getValue() && numCols >= 4 ) {
if ( newKid = getPart( "capTextureBinding", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "capTextureCoords", FALSE ))
answer->addChild( newKid );
}
if ( withTopCap.getValue()
&& crossSectionClosed.getValue() && numCols >= 4 ) {
if ( newKid = getPart( "topCapCoords", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "topCapFaces", FALSE ))
answer->addChild( newKid );
}
if ( withBottomCap.getValue()
&& crossSectionClosed.getValue() && numCols >= 4 ) {
if ( newKid = getPart( "bottomCapCoords", FALSE ))
answer->addChild( newKid );
if ( newKid = getPart( "bottomCapFaces", FALSE ))
answer->addChild( newKid );
}
if ( newKid = getPart( "childList", FALSE ))
answer->addChild( newKid );
answer->unrefNoDelete();
return answer;
}
void
GeneralizedCylinder::changeCurveClosure( char *curveName, SbBool newClosed )
{
SoCoordinate3 *coords = SO_GET_PART( this, curveName, SoCoordinate3 );
SoMFVec3f *cField = &coords->point;
int numC = cField->getNum();
if ( newClosed == TRUE ) {
if ( numC > 0 ) {
// make a new final point that is a copy of the zero'th
if ( numC > 1 && ((*cField)[0] == (*cField)[numC-1] ))
// First make sure that the first and last points aren't already
// already the same.
return;
else
cField->set1Value( numC, (*cField)[0] );
}
}
else if ( numC > 1 ) {
// remove the last point
if ( (*cField)[0] != (*cField)[numC-1] )
// First make sure that the first and last points are the same.
return;
else
cField->deleteValues( numC - 1, 1 );
}
}
void
GeneralizedCylinder::changeWithTextureCoords( SbBool newWith )
{
if ( withTextureCoords.getValue() != newWith )
withTextureCoords = newWith;
if ( newWith ) {
set( "textureBinding { value PER_VERTEX }");
set( "capTextureBinding { value PER_VERTEX_INDEXED }");
set( "texture2 { filename \"defaultTexture.rgb\" }" );
}
else {
// The texture2 and texture2Transform parts may have some useful
// information, so we check them before deleting.
// The others can just go bye-bye
SoTexture2 *tex2 = (SoTexture2 *) getPart("texture2",FALSE);
if ( tex2 != NULL ) {
tex2->filename = "";
if (tex2->hasDefaultValues())
setPart("texture2", NULL );
}
SoTexture2Transform *txXf
= (SoTexture2Transform *) getPart("texture2Transform",FALSE);
if ( txXf != NULL ) {
if (txXf->hasDefaultValues())
setPart("texture2Transform", NULL );
}
setPart("textureBinding", NULL );
setPart("capTextureBinding", NULL );
setPart("textureCoords", NULL );
setPart("capTextureCoords", NULL );
}
}
void
GeneralizedCylinder::calculateFullProfile()
{
SoCoordinate3 *profC = SO_GET_PART( this, "profileCoords", SoCoordinate3 );
SoCoordinate3 *spinC = SO_GET_PART( this, "spineCoords", SoCoordinate3 );
SoCoordinate3 *twisC = SO_GET_PART( this, "twistCoords", SoCoordinate3 );
int numInProf = profC->point.getNum();
// Start by just copying profC into fullProfile...
int i = numInProf - fullProfile->point.getNum();
if ( i > 0 )
fullProfile->point.insertSpace(0, i);
else if ( i < 0 )
fullProfile->point.deleteValues(0, -i);
fullProfile->point.setValues(0,numInProf,profC->point.getValues(0));
// Next, add in any points in the spine curve that are not demarcated
// in the profile curve.
for ( i = 0; i < spinC->point.getNum(); i++ ) {
// Get parametric distance of point along length of spine.
float pDist = spineParamDistances[i];
float pHeight = profileMinY + pDist * profileHeight;
// Translate this parametric distance into a point or points in
// the profile curve.
SbVec3f p0, p1, profPoint;
float amt;
for ( int j = 0; j < fullProfile->point.getNum() - 1; j++ ) {
p0 = fullProfile->point[j];
p1 = fullProfile->point[j+1];
// If the spine point falls at a point along the profile that
// lies between p0 and p1, then interpolate and add it in.
if ( pHeight > p0[1] && pHeight < p1[1] ) {
amt = (pHeight - p0[1]) / (p1[1] - p0[1]);
profPoint = p0 + amt * ( p1 - p0);
// Add the point
fullProfile->point.insertSpace(j+1,1);
fullProfile->point.set1Value(j+1,profPoint);
j++;
}
else if ( pHeight < p0[1] && pHeight > p1[1] ) {
amt = (pHeight - p1[1]) / (p0[1] - p1[1]);
profPoint = p1 + amt * ( p0 - p1);
// Add the point
fullProfile->point.insertSpace(j+1,1);
fullProfile->point.set1Value(j+1,profPoint);
j++;
}
}
}
// Next, add in any points in the twist curve that are not demarcated
// in the profile curve.
for ( i = 0; i < twisC->point.getNum(); i++ ) {
}
if ( numInProf < 2 )
return;
if ( minNumRows.getValue() > numInProf ) {
float overDist = profileLength / minNumRows.getValue();
SbVec3f p0, p1, newPt, diff;
float length;
for ( int i = 0; i < fullProfile->point.getNum() - 1; i++ ) {
p0 = fullProfile->point[i];
p1 = fullProfile->point[i+1];
diff = p1 - p0;
length = diff.length();
diff.normalize();
if ( length > overDist ) {
newPt = p0 + diff * overDist;
fullProfile->point.insertSpace(i+1,1);
fullProfile->point.set1Value(i+1,newPt);
}
}
}
}
void
GeneralizedCylinder::calculateFullCrossSection()
{
SoCoordinate3 *crosC = SO_GET_PART( this, "crossSectionCoords", SoCoordinate3 );
int numInCross = crosC->point.getNum();
// Start by just copying crosC into fullCrossSection...
int i = numInCross - fullCrossSection->point.getNum();
if ( i > 0 )
fullCrossSection->point.insertSpace(0, i);
else if ( i < 0 )
fullCrossSection->point.deleteValues(0, -i);
fullCrossSection->point.setValues( 0, numInCross, crosC->point.getValues(0));
if ( numInCross < 2 )
return;
if ( minNumCols.getValue() > numInCross ) {
float overDist = crossSectionLength / minNumCols.getValue();
SbVec3f p0, p1, newPt, diff;
float length;
for ( int i = 0; i < fullCrossSection->point.getNum() - 1; i++ ) {
p0 = fullCrossSection->point[i];
p1 = fullCrossSection->point[i+1];
diff = p1 - p0;
length = diff.length();
diff.normalize();
if ( length > overDist ) {
newPt = p0 + diff * overDist;
fullCrossSection->point.insertSpace(i+1,1);
fullCrossSection->point.set1Value(i+1,newPt);
}
}
}
}
void
GeneralizedCylinder::fieldsChangedCB(void *data, SoSensor *sens )
{
if ( ((SoDataSensor *)sens)->getTriggerNode() != data )
return;
GeneralizedCylinder *s = (GeneralizedCylinder *)data;
SoField *triggerField = ((SoDataSensor *)sens)->getTriggerField();
SbBool flagFieldChanged = FALSE;
if (triggerField == &s->renderShapeType)
flagFieldChanged = TRUE;
else if (triggerField == &s->normsFlipped)
flagFieldChanged = TRUE;
else if (triggerField == &s->profileClosed)
flagFieldChanged = TRUE;
else if (triggerField == &s->crossSectionClosed)
flagFieldChanged = TRUE;
else if (triggerField == &s->spineClosed)
flagFieldChanged = TRUE;
else if (triggerField == &s->twistClosed)
flagFieldChanged = TRUE;
else if (triggerField == &s->minNumRows)
flagFieldChanged = TRUE;
else if (triggerField == &s->minNumCols)
flagFieldChanged = TRUE;
else if (triggerField == &s->withSides)
flagFieldChanged = TRUE;
else if (triggerField == &s->withTopCap)
flagFieldChanged = TRUE;
else if (triggerField == &s->withBottomCap)
flagFieldChanged = TRUE;
else if (triggerField == &s->withTextureCoords)
flagFieldChanged = TRUE;
if (flagFieldChanged == FALSE )
return;
// Special stuff to do if a curve's closurehas been changed
if ( s->profileClosed.getValue() != s->profileAlreadyClosed ) {
s->changeCurveClosure( "profileCoords", s->profileClosed.getValue() );
s->profileAlreadyClosed = s->profileClosed.getValue();
}
if ( s->crossSectionClosed.getValue() != s->crossSectionAlreadyClosed ) {
s->changeCurveClosure( "crossSectionCoords",
s->crossSectionClosed.getValue() );
s->crossSectionAlreadyClosed = s->crossSectionClosed.getValue();
}
if ( s->spineClosed.getValue() != s->spineAlreadyClosed ) {
s->changeCurveClosure( "spineCoords", s->spineClosed.getValue() );
s->spineAlreadyClosed = s->spineClosed.getValue();
}
if ( s->twistClosed.getValue() != s->twistAlreadyClosed ) {
s->changeCurveClosure( "twistCoords", s->twistClosed.getValue() );
s->twistAlreadyClosed = s->twistClosed.getValue();
}
// Special stuff to do if a curve's texture status has been changed
if ( s->withTextureCoords.getValue() != s->alreadyWithTextureCoords) {
s->changeWithTextureCoords( s->withTextureCoords.getValue() );
s->alreadyWithTextureCoords = s->withTextureCoords.getValue();
}
s->updateSurface();
}
void
GeneralizedCylinder::inputChangedCB(void *data, SoSensor *sens )
{
GeneralizedCylinder *s = (GeneralizedCylinder *)data;
SoNode *notifier = ((SoNodeSensor *)sens)->getTriggerNode();
SoCoordinate3 *coordPart = NULL;
SbBool isClosed;
coordPart = SO_CHECK_PART( s, "profileCoords", SoCoordinate3 );
if ( notifier == coordPart )
isClosed = s->profileClosed.getValue();
else {
coordPart = SO_CHECK_PART( s, "crossSectionCoords", SoCoordinate3 );
if ( notifier == coordPart )
isClosed = s->crossSectionClosed.getValue();
else {
coordPart = SO_CHECK_PART( s, "spineCoords", SoCoordinate3 );
if ( notifier == coordPart )
isClosed = s->spineClosed.getValue();
else {
coordPart = SO_CHECK_PART( s, "twistCoords", SoCoordinate3 );
if ( notifier == coordPart )
isClosed = s->twistClosed.getValue();
else
coordPart = NULL;
}
}
}
if (coordPart == NULL)
return;
if (isClosed) {
SoMFVec3f *cField = &(coordPart->point);
int numC = cField->getNum();
// make the last point follow the first point.
if ( numC == 3 )
cField->set1Value( numC, (*cField)[0] );
else if ( numC > 3 && (*cField)[0] != (*cField)[numC - 1] )
cField->set1Value( numC - 1, (*cField)[0] );
}
// Clamp the values of the profile coordinates so that
// no values are below zero:
if (coordPart == s->profileCoords.getValue()) {
SbBool changed = FALSE;
coordPart->point.enableNotify(FALSE);
SbVec3f *pCoords = coordPart->point.startEditing();
for ( int i = 0; i < coordPart->point.getNum(); i++ ) {
if ( pCoords[i][0] < 0.0 ) {
pCoords[i].setValue( 0.0, pCoords[i][1], pCoords[i][2] );
changed = TRUE;
}
}
coordPart->point.finishEditing();
coordPart->point.enableNotify(TRUE);
if (changed) {
// return now -- changing the coords will trigger notification
// and bring us back here.
coordPart->point.touch();
}
}
s->updateSurface();
}
void
GeneralizedCylinder::updateSurroundingManip()
{
SoNode *n;
// Is there a transform?
n = getPart( "transform", FALSE);
if (n==NULL) return;
// If it's not a transform manip, then return...
if ( !n->isOfType( SoTransformManip::getClassTypeId() ) )
return;
SoTransformManip *xfm = (SoTransformManip *) n;
// Try to get the dragger from the manip.
SoDragger *d = xfm->getDragger();
if (d==NULL)
return;
SoSurroundScale *ss
= (SoSurroundScale *) d->getPart( "surroundScale", FALSE );
if ( ss != NULL)
ss->invalidate();
}
void
GeneralizedCylinder::changeTransformType( SoType newType )
{
// Return if manip is already of correct type.
SoTransform *curXf = (SoTransform *) getPart("transform",FALSE);
if ( curXf && (curXf->getTypeId() == newType ))
return;
if ( newType.isBad() ||
! newType.isDerivedFrom(SoTransformManip::getClassTypeId() ) ||
! newType.canCreateInstance() ) {
// If no transform part, just return:
SoFullPath *xfPath = (SoFullPath *) createPathToPart("transform",FALSE);
if ( ! xfPath )
return;
// If tail is not an SoTransformManip, just return.
SoNode *tail = xfPath->getTail();
if ( ! tail->isOfType( SoTransformManip::getClassTypeId() ) )
return;
// Tail is an SoTransformManip. Tell it to replace itself with
// a regular transform
xfPath->ref();
tail->ref();
SoTransformManip *m = (SoTransformManip *) tail;
m->replaceManip(xfPath, new SoTransform );
tail->unref();
xfPath->unref();
}
else {
// Make a manip of given type, tell it to insert itself in the path
SoPath *xfPath = createPathToPart("transform",TRUE);
SoTransformManip *m = (SoTransformManip *) newType.createInstance();
xfPath->ref();
m->replaceNode(xfPath );
xfPath->unref();
}
}