/******************************Module*Header*******************************\ * Module Name: pipes.c * * Core pipes code * * Copyright (c) 1994-1995 Microsoft Corporation * \**************************************************************************/ #include #include #include #include #include #include #include #include #include #include "gl/glaux.h" #include "pipes.h" #include "objects.h" #include "material.h" #include "util.h" GLenum polyMode; GLenum dithering; GLenum shade; GLenum projMode; int drawMode; int bTexture; int bTextureCoords; enum { ELBOW_JOINT = 0, BALL_JOINT }; #define TEAPOT 66 // these attributes correspond to screen-saver dialog items int jointStyle; int bCycleJointStyles; int tessLevel; int textureQuality; // initial viewing context #define NUM_DIV 16 // divisions in window in longest dimension #define NUM_NODE (NUM_DIV - 1) // num nodes in longest dimension VC vc; VC *vp; // for now, static array static Node node[NUM_NODE][NUM_NODE][NUM_NODE]; static int lastDir; // last direction taken by pipe static int notchVec; // current pipe notch vector // forward decl'ns void ResetPipes(void); void DrawPipes(void); int (*drawNext)( int ); /*----------------------------------------------------------------------- | | | InitPipes( int mode ): | | - One time init stuff | | | -----------------------------------------------------------------------*/ void InitPipes( int mode ) { static float ambient[] = {0.1f, 0.1f, 0.1f, 1.0f}; static float diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; static float position[] = {90.0f, 90.0f, 150.0f, 0.0f}; static float lmodel_ambient[] = {1.0f, 1.0f, 1.0f, 1.0f}; time_t timer; time( &timer ); srand( timer ); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glFrontFace(GL_CCW); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glEnable( GL_AUTO_NORMAL ); // needed for GL_MAP2_VERTEX (tea) glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); glLightfv(GL_LIGHT0, GL_POSITION, position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); ChooseMaterial(); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); dithering = GL_TRUE; shade = GL_TRUE; polyMode = GL_BACK; projMode = GL_TRUE; drawMode = mode; vp = &vc; // set some initial viewing and size params vc.zTrans = -75.0f; vc.viewDist = -vc.zTrans; vc.numDiv = NUM_DIV; vc.radius = 1.0f; vc.divSize = 7.0f; vc.persp.viewAngle = 90.0f; vc.persp.zNear = 1.0f; vc.yRot = 0.0f; if( bTexture ) vc.numPipes = 3; else vc.numPipes = 5; // Build objects BuildLists(); } /*----------------------------------------------------------------------- | | | SetProjMatrix(); | | - sets ortho or perspective viewing dimensions | | | -----------------------------------------------------------------------*/ void SetProjMatrix() { mfPoint3df *world = &vc.world; Perspective *persp = &vc.persp; glMatrixMode(GL_PROJECTION); glLoadIdentity(); persp->aspectRatio = world->x / world->y; persp->zFar = vc.viewDist + world->z*2; if( projMode ) { gluPerspective( persp->viewAngle, persp->aspectRatio, persp->zNear, persp->zFar ); } else { glOrtho( -world->x/2, world->x/2, -world->y/2, world->y/2, -world->z, world->z ); } glMatrixMode(GL_MODELVIEW); // reset drawing state ResetPipes(); } /*----------------------------------------------------------------------- | | | ReshapePipes( width, height ): | | - called on resize, expose | | - always called on app startup | | - use it to reset projection matrix, model dimensions, etc. | | | -----------------------------------------------------------------------*/ void ReshapePipes(int width, int height) { float ratio; glViewport(0, 0, (GLint)width, (GLint)height); vc.winSize.width = width; vc.winSize.height = height; // adjust world dimensions to fit viewport, and adjust node counts if( width >= height ) { ratio = (float)height/width; vc.world.x = vc.numDiv * vc.divSize; vc.world.y = ratio * vc.world.x; vc.world.z = vc.world.x; vc.numNodes.x = vc.numDiv - 1; vc.numNodes.y = (int) (ratio * vc.numNodes.x); vc.numNodes.z = vc.numNodes.x; } else { ratio = (float)width/height; vc.world.y = vc.numDiv * vc.divSize; vc.world.x = ratio * vc.world.y; vc.world.z = vc.world.y; vc.numNodes.y = vc.numDiv - 1; vc.numNodes.x = (int) (ratio * vc.numNodes.y); vc.numNodes.z = vc.numNodes.y; } // reset stuff not done on PipeReset() vc.yRot = 0.0f; // set view matrix (resets pipe drawing as well) SetProjMatrix(); } /*----------------------------------------------------------------------- | | | getNeighbourNodes( pos ): | | - get addresses of the neigbour nodes, | | and put them in supplied matrix | | - boundary hits are returned as NULL | | | -----------------------------------------------------------------------*/ static void getNeighbourNodes( mfPoint3di *pos, Node **nNode ) { Node *theNode = &node[pos->z][pos->y][pos->x]; if( pos->x == 0 ) nNode[MINUS_X] = (Node *) NULL; else nNode[MINUS_X] = theNode - 1; if( pos->x == (vc.numNodes.x - 1) ) nNode[PLUS_X] = (Node *) NULL; else nNode[PLUS_X] = theNode + 1; if( pos->y == 0 ) nNode[MINUS_Y] = (Node *) NULL; else nNode[MINUS_Y] = theNode - NUM_NODE; if( pos->y == (vc.numNodes.y - 1) ) nNode[PLUS_Y] = (Node *) NULL; else nNode[PLUS_Y] = theNode + NUM_NODE; if( pos->z == 0 ) nNode[MINUS_Z] = (Node *) NULL; else nNode[MINUS_Z] = theNode - NUM_NODE*NUM_NODE; if( pos->z == (vc.numNodes.z - 1) ) nNode[PLUS_Z] = (Node *) NULL; else nNode[PLUS_Z] = theNode + NUM_NODE*NUM_NODE; } /*----------------------------------------------------------------------- | | | getEmptyNeighbourNodes() | | - get list of direction indices of empty node neighbours, | | and put them in supplied matrix | | - return number of empty node neighbours | | - currently, if we find a node that is empty in the current | | direction, we duplicate it in the empty set, thereby making | | it a bit more likely to go straight. | | | -----------------------------------------------------------------------*/ static int getEmptyNeighbours( Node **nNode, int *nEmpty ) { int i, count = 0; for( i = 0; i < NUM_DIRS; i ++ ) { if( nNode[i] && nNode[i]->empty ) // weight straight { nEmpty[count++] = i; if( i == lastDir ) nEmpty[count++] = i; } } return count; } /*----------------------------------------------------------------------- | | | updateCurrentPosition( newDir ): | | | -----------------------------------------------------------------------*/ static void updateCurrentPosition( int newDir ) { switch( newDir ) { case PLUS_X: vc.curPos.x += 1; break; case MINUS_X: vc.curPos.x -= 1; break; case PLUS_Y: vc.curPos.y += 1; break; case MINUS_Y: vc.curPos.y -= 1; break; case PLUS_Z: vc.curPos.z += 1; break; case MINUS_Z: vc.curPos.z -= 1; break; } } /*----------------------------------------------------------------------- | | | align_plusz( int newDir ) | | - Aligns the z axis along specified direction | | | -----------------------------------------------------------------------*/ static void align_plusz( int newDir ) { // align +z along new direction switch( newDir ) { case PLUS_X: glRotatef( 90.0f, 0.0f, 1.0f, 0.0f); break; case MINUS_X: glRotatef( -90.0f, 0.0f, 1.0f, 0.0f); break; case PLUS_Y: glRotatef( -90.0f, 1.0f, 0.0f, 0.0f); break; case MINUS_Y: glRotatef( 90.0f, 1.0f, 0.0f, 0.0f); break; case PLUS_Z: glRotatef( 0.0f, 0.0f, 1.0f, 0.0f); break; case MINUS_Z: glRotatef( 180.0f, 0.0f, 1.0f, 0.0f); break; } } static float RotZ[NUM_DIRS][NUM_DIRS] = { 0.0f, 0.0f, 90.0f, 90.0f, 90.0f, -90.0f, 0.0f, 0.0f, -90.0f, -90.0f, -90.0f, 90.0f, 180.0f, 180.0f, 0.0f, 0.0f, 180.0f, 180.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -90.0f, 90.0f, 0.0f, 180.0f, 0.0f, 0.0f, 90.0f, -90.0f, 180.0f, 0.0f, 0.0f, 0.0f }; /*----------------------------------------------------------------------- | | | align_plusy( int lastDir, int newDir ) | | - Assuming +z axis is already aligned with newDir, align | | +y axis BACK along lastDir | | | -----------------------------------------------------------------------*/ static void align_plusy( int oldDir, int newDir ) { GLfloat rotz; rotz = RotZ[oldDir][newDir]; glRotatef( rotz, 0.0f, 0.0f, 1.0f ); } // defCylNotch shows where the notch for the default cylinder will be, // in absolute coords, once we do an align_plusz static GLint defCylNotch[NUM_DIRS] = { PLUS_Y, PLUS_Y, MINUS_Z, PLUS_Z, PLUS_Y, PLUS_Y }; // given a dir, determine how much to rotate cylinder around z to match notches // format is [newDir][notchVec] #define fXX 1.0f // float don't care value #define iXX -1 // int don't care value static GLfloat alignNotchRot[NUM_DIRS][NUM_DIRS] = { fXX, fXX, 0.0f, 180.0f, 90.0f, -90.0f, fXX, fXX, 0.0f, 180.0f, -90.0f, 90.0f, -90.0f, 90.0f, fXX, fXX, 180.0f, 0.0f, -90.0f, 90.0f, fXX, fXX, 0.0f, 180.0f, -90.0f, 90.0f, 0.0f, 180.0f, fXX, fXX, 90.0f, -90.0f, 0.0f, 180.0f, fXX, fXX }; /*----------------------------------------------------------------------- | | | align_notch( int newDir ) | | - a cylinder is notched, and we have to line this up | | with the previous primitive's notch which is maintained as | | notchVec. | | - this adds a rotation around z to achieve this | | | -----------------------------------------------------------------------*/ static void align_notch( int newDir, int notch ) { GLfloat rotz; GLint curNotch; // figure out where notch is presently after +z alignment curNotch = defCylNotch[newDir]; // (don't need this now we have lut) // look up rotation value in table rotz = alignNotchRot[newDir][notch]; if( rotz != 0.0f ) glRotatef( rotz, 0.0f, 0.0f, 1.0f ); } /*----------------------------------------------------------------------- | | | ChooseJointType | | - Decides which type of joint to draw | | | -----------------------------------------------------------------------*/ #define BLUE_MOON 153 static int ChooseJointType() { switch( jointStyle ) { case ELBOWS: return ELBOW_JOINT; case BALLS: return BALL_JOINT; case EITHER: // draw a teapot once in a blue moon if( mfRand(1000) == BLUE_MOON ) return( TEAPOT ); // otherwise an elbow or a ball return( mfRand( 2 ) ); } } // this array supplies the sequence of elbow notch vectors, given // oldDir and newDir (0's are don't cares's) // it is also used to determine the ending notch of an elbow static GLint notchElbDir[NUM_DIRS][NUM_DIRS][4] = { // oldDir = +x iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, PLUS_Y, MINUS_Z, MINUS_Y, PLUS_Z, MINUS_Y, PLUS_Z, PLUS_Y, MINUS_Z, PLUS_Z, PLUS_Y, MINUS_Z, MINUS_Y, MINUS_Z, MINUS_Y, PLUS_Z, PLUS_Y, // oldDir = -x iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, PLUS_Y, PLUS_Z, MINUS_Y, MINUS_Z, MINUS_Y, MINUS_Z, PLUS_Y, PLUS_Z, PLUS_Z, MINUS_Y, MINUS_Z, PLUS_Y, MINUS_Z, PLUS_Y, PLUS_Z, MINUS_Y, // oldDir = +y PLUS_X, PLUS_Z, MINUS_X, MINUS_Z, MINUS_X, MINUS_Z, PLUS_X, PLUS_Z, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, PLUS_Z, MINUS_X, MINUS_Z, PLUS_X, MINUS_Z, PLUS_X, PLUS_Z, MINUS_X, // oldDir = -y PLUS_X, MINUS_Z, MINUS_X, PLUS_Z, MINUS_X, PLUS_Z, PLUS_X, MINUS_Z, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, PLUS_Z, PLUS_X, MINUS_Z, MINUS_X, MINUS_Z, MINUS_X, PLUS_Z, PLUS_X, // oldDir = +z PLUS_X, MINUS_Y, MINUS_X, PLUS_Y, MINUS_X, PLUS_Y, PLUS_X, MINUS_Y, PLUS_Y, PLUS_X, MINUS_Y, MINUS_X, MINUS_Y, MINUS_X, PLUS_Y, PLUS_X, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, // oldDir = -z PLUS_X, PLUS_Y, MINUS_X, MINUS_Y, MINUS_X, MINUS_Y, PLUS_X, PLUS_Y, PLUS_Y, MINUS_X, MINUS_Y, PLUS_X, MINUS_Y, PLUS_X, PLUS_Y, MINUS_X, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX }; // this array tells you which way the notch will be once you make // a turn // format: notchTurn[oldDir][newDir][notchVec] static GLint notchTurn[NUM_DIRS][NUM_DIRS][NUM_DIRS] = { // oldDir = +x iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, MINUS_X,PLUS_X, PLUS_Z, MINUS_Z, iXX, iXX, PLUS_X, MINUS_X,PLUS_Z, MINUS_Z, iXX, iXX, PLUS_Y, MINUS_Y,MINUS_X,PLUS_X, iXX, iXX, PLUS_Y, MINUS_Y,PLUS_X, MINUS_X, // oldDir = -x iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, PLUS_X, MINUS_X,PLUS_Z, MINUS_Z, iXX, iXX, MINUS_X,PLUS_X, PLUS_Z, MINUS_Z, iXX, iXX, PLUS_Y, MINUS_Y,PLUS_X, MINUS_X, iXX, iXX, PLUS_Y, MINUS_Y,MINUS_X,PLUS_X, // oldDir = +y MINUS_Y,PLUS_Y, iXX, iXX, PLUS_Z, MINUS_Z, PLUS_Y, MINUS_Y,iXX, iXX, PLUS_Z, MINUS_Z, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, PLUS_X, MINUS_X,iXX, iXX, MINUS_Y,PLUS_Y, PLUS_X, MINUS_X,iXX, iXX, PLUS_Y, MINUS_Y, // oldDir = -y PLUS_Y, MINUS_Y,iXX, iXX, PLUS_Z, MINUS_Z, MINUS_Y,PLUS_Y, iXX, iXX, PLUS_Z, MINUS_Z, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, PLUS_X, MINUS_X,iXX, iXX, PLUS_Y, MINUS_Y, PLUS_X, MINUS_X,iXX, iXX, MINUS_Y,PLUS_Y, // oldDir = +z MINUS_Z,PLUS_Z, PLUS_Y, MINUS_Y,iXX, iXX, PLUS_Z, MINUS_Z,PLUS_Y, MINUS_Y,iXX, iXX, PLUS_X, MINUS_X,MINUS_Z,PLUS_Z, iXX, iXX, PLUS_X, MINUS_X,PLUS_Z, MINUS_Z,iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, // oldDir = -z PLUS_Z, MINUS_Z,PLUS_Y, MINUS_Y,iXX, iXX, MINUS_Z,PLUS_Z, PLUS_Y, MINUS_Y,iXX, iXX, PLUS_X, MINUS_X,PLUS_Z, MINUS_Z,iXX, iXX, PLUS_X, MINUS_X,MINUS_Z,PLUS_Z, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX, iXX }; static GLint oppositeDir[NUM_DIRS] = { MINUS_X, PLUS_X, MINUS_Y, PLUS_Y, MINUS_Z, PLUS_Z }; /*----------------------------------------------------------------------- | | | ChooseElbow( int newDir, int oldDir ) | | - Decides which elbow to draw | | - The beginning of each elbow is aligned along +y, and we have | | to choose the one with the notch in correct position | | - The 'primary' start notch (elbow[0]) is in same direction as | | newDir, | | and successive elbows rotate this notch CCW around +y | | | -----------------------------------------------------------------------*/ static GLint ChooseElbow( int oldDir, int newDir ) { int i; // precomputed table supplies correct elbow orientation for( i = 0; i < 4; i ++ ) { if( notchElbDir[oldDir][newDir][i] == notchVec ) return i; } // we shouldn't arrive here return -1; } /*----------------------------------------------------------------------\ | | | drawPipeSection(int dir) | | | | - Draws a continuous pipe section | | - if turning, draws a joint and a short cylinder, otherwise | | draws a long cylinder. | | - int dir: new absolute direction, or RANDOM_DIR | | - the 'current node' is set as the one we draw thru the NEXT | | time around. | | - return: 0 if couldn't do it | | 1 if successful | | | | | \----------------------------------------------------------------------*/ static int drawPipeSection( int dir ) { static Node *nNode[NUM_DIRS]; // weight going straight a little more static int nEmpty[NUM_DIRS+1]; int numEmpty, newDir; int jointType; int iElbow; VC *vp = &vc; // find out who the neighbours are: this returns addresses of the 6 // neighbours, or NULL, if they're out of bounds getNeighbourNodes( &vc.curPos, nNode ); // determine new direction to go in if( dir != RANDOM_DIR ) { // choose absolute direction if( (nNode[dir] != NULL) && nNode[dir]->empty ) { newDir = dir; } else { // can't go in that direction return 0; } } else { // randomnly choose one of the empty nodes // who's empty ?: returns the number of empty nodes, and fills the // nEmpty matrix with the direction indices of the empty nodes numEmpty = getEmptyNeighbours( nNode, nEmpty ); if( numEmpty == 0 ) { // no empty nodes - nowhere to go return 0; } // randomnly choose an empty node: by direction index newDir = nEmpty[mfRand( numEmpty )]; } // push matrix that has initial zTrans and rotation glPushMatrix(); // translate to current position glTranslatef( (vc.curPos.x - (vc.numNodes.x - 1)/2.0f )*vc.divSize, (vc.curPos.y - (vc.numNodes.y - 1)/2.0f )*vc.divSize, (vc.curPos.z - (vc.numNodes.z - 1)/2.0f )*vc.divSize ); // draw joint if necessary, and pipe if( newDir != lastDir ) { // turning! - we have to draw joint jointType = ChooseJointType(); switch( jointType ) { case BALL_JOINT: if( bTexture ) { // use special texture-friendly balls align_plusz( newDir ); glPushMatrix(); align_plusy( lastDir, newDir ); // translate forward 1.0*r along +z to get set for drawing elbow glTranslatef( 0.0f, 0.0f, vc.radius ); // decide which elbow orientation to use iElbow = ChooseElbow( lastDir, newDir ); glCallList( balls[iElbow] ); glPopMatrix(); } else { // draw ball in default orientation glCallList( ball ); align_plusz( newDir ); } // move ahead 1.0*r to draw pipe glTranslatef( 0.0f, 0.0f, vc.radius ); break; case ELBOW_JOINT: align_plusz( newDir ); // the align_plusy() here will screw up our notch calcs, so // we push-pop glPushMatrix(); align_plusy( lastDir, newDir ); // translate forward 1.0*r along +z to get set for drawing elbow glTranslatef( 0.0f, 0.0f, vc.radius ); // decide which elbow orientation to use iElbow = ChooseElbow( lastDir, newDir ); if( iElbow == -1 ) { iElbow = 0; // recover } glCallList( elbows[iElbow] ); glPopMatrix(); glTranslatef( 0.0f, 0.0f, vc.radius ); break; default: // Horrors! It's the teapot! glFrontFace( GL_CW ); glEnable( GL_NORMALIZE ); auxSolidTeapot(2.5 * vc.radius); glDisable( GL_NORMALIZE ); glFrontFace( GL_CCW ); align_plusz( newDir ); // move ahead 1.0*r to draw pipe glTranslatef( 0.0f, 0.0f, vc.radius ); } // update the current notch vector notchVec = notchTurn[lastDir][newDir][notchVec]; // draw short pipe align_notch( newDir, notchVec ); glCallList( shortPipe ); } else { // no turn // draw long pipe, from point 1.0*r back align_plusz( newDir ); align_notch( newDir, notchVec ); glTranslatef( 0.0f, 0.0f, -vc.radius ); glCallList( longPipe ); } glPopMatrix(); // mark new node as non-empty nNode[newDir]->empty = GL_FALSE; vc.curNode = nNode[newDir]; updateCurrentPosition( newDir ); lastDir = newDir; return 1; } /*----------------------------------------------------------------------\ | | | drawFirstPipeSection(int dir) | | | | - Draws a starting cap and a short pipe section | | - int dir: new absolute direction, or RANDOM_DIR | | - the 'current node' is set as the one we draw thru the NEXT | | time around. | | - return: 0 if couldn't do it | | 1 if successful | | | | | \----------------------------------------------------------------------*/ static int drawFirstPipeSection( int dir ) { static Node *nNode[NUM_DIRS]; // weight going straight a little more static int nEmpty[NUM_DIRS+1]; int numEmpty, newDir; VC *vp = &vc; // find out who the neighbours are: this returns addresses of the 6 // neighbours, or NULL, if they're out of bounds getNeighbourNodes( &vc.curPos, nNode ); // determine new direction to go in if( dir != RANDOM_DIR ) { // choose absolute direction if( (nNode[dir] != NULL) && nNode[dir]->empty ) { newDir = dir; } else { // can't go in that direction return 0; } } else { // randomnly choose one of the empty nodes numEmpty = getEmptyNeighbours( nNode, nEmpty ); if( numEmpty == 0 ) { // no empty nodes - nowhere to go return 0; } // randomnly choose an empty node: by direction index newDir = nEmpty[mfRand( numEmpty )]; } // push matrix that has initial zTrans and rotation glPushMatrix(); // translate to current position glTranslatef( (vc.curPos.x - (vc.numNodes.x - 1)/2.0f )*vc.divSize, (vc.curPos.y - (vc.numNodes.y - 1)/2.0f )*vc.divSize, (vc.curPos.z - (vc.numNodes.z - 1)/2.0f )*vc.divSize ); // draw ball if( bTexture ) { align_plusz( newDir ); glCallList( ball ); } else { // draw ball in default orientation glCallList( ball ); align_plusz( newDir ); } // set initial notch vector notchVec = defCylNotch[newDir]; // move ahead 1.0*r to draw pipe glTranslatef( 0.0f, 0.0f, vc.radius ); // draw short pipe align_notch( newDir, notchVec ); glCallList( shortPipe ); glPopMatrix(); // mark new node as non-empty nNode[newDir]->empty = GL_FALSE; vc.curNode = nNode[newDir]; updateCurrentPosition( newDir ); lastDir = newDir; // set normal drawing mode drawNext = drawPipeSection; return 1; } /*----------------------------------------------------------------------- | | | DrawEndCap(): | | - Draws a ball, used to cap end of a pipe | | | -----------------------------------------------------------------------*/ void DrawEndCap() { glPushMatrix(); // translate to current position glTranslatef( (vc.curPos.x - (vc.numNodes.x - 1)/2.0f )*vc.divSize, (vc.curPos.y - (vc.numNodes.y - 1)/2.0f )*vc.divSize, (vc.curPos.z - (vc.numNodes.z - 1)/2.0f )*vc.divSize ); if( bTexture ) { glPushMatrix(); align_plusz( lastDir ); align_notch( lastDir, notchVec ); glCallList( ball ); glPopMatrix(); } else glCallList( ball ); glPopMatrix(); } /*----------------------------------------------------------------------- | | | ResetPipes(): | | - Resets drawing parameters | | | -----------------------------------------------------------------------*/ void ResetPipes(void) { int i; Node *pNode; // ! flush gl cmds before calling gdi ! glFlush(); RectWipeClear( vc.winSize.width, vc.winSize.height ); glClear(GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f, 0.0f, vc.zTrans); // Rotate Scene glRotatef( vc.yRot, 0.0f, 1.0f, 0.0f ); vc.yRot += 9.73156f; vc.viewDist = -vc.zTrans; vc.curPos.x = vc.numNodes.x / 2; vc.curPos.y = vc.numNodes.y / 2; vc.curPos.z = vc.numNodes.z / 2; vc.curNode = (Node *) &node[vc.curPos.z][vc.curPos.y][vc.curPos.x]; // Reset the node states pNode = &node[0][0][0]; for( i = 0; i < (NUM_NODE)*(NUM_NODE)*(NUM_NODE); i++, pNode++ ) pNode->empty = GL_TRUE; // Mark starting node as taken node[vc.curPos.z][vc.curPos.y][vc.curPos.x].empty = GL_FALSE; // Set the joint style if( bCycleJointStyles ) { if( ++jointStyle >= NUM_JOINT_STYLES ) jointStyle = 0; } drawNext = drawFirstPipeSection; } /*----------------------------------------------------------------------- | | | DrawPipes(void): | | - Draws next section in random orientation | | | -----------------------------------------------------------------------*/ // If random search takes longer than twice the total number // of nodes, give up the random search. There may not be any // empty nodes. #define INFINITE_LOOP (2 * NUM_NODE * NUM_NODE * NUM_NODE) void DrawPipes(void) { static int pipeCount = 0; int infLoopDetect = 0; int count = 0; int x,y,z; BOOL bFoundNode = FALSE; if( drawMode != MF_MANUAL ) { if( !drawNext(RANDOM_DIR) ) { // deadlock -- nowhere for current pipe to go. // Cap off last pipe. DrawEndCap(); ChooseMaterial(); // If number pipes exceeds max, then reset the pipes and exit. // Otherwise, start a new pipe. if( pipeCount++ >= vc.numPipes ) { pipeCount = 0; ResetPipes(); return; } else { // Find empty node while( !bFoundNode ) { // Pick a random node. x = mfRand( vc.numNodes.x ); y = mfRand( vc.numNodes.y ); z = mfRand( vc.numNodes.z ); // If its empty, we're done. if( node[z][y][x].empty ) bFoundNode = TRUE; else { // Watch out for infinite loops! After trying for // awhile, give up on the random search and look // for the first empty node. if ( infLoopDetect++ > INFINITE_LOOP ) { // Search for first empty node. for ( x = 0; !bFoundNode && (x < vc.numNodes.x); x++ ) for ( y = 0; !bFoundNode && (y < vc.numNodes.y); y++ ) for ( z = 0; !bFoundNode && (z < vc.numNodes.z); z++ ) if ( node[z][y][x].empty ) bFoundNode = TRUE; // If nothing found, there are no more empty nodes. // Reset the pipes and exit. if ( !bFoundNode ) { pipeCount = 0; ResetPipes(); return; } } } } // start drawing at new node next time we come thru vc.curPos.x = x; vc.curPos.y = y; vc.curPos.z = z; node[z][y][x].empty = GL_FALSE; vc.curNode = (Node *) &node[vc.curPos.z][vc.curPos.y][vc.curPos.x]; drawNext = drawFirstPipeSection; return; } } } glFlush(); }