WindowsXP-SP1/shell/osshell/control/scrnsave/pipes/npipe.cpp
2020-09-30 16:53:49 +02:00

462 lines
15 KiB
C++

//-----------------------------------------------------------------------------
// File: npipe.cpp
//
// Desc: Normal pipes code
//
// Copyright (c) 1994-2000 Microsoft Corporation
//-----------------------------------------------------------------------------
#include "stdafx.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 int defCylNotch[NUM_DIRS] =
{ PLUS_Y, PLUS_Y, MINUS_Z, PLUS_Z, PLUS_Y, PLUS_Y };
//-----------------------------------------------------------------------------
// Name: NORMAL_PIPE constructor
// Desc:
//-----------------------------------------------------------------------------
NORMAL_PIPE::NORMAL_PIPE( STATE *pState ) : PIPE( pState )
{
m_type = TYPE_NORMAL;
m_pNState = pState->m_pNState;
// choose weighting of going straight
if( ! CPipesScreensaver::iRand( 20 ) )
m_weightStraight = CPipesScreensaver::iRand2( MAX_WEIGHT_STRAIGHT/4, MAX_WEIGHT_STRAIGHT );
else
m_weightStraight = 1 + CPipesScreensaver::iRand( 4 );
}
//-----------------------------------------------------------------------------
// Name: Start
// Desc: 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() )
{
m_status = PIPE_OUT_OF_NODES;
return;
}
// set a material
ChooseMaterial();
m_pState->m_pd3dDevice->SetTexture( 0, m_pState->m_textureInfo[0].pTexture );
m_pState->m_pd3dDevice->SetMaterial( m_pMat );
// push matrix that has initial zTrans and rotation
m_pWorldMatrixStack->Push();
// Translate to current position
TranslateToCurrentPosition();
// Pick a random lastDir
m_lastDir = CPipesScreensaver::iRand( NUM_DIRS );
newDir = ChooseNewDirection();
if( newDir == DIR_NONE )
{
// pipe is stuck at the start node, draw nothing
m_status = PIPE_STUCK;
m_pWorldMatrixStack->Pop();
return;
}
else
{
m_status = PIPE_ACTIVE;
}
// set initial notch vector
m_notchVec = defCylNotch[newDir];
DrawStartCap( newDir );
// move ahead 1.0*r to draw pipe
m_pWorldMatrixStack->TranslateLocal( 0.0f, 0.0f, m_radius );
// draw short pipe
align_notch( newDir, m_notchVec );
m_pNState->m_pShortPipe->Draw( m_pWorldMatrixStack->GetTop() );
m_pWorldMatrixStack->Pop();
UpdateCurrentPosition( newDir );
m_lastDir = newDir;
}
//-----------------------------------------------------------------------------
// Name: Draw
// Desc: - 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;
m_pState->m_pd3dDevice->SetTexture( 0, m_pState->m_textureInfo[0].pTexture );
m_pState->m_pd3dDevice->SetMaterial( m_pMat );
newDir = ChooseNewDirection();
if( newDir == DIR_NONE )
{
// no empty nodes - nowhere to go
DrawEndCap();
m_status = PIPE_STUCK;
return;
}
// push matrix that has initial zTrans and rotation
m_pWorldMatrixStack->Push();
// Translate to current position
TranslateToCurrentPosition();
// draw joint if necessary, and pipe
if( newDir != m_lastDir )
{
// turning! - we have to draw joint
DrawJoint( newDir );
// draw short pipe
align_notch( newDir, m_notchVec );
m_pNState->m_pShortPipe->Draw( m_pWorldMatrixStack->GetTop() );
}
else
{
// no turn -- draw long pipe, from point 1.0*r back
align_plusz( newDir );
align_notch( newDir, m_notchVec );
m_pWorldMatrixStack->TranslateLocal( 0.0f, 0.0f, -m_radius );
m_pNState->m_pLongPipe->Draw( m_pWorldMatrixStack->GetTop() );
}
m_pWorldMatrixStack->Pop();
UpdateCurrentPosition( newDir );
m_lastDir = newDir;
}
//-----------------------------------------------------------------------------
// Name: DrawStartCap
// Desc: Cap the start of the pipe with a ball
//-----------------------------------------------------------------------------
void NORMAL_PIPE::DrawStartCap( int newDir )
{
if( m_pState->m_bUseTexture )
{
align_plusz( newDir );
m_pNState->m_pBallCap->Draw( m_pWorldMatrixStack->GetTop() );
}
else
{
// draw big ball in default orientation
m_pNState->m_pBigBall->Draw( m_pWorldMatrixStack->GetTop() );
align_plusz( newDir );
}
}
//-----------------------------------------------------------------------------
// Name: DrawEndCap():
// Desc: - Draws a ball, used to cap end of a pipe
//-----------------------------------------------------------------------------
void NORMAL_PIPE::DrawEndCap()
{
m_pWorldMatrixStack->Push();
// Translate to current position
TranslateToCurrentPosition();
if( m_pState->m_bUseTexture )
{
align_plusz( m_lastDir );
align_notch( m_lastDir, m_notchVec );
m_pNState->m_pBallCap->Draw( m_pWorldMatrixStack->GetTop() );
}
else
{
m_pNState->m_pBigBall->Draw( m_pWorldMatrixStack->GetTop() );
}
m_pWorldMatrixStack->Pop();
}
//-----------------------------------------------------------------------------
// Name:
// Desc: 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 int 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
};
//-----------------------------------------------------------------------------
// Name: ChooseElbow
// Desc: - 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
//-----------------------------------------------------------------------------
int 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] == m_notchVec )
return i;
}
// we shouldn't arrive here
return -1;
}
//-----------------------------------------------------------------------------
// Name: DrawJoint
// Desc: Draw a joint between 2 pipes
//-----------------------------------------------------------------------------
void NORMAL_PIPE::DrawJoint( int newDir )
{
int jointType;
int iBend;
jointType = m_pNState->ChooseJointType();
#if PIPES_DEBUG
if( newDir == oppositeDir[lastDir] )
OutputDebugString( "Warning: opposite dir chosen!\n" );
#endif
switch( jointType )
{
case BALL_JOINT:
{
if( m_pState->m_bUseTexture )
{
// use special texture-friendly ballJoints
align_plusz( newDir );
m_pWorldMatrixStack->Push();
align_plusy( m_lastDir, newDir );
// translate forward 1.0*r along +z to get set for drawing elbow
m_pWorldMatrixStack->TranslateLocal( 0.0f, 0.0f, m_radius );
// decide which elbow orientation to use
iBend = ChooseElbow( m_lastDir, newDir );
m_pNState->m_pBallJoints[iBend]->Draw( m_pWorldMatrixStack->GetTop() );
m_pWorldMatrixStack->Pop();
}
else
{
// draw big ball in default orientation
m_pNState->m_pBigBall->Draw( m_pWorldMatrixStack->GetTop() );
align_plusz( newDir );
}
// move ahead 1.0*r to draw pipe
m_pWorldMatrixStack->TranslateLocal( 0.0f, 0.0f, m_radius );
break;
}
case ELBOW_JOINT:
default:
{
align_plusz( newDir );
// the align_plusy() here will mess up
// our notch calcs, so we push-pop
m_pWorldMatrixStack->Push();
align_plusy( m_lastDir, newDir );
// translate forward 1.0*r along +z to get set for drawing elbow
m_pWorldMatrixStack->TranslateLocal( 0.0f, 0.0f, m_radius );
// decide which elbow orientation to use
iBend = ChooseElbow( m_lastDir, newDir );
if( iBend == -1 )
{
#if PIPES_DEBUG
OutputDebugString( "Bad result from ChooseElbow()\n" );
#endif
iBend = 0; // recover
}
m_pNState->m_pElbows[iBend]->Draw( m_pWorldMatrixStack->GetTop() );
m_pWorldMatrixStack->Pop();
m_pWorldMatrixStack->TranslateLocal( 0.0f, 0.0f, m_radius );
break;
}
}
// update the current notch vector
m_notchVec = notchTurn[m_lastDir][newDir][m_notchVec];
#if PIPES_DEBUG
if( m_notchVec == iXX )
OutputDebugString( "notchTurn gave bad value\n" );
#endif
}
//-----------------------------------------------------------------------------
// Name: align_plusy
// Desc: - Assuming +z axis is already aligned with newDir, align
// +y axis BACK along lastDir
//-----------------------------------------------------------------------------
void NORMAL_PIPE::align_plusy( int oldDir, int newDir )
{
static D3DXVECTOR3 zAxis = D3DXVECTOR3(0.0f,0.0f,1.0f);
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
};
float rotz = RotZ[oldDir][newDir];
if( rotz != 0.0f )
m_pWorldMatrixStack->RotateAxisLocal( &zAxis, SS_DEG_TO_RAD(rotz) );
}
//-----------------------------------------------------------------------------
// Name: align_notch
// Desc: - 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
//-----------------------------------------------------------------------------
void NORMAL_PIPE::align_notch( int newDir, int notch )
{
float rotz;
int curNotch;
// figure out where notch is presently after +z alignment
curNotch = defCylNotch[newDir];
// (don't need this now we have lut)
// given a dir, determine how much to rotate cylinder around z to match notches
// format is [newDir][notchVec]
static float 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
};
// look up rotation value in table
rotz = alignNotchRot[newDir][notch];
#if PIPES_DEBUG
if( rotz == fXX )
{
printf( "align_notch(): unexpected value\n" );
return;
}
#endif
static D3DXVECTOR3 zAxis = D3DXVECTOR3(0.0f,0.0f,1.0f);
if( rotz != 0.0f )
m_pWorldMatrixStack->RotateAxisLocal( &zAxis, SS_DEG_TO_RAD(rotz) );
}