NPIPE.CXX

/******************************Module*Header*******************************\ 
* Module Name: npipe.cxx
*
* Normal pipes code
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "npipe.h"
#include "state.h"


static void align_notch( int newDir, int notch );
static void align_plusy( int oldDir, int newDir );

// 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 };


/**************************************************************************\
* NORMAL_PIPE constructor
*
*
\**************************************************************************/

NORMAL_PIPE::NORMAL_PIPE( STATE *pState )
: PIPE( pState )
{

type = TYPE_NORMAL;
pNState = pState->pNState;

// choose weighting of going straight
if( ! ss_iRand( 20 ) )
weightStraight = ss_iRand2( MAX_WEIGHT_STRAIGHT/4, MAX_WEIGHT_STRAIGHT );
else
weightStraight = 1 + ss_iRand( 4 );
}

/**************************************************************************\
* Start
*
* Start drawing a new normal pipe
*
* - Draw a start cap and short pipe in new direction
*
\**************************************************************************/

void
NORMAL_PIPE::Start( )
{
int newDir;

// Set start position

if( !SetStartPos() ) {
status = PIPE_OUT_OF_NODES;
return;
}

// set a material

ChooseMaterial();

// push matrix that has initial zTrans and rotation
glPushMatrix();

// Translate to current position
TranslateToCurrentPosition();

// Pick a random lastDir
lastDir = ss_iRand( NUM_DIRS );

newDir = ChooseNewDirection();

if( newDir == DIR_NONE ) {
// pipe is stuck at the start node, draw something
status = PIPE_STUCK;
DrawTeapot();
glPopMatrix();
return;
} else
status = PIPE_ACTIVE;

// set initial notch vector
notchVec = defCylNotch[newDir];

DrawStartCap( newDir );

// move ahead 1.0*r to draw pipe
glTranslatef( 0.0f, 0.0f, radius );

// draw short pipe
align_notch( newDir, notchVec );
pNState->shortPipe->Draw();

glPopMatrix();

UpdateCurrentPosition( newDir );

lastDir = newDir;
}

/**************************************************************************\
* Draw
*
* - if turning, draws a joint and a short cylinder, otherwise
* draws a long cylinder.
* - the 'current node' is set as the one we draw thru the NEXT
* time around.
*
\**************************************************************************/

void
NORMAL_PIPE::Draw()
{
int newDir;

newDir = ChooseNewDirection();

if( newDir == DIR_NONE ) { // no empty nodes - nowhere to go
DrawEndCap();
status = PIPE_STUCK;
return;
}

// push matrix that has initial zTrans and rotation
glPushMatrix();

// Translate to current position
TranslateToCurrentPosition();

// draw joint if necessary, and pipe

if( newDir != lastDir ) { // turning! - we have to draw joint
DrawJoint( newDir );

// draw short pipe
align_notch( newDir, notchVec );
pNState->shortPipe->Draw();
}
else { // no turn
// draw long pipe, from point 1.0*r back
align_plusz( newDir );
align_notch( newDir, notchVec );
glTranslatef( 0.0f, 0.0f, -radius );
pNState->longPipe->Draw();
}

glPopMatrix();

UpdateCurrentPosition( newDir );

lastDir = newDir;
}

/**************************************************************************\
* DrawStartCap
*
* Cap the start of the pipe with a ball
*
\**************************************************************************/

void
NORMAL_PIPE::DrawStartCap( int newDir )
{
if( bTexture ) {
align_plusz( newDir );
pNState->ballCap->Draw();
}
else {
// draw big ball in default orientation
pNState->bigBall->Draw();
align_plusz( newDir );
}
}

/**************************************************************************\
* DrawEndCap():
*
* - Draws a ball, used to cap end of a pipe
*
\**************************************************************************/

void
NORMAL_PIPE::DrawEndCap( )
{
glPushMatrix();

// Translate to current position
TranslateToCurrentPosition();

if( bTexture ) {
glPushMatrix();
align_plusz( lastDir );
align_notch( lastDir, notchVec );
pNState->ballCap->Draw();
glPopMatrix();
}
else
pNState->bigBall->Draw();

glPopMatrix();
}

/**************************************************************************\
* ChooseElbow
*
* - 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
*
\**************************************************************************/


// this array supplies the sequence of elbow notch vectors, given
// oldDir and newDir (0's are don't cares)
// 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
};

GLint
NORMAL_PIPE::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;
}

/**************************************************************************\
* DrawJoint
*
* Draw a joint between 2 pipes
*
\**************************************************************************/

void
NORMAL_PIPE::DrawJoint( int newDir )
{
int jointType;
int iBend;

jointType = pNState->ChooseJointType();
#if PIPES_DEBUG
if( newDir == oppositeDir[lastDir] )
printf( "Warning: opposite dir chosen!\n" );
#endif

switch( jointType ) {
case BALL_JOINT:
if( bTexture ) {
// use special texture-friendly ballJoints

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, radius );
// decide which elbow orientation to use
iBend = ChooseElbow( lastDir, newDir );
pNState->ballJoints[iBend]->Draw();

glPopMatrix();
}
else {
// draw big ball in default orientation
pNState->bigBall->Draw();
align_plusz( newDir );
}
// move ahead 1.0*r to draw pipe
glTranslatef( 0.0f, 0.0f, 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, radius );
// decide which elbow orientation to use
iBend = ChooseElbow( lastDir, newDir );
if( iBend == -1 ) {
#if PIPES_DEBUG
printf( "ChooseElbow() screwed up\n" );
#endif
iBend = 0; // recover
}
pNState->elbows[iBend]->Draw();

glPopMatrix();

glTranslatef( 0.0f, 0.0f, radius );
break;

default:
// Horrors! It's the teapot!
DrawTeapot();
align_plusz( newDir );
// move ahead 1.0*r to draw pipe
glTranslatef( 0.0f, 0.0f, radius );
}

// update the current notch vector
notchVec = notchTurn[lastDir][newDir][notchVec];
#if PIPES_DEBUG
if( notchVec == iXX )
printf( "notchTurn gave bad value\n" );
#endif
}


/**************************************************************************\
* Geometry functions
\**************************************************************************/


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 );
}

// given a dir, determine how much to rotate cylinder around z to match notches
// format is [newDir][notchVec]

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 PIPES_DEBUG
if( rotz == fXX ) {
printf( "align_notch(): unexpected value\n" );
return;
}
#endif

if( rotz != 0.0f )
glRotatef( rotz, 0.0f, 0.0f, 1.0f );
}