/************************************************************ * * * tunnel2.c * * * * * LPGE 1997 * * * * Copyright (C) 1997 Sony Computer Entertainment Inc. * * All Rights Reserved * * * ***********************************************************/ /**************************************************************************** includes ****************************************************************************/ #include "tunnel.h" #include "tunnel2.h" #include "flying.h" #include "tracks.h" #include "menu.h" #include "camera.h" /**************************************************************************** globals ****************************************************************************/ TunnelDescription *SetTracks; int TunnelDrawProcessSectionLimits[MAX_DRAW_PROCESSES_PER_DESCRIPTION*2]; int TrackViewerMainMode; int TrackViewerZAngle; GsCOORDINATE2 TrackViewerCoord; int TrackViewerSection; int TrackViewerCameraControlMode; int TrackViewerAutoCameraMode; int TrackViewerShipViewMode; int TrackViewerAdjustCameraAngleFlag; int TrackViewerDirection; int FrameWhenTrackViewingStarts; ObjectHandler ViewerObject; int TrackViewerDeviationAngle; int TrackViewerTwisterDirection, TrackViewerTwisterPeriod; int TrackViewerRollerCoasterSameSpeedPeriod; int TrackViewerRollerCoasterChangeSpeedPeriod; int TrackViewerRollerCoasterState; // 0 low, 1 accelerate, // 2 high, 3 decelerate int TrackViewerRollerCoasterFrameWhenStateChangedLast; int TrackViewerWholeTrackCentreDistance; SVECTOR TrackViewerWholeTrackCoordTwist; int ExternalAutoViewerTwistAxis; int ExternalAutoViewerTwistDirection; int ExternalAutoViewerZoomSpeed; int RollingDemoActiveFlag; int RollingDemoDeliberateFlag; int RollingDemoStartFrame; int RollingDemoMainMode; int RollingDemoFirstTimeEverFlag; int RollingDemoAiFlierModelNumber; int PlayerOneModelNumber; int PlayerTwoModelNumber; int HighlightSectionOffsetWithinSection; int HighlightSpeed, HighlightMaxSpeed; int HighlightSectionSpacing; int HighlightPeriod; int ShortSnakeLengthInSections, ShortSnakePositionCounter; int SnakeTwistingDirection; int SnakePeriod, SnakeMaxSpeed; int SnakeBaseSectionPolygonIndexCounter; int PreviousSnakeBaseSectionPolygonIndexCounter; int SnakePreviousBaseSection, SnakePreviousLength; int PreviousSnakeDirection; TMD_NORM FirstSnakeNormalStore[MAX_NUMBER_SECTIONS_DRAWN]; TMD_NORM SecondSnakeNormalStore[MAX_NUMBER_SECTIONS_DRAWN]; TMD_NORM GeneralSnakeNormalStore[MAX_NUMBER_SHAPES_PER_SECTION][MAX_NUMBER_SECTIONS_DRAWN]; GsIMAGE *SnakeTexture; int SnakeWidth, PreviousSnakeWidth; /**************************************************************************** macros ****************************************************************************/ #define ValidID(id) ( (id >= 0) && (id < NumberTunnelSections) ) #define PrintDirection(direction) \ switch(direction) \ { \ case FORWARDS: printf("FORWARDS\n"); break; \ case BACKWARDS: printf("BACKWARDS\n"); break; \ default: printf("BAD direction %d\n", direction); exit(1); \ } #define ANTI_DIRECTION(direction) (direction) == BACKWARDS ? FORWARDS : BACKWARDS #define setNORMAL(normal,x,y,z) (normal)->nx = (x), (normal)->ny = (y), (normal)->nz = (z) /**************************************************************************** local prototypes ****************************************************************************/ void EffectTwistInExternalAutoViewer (int axis, int direction); void MoveExternalAutoViewer (void); void CalculateDrawProcessSectionLimits (TunnelDescription *description); GsIMAGE *GetNewSnakeTexture (int sectionID); void HandleSectionHighlightingForTrack (void); void HandleFullLengthSnakeOscillation (void); int PolygonEffectUsesNormal (int effectID); int PolygonEffectUsesTexture (int effectID); void MakePolygonNormalChange (TMD_NORM *normal, int change); void MakePolygonTextureChange (u_long addr, int index, int change, int sectionID); void RevertPolygonTextureChange (u_long addr, int index, int sectionID); void ResetNormalsFromFullSnakeOscillation (void); void HandleShortSnake (void); void ResetNormalsFromShortSnakeOscillation (void); void HandleTwoSnakesInHelix (void); void ResetNormalsFromTwoSnakeOscillation (void); void HandleTwoQuartersSnake (void); void ResetTextureDataFromTwoQuartersSnakeOscillation (void); void HandleSnakeGrowingAndShrinking (void); void ResetTextureDataFromSnakeGrowingAndShrinking (void); int GetSnakeDirection (void); int DrawProcessVisible (int processIndex); int RegionVisibleFromDrawRegion (int start, int end, int start2, int end2, int direction2); /**************************************************************************** functions ****************************************************************************/ #if 0 // not used void PrintSpline (VECTOR* spline, int numberPoints) { #if (DEVELOPMENT_ENVIRONMENT==YAROZE) int i; assert(numberPoints > 0); printf("\n\n\n\nPrinting Spline with %d points\n\n\n", numberPoints); for (i = 0; i < numberPoints; i++) { dumpVECTOR( &spline[i]); } printf("\n\nEnd of Spline\n\n\n"); #endif } #endif // not used #if 0 // not used void PrintTunnelChunk (TunnelChunk* chunk) { #if (DEVELOPMENT_ENVIRONMENT==YAROZE) printf("\n\nPrinting Tunnel Chunk :-\n"); printf("%d sections\n", chunk->numberSections); switch (chunk->type) { case STRAIGHT: printf("Straight "); break; case SIMPLE_CURVE: printf("Simple curve "); if (chunk->thetaZ != 0) { if (chunk->thetaZ > 0) printf("clockwise twist %d\n", chunk->thetaZ); else printf("anticlockwise twist %d\n", chunk->thetaZ); } if (chunk->thetaX != 0) { assert(chunk->thetaY == 0); if (chunk->thetaX > 0) printf("curve downward %d\n", chunk->thetaX); else printf("curve upward %d\n", chunk->thetaX); } else { assert(chunk->thetaY != 0); if (chunk->thetaY > 0) printf("curve left %d\n", chunk->thetaY); else printf("curve right %d\n", chunk->thetaY); } break; default: assert(FALSE); } #endif } #endif // not used #if 0 // not used void PrintTunnelDescription (TunnelDescription* description) { int i; printf("\n\n\nPrinting TunnelDescription :-\n\n\n"); printf("%d chunks\n", description->numberChunks); printf("shapeID %d\n", description->classicShapeID); printf("%d sections\n", NumberSectionsInTunnelDescription(description)); printf("Here are the chunks themselves :-\n\n"); for (i = 0; i < description->numberChunks; i++) { PrintTunnelChunk( &description->listOfChunks[i]); } printf("twoDflag %d\n", description->twoDflag); printf("sectionLength %d\n", description->sectionLength); printf("outerRadius %d\n", description->outerRadius); printf("shapesPerSection %d\n", description->shapesPerSection); printf("numberSectionsDrawn %d\n", description->numberSectionsDrawn); printf("drawBackOffset %d\n", description->drawBackOffset); printf("numberSectionsSubdivided %d\n", description->numberSectionsSubdivided); printf("subdivisionBackOffset %d\n", description->subdivisionBackOffset); printf("subdivisionExtent %d\n", description->subdivisionExtent); printf("texturingType %d\n", description->texturingType); printf("textureNumber %d\n", description->textureNumber); printf("numberImages %d\n", description->numberImages); for (i = 0; i < description->numberImages; i++) { printf("index %d\n", i); printf("Image ID: %d\n", description->listOfImageIds[i]); printf("twist flag %d\n", description->listOfTwistFlags[i]); dumpRECT( &description->listOfOffsets[i]); } printf("tunnelLightingEffect %d\n", description->tunnelLightingEffect); printf("lightEffectPeriod %d\n", description->lightEffectPeriod); printf("fogActive %d\n", description->fogActive); printf("dqa %d dqb %d\n", description->dqa, description->dqb); printf("fogR %d fogG %d fogB %d\n", description->fogR, description->fogG, description->fogB); printf("fogEffectFlag %d\n", description->fogEffectFlag); printf("ambientEffectFlag %d\n", description->ambientEffectFlag); printf("ambientRed %d\n", description->ambientRed); printf("ambientGreen %d\n", description->ambientGreen); printf("ambientBlue %d\n", description->ambientBlue); printf("totalDistance %d\n", description->totalDistance); printf("bestLapTimeInFrames %d\n", description->bestLapTimeInFrames); printf("numberClutAnimations: %d\n", description->numberClutAnimations); for (i = 0; i < description->numberClutAnimations; i++) { printf("info on clut animation %d\n\n", i); printf("image pointer %u\n", (u_int) description->listOfClutAnimations[i]->image); printf("animationPeriod %d\n", description->listOfClutAnimations[i]->animationPeriod); printf("animationFunctionLabel %d\n", description->listOfClutAnimations[i]->animationFunctionLabel); printf("numberCells %d\n", description->listOfClutAnimations[i]->numberCells); } printf("numberDrawProcesses: %d\n", description->numberDrawProcesses); for (i = 0; i < description->numberDrawProcesses; i++) { printf("info on draw process %d\n\n", i); printf("id %d\n", description->drawingProcesses[i].id); printf("alive %d\n", description->drawingProcesses[i].alive); printf("clipArea: \n"); dumpRECT( &description->drawingProcesses[i].clipArea); printf("offsets: [0] %d [1] %d\n", description->drawingProcesses[i].offsets[0], description->drawingProcesses[i].offsets[1]); printf("otLabel %d\n", description->drawingProcesses[i].otLabel); printf("packetAreaLabel %d\n", description->drawingProcesses[i].packetAreaLabel); } printf("tunnelHighlightFlag %d\n", description->tunnelHighlightFlag); printf("highlightEffectId %d\n", description->highlightEffectId); printf("highlightData1 %d\n", description->highlightData1); printf("highlightData2 %d\n", description->highlightData2); printf("\n\nEnd of TunnelDescription\n\n\n\n"); } #endif // not used void MakeTunnelChunk (TunnelChunk* chunk, int numberSections, int thetaX, int thetaY, int thetaZ) { assert(numberSections > 0); assert(numberSections <= MAX_SECTIONS_PER_CHUNK); if (thetaX != 0 || thetaY != 0) chunk->type = SIMPLE_CURVE; else if (thetaZ != 0) chunk->type = Z_ONLY_CURVE; else chunk->type = STRAIGHT; switch (chunk->type) { case STRAIGHT: assert(thetaX == 0); assert(thetaY == 0); assert(thetaZ == 0); break; case SIMPLE_CURVE: if (thetaX == 0) { assert(abs(thetaY) <= MAXIMUM_PURE_Y_ROTATION); } else if (thetaY == 0) { assert(abs(thetaX) <= MAXIMUM_PURE_X_ROTATION); } else { assert(FALSE); // can't have both } assert(abs(thetaZ) <= MAXIMUM_Z_ROTATION); break; case Z_ONLY_CURVE: assert(thetaX == 0); assert(thetaY == 0); assert(abs(thetaZ) <= MAXIMUM_Z_ROTATION); break; default: assert(FALSE); } chunk->thetaX = thetaX; chunk->thetaY = thetaY; chunk->thetaZ = thetaZ; chunk->numberSections = numberSections; } void CopyChunk (TunnelChunk* from, TunnelChunk* to) { assert(from->type != Z_ONLY_CURVE); // not allowed to->type = from->type; to->numberSections = from->numberSections; to->thetaX = from->thetaX; to->thetaY = from->thetaY; to->thetaZ = from->thetaZ; } void MakeTunnelDescriptionFromChunks (TunnelDescription* description, int numberChunks, TunnelChunk* listOfChunks) { int i; assert(numberChunks > 0); assert(numberChunks <= MAX_CHUNKS_PER_DESCRIPTION); description->numberChunks = numberChunks; for (i = 0; i < numberChunks; i++) { CopyChunk( &listOfChunks[i], &description->listOfChunks[i]); } InferTwoDFlagForDescription(description); description->totalDistance = NumberSectionsInTunnelDescription(description) * description->sectionLength; description->bestLapTimeInFrames = DEFAULT_BEST_LAP_TIME_FRAMES_PER_SECTION * NumberSectionsInTunnelDescription(description); // * description->totalDistance; } void InferTwoDFlagForDescription (TunnelDescription* description) { TunnelChunk *thisChunk; int i; int flag = FALSE; for (i = 0; i < description->numberChunks; i++) { thisChunk = &description->listOfChunks[i]; switch(thisChunk->type) { case STRAIGHT: // no effect break; case Z_ONLY_CURVE: assert(FALSE); // not allowed yet break; case SIMPLE_CURVE: if ( (thisChunk->thetaZ != 0) || (thisChunk->thetaX != 0 && thisChunk->thetaY != 0)) { flag = FALSE; goto endOfInferTwoDFlagForDescription; } switch (flag) { case FALSE: if (thisChunk->thetaX != 0) { assert(thisChunk->thetaY == 0); flag = Y_Z_PLANE_ONLY; } else { if (thisChunk->thetaY != 0) flag = X_Z_PLANE_ONLY; } break; case X_Z_PLANE_ONLY: // only thetaY turns allowed if (thisChunk->thetaX != 0) { flag = FALSE; goto endOfInferTwoDFlagForDescription; } break; case Y_Z_PLANE_ONLY: // only thetaX turns allowed if (thisChunk->thetaY != 0) { flag = FALSE; goto endOfInferTwoDFlagForDescription; } break; default: assert(FALSE); } break; } } endOfInferTwoDFlagForDescription: // jump-to label description->twoDflag = flag; //assert(flag == X_Z_PLANE_ONLY); // FORCE flat tunnels only } void InitDescription (TunnelDescription* description) { int i; description->numberChunks = -1; description->classicShapeID = -1; for (i = 0; i < MAX_CHUNKS_PER_DESCRIPTION; i++) { description->listOfChunks[i].type = -1; description->listOfChunks[i].numberSections = -1; description->listOfChunks[i].thetaX = 1 << 15; description->listOfChunks[i].thetaY = 1 << 15; description->listOfChunks[i].thetaZ = 1 << 15; } description->twoDflag = FALSE; description->sectionLength = DEFAULT_TRACK_SECTION_LENGTH; description->outerRadius = DEFAULT_TRACK_OUTER_RADIUS; description->shapesPerSection = DEFAULT_NUMBER_SHAPES_PER_SECTION; description->numberSectionsDrawn = DEFAULT_NUMBER_SECTIONS_DRAWN; description->drawBackOffset = DEFAULT_DRAW_BACK_OFFSET; description->numberSectionsSubdivided = DEFAULT_NUMBER_SECTIONS_SUBDIVIDED; description->subdivisionBackOffset = DEFAULT_SUBDIVISION_BACK_OFFSET; description->subdivisionExtent = DEFAULT_SUBDIVISION_EXTENT; description->texturingType = FALSE; description->textureNumber = 0; description->numberImages = 0; for (i = 0; i < MAX_IMAGES_PER_DESCRIPTION; i++) { // description->listOfImages[i] = NULL; description->listOfImageIds[i] = -1; description->listOfTwistFlags[i] = FALSE; setRECT( &description->listOfOffsets[i], 0, 0, 0, 0); } description->tunnelLightingEffect = FALSE; description->lightEffectPeriod = 0; description->fogActive = FALSE; description->dqa = 0; description->dqb = 0; description->fogR = 0; description->fogG = 0; description->fogB = 0; description->fogEffectFlag = FALSE; description->ambientEffectFlag = FALSE; description->ambientRed = ONE/4; description->ambientGreen = ONE/4; description->ambientBlue = ONE/4; description->totalDistance = 0; description->bestLapTimeInFrames = 0; description->numberClutAnimations = 0; for (i = 0; i < MAX_CLUT_ANIMATIONS_PER_DESCRIPTION; i++) { description->listOfClutAnimations[i] = NULL; } description->numberDrawProcesses = 0; for (i = 0; i < MAX_DRAW_PROCESSES_PER_DESCRIPTION; i++) { description->drawingProcesses[i].id = i; description->drawingProcesses[i].alive = FALSE; setRECT( &description->drawingProcesses[i].clipArea, 0, 0, 0, 0); description->drawingProcesses[i].offsets[0] = 0; description->drawingProcesses[i].offsets[1] = 0; description->drawingProcesses[i].otLabel = -1; description->drawingProcesses[i].packetAreaLabel = -1; } description->tunnelHighlightFlag = FALSE; description->highlightEffectId = -1; description->highlightData1 = 0; description->highlightData2 = 0; } void CopyDescription (TunnelDescription* from, TunnelDescription* to) { int i; to->numberChunks = from->numberChunks; for (i = 0; i < from->numberChunks; i++) { CopyChunk( &from->listOfChunks[i], &to->listOfChunks[i]); } to->classicShapeID = from->classicShapeID; to->twoDflag = from->twoDflag; to->sectionLength = from->sectionLength; to->outerRadius = from->outerRadius; to->shapesPerSection = from->shapesPerSection; to->numberSectionsDrawn = from->numberSectionsDrawn; to->drawBackOffset = from->drawBackOffset; to->numberSectionsSubdivided = from->numberSectionsSubdivided; to->subdivisionBackOffset = from->subdivisionBackOffset; to->subdivisionExtent = from->subdivisionExtent; to->texturingType = from->texturingType; to->textureNumber = from->textureNumber; to->numberImages = from->numberImages; for (i = 0; i < to->numberImages; i++) { // to->listOfImages[i] = from->listOfImages[i]; to->listOfImageIds[i] = from->listOfImageIds[i]; to->listOfTwistFlags[i] = from->listOfTwistFlags[i]; setRECT( &to->listOfOffsets[i], from->listOfOffsets[i].x, from->listOfOffsets[i].y, from->listOfOffsets[i].w, from->listOfOffsets[i].h); } to->tunnelLightingEffect = from->tunnelLightingEffect; to->lightEffectPeriod = from->lightEffectPeriod; to->fogActive = from->fogActive; to->dqa = from->dqa; to->dqb = from->dqb; to->fogR = from->fogR; to->fogG = from->fogG; to->fogB = from->fogB; to->fogEffectFlag = from->fogEffectFlag; to->ambientEffectFlag = from->ambientEffectFlag; to->ambientRed = from->ambientRed; to->ambientGreen = from->ambientGreen; to->ambientBlue = from->ambientBlue; to->totalDistance = from->totalDistance; to->bestLapTimeInFrames = from->bestLapTimeInFrames; to->numberClutAnimations = from->numberClutAnimations; for (i = 0; i < MAX_CLUT_ANIMATIONS_PER_DESCRIPTION; i++) { to->listOfClutAnimations[i] = from->listOfClutAnimations[i]; } to->numberDrawProcesses = from->numberDrawProcesses; for (i = 0; i < MAX_DRAW_PROCESSES_PER_DESCRIPTION; i++) { to->drawingProcesses[i].id = from->drawingProcesses[i].id; to->drawingProcesses[i].alive = from->drawingProcesses[i].alive; setRECT( &to->drawingProcesses[i].clipArea, from->drawingProcesses[i].clipArea.x, from->drawingProcesses[i].clipArea.y, from->drawingProcesses[i].clipArea.w, from->drawingProcesses[i].clipArea.h); to->drawingProcesses[i].offsets[0] = from->drawingProcesses[i].offsets[0]; to->drawingProcesses[i].offsets[1] = from->drawingProcesses[i].offsets[1]; to->drawingProcesses[i].otLabel = from->drawingProcesses[i].otLabel; to->drawingProcesses[i].packetAreaLabel = from->drawingProcesses[i].otLabel; } to->tunnelHighlightFlag = from->tunnelHighlightFlag; to->highlightEffectId = from->highlightEffectId; to->highlightData1 = from->highlightData1; to->highlightData2 = from->highlightData2; } int NumberSectionsInTunnelDescription (TunnelDescription* description) { int numberSections; int i; numberSections = 0; for (i = 0; i < description->numberChunks; i++) { numberSections += description->listOfChunks[i].numberSections; } return numberSections; } int WhichChunkInDescription (TunnelDescription *description, int sectionID) { int chunkID = -1; int runningTotal = 0; int i; assert(sectionID >= 0); assert(sectionID < NumberSectionsInTunnelDescription(description)); for (i = 0; i < description->numberChunks; i++) { if (sectionID >= runningTotal && sectionID < runningTotal + description->listOfChunks[i].numberSections) { chunkID = i; break; } else runningTotal += description->listOfChunks[i].numberSections; } assert(chunkID != -1); return chunkID; } void GetChunkNumberAndOffset (TunnelDescription *description, int sectionID, int *chunkID, int *chunkOffset) { int runningTotal = 0; int i; assert(sectionID >= 0); assert(sectionID < NumberSectionsInTunnelDescription(description)); *chunkID = -1; *chunkOffset = -1; for (i = 0; i < description->numberChunks; i++) { if (sectionID >= runningTotal && sectionID < runningTotal + description->listOfChunks[i].numberSections) { *chunkID = i; *chunkOffset = sectionID - runningTotal; break; } else runningTotal += description->listOfChunks[i].numberSections; } assert(*chunkID != -1); assert(*chunkOffset != -1); } void SetTextureDataForTunnelDescription (TunnelDescription *description, int textureType, int numberImages, int *listOfImageIDs, int *twistFlags, RECT *listOfOffsets, int textureNdata) { int i; assert(textureType >= SINGLE_TUNNEL_TEXTURE); assert(textureType <= SPREAD_N_TEXTURES_EVENLY); assert(numberImages > 0); assert(numberImages <= MAX_IMAGES_PER_DESCRIPTION); assert(textureNdata >= 0); // zero for null description->texturingType = textureType; description->numberImages = numberImages; description->textureNumber = textureNdata; // store ids of images only for (i = 0; i < numberImages; i++) { description->listOfImageIds[i] = listOfImageIDs[i]; description->listOfTwistFlags[i] = twistFlags[i]; setRECT( &description->listOfOffsets[i], listOfOffsets[i].x, listOfOffsets[i].y, listOfOffsets[i].w, listOfOffsets[i].h); } } void InitialiseLightingForTrack (TunnelDescription *description) { switch(description->tunnelLightingEffect) { case FALSE: //SetThreeWhiteLights(); SetTheAbsenceOfFlatLight(); break; case THREE_COLOURED_LIGHTS: SetThreeColouredLights(); break; case CYCLE_THREE_LIGHTS_AROUND_AXES: SetThreeColouredLights(); break; case CYCLE_THREE_LIGHTS_BRIGHTLY_AROUND_AXES: SetThreeColouredLights(); break; case CYCLE_LIGHTS_BRIGHTNESS: SetThreeColouredLights(); break; case CYCLE_THREE_LIGHTS_DIRECTION_AND_COLOUR: SetThreeColouredLights(); break; case THREE_WHITE_LIGHTS: SetThreeWhiteLights(); break; default: assert(FALSE); } } void SetTrackLighting (TunnelDescription *description, int lightingEffect, int lightEffectPeriod) { assert(lightingEffect >= CYCLE_THREE_LIGHTS_AROUND_AXES); assert(lightingEffect <= THREE_WHITE_LIGHTS); assert(lightEffectPeriod > 0); description->tunnelLightingEffect = lightingEffect; description->lightEffectPeriod = lightEffectPeriod; } #if 0 // NOT USED void SetDefaultLightingAndTexturingForDescription (TunnelDescription *description) { GsIMAGE *imageList[1]; int twistFlagList[1]; RECT offsetList[1]; description->texturingType = FALSE; description->tunnelLightingEffect = FALSE; imageList[0] = &SimpleBlueTextureInfo; // &BarRedTextureInfo; twistFlagList[0] = FALSE; setRECT( &offsetList[0], 0, 0, 64, 64); SetTextureDataForTunnelDescription (description, SINGLE_TUNNEL_TEXTURE, 1, imageList, twistFlagList, offsetList, 0); } #endif // as rough guide: dqb vaguely proportional to fogging, // -dqa highly proportional: by -5000, fog is everywhere, by -28000, all gone, // odd effects on objects when between -28000 and -32000 void SetTrackFogging (TunnelDescription *description, int dqa, int dqb, int fogR, int fogG, int fogB) { assert(dqa >= -32767 && dqa <= 32768); assert(fogR >= 0 && fogR <= 255); assert(fogG >= 0 && fogG <= 255); assert(fogB >= 0 && fogB <= 255); description->fogActive = TRUE; description->dqa = dqa; description->dqb = dqb; description->fogR = fogR; description->fogG = fogG; description->fogB = fogB; } void InitialiseFoggingForTrack (TunnelDescription *description) { //int adjustmentFlag; if (description->fogActive != TRUE) { OverallLightMode = 0; GsSetLightMode(OverallLightMode); return; } else { OverallLightMode = 1; GsSetLightMode(OverallLightMode); TheFogging.dqa = description->dqa; TheFogging.dqb = description->dqb; TheFogging.rfc = description->fogR; TheFogging.gfc = description->fogG; TheFogging.bfc = description->fogB; GsSetFogParam( &TheFogging); } } void SetFogEffectForTrack (TunnelDescription *description, int fogEffectID) { assert(description != NULL); assert(fogEffectID != FALSE); description->fogEffectFlag = fogEffectID; } void HandleTunnelFogEffect (void) { switch(TheTunnelDescription.fogEffectFlag) { case FALSE: assert(FALSE); // should never get here break; case SECOND: OscillateFoggingColourBrightness(); break; case THIRD: OscillateFoggingColourThroughSpectrum(); break; default: assert(FALSE); } } void OscillateFoggingColourBrightness (void) { int period = 256; int time, phase, subTime; time = frameNumber % period; assert(period != 0); phase = (time * 2) / period; switch(phase) { case 0: // white to black subTime = time; TheFogging.rfc = 128 - subTime; TheFogging.gfc = 128 - subTime; TheFogging.bfc = 128 - subTime; break; case 1: // black to white subTime = time - (period/2); TheFogging.rfc = subTime; TheFogging.gfc = subTime; TheFogging.bfc = subTime; break; default: assert(FALSE); } GsSetFogParam( &TheFogging); } void OscillateFoggingColourThroughSpectrum (void) { int period = 384; int time, phase, subTime; time = frameNumber % period; assert(period != 0); phase = (time * 3) / period; switch(phase) { case 0: // red to green subTime = time; TheFogging.rfc = 128 - subTime; TheFogging.gfc = subTime; TheFogging.bfc = 0; break; case 1: // green to blue subTime = time - (period/3); TheFogging.rfc = 0; TheFogging.gfc = 128 - subTime; TheFogging.bfc = subTime; break; case 2: // blue to red subTime = time - ((2 * period)/3); TheFogging.rfc = subTime; TheFogging.gfc = 0; TheFogging.bfc = 128 - subTime; break; default: assert(FALSE); } GsSetFogParam( &TheFogging); } void SetAmbientLevelForTrack (TunnelDescription *description, int r, int g, int b) { assert(description != NULL); assert(r >= 0); assert(r <= ONE*4); assert(g >= 0); assert(g <= ONE*4); assert(b >= 0); assert(b <= ONE*4); description->ambientRed = r; description->ambientGreen = g; description->ambientBlue = b; } void InitialiseAmbientLightForTrack (TunnelDescription *description) { assert(description != NULL); GsSetAmbient(description->ambientRed, description->ambientGreen, description->ambientBlue); } void SetAmbientEffectForTrack (TunnelDescription *description, int effectID) { assert(description != NULL); assert(effectID >= 0); description->ambientEffectFlag = effectID; } void HandleTunnelAmbientEffect (void) { switch(TheTunnelDescription.ambientEffectFlag) { case FALSE: assert(FALSE); // should never get here break; case SECOND: OscillateAmbientColours(); break; case THIRD: SetAmbientByShipSpeed(); break; default: assert(FALSE); } } void OscillateAmbientColours (void) { int period = 384; int time, phase, subTime; int r, g, b; time = frameNumber % period; assert(period != 0); phase = (time * 3) / period; switch(phase) { case 0: // red to green subTime = time; r = 128 - subTime; g = subTime; b = 0; break; case 1: // green to blue subTime = time - (period/3); r = 0; g = 128 - subTime; b = subTime; break; case 2: // blue to red subTime = time - ((2 * period)/3); r = subTime; g = 0; b = 128 - subTime; break; default: assert(FALSE); } // bring up from 0-128 to 0-ONE/2 r <<= 4; g <<= 4; b <<= 4; GsSetAmbient(r, g, b); } void SetAmbientByShipSpeed (void) { int speedFraction; speedFraction = GetSpeedFractionOfViewShip(); speedFraction >>= 1; GsSetAmbient(speedFraction, speedFraction, speedFraction); } void SetDescriptionSizeParameters (TunnelDescription *description, int sectionLength, int outerRadius) { assert(sectionLength > 0); assert(sectionLength < 8192); assert(outerRadius > 0); assert(outerRadius < 4096); description->sectionLength = sectionLength; description->outerRadius = outerRadius; description->totalDistance = NumberSectionsInTunnelDescription(description) * sectionLength; } void SetDescriptionShapesPerSection (TunnelDescription *description, int number) { //assert(number >= 8); assert(number >= 4); assert(number <= MAX_NUMBER_SHAPES_PER_SECTION); assert(description != NULL); description->shapesPerSection = number; } void SetDescriptionNumberSectionsDrawn (TunnelDescription *description, int number) { assert(description != NULL); assert(number >= 4); assert(number <= 48); description->numberSectionsDrawn = number; } void GenerateRotationListFromTunnelDescription (TunnelDescription* description, SVECTOR* rotationList, int maxNumberRotations) { TunnelChunk *thisChunk; int numSections; int rotationIndex; int i, j; rotationIndex = 0; for (i = 0; i < description->numberChunks; i++) { thisChunk = &description->listOfChunks[i]; numSections = thisChunk->numberSections; assert(rotationIndex < maxNumberRotations); setVECTOR( &rotationList[rotationIndex], thisChunk->thetaX, thisChunk->thetaY, thisChunk->thetaZ); assert(rotationIndex + numSections < maxNumberRotations); for (j = 1; j < numSections; j++) { setVECTOR( &rotationList[rotationIndex+j], thisChunk->thetaX, thisChunk->thetaY, 0); } rotationIndex += numSections; } } void BuildTunnelFromDescription (TunnelDescription* description, VECTOR* startPoint, MATRIX* startOrientation) { CopyDescription(description, &TheTunnelDescription); NumberTunnelSections = NumberSectionsInTunnelDescription(description); assert(NumberTunnelSections > 1); assert(NumberTunnelSections < MAX_TUNNEL_SECTIONS); TunnelSectionLength = description->sectionLength; TunnelOuterRadius = description->outerRadius; TunnelInnerRadius = (TunnelOuterRadius * rcos(TunnelSectionShapeAngle/2) ) >> 12; TunnelMiddleRadius = (TunnelOuterRadius + TunnelInnerRadius) / 2; NumberShapesPerSection = description->shapesPerSection; assert(NumberShapesPerSection != 0); TunnelSectionShapeAngle = ONE / NumberShapesPerSection; NumberTunnelSectionsDrawn = description->numberSectionsDrawn; TunnelBaseDescription = SECTION_ROTATIONS; GenerateRotationListFromTunnelDescription (&TheTunnelDescription, TunnelSectionRotations, NumberTunnelSections+1); BuildTheTunnel(); InitialiseTrackClutAnimation( &TheTunnelDescription); InitialiseLightingForTrack( &TheTunnelDescription); InitialiseFoggingForTrack( &TheTunnelDescription); InitialiseAmbientLightForTrack( &TheTunnelDescription); InitialiseHighlightingForTrack( &TheTunnelDescription); CalculateDrawProcessSectionLimits( &TheTunnelDescription); DrawSync(0); } void CalculateDrawProcessSectionLimits (TunnelDescription *description) { int i, j; DrawProcess *process; int imageID, imageCounter; int start, end; for (i = 0; i < MAX_DRAW_PROCESSES_PER_DESCRIPTION*2; i++) { TunnelDrawProcessSectionLimits[i] = -1; } switch(description->texturingType) { case SPREAD_N_TEXTURES_EVENLY: assert(description->numberImages > 0); for (i = 0; i < description->numberDrawProcesses; i++) { process = &TheTunnelDescription.drawingProcesses[i]; if (process->clipArea.x == Wave16TextureInfo.px && process->clipArea.y == Wave16TextureInfo.py) { imageID = GetImageIdGivenPointer( &Wave16TextureInfo); } else if (process->clipArea.x == DynamicAreaTwoTextureInfo.px && process->clipArea.y == DynamicAreaTwoTextureInfo.py) { imageID = GetImageIdGivenPointer( &DynamicAreaTwoTextureInfo); } else if (process->clipArea.x == DynamicAreaThreeTextureInfo.px && process->clipArea.y == DynamicAreaThreeTextureInfo.py) { imageID = GetImageIdGivenPointer( &DynamicAreaThreeTextureInfo); } else if (process->clipArea.x == DynamicAreaFourTextureInfo.px && process->clipArea.y == DynamicAreaFourTextureInfo.py) { imageID = GetImageIdGivenPointer( &DynamicAreaFourTextureInfo); } else assert(FALSE); imageCounter = -1; for (j = 0; j < description->numberImages; j++) { if (description->listOfImageIds[j] == imageID) { imageCounter = j; break; } } //assert(imageCounter != -1); if (imageCounter == -1) { int k; printf("here are the description image IDs\n"); for (k = 0; k < description->numberImages * 2; k++) { printf("%d\t", description->listOfImageIds[j]); } printf("given imageID %d\n", imageID); imageCounter = 0; } start = (imageCounter * NumberTunnelSections) / description->numberImages; assert(ValidID(start)); end = (((imageCounter+1) * NumberTunnelSections) / description->numberImages) - 1; if (end >= NumberTunnelSections) end = NumberTunnelSections-1; assert(ValidID(end)); TunnelDrawProcessSectionLimits[i*2] = start; TunnelDrawProcessSectionLimits[(i*2)+1] = end; } break; case SINGLE_TUNNEL_TEXTURE: TunnelDrawProcessSectionLimits[0] = 0; TunnelDrawProcessSectionLimits[1] = NumberTunnelSections-1; break; default: // more here, not yet used assert(FALSE); } } void InferSoundToGraphicUseFromTunnelDescription (void) { int i, id; SoundToGraphicInUseFlag = FALSE; if (TheTunnelDescription.numberDrawProcesses > 0) { for (i = 0; i < TheTunnelDescription.numberDrawProcesses; i++) { id = TheTunnelDescription.drawingProcesses[i].id; if (DrawProcessUsesSoundToLight(id) == TRUE) { SoundToGraphicInUseFlag = TRUE; return; } } } } #if 0 // not used void GenerateAndPrintSplineListOfDescription (TunnelDescription* description, VECTOR* startPoint, MATRIX* startOrientation) { int numberOfSections; int previousRealNumberOfSections; previousRealNumberOfSections = NumberTunnelSections; numberOfSections = NumberSectionsInTunnelDescription(description) + 1; assert(numberOfSections > 1); assert(numberOfSections < MAX_TUNNEL_SECTIONS); PRINT("Generating and printing spline list for given description\n\n"); NumberTunnelSections = numberOfSections; GenerateRotationListFromTunnelDescription (description, TunnelSectionRotations, numberOfSections); VerifySectionRotationDescription(); SortTunnelMainDescriptionsFromRotationsList(); PrintTunnelSplinePoints(); // restore previous value NumberTunnelSections = previousRealNumberOfSections; } #endif // not used void BuildTunnelFromSetTrack (int trackNumber) { VECTOR start; MATRIX orientation; TunnelDescription *chosenSetTrack; assert(trackNumber >= 0); assert(trackNumber < NUMBER_SET_TRACKS); // basic setup setVECTOR( &start, 0, 0, 0); InitMatrix( &orientation); chosenSetTrack = &SetTracks[trackNumber]; BuildTunnelFromDescription (chosenSetTrack, &start, &orientation); } void FirstEverTunnelInit (void) { InitTunnelToVoid(); SetBasicTunnelParameters(); #if (TRACK_CREATION_METHOD==CREATE_TRACKS_BY_FUNCTION) { SetTracks = (TunnelDescription *) (TRACK_DATAFILE_ADDRESS); CreateTheSetTracks(); } #elif (TRACK_CREATION_METHOD==CREATE_TRACKS_BY_LOADING_DATAFILE) { // tracks already present in VRAM as loaded datafile SetTracks = (TunnelDescription *) (TRACK_DATAFILE_ADDRESS); } #else #error "bad value track creation method\n" #endif TunnelSideAppearanceFlag = VISIBLE_FROM_INSIDE; TunnelSectionClippingFlag = TRUE; NumberTunnelSectionsDrawn = DEFAULT_NUMBER_SECTIONS_DRAWN; StartDrawingSectionId = 0; EndDrawingSectionId = 1; TrackViewerMainMode = VIEW_TRACK_FROM_INSIDE; TrackViewerShipViewMode = VIEW_FIXED_BEHIND_ABOVE_OBJECT; TrackViewerAdjustCameraAngleFlag = TRUE; TrackViewerDirection = FORWARDS; TrackViewerTwisterDirection = TILT_CLOCKWISE; TrackViewerTwisterPeriod = MaximumGameFramesPerSecond * 4; TrackViewerRollerCoasterSameSpeedPeriod = MaximumGameFramesPerSecond * 2; TrackViewerRollerCoasterChangeSpeedPeriod = 5 * MaximumGameFramesPerSecond; // highlighting data HighlightMaxSpeed = ONE/4; HighlightSectionSpacing = 4; // 3 assert(MaximumGameFramesPerSecond > 0); HighlightPeriod = MaximumGameFramesPerSecond * 8; // polygon snake data ShortSnakeLengthInSections = 6; ShortSnakePositionCounter = 0; SnakeTwistingDirection = CLOCKWISE; SnakePeriod = MaximumGameFramesPerSecond * 8; SnakeMaxSpeed = ONE/2; // ONE/4 SnakeBaseSectionPolygonIndexCounter = 0; PreviousSnakeBaseSectionPolygonIndexCounter = 0; SnakePreviousBaseSection = 0; SnakePreviousLength = 1; PreviousSnakeDirection = -1; } void SortTunnelToSetTrack (int setTracklD) { InitTunnelToVoid(); SetBasicTunnelParameters(); BuildTunnelFromSetTrack (setTracklD); CreateTheTunnel2(); } void AssertObjectHasNoCollisions (ObjectHandler* object, int newSection, VECTOR* newPos) { int collisionFlag = FALSE; VECTOR newWorldPos, *splinePoint; VECTOR relativeNewPos; int effectiveRadius; TunnelSection* section; assert(ValidID(newSection)); section = &TheTunnel[newSection]; splinePoint = §ion->splinePoint; // express new ship position in local section coords setVECTOR( &newWorldPos, newPos->vx - splinePoint->vx, newPos->vy - splinePoint->vy, newPos->vz - splinePoint->vz); ExpressSuperPointInSub( &newWorldPos, §ion->coordinateData, &relativeNewPos); // circle which ship is OK inside effectiveRadius = TunnelMiddleRadius - object->collisionRadius; // ensure they can be squared without overflow assert(abs(relativeNewPos.vx) < MAX_VECTOR_ELEMENT_SIZE); assert(abs(relativeNewPos.vy) < MAX_VECTOR_ELEMENT_SIZE); // standard circle-centred-at-origin comparison // ie x**2 + y**2 >= r**2 ?? if ( ((relativeNewPos.vx * relativeNewPos.vx) + (relativeNewPos.vy * relativeNewPos.vy)) >= (effectiveRadius * effectiveRadius) ) { collisionFlag = TRUE; } assert(collisionFlag == FALSE); } // check for overtakings and update ship places in race accordingly void HandleRacePositions (void) { int i, j; ObjectHandler *first, *second; ObjectHandler *betterBefore, *worseBefore; int betterGrossAfter, worseGrossAfter; int worsePlace, betterPlace; assert(NumberOfShipsInRace >= 1); // if only one, no overtakings if (NumberOfShipsInRace == 1) return; for (i = 0; i < NumberOfShipsInRace; i++) { for (j = i+1; j < NumberOfShipsInRace; j++) { if (i == j) continue; first = ShipsInTunnel[i]; second = ShipsInTunnel[j]; assert(first != NULL); assert(second != NULL); assert(first != second); if (first->racingFlag == FALSE || second->racingFlag == FALSE) { continue; } if (first->placeInRace < second->placeInRace) { betterBefore = first; worseBefore = second; } else { assert(first->placeInRace > second->placeInRace); betterBefore = second; worseBefore = first; } if (worseBefore->advancedFlag != TRUE) { continue; // cannot have overtaken } betterGrossAfter = (betterBefore->furthestLap * NumberTunnelSections) + betterBefore->furthestSection; worseGrossAfter = (worseBefore->furthestLap * NumberTunnelSections) + worseBefore->furthestSection; if (worseGrossAfter > betterGrossAfter) // overtaking { worsePlace = worseBefore->placeInRace; betterPlace = betterBefore->placeInRace; // swap places worseBefore->placeInRace = betterPlace; betterBefore->placeInRace = worsePlace; } } } } // only two values of treatment flag: // IGNORE_COLLISIONS or DETECT_AND_HANDLE_COLLISIONS void HandleAllShipCollisions (void) { int i, j; ObjectHandler *first, *second; int firstRadius, secondRadius, sumRadius; int dx, dy, dz; MATRIX *firstMatrix, *secondMatrix; for (i = 0; i < MAX_SHIPS_PER_RACE; i++) { for (j = i+1; j < MAX_SHIPS_PER_RACE; j++) { first = ShipsInTunnel[i]; second = ShipsInTunnel[j]; if (first != NULL && second != NULL) { if (first->shipCollisionTreatmentFlag == IGNORE_COLLISIONS || second->shipCollisionTreatmentFlag == IGNORE_COLLISIONS) { continue; } firstRadius = first->collisionRadius; secondRadius = second->collisionRadius; sumRadius = firstRadius + secondRadius; firstMatrix = &first->matrix; secondMatrix = &second->matrix; dx = firstMatrix->t[0] - secondMatrix->t[0]; dy = firstMatrix->t[1] - secondMatrix->t[1]; dz = firstMatrix->t[2] - secondMatrix->t[2]; if ( ((dx * dx) + (dy * dy) + (dz * dz)) < (sumRadius * sumRadius) ) { HandleShipCollisions(first, second); } } } } } // simple handler here: just push them away from each other // and reverse their velocities // to avoid any more collisions in next few dozen frames void HandleShipCollisions (ObjectHandler *first, ObjectHandler *second) { int safeDistance; int actualDistance; VECTOR firstToSecond; int discrepancy; VECTOR firstMove, secondMove; int scaler; // the factor is 0-ONE with ONE meaning bring-to-dead-stop scaler = ONE - SHIP_TO_SHIP_COLLISION_VELOCITY_DAMPING_FACTOR; setVECTOR( &firstToSecond, second->matrix.t[0] - first->matrix.t[0], second->matrix.t[1] - first->matrix.t[1], second->matrix.t[2] - first->matrix.t[2]); actualDistance = SizeOfVector2( &firstToSecond); safeDistance = first->collisionRadius + second->collisionRadius; discrepancy = safeDistance - actualDistance; //assert(discrepancy > 0); // no collision if < zero if (discrepancy <= 0) discrepancy = 1; // to be one safe side, move apart by disrepancy // rather than halfDiscrepancy setVECTOR( &firstMove, -firstToSecond.vx, -firstToSecond.vy, -firstToSecond.vz); if (actualDistance == 0) { printf("TWO SHIPS AT SAME POSITION\n"); PRINT("first: type %d id %d\n", first->type, first->id); PRINT("first: startFrame %d currentPositionIndex %d\n", first->startFrame, first->currentPositionIndex); PRINT("second: type %d id %d\n", second->type, second->id); PRINT("second: startFrame %d currentPositionIndex %d\n", second->startFrame, second->currentPositionIndex); // pretend that one is a little in front of the other setVECTOR( &firstMove, 0, 0, 20); } ScaleVectorToSize2( &firstMove, discrepancy); first->matrix.t[0] += firstMove.vx; first->matrix.t[1] += firstMove.vy; first->matrix.t[2] += firstMove.vz; setVECTOR( &first->position, first->matrix.t[0], first->matrix.t[1], first->matrix.t[2]); setVECTOR( &secondMove, -firstMove.vx, -firstMove.vy, -firstMove.vz); second->matrix.t[0] += secondMove.vx; second->matrix.t[1] += secondMove.vy; second->matrix.t[2] += secondMove.vz; setVECTOR( &second->position, second->matrix.t[0], second->matrix.t[1], second->matrix.t[2]); // now reverse the velocity of each, and dampen both first->velocity.vx = (-first->velocity.vx * scaler) >> 12; first->velocity.vy = (-first->velocity.vy * scaler) >> 12; first->velocity.vz = (-first->velocity.vz * scaler) >> 12; second->velocity.vx = (-second->velocity.vx * scaler) >> 12; second->velocity.vy = (-second->velocity.vy * scaler) >> 12; second->velocity.vz = (-second->velocity.vz * scaler) >> 12; } // simplest measure of how much of race ship has completed int ShipGrossPositionInRace (ObjectHandler* ship) { int grossPosition; grossPosition = (ship->furthestLap * NumberTunnelSections) + ship->furthestSection; return grossPosition; } // when ship X finishes and the race finishes also, // flesh out remaining lap times for those ships still racing void FleshOutLapTimesForShipsStillRacing (void) { ObjectHandler *ship; int i, j; int mostRecentLapTime; int furthestLap; for (i = 0; i < NumberOfShipsInRace; i++) { ship = ShipsInTunnel[i]; if (ship->racingFlag == FALSE) continue; furthestLap = ship->furthestLap; switch(furthestLap) { case 0: // not yet done a single lap, set all to default for (j = 0; j <= NumberOfLapsInRace; j++) ship->framesWhenLapsEnd[j] = FrameWhenRaceStarts + (j * DEFAULT_LAP_TIME); break; default: assert(furthestLap > 0); mostRecentLapTime = ship->framesWhenLapsEnd[furthestLap] - ship->framesWhenLapsEnd[furthestLap-1]; for (j = furthestLap+1; j <= NumberOfLapsInRace; j++) { ship->framesWhenLapsEnd[j] = ship->framesWhenLapsEnd[j-1] + mostRecentLapTime; } } } } void HandleSomeShipsMovementInTunnel (ObjectHandler* object) { VECTOR oldPos, newPos, worldMove; int startSection, nextSection; // BLAGG to allow other types of movement if (object->properMovementFlag != TRUE) { return; } // store position before move setVECTOR (&oldPos, object->position.vx, object->position.vy, object->position.vz); // store which section ship inside before move startSection = object->tunnelSection; assert(ValidID(startSection)); SortMatrixFromAngle(object); // find the object velocity in world coordinate terms ApplyMatrixLV(&object->matrix, &object->velocity, &worldMove); // hence new position setVECTOR( &newPos, oldPos.vx + worldMove.vx, oldPos.vy + worldMove.vy, oldPos.vz + worldMove.vz); // find which section ship will be in after movement nextSection = FindSectionPointIsIn ( &newPos, startSection); // sort ship-tunnel collisions switch(object->tunnelCollisionTreatmentFlag) { case IGNORE_COLLISIONS: HandleLackOfCollision(object, &newPos); break; case ASSERT_NO_COLLISIONS_ONLY: AssertObjectHasNoCollisions(object, nextSection, &newPos); HandleLackOfCollision(object, &newPos); break; case DETECT_AND_HANDLE_COLLISIONS: DetectAndHandleCollisionWithTunnel(object, nextSection, &oldPos, &newPos, &worldMove); break; default: assert(FALSE); } // sort decay of both types of momentum switch(object->specialMovementFlag) { case HUMAN_PLAYER: // handle all these properly case PRERECORDED_SLOW: case PRERECORDED_MEDIUM: case PRERECORDED_FAST: // movement momentum: accounting for friction ScaleVector(&object->velocity, ONE - object->movementFrictionCoefficient); // rotational momentum object->angle += object->tilt; while (object->angle < 0) object->angle += ONE; while (object->angle >= ONE) object->angle -= ONE; object->tilt *= (ONE - object->rotationFrictionCoefficient); object->tilt /= ONE; break; case AI_FLIER_1: // BAD CASE OF CHEATING break; case AI_FLIER_2: object->angle += object->tilt; while (object->angle < 0) object->angle += ONE; while (object->angle >= ONE) object->angle -= ONE; object->tilt *= (ONE - object->rotationFrictionCoefficient); object->tilt /= ONE; break; case AI_FLIER_3: object->angle += object->tilt; while (object->angle < 0) object->angle += ONE; while (object->angle >= ONE) object->angle -= ONE; object->tilt *= (ONE - object->rotationFrictionCoefficient); object->tilt /= ONE; break; default: assert(FALSE); } if (ValidID(nextSection) != TRUE) { PRINT("BAD section %d at line %d\n", nextSection, __LINE__); PRINT("startSection %d\n", startSection); PRINT("newPos: "); dumpVECTOR( &newPos); PRINT("object %d player1 %d player2 %d\n", (int) object, (int) &PlayerOnesShip, (int) &PlayerTwosShip); } if (object->properMovementFlag == TRUE) { // if ship crosses from one section into the next if (startSection != nextSection) { if (object->racingFlag == TRUE) { SortShipAdvancing(object, startSection, nextSection); } } else object->advancedFlag = FALSE; } // deal with momentums if (object->movementMomentumFlag == FALSE) { setVECTOR( &object->velocity, 0, 0, 0); } if (object->rotationMomentumFlag == FALSE) { object->tilt = 0; } } void ResetTrackViewingData (void) { TrackViewerZAngle = 0; GsInitCoordinate2( WORLD, &TrackViewerCoord); CopyMatrix( &TheTunnel[0].coordinateData.coord, &TrackViewerCoord.coord); TrackViewerSection = 0; TrackViewerRollerCoasterState = 0; TrackViewerWholeTrackCentreDistance = DEFAULT_WHOLE_TRACK_CENTRE_DISTANCE; setVECTOR( &TrackViewerWholeTrackCoordTwist, 0, 0 , 0); } void SetUpTheViewer (void) { VECTOR offsetVector; MATRIX offsetMatrix; SVECTOR initialTwist; GsCOORDINATE2 temp; switch(TrackViewerMainMode) { case VIEW_TRACK_FROM_INSIDE: ProjectionDistance = FIXED_PROJECTION_DISTANCE; GsSetProjection(ProjectionDistance); InitSingleObject( &ViewerObject); ViewerObject.viewMode = VIEW_SWIVELS_BEHIND_OBJECT; SetNewViewShip( &ViewerObject); ViewerObject.properMovementFlag = FALSE; ViewerObject.specialMovementFlag = AI_FLIER_1; ViewerObject.racingFlag = FALSE; ViewerObject.modelFlag = TMD_RIGHT_WAY; ViewerObject.speedFactor = 128; ViewerObject.currentPositionIndex = 0; ViewerObject.scalingFlag = FALSE; switch(TrackViewerCameraControlMode) { case TRACK_VIEWER_USER_CONTROLLED_CAMERA: ViewerObject.discreteSpeed = NORMAL_SPEED_32; break; case TRACK_VIEWER_AUTO_CAMERA: switch(TrackViewerAutoCameraMode) { case TRACK_VIEWER_AUTO_CAMERA_SLOW_MODE: ViewerObject.discreteSpeed = NORMAL_SPEED_32 * 4; break; case TRACK_VIEWER_AUTO_CAMERA_FAST_MODE: ViewerObject.discreteSpeed = NORMAL_SPEED_32 * 12; break; case TRACK_VIEWER_AUTO_CAMERA_SUPER_FAST_MODE: ViewerObject.discreteSpeed = MAXIMUM_SPEED_32; break; case TRACK_VIEWER_AUTO_CAMERA_TWISTER_MODE: ViewerObject.discreteSpeed = NORMAL_SPEED_32 * 8; TrackViewerTwisterDirection = TILT_CLOCKWISE + (rand() % 2); break; case TRACK_VIEWER_AUTO_CAMERA_ROLLERCOASTER_MODE: ViewerObject.discreteSpeed = NORMAL_SPEED_32; break; case TRACK_VIEWER_AUTO_CAMERA_TWISTING_ROLLERCOASTER_MODE: ViewerObject.discreteSpeed = NORMAL_SPEED_32; break; case TRACK_VIEWER_AUTO_CAMERA_ZOOM_ROLLERCOASTER_MODE: ViewerObject.discreteSpeed = NORMAL_SPEED_32; break; default: assert(FALSE); } break; default: assert(FALSE); } assert(ViewerObject.discreteSpeed != 0); switch(TrackViewerDirection) { case FORWARDS: // do nowt break; case BACKWARDS: // reverse ViewerObject.discreteSpeed = -ViewerObject.discreteSpeed; break; default: assert(FALSE); } TrackViewerRollerCoasterState = 0; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; ViewerObject.alive = TRUE; ViewerObject.racingFlag = FALSE; setVECTOR( &offsetVector, 0, 0, TunnelSectionLength/2); InitMatrix( &offsetMatrix); PutObjectInTunnel( &ViewerObject, 0, &offsetVector, &offsetMatrix); ViewerObject.angle = 0; break; case VIEW_TRACK_FROM_OUTSIDE: // USES LOW_RES to avoid flicker in interlace ResetGraphicResolution (320, 240); ProjectionDistance = 260; GsSetProjection(ProjectionDistance); TrackViewerWholeTrackCentreDistance = DEFAULT_WHOLE_TRACK_CENTRE_DISTANCE; TheView.vrx = 0; TheView.vry = 0; TheView.vrz = 0; TheView.vpx = 0; TheView.vpy = 0; TheView.vpz = -TrackViewerWholeTrackCentreDistance; TheView.rz = 0; GsInitCoordinate2( WORLD, &TrackViewerCoord); ViewTypeFlag = REFERENCE_VIEW; // want to look from above or side, so twist the camera switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: setVECTOR( &initialTwist, ONE/4, 0, 0); break; case Y_Z_PLANE_ONLY: setVECTOR( &initialTwist, 0, ONE/4, 0); break; default: assert(FALSE); } RotateCoordinateSystem( &TrackViewerCoord, &initialTwist, &temp); CopyCoordinateSystem( &temp, &TrackViewerCoord); TheView.super = &TrackViewerCoord; TrackViewerCoord.coord.t[0] = TunnelCentrePoint.vx; TrackViewerCoord.coord.t[1] = TunnelCentrePoint.vy; TrackViewerCoord.coord.t[2] = TunnelCentrePoint.vz; setVECTOR( &TrackViewerWholeTrackCoordTwist, 0, 0 , 0); break; default: assert(FALSE); } } void HandleTrackViewer (void) { switch(TrackViewerMainMode) { case VIEW_TRACK_FROM_INSIDE: MoveTrackViewer(); // set TrackViewerSection TheView.rz = TrackViewerZAngle << 12; break; case VIEW_TRACK_FROM_OUTSIDE: switch(TrackViewerCameraControlMode) { case TRACK_VIEWER_USER_CONTROLLED_CAMERA: // no auto-handle break; case TRACK_VIEWER_AUTO_CAMERA: MoveExternalAutoViewer(); break; default: assert(FALSE); } RotateCameraForWholeTrack(); break; default: assert(FALSE); } } void EffectTwistInExternalAutoViewer (int axis, int direction) { switch(axis) { case X_AXIS: switch(direction) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vx += 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vx -= 32 * FrameRateDivider; break; default: assert(FALSE); } break; case Y_AXIS: switch(direction) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vy += 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vy -= 32 * FrameRateDivider; break; default: assert(FALSE); } break; case Z_AXIS: switch(direction) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vz += 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vz -= 32 * FrameRateDivider; break; default: assert(FALSE); } break; default: assert(FALSE); } } // 5 seconds NTSC, 6 seconds PAL #define EXTERNAL_AUTO_VIEWER_CYCLE_DURATION 300 void MoveExternalAutoViewer (void) { int time, cycleRatio, subPhase, singlePhaseDuration; int firstPassInNewPhase; static int previousSubPhase = -1; int close, medium, far; close = DEFAULT_WHOLE_TRACK_CENTRE_DISTANCE / 2; medium = DEFAULT_WHOLE_TRACK_CENTRE_DISTANCE; far = (3 * DEFAULT_WHOLE_TRACK_CENTRE_DISTANCE) / 2; time = (frameNumber - FrameWhenTrackViewingStarts) % EXTERNAL_AUTO_VIEWER_CYCLE_DURATION; cycleRatio = (time * ONE) / EXTERNAL_AUTO_VIEWER_CYCLE_DURATION; assert(cycleRatio >= 0); assert(cycleRatio < ONE); subPhase = (cycleRatio * 10) >> 12; assert(subPhase >= 0); assert(subPhase < 10); singlePhaseDuration = EXTERNAL_AUTO_VIEWER_CYCLE_DURATION / 10; assert(singlePhaseDuration > 0); if (previousSubPhase != subPhase) firstPassInNewPhase = TRUE; else firstPassInNewPhase = FALSE; previousSubPhase = subPhase; switch(subPhase) { case 0: if (firstPassInNewPhase) { ExternalAutoViewerTwistDirection = CLOCKWISE + (rand() % 2); } switch(ExternalAutoViewerTwistDirection) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vx += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz += 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vx -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz -= 32 * FrameRateDivider; break; default: assert(FALSE); } break; case 1: if (firstPassInNewPhase) { ExternalAutoViewerTwistDirection = CLOCKWISE + (rand() % 2); } switch(ExternalAutoViewerTwistDirection) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vx += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz -= 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vx -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz += 32 * FrameRateDivider; break; default: assert(FALSE); } break; case 2: ExternalAutoViewerZoomSpeed = -((medium - close) / singlePhaseDuration); TrackViewerWholeTrackCentreDistance += ExternalAutoViewerZoomSpeed; TheView.vpz = -TrackViewerWholeTrackCentreDistance; break; case 3: if (firstPassInNewPhase) { ExternalAutoViewerTwistDirection = CLOCKWISE + (rand() % 2); } switch(ExternalAutoViewerTwistDirection) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vx -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz -= 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vx += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz += 32 * FrameRateDivider; break; default: assert(FALSE); } break; case 4: ExternalAutoViewerZoomSpeed = ((medium - close) / singlePhaseDuration); TrackViewerWholeTrackCentreDistance += ExternalAutoViewerZoomSpeed; TheView.vpz = -TrackViewerWholeTrackCentreDistance; break; case 5: if (firstPassInNewPhase) { ExternalAutoViewerTwistDirection = CLOCKWISE + (rand() % 2); } switch(ExternalAutoViewerTwistDirection) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vx -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz += 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vx += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz -= 32 * FrameRateDivider; break; default: assert(FALSE); } break; case 6: if (firstPassInNewPhase) { ExternalAutoViewerTwistDirection = CLOCKWISE + (rand() % 2); } switch(ExternalAutoViewerTwistDirection) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vx += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz -= 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vx -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz += 32 * FrameRateDivider; break; default: assert(FALSE); } break; case 7: if (firstPassInNewPhase) { ExternalAutoViewerTwistDirection = CLOCKWISE + (rand() % 2); } switch(ExternalAutoViewerTwistDirection) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vx += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz += 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vx -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz -= 32 * FrameRateDivider; break; default: assert(FALSE); } break; case 8: ExternalAutoViewerZoomSpeed = ((far - medium) / singlePhaseDuration); TrackViewerWholeTrackCentreDistance += ExternalAutoViewerZoomSpeed; TheView.vpz = -TrackViewerWholeTrackCentreDistance; break; case 9: if (firstPassInNewPhase) { ExternalAutoViewerTwistDirection = CLOCKWISE + (rand() % 2); } switch(ExternalAutoViewerTwistDirection) { case CLOCKWISE: TrackViewerWholeTrackCoordTwist.vx += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy += 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz += 32 * FrameRateDivider; break; case ANTICLOCKWISE: TrackViewerWholeTrackCoordTwist.vx -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vy -= 32 * FrameRateDivider; TrackViewerWholeTrackCoordTwist.vz -= 32 * FrameRateDivider; break; default: assert(FALSE); } break; default: assert(FALSE); } // exit condition ... if (cycleRatio >= ((ONE * 99) / 100)) { if (RollingDemoActiveFlag == TRUE) { RollingDemoActiveFlag = FALSE; RollingDemoStartFrame = -1; if (RollingDemoDeliberateFlag == TRUE) SetANewGameState(SETTING_UP_ROLLING_DEMO); else SetANewGameState(MAIN_MENU); } else SetANewGameState(SETTING_UP_TRACK_VIEWING); } } void MoveTrackViewer (void) { int increaseSoFar; int time, phase; assert(ViewerObject.speedFactor != 0); HandleSplinePathFollower( &ViewerObject); SortCoordFromMatrix( &ViewerObject); CopyMatrix( &ViewerObject.coord.coord, &TrackViewerCoord.coord); TrackViewerCoord.flg = 0; TrackViewerSection = ViewerObject.currentPositionIndex / ViewerObject.speedFactor; switch(TrackViewerCameraControlMode) { case TRACK_VIEWER_USER_CONTROLLED_CAMERA: // no auto-handle break; case TRACK_VIEWER_AUTO_CAMERA: // auto moving switch(TrackViewerAutoCameraMode) { case TRACK_VIEWER_AUTO_CAMERA_SLOW_MODE: // no change in speed case TRACK_VIEWER_AUTO_CAMERA_FAST_MODE: // no change in speed case TRACK_VIEWER_AUTO_CAMERA_SUPER_FAST_MODE: // no change in speed break; case TRACK_VIEWER_AUTO_CAMERA_TWISTER_MODE: switch(TrackViewerTwisterDirection) { case TILT_CLOCKWISE: TrackViewerZAngle += 3; break; case TILT_ANTICLOCKWISE: TrackViewerZAngle -= 3; break; default: assert(FALSE); } if ((frameNumber % TrackViewerTwisterPeriod) == 0) // flip direction { switch(TrackViewerTwisterDirection) { case TILT_CLOCKWISE: TrackViewerTwisterDirection = TILT_ANTICLOCKWISE; break; case TILT_ANTICLOCKWISE: TrackViewerTwisterDirection = TILT_CLOCKWISE; break; default: assert(FALSE); } } break; case TRACK_VIEWER_AUTO_CAMERA_ROLLERCOASTER_MODE: switch(TrackViewerRollerCoasterState) { case 0: // stay at low speed ViewerObject.discreteSpeed = NORMAL_SPEED_32; if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterSameSpeedPeriod) { TrackViewerRollerCoasterState = 1; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; case 1: // accelerating increaseSoFar = ((frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast) * (MAXIMUM_SPEED_32 - NORMAL_SPEED_32)) / TrackViewerRollerCoasterChangeSpeedPeriod; ViewerObject.discreteSpeed = NORMAL_SPEED_32 + increaseSoFar; assert(ViewerObject.discreteSpeed >= NORMAL_SPEED_32); assert(ViewerObject.discreteSpeed <= MAXIMUM_SPEED_32); if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterChangeSpeedPeriod) { TrackViewerRollerCoasterState = 2; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; case 2: // stay at high speed ViewerObject.discreteSpeed = MAXIMUM_SPEED_32; if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterSameSpeedPeriod) { TrackViewerRollerCoasterState = 3; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; case 3: // decelerating increaseSoFar = -((frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast) * (MAXIMUM_SPEED_32 - NORMAL_SPEED_32)) / TrackViewerRollerCoasterChangeSpeedPeriod; ViewerObject.discreteSpeed = MAXIMUM_SPEED_32 + increaseSoFar; assert(ViewerObject.discreteSpeed >= NORMAL_SPEED_32); assert(ViewerObject.discreteSpeed <= MAXIMUM_SPEED_32); if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterChangeSpeedPeriod) { TrackViewerRollerCoasterState = 0; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; default: assert(FALSE); } switch(TrackViewerDirection) { case FORWARDS: // do nowt break; case BACKWARDS: // reverse ViewerObject.discreteSpeed = -ViewerObject.discreteSpeed; break; default: assert(FALSE); } break; case TRACK_VIEWER_AUTO_CAMERA_TWISTING_ROLLERCOASTER_MODE: switch(TrackViewerRollerCoasterState) { case 0: // stay at low speed ViewerObject.discreteSpeed = NORMAL_SPEED_32; if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterSameSpeedPeriod) { TrackViewerRollerCoasterState = 1; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; case 1: // accelerating increaseSoFar = ((frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast) * (MAXIMUM_SPEED_32 - NORMAL_SPEED_32)) / TrackViewerRollerCoasterChangeSpeedPeriod; ViewerObject.discreteSpeed = NORMAL_SPEED_32 + increaseSoFar; assert(ViewerObject.discreteSpeed >= NORMAL_SPEED_32); assert(ViewerObject.discreteSpeed <= MAXIMUM_SPEED_32); if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterChangeSpeedPeriod) { TrackViewerRollerCoasterState = 2; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; case 2: // stay at high speed ViewerObject.discreteSpeed = MAXIMUM_SPEED_32; if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterSameSpeedPeriod) { TrackViewerRollerCoasterState = 3; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; case 3: // decelerating increaseSoFar = -((frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast) * (MAXIMUM_SPEED_32 - NORMAL_SPEED_32)) / TrackViewerRollerCoasterChangeSpeedPeriod; ViewerObject.discreteSpeed = MAXIMUM_SPEED_32 + increaseSoFar; assert(ViewerObject.discreteSpeed >= NORMAL_SPEED_32); assert(ViewerObject.discreteSpeed <= MAXIMUM_SPEED_32); if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterChangeSpeedPeriod) { TrackViewerRollerCoasterState = 0; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; default: assert(FALSE); } time = frameNumber % 180; phase = (time * 6) / 180; switch(phase) { case 0: TrackViewerZAngle -= 3; break; case 1: TrackViewerZAngle -= 3; break; case 2: break; case 3: TrackViewerZAngle += 3; break; case 4: TrackViewerZAngle += 3; break; case 5: break; default: assert(FALSE); } switch(TrackViewerDirection) { case FORWARDS: // do nowt break; case BACKWARDS: // reverse ViewerObject.discreteSpeed = -ViewerObject.discreteSpeed; break; default: assert(FALSE); } break; case TRACK_VIEWER_AUTO_CAMERA_ZOOM_ROLLERCOASTER_MODE: switch(TrackViewerRollerCoasterState) { case 0: // stay at low speed ViewerObject.discreteSpeed = NORMAL_SPEED_32; if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterSameSpeedPeriod) { TrackViewerRollerCoasterState = 1; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; case 1: // accelerating increaseSoFar = ((frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast) * (MAXIMUM_SPEED_32 - NORMAL_SPEED_32)) / TrackViewerRollerCoasterChangeSpeedPeriod; ViewerObject.discreteSpeed = NORMAL_SPEED_32 + increaseSoFar; assert(ViewerObject.discreteSpeed >= NORMAL_SPEED_32); assert(ViewerObject.discreteSpeed <= MAXIMUM_SPEED_32); if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterChangeSpeedPeriod) { TrackViewerRollerCoasterState = 2; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; case 2: // stay at high speed ViewerObject.discreteSpeed = MAXIMUM_SPEED_32; if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterSameSpeedPeriod) { TrackViewerRollerCoasterState = 3; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; case 3: // decelerating increaseSoFar = -((frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast) * (MAXIMUM_SPEED_32 - NORMAL_SPEED_32)) / TrackViewerRollerCoasterChangeSpeedPeriod; ViewerObject.discreteSpeed = MAXIMUM_SPEED_32 + increaseSoFar; assert(ViewerObject.discreteSpeed >= NORMAL_SPEED_32); assert(ViewerObject.discreteSpeed <= MAXIMUM_SPEED_32); if (frameNumber - TrackViewerRollerCoasterFrameWhenStateChangedLast >= TrackViewerRollerCoasterChangeSpeedPeriod) { TrackViewerRollerCoasterState = 0; TrackViewerRollerCoasterFrameWhenStateChangedLast = frameNumber; } break; default: assert(FALSE); } switch(TrackViewerDirection) { case FORWARDS: // do nowt break; case BACKWARDS: // reverse ViewerObject.discreteSpeed = -ViewerObject.discreteSpeed; break; default: assert(FALSE); } time = frameNumber % 180; phase = (time * 6) / 180; switch(phase) { case 0: TrackViewerZAngle -= 3; break; case 1: TrackViewerZAngle -= 3; break; case 2: break; case 3: TrackViewerZAngle += 3; break; case 4: TrackViewerZAngle += 3; break; case 5: break; default: assert(FALSE); } ProjectionDistance = VERY_LOW_PROJECTION_DISTANCE; GsSetProjection(ProjectionDistance); break; default: assert(FALSE); } break; default: assert(FALSE); } } // NOTE: WILL NOT WORK for anything other than spline path follower void GetCurveStatus (ObjectHandler *object, int *status, int *fraction) { int sectionID, offset, speed; int previousID, nextID; int previousAngle, currentAngle; speed = object->speedFactor; assert(speed != 0); sectionID = object->currentPositionIndex / speed; assert(ValidID(sectionID)); offset = object->currentPositionIndex % speed; if (offset < speed/2) { previousID = GetRelativeSection(sectionID, 1, BACKWARDS); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: previousAngle = TheTunnel[previousID].thetaY; currentAngle = TheTunnel[sectionID].thetaY; break; case Y_Z_PLANE_ONLY: previousAngle = TheTunnel[previousID].thetaX; currentAngle = TheTunnel[sectionID].thetaX; break; default: assert(FALSE); } } else { nextID = GetRelativeSection(sectionID, 1, FORWARDS); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: previousAngle = TheTunnel[sectionID].thetaY; currentAngle = TheTunnel[nextID].thetaY; break; case Y_Z_PLANE_ONLY: previousAngle = TheTunnel[sectionID].thetaX; currentAngle = TheTunnel[nextID].thetaX; break; default: assert(FALSE); } } if (previousAngle == 0) { if (currentAngle == 0) { *status = ON_STRAIGHT; *fraction = 0; } else { *status = ENTERING_CURVE; if (offset < speed/2) { *fraction = ((offset + (speed/2)) << 12) / speed; } else { *fraction = ((offset - (speed/2)) << 12) / speed; } } } else { if (currentAngle == 0) { *status = EXITING_CURVE; if (offset < speed/2) { *fraction = ONE - (((offset + (speed/2)) << 12) / speed); } else { *fraction = ONE - (((offset - (speed/2)) << 12) / speed); } } else { assert(previousAngle == currentAngle); // simple curves separated by straights *status = DURING_CURVE; *fraction = ONE; } } } void RotateCameraForWholeTrack (void) { GsCOORDINATE2 temp; RotateCoordinateSystem( &TrackViewerCoord, &TrackViewerWholeTrackCoordTwist, &temp); CopyCoordinateSystem( &temp, &TrackViewerCoord); setVECTOR( &TrackViewerWholeTrackCoordTwist, 0, 0, 0); TrackViewerCoord.coord.t[0] = TunnelCentrePoint.vx; TrackViewerCoord.coord.t[1] = TunnelCentrePoint.vy; TrackViewerCoord.coord.t[2] = TunnelCentrePoint.vz; TrackViewerCoord.flg = 0; } void CalculateEachSectionsTotalAngle (void) { int angle; int i; angle = 0; for (i = 0; i < NumberTunnelSections; i++) { switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: angle += TheTunnel[i].thetaY; // 2d angle on xz plane break; case Y_Z_PLANE_ONLY: angle += TheTunnel[i].thetaX; // 2d angle on yz plane break; default: assert(FALSE); } while (angle >= ONE) angle -= ONE; while (angle < 0) angle += ONE; assert(angle >= 0 && angle < ONE); TheTunnel[i].totalAngle = angle; } } #if 0 // not used void PrintTunnelSectionAngles (void) { #if (DEVELOPMENT_ENVIRONMENT==YAROZE) int i; printf("\n\n\nPrinting the tunnel section angles\n"); for (i = 0; i < NumberTunnelSections; i++) { printf("section %d, total angle %d\n", i, TheTunnel[i].totalAngle); } printf("\n\n\n"); #endif } #endif // not used #if 0 // not used // print four points per section void PrintTunnelSplineCurve (void) { #if (DEVELOPMENT_ENVIRONMENT==YAROZE) int granularity, i, j; VECTOR point; granularity = 512; for (i = 0; i < NumberTunnelSections; i++) { printf("section %d\n", i); for (j = 0; j < 4096; j += granularity) { GetPointOnSplinePath (i, j, &point); dumpVECTOR( &point); } } #endif } #endif // not used void InitialiseTrackClutAnimation (TunnelDescription *description) { RECT rect; GsIMAGE *image; ClutAnimation *animationData; int i; if (description->numberClutAnimations == 0) return; for (i = 0; i < description->numberClutAnimations; i++) { animationData = description->listOfClutAnimations[i]; image = animationData->image; setRECT( &rect, image->cx, image->cy, image->cw, image->ch); switch(image->pmode) { case 8: // 4-bit image StoreImage( &rect, (u_long*) animationData->initialFourBitClut); break; case 9: // 8-bit StoreImage( &rect, (u_long*) animationData->initialEightBitClut); break; default: assert(FALSE); } DrawSync(0); } } #if 0 // not used void SetClutAnimation (ClutAnimation *animation, GsIMAGE *image, int period, int functionLabel, int numberCells) { assert(animation != NULL); assert(image != NULL); assert(period > 0); assert(functionLabel >= 0); assert(numberCells > 0); switch(image->pmode) { case 8: assert(numberCells <= 16); break; case 9: assert(numberCells <= 256); break; default: assert(FALSE); } animation->image = image; animation->animationPeriod = period; animation->animationFunctionLabel = functionLabel; animation->numberCells = numberCells; } void SetClutAnimationsForTrackDescription (TunnelDescription *description, int numberAnimations, ClutAnimation *listOfAnimations) { int i; assert(description != NULL); assert(listOfAnimations != NULL); assert(numberAnimations > 0); assert(numberAnimations <= MAX_CLUT_ANIMATIONS_PER_DESCRIPTION); description->numberClutAnimations = numberAnimations; for (i = 0; i < numberAnimations; i++) { description->listOfClutAnimations[i] = &(listOfAnimations[i]); } } #endif // not used void HandleTunnelClutAnimations (void) { #if 0 // not used ClutAnimation *animation; int i; assert(TheTunnelDescription.numberClutAnimations > 0); assert(TheTunnelDescription.numberClutAnimations <= MAX_CLUT_ANIMATIONS_PER_DESCRIPTION); for (i = 0; i < TheTunnelDescription.numberClutAnimations; i++) { animation = TheTunnelDescription.listOfClutAnimations[i]; switch(animation->animationFunctionLabel) { case FIRST: SimpleCycleFourBitClut(animation); break; case SECOND: SimpleCycleEightBitClut(animation); break; case THIRD: CycleFirstAndSecond256CellsThroughRainbow(animation); break; default: assert(FALSE); } } #endif // not used } #if 0 // not used void SimpleCycleFourBitClut (ClutAnimation *animation) { int time; int shiftAlong; RECT rect; int i; assert(animation->image->pmode == 8); // ensure four bit setRECT( &rect, animation->image->cx, animation->image->cy, animation->image->cw, animation->image->ch); time = frameNumber % animation->animationPeriod; assert(animation->animationPeriod != 0); shiftAlong = (16 * time) / animation->animationPeriod; for (i = 0; i < 16 - shiftAlong; i++) { animation->bufferFourBitClut[i + shiftAlong] = animation->initialFourBitClut[i]; } for (i = shiftAlong; i < 16; i++) { animation->bufferFourBitClut[i - shiftAlong] = animation->initialFourBitClut[i]; } LoadImage( &rect, (u_long*) animation->bufferFourBitClut); } void SimpleCycleEightBitClut (ClutAnimation *animation) { int time; int shiftAlong; RECT rect; int numberCells; int i; assert(animation->image->pmode == 9); // ensure eight bit setRECT( &rect, animation->image->cx, animation->image->cy, animation->image->cw, animation->image->ch); numberCells = animation->numberCells; time = frameNumber % animation->animationPeriod; assert(animation->animationPeriod!= 0); shiftAlong = (numberCells * time) / animation->animationPeriod; for (i = 0; i < numberCells - shiftAlong; i++) { animation->bufferEightBitClut[i + shiftAlong] = animation->initialEightBitClut[i]; } for (i = shiftAlong; i < numberCells; i++) { animation->bufferEightBitClut[i - shiftAlong] = animation->initialEightBitClut[i]; } LoadImage( &rect, (u_long*) animation->bufferEightBitClut); } // we have lots of b/w textures with first cell black and second cell white // 16 bit colour: top bit semi-transparency, then 5 r, 5 g, 5 b // eg 16 is non-trans blue; ffff is sem-trans white void CycleFirstAndSecond256CellsThroughRainbow (ClutAnimation *animation) { RECT rect; u_short white, black; int time, phase, quarterPeriod, subTime; int r, g, b; int i; assert(animation->image->pmode == 9); // ensure eight bit setRECT( &rect, animation->image->cx, animation->image->cy, animation->image->cw, animation->image->ch); time = frameNumber % animation->animationPeriod; assert(animation->animationPeriod!= 0); phase = (time * 4) / animation->animationPeriod; quarterPeriod = animation->animationPeriod / 4; subTime = time - (phase * quarterPeriod); assert(quarterPeriod != 0); switch(phase) { case 0: // turn into red g = (31 * (quarterPeriod - subTime)) / quarterPeriod; b = (31 * (quarterPeriod - subTime)) / quarterPeriod; r = 31; break; case 1: // red to blue r = (31 * (quarterPeriod - subTime)) / quarterPeriod; b = (subTime * 31) / quarterPeriod; g = 0; break; case 2: // blue to green b = (31 * (quarterPeriod - subTime)) / quarterPeriod; g = (subTime * 31) / quarterPeriod; r = 0; break; case 3: // green to white r = (31 * (quarterPeriod - subTime)) / quarterPeriod; b = (31 * (quarterPeriod - subTime)) / quarterPeriod; g = 31; break; default: assert(FALSE); } white = (r << 10) + (g << 5) + b; black = 0; // do nowt with black for now (blues, purples, greens, browns) animation->bufferEightBitClut[0] = black; animation->bufferEightBitClut[1] = white; for (i = 2; i < 256; i++) animation->bufferEightBitClut[i] = 0; LoadImage( &rect, (u_long*) animation->bufferEightBitClut); } #endif // not used void ResetCluts (void) { ClutAnimation *animation; RECT rect; int i; // force ending of all background draw processing DrawSync(0); VSync(0); ResetGraph(1); if (TheTunnelDescription.numberClutAnimations <= 0) return; for (i = 0; i < TheTunnelDescription.numberClutAnimations; i++) { animation = TheTunnelDescription.listOfClutAnimations[i]; setRECT( &rect, animation->image->cx, animation->image->cy, animation->image->cw, animation->image->ch); switch(animation->image->pmode) { case 8: LoadImage( &rect, (u_long*) animation->initialFourBitClut); break; case 9: LoadImage( &rect, (u_long*) animation->initialEightBitClut); break; default: assert(FALSE); } DrawSync(0); } } void HandleTunnelDrawProcesses (void) { DrawProcess *drawProcess; int i; assert(TheTunnelDescription.numberDrawProcesses > 0); assert(TheTunnelDescription.numberDrawProcesses <= MAX_DRAW_PROCESSES_PER_DESCRIPTION); for (i = 0; i < TheTunnelDescription.numberDrawProcesses; i++) { drawProcess = &TheTunnelDescription.drawingProcesses[i]; if (DrawProcessesClippingFlag == TRUE) { if (DrawProcessVisible(i) == FALSE) continue; } ExecuteSingleDrawProcess(drawProcess); #if 0 if (frameNumber % 15 == 0) { PRINT("executing draw process number %d\n", i); } #endif } } int DrawProcessVisible (int processIndex) { int visible; int drawDistance, drawDirection; int start, end; assert(processIndex >= 0); assert(processIndex < TheTunnelDescription.numberDrawProcesses); switch(TheTunnelDescription.texturingType) { case SINGLE_TUNNEL_TEXTURE: visible = TRUE; break; case SPREAD_N_TEXTURES_EVENLY: if (TheTunnelDescription.numberImages == 1) { visible = TRUE; } else { //assert(ValidID(StartDrawingSectionId)); if (ValidID(StartDrawingSectionId) == FALSE) { // this occurs due to point of exit from // main gui loops; easily fixable // as is, handles it fine //printf("bad StartDrawingSectionId %d\n", // StartDrawingSectionId); return TRUE; } //assert(ValidID(EndDrawingSectionId)); if (ValidID(EndDrawingSectionId) == FALSE) { // this occurs due to point of exit from // main gui loops; easily fixable // as is, handles it fine //printf("bad EndDrawingSectionId %d\n", // EndDrawingSectionId); return TRUE; } FindShortestDistance (StartDrawingSectionId, EndDrawingSectionId, &drawDistance, &drawDirection); //assert(drawDistance <= MAX_NUMBER_SECTIONS_DRAWN); if (drawDistance > MAX_NUMBER_SECTIONS_DRAWN) { FindShortestDistance (EndDrawingSectionId, StartDrawingSectionId, &drawDistance, &drawDirection); if (drawDistance > MAX_NUMBER_SECTIONS_DRAWN) return TRUE; } start = TunnelDrawProcessSectionLimits[(processIndex*2)]; assert(ValidID(start)); end = TunnelDrawProcessSectionLimits[((processIndex*2)+1)]; assert(ValidID(end)); visible = RegionVisibleFromDrawRegion(start, end, StartDrawingSectionId, EndDrawingSectionId, drawDirection); } break; default: assert(FALSE); } return visible; } // first region is assumed to be in direction FORWARDS from start to end int RegionVisibleFromDrawRegion (int start, int end, int startDraw, int endDraw, int direction2) { int regionNumberSections, regionDirection, sectionID, i; int visible; assert(ValidID(start)); assert(ValidID(end)); assert(ValidID(startDraw)); assert(ValidID(endDraw)); assert(startDraw != endDraw); FindShortestDistance (start, end, ®ionNumberSections, ®ionDirection); //assert(regionDirection == FORWARDS); #if 1 if (regionDirection != FORWARDS) { printf("start %d\n", start); printf("end %d\n", end); printf("regionNumberSections %d\n", regionNumberSections); printf("regionDirection %d\n", regionDirection); } #endif regionNumberSections++; // num sections total, not distance between visible = FALSE; for (i = 0; i < regionNumberSections; i++) { sectionID = GetRelativeSection(start, i, FORWARDS); if (SectionBetweenLimits (sectionID, startDraw, endDraw) == TRUE) { visible = TRUE; break; } } return visible; } // know which bufferIndex: global externed from main void ExecuteSingleDrawProcess (DrawProcess *process) { RECT previousClip; u_short previousOffsets[2]; GsOT *ot; assert(process->alive == TRUE); if (process->screenShowFlag == FALSE && DebugDynTexMarker == TRUE) return; if (DebugDynTexMarker == FALSE) { // store previous setRECT( &previousClip, GsDRAWENV.clip.x, GsDRAWENV.clip.y, GsDRAWENV.clip.w, GsDRAWENV.clip.h); previousOffsets[0] = GsDRAWENV.ofs[0]; previousOffsets[1] = GsDRAWENV.ofs[1]; } #if 0 if (frameNumber % 15 == 0) { printf("previous business :-\n"); dumpRECT( &previousClip); printf("previous offsets %d, %d\n\n\n", previousOffsets[0], previousOffsets[1]); } #endif if (DebugDynTexMarker == FALSE) { // set new drawing environment setRECT( &GsDRAWENV.clip, process->clipArea.x, process->clipArea.y, process->clipArea.w, process->clipArea.h); GsDRAWENV.ofs[0] = process->offsets[0]; GsDRAWENV.ofs[1] = process->offsets[1]; PutDrawEnv( &GsDRAWENV); } #if 0 if (frameNumber % 15 == 0) { printf("newly set business :-\n"); dumpRECT( &GsDRAWENV.clip); printf("new offsets %d, %d\n\n\n", GsDRAWENV.ofs[0], GsDRAWENV.ofs[1]); } #endif // get the right OT switch(process->otLabel) { case FIRST: ot = &Wot2[bufferIndex]; break; case SECOND: ot = &Wot3[bufferIndex]; break; case THIRD: ot = &Wot4[bufferIndex]; break; case FOURTH: ot = &Wot5[bufferIndex]; break; case FIFTH: ot = &Wot7[bufferIndex]; break; case SIXTH: assert(FALSE); break; default: assert(FALSE); } GsClearOt(0, 0, ot); if (DebugDynTexMarker == TRUE) ot = &Wot[bufferIndex]; if (DebugDynTexMarker == FALSE) { // get the right packet area, use as work base for GPU switch(process->packetAreaLabel) { case FIRST: GsSetWorkBase( (PACKET*) packetArea2[bufferIndex]); break; case SECOND: GsSetWorkBase( (PACKET*) packetArea3[bufferIndex]); break; case THIRD: GsSetWorkBase( (PACKET*) packetArea4[bufferIndex]); break; case FOURTH: GsSetWorkBase( (PACKET*) packetArea5[bufferIndex]); break; case FIFTH: GsSetWorkBase( (PACKET*) packetArea7[bufferIndex]); break; case SIXTH: assert(FALSE); break; default: assert(FALSE); } } #if 0 if (process == &ShipDrawProcess && frameNumber % 240 == 0) { printf("about to DoWorkOfDrawProcess2 for ship draw proc\n"); } #endif // actually register draw requests into OT DoWorkOfDrawProcess2( process->id, ot); if (DebugDynTexMarker == FALSE) { GsDrawOt(ot); } if (DebugDynTexMarker == FALSE) { // restore previous settings setRECT( &GsDRAWENV.clip, previousClip.x, previousClip.y, previousClip.w, previousClip.h); GsDRAWENV.ofs[0] = previousOffsets[0]; GsDRAWENV.ofs[1] = previousOffsets[1]; PutDrawEnv(&GsDRAWENV); } #if 0 if (frameNumber % 15 == 0) { printf("newly restored business :-\n"); dumpRECT( &GsDRAWENV.clip); printf("newly restored offsets %d, %d\n\n\n", GsDRAWENV.ofs[0], GsDRAWENV.ofs[1]); } #endif } void SetDrawProcess2 (DrawProcess *process, GsIMAGE *dynamicTextureArea, int id, int otLabel, int packetAreaLabel) { assert(process != NULL); process->id = id; process->alive = TRUE; // use 32x32 area since that will fit into texture cache setRECT( &process->clipArea, dynamicTextureArea->px, dynamicTextureArea->py, 32, 32); process->offsets[0] = dynamicTextureArea->px; process->offsets[1] = dynamicTextureArea->py; process->otLabel = otLabel; process->packetAreaLabel = packetAreaLabel; process->screenShowFlag = FALSE; } void SetDrawProcessesForTrack (TunnelDescription *description, int numberProcesses, DrawProcess *listOfProcesses) { int i; assert(description != NULL); assert(listOfProcesses != NULL); assert(numberProcesses > 0); assert(numberProcesses <= MAX_DRAW_PROCESSES_PER_DESCRIPTION); description->numberDrawProcesses = numberProcesses; for (i = 0; i < numberProcesses; i++) { description->drawingProcesses[i].id = listOfProcesses[i].id; description->drawingProcesses[i].alive = listOfProcesses[i].alive; setRECT( &description->drawingProcesses[i].clipArea, listOfProcesses[i].clipArea.x, listOfProcesses[i].clipArea.y, listOfProcesses[i].clipArea.w, listOfProcesses[i].clipArea.h); description->drawingProcesses[i].offsets[0] = listOfProcesses[i].offsets[0]; description->drawingProcesses[i].offsets[1] = listOfProcesses[i].offsets[1]; description->drawingProcesses[i].otLabel = listOfProcesses[i].otLabel; description->drawingProcesses[i].packetAreaLabel = listOfProcesses[i].packetAreaLabel; } } void ClearAllDrawingBusiness (void) { DrawSync(0); VSync(0); GsClearOt(0, 0, &Wot[0]); GsClearOt(0, 0, &Wot[1]); GsClearOt(0, 0, &Wot2[0]); GsClearOt(0, 0, &Wot2[1]); GsClearOt(0, 0, &Wot3[0]); GsClearOt(0, 0, &Wot3[1]); GsClearOt(0, 0, &Wot4[0]); GsClearOt(0, 0, &Wot4[1]); GsClearOt(0, 0, &Wot5[0]); GsClearOt(0, 0, &Wot5[1]); GsClearOt(0, 0, &Wot6[0]); GsClearOt(0, 0, &Wot6[1]); GsClearOt(0, 0, &Wot7[0]); GsClearOt(0, 0, &Wot7[1]); } int GetOffsetOfPoint (VECTOR *point, int sectionID) { int offsetRatio; VECTOR copy, relativePoint; VECTOR *splinePoint; assert(ValidID(sectionID)); splinePoint = &TheTunnel[sectionID].splinePoint; setVECTOR( ©, point->vx, point->vy, point->vz); copy.vx -= splinePoint->vx; copy.vy -= splinePoint->vy; copy.vz -= splinePoint->vz; ExpressSuperPointInSub( ©, &TheTunnel[sectionID].coordinateData, &relativePoint); assert(TunnelSectionLength > 0); offsetRatio = (relativePoint.vz << 12) / TunnelSectionLength; if (offsetRatio >= ONE) offsetRatio = ONE-1; else if (offsetRatio < 0) offsetRatio = 0; return offsetRatio; } int GetTotalAngleAtPoint (int sectionID, int offsetRatio) { int angle; int nextID, previousID; int currentAngle, nextAngle, currentTotalAngle; int fraction; assert(ValidID(sectionID)); assert(offsetRatio >= 0); assert(offsetRatio < ONE); if (offsetRatio < 2048) { previousID = GetRelativeSection(sectionID, 1, BACKWARDS); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: currentAngle = TheTunnel[sectionID].thetaY; break; case Y_Z_PLANE_ONLY: currentAngle = TheTunnel[sectionID].thetaX; break; default: assert(FALSE); } currentTotalAngle = TheTunnel[previousID].totalAngle; fraction = offsetRatio + 2048; angle = currentTotalAngle + ((fraction * currentAngle) >> 12); } else { nextID = GetRelativeSection(sectionID, 1, FORWARDS); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: nextAngle = TheTunnel[nextID].thetaY; break; case Y_Z_PLANE_ONLY: nextAngle = TheTunnel[nextID].thetaX; break; default: assert(FALSE); } currentTotalAngle = TheTunnel[sectionID].totalAngle; fraction = offsetRatio - 2048; angle = currentTotalAngle + ((fraction * nextAngle) >> 12); } while (angle >= ONE) angle -= ONE; while (angle < 0) angle += ONE; return angle; } // 'angle at point': amount of curving going on just there // either zero or the thetaX/thetaY of a section // ie no partial angles int GetAngularChangeAtPoint (int sectionID, int offsetRatio) { int angularChange; int nextID; assert(ValidID(sectionID)); assert(offsetRatio >= 0); assert(offsetRatio < ONE); if (offsetRatio < 2048) { switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: angularChange = TheTunnel[sectionID].thetaY; break; case Y_Z_PLANE_ONLY: angularChange = TheTunnel[sectionID].thetaX; break; default: assert(FALSE); } } else { nextID = GetRelativeSection(sectionID, 1, FORWARDS); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: angularChange = TheTunnel[nextID].thetaY; break; case Y_Z_PLANE_ONLY: angularChange = TheTunnel[nextID].thetaX; break; default: assert(FALSE); } } return angularChange; } void ProperGetCurveStatus (int sectionID, int offsetRatio, int *statusFlag, int *angleOnThisCurve) { int previousID, nextID; int presentAngle, pastAngle, futureAngle; int fraction; assert(ValidID(sectionID)); assert(offsetRatio >= 0); assert(offsetRatio < ONE); previousID = GetRelativeSection(sectionID, 1, BACKWARDS); nextID = GetRelativeSection(sectionID, 1, FORWARDS); if (offsetRatio < 2048) { switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: presentAngle = TheTunnel[sectionID].thetaY; break; case Y_Z_PLANE_ONLY: presentAngle = TheTunnel[sectionID].thetaX; break; default: assert(FALSE); } if (presentAngle == 0) { *statusFlag = ON_STRAIGHT; *angleOnThisCurve = 0; } else { pastAngle = GetAngularChangeAtPoint(previousID, offsetRatio); if (pastAngle == 0) { *statusFlag = ENTERING_CURVE; fraction = offsetRatio + 2048; *angleOnThisCurve = (fraction * presentAngle) >> 12; } else { futureAngle = GetAngularChangeAtPoint(nextID, offsetRatio); if (futureAngle == 0) { *statusFlag = EXITING_CURVE; fraction = ONE - (offsetRatio + 2048); *angleOnThisCurve = (fraction * presentAngle) >> 12; } else { *statusFlag = DURING_CURVE; *angleOnThisCurve = presentAngle; } } } } else { switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: presentAngle = TheTunnel[nextID].thetaY; break; case Y_Z_PLANE_ONLY: presentAngle = TheTunnel[nextID].thetaX; break; default: assert(FALSE); } if (presentAngle == 0) { *statusFlag = ON_STRAIGHT; *angleOnThisCurve = 0; } else { pastAngle = GetAngularChangeAtPoint(previousID, offsetRatio); if (pastAngle == 0) { *statusFlag = ENTERING_CURVE; fraction = offsetRatio - 2048; *angleOnThisCurve = (fraction * presentAngle) >> 12; } else { futureAngle = GetAngularChangeAtPoint(nextID, offsetRatio); if (futureAngle == 0) { *statusFlag = EXITING_CURVE; fraction = ONE - (offsetRatio - 2048); *angleOnThisCurve = (fraction * presentAngle) >> 12; } else { *statusFlag = DURING_CURVE; *angleOnThisCurve = presentAngle; } } } } } void GetNewSectionAndOffset (int sectionID, int offsetRatio, int sectionIncrement, int offsetIncrement, int *newSectionID, int *newOffsetRatio) { int tempSectionID; assert(ValidID(sectionID)); assert(offsetRatio >= 0); assert(offsetRatio < ONE); assert(abs(sectionIncrement) < NumberTunnelSections); assert(abs(offsetIncrement) < ONE); if (sectionIncrement >= 0) { tempSectionID = GetRelativeSection(sectionID, sectionIncrement, FORWARDS); } else { tempSectionID = GetRelativeSection(sectionID, abs(sectionIncrement), BACKWARDS); } *newOffsetRatio = offsetRatio + offsetIncrement; if (*newOffsetRatio >= ONE) { *newOffsetRatio -= ONE; tempSectionID = GetRelativeSection(tempSectionID, 1, FORWARDS); } else if (*newOffsetRatio < 0) { *newOffsetRatio += ONE; tempSectionID = GetRelativeSection(tempSectionID, 1, BACKWARDS); } *newSectionID = tempSectionID; assert(ValidID(*newSectionID)); assert(*newOffsetRatio >= 0); assert(*newOffsetRatio < ONE); } void GetDividedSectionAndOffset (int numberSections, int offsetRatio, int divisor, int *newNumberSections, int *newOffsetRatio) { int totalThere, newTotalThere; assert(numberSections >= 0); assert(offsetRatio >= 0); assert(offsetRatio < ONE); assert(divisor > 1); totalThere = (numberSections * ONE) + offsetRatio; newTotalThere = totalThere / divisor; (*newNumberSections) = 0; (*newOffsetRatio) = newTotalThere; while ((*newOffsetRatio) >= ONE) { (*newNumberSections)++; (*newOffsetRatio) -= ONE; } } // the angle representing the degree of curve at that point int GetTunnelCurvatureAtPoint (int sectionID, int offsetRatio) { int presentAngle, pastAngle, futureAngle; int curvature; int previousSectionID, nextSectionID; assert(ValidID(sectionID)); assert(offsetRatio >= 0); assert(offsetRatio < ONE); if (offsetRatio < 2048) { previousSectionID = GetRelativeSection(sectionID, 1, BACKWARDS); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: presentAngle = TheTunnel[sectionID].thetaY; pastAngle = TheTunnel[previousSectionID].thetaY; break; case Y_Z_PLANE_ONLY: presentAngle = TheTunnel[sectionID].thetaX; pastAngle = TheTunnel[previousSectionID].thetaX; break; default: assert(FALSE); } curvature = ((presentAngle * (offsetRatio + 2048)) + (pastAngle * (2048 - offsetRatio))) >> 12; } else { nextSectionID = GetRelativeSection(sectionID, 1, FORWARDS); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: presentAngle = TheTunnel[sectionID].thetaY; futureAngle = TheTunnel[nextSectionID].thetaY; break; case Y_Z_PLANE_ONLY: presentAngle = TheTunnel[sectionID].thetaX; futureAngle = TheTunnel[nextSectionID].thetaX; break; default: assert(FALSE); } curvature = ((presentAngle * (6144 - offsetRatio)) + (futureAngle * (offsetRatio - 2048))) >> 12; } return curvature; } void SetTrackHighlightEffect (TunnelDescription *track, int effectID, int data1, int data2) { assert(track != NULL); track->tunnelHighlightFlag = TRUE; track->highlightEffectId = effectID; track->highlightData1 = data1; track->highlightData2 = data2; } void InitialiseHighlightingForTrack (TunnelDescription *description) { assert(description != NULL); if (description->tunnelHighlightFlag == TRUE) { // no point having highlight without flat lights // since the highlights cannot be seen assert(description->tunnelLightingEffect != FALSE); switch(description->highlightEffectId) { case EVEN_SPACED_SECTIONS_MOVE_BACK_AND_FORTH: HighlightSectionOffsetWithinSection = 0; break; case FULL_LENGTH_SNAKE_OSCILLATES_AROUND_TUNNEL: SnakeTwistingDirection = CLOCKWISE + (rand() % 2); SnakeBaseSectionPolygonIndexCounter = 0; break; case SHORT_SNAKE_SPIRALS_BACK_AND_FORTH: SnakeTwistingDirection = CLOCKWISE + (rand() % 2); SnakeBaseSectionPolygonIndexCounter = 0; ShortSnakePositionCounter = 0; break; case TWO_SNAKES_OSCILLATE_AROUND_TUNNEL_IN_HELIX: SnakeTwistingDirection = CLOCKWISE + (rand() % 2); SnakeBaseSectionPolygonIndexCounter = 0; break; case TWO_QUARTERS_SNAKE_OSCILLATES: SnakeTexture = GetNewSnakeTexture(0); SnakeTwistingDirection = CLOCKWISE + (rand() % 2); SnakeBaseSectionPolygonIndexCounter = 0; break; case SNAKE_GROWS_AND_SHRINKS: SnakeTexture = GetNewSnakeTexture(0); SnakeTwistingDirection = CLOCKWISE + (rand() % 2); SnakeBaseSectionPolygonIndexCounter = 0; break; default: assert(FALSE); } } } GsIMAGE *GetNewSnakeTexture (int sectionID) { int index; GsIMAGE *image, *original; assert(ValidID(sectionID)); original = GetTextureInfoOnTunnelSection (sectionID); for (;;) { index = rand() % NumberGlobalImages; image = GlobalImageList[index]; assert(image != NULL); if (image->pw <= 64 && image->py <= 64 && image->pmode == original->pmode) break; } return image; } void HandleHighlightingForTrack (void) { assert(TheTunnelDescription.tunnelHighlightFlag == TRUE); switch(TheTunnelDescription.highlightEffectId) { case EVEN_SPACED_SECTIONS_MOVE_BACK_AND_FORTH: HandleSectionHighlightingForTrack(); break; case FULL_LENGTH_SNAKE_OSCILLATES_AROUND_TUNNEL: HandleFullLengthSnakeOscillation(); break; case SHORT_SNAKE_SPIRALS_BACK_AND_FORTH: HandleShortSnake(); break; case TWO_SNAKES_OSCILLATE_AROUND_TUNNEL_IN_HELIX: HandleTwoSnakesInHelix(); break; case TWO_QUARTERS_SNAKE_OSCILLATES: HandleTwoQuartersSnake(); break; case SNAKE_GROWS_AND_SHRINKS: HandleSnakeGrowingAndShrinking(); break; default: assert(FALSE); } } void ResetDataDueToHighlighting (void) { assert(TheTunnelDescription.tunnelHighlightFlag == TRUE); switch(TheTunnelDescription.highlightEffectId) { case EVEN_SPACED_SECTIONS_MOVE_BACK_AND_FORTH: // do nowt break; case FULL_LENGTH_SNAKE_OSCILLATES_AROUND_TUNNEL: ResetNormalsFromFullSnakeOscillation(); break; case SHORT_SNAKE_SPIRALS_BACK_AND_FORTH: ResetNormalsFromShortSnakeOscillation(); break; case TWO_SNAKES_OSCILLATE_AROUND_TUNNEL_IN_HELIX: ResetNormalsFromTwoSnakeOscillation(); break; case TWO_QUARTERS_SNAKE_OSCILLATES: ResetTextureDataFromTwoQuartersSnakeOscillation(); break; case SNAKE_GROWS_AND_SHRINKS: ResetTextureDataFromSnakeGrowingAndShrinking(); break; default: assert(FALSE); } } void HandleSectionHighlightingForTrack (void) { int time, cycleRatio; int numberDrawn, sectionID, baseSection, sectionOffset, data1, i; int snakeDirection; snakeDirection = GetSnakeDirection(); assert(HighlightPeriod > 0); time = frameNumber % HighlightPeriod; cycleRatio = (time * ONE) / HighlightPeriod; if (cycleRatio < 2048) HighlightSpeed = HighlightMaxSpeed - ((HighlightMaxSpeed*cycleRatio)/1024); else HighlightSpeed = -HighlightMaxSpeed + ((HighlightMaxSpeed*(cycleRatio-2048))/1024); // update offset within section first HighlightSectionOffsetWithinSection += HighlightSpeed; if (HighlightSectionOffsetWithinSection < 0) { HighlightSectionOffsetWithinSection += (ONE * HighlightSectionSpacing); assert(HighlightSectionOffsetWithinSection >= 0); } if (HighlightSectionOffsetWithinSection >= (ONE * HighlightSectionSpacing)) { HighlightSectionOffsetWithinSection -= (ONE * HighlightSectionSpacing); assert(HighlightSectionOffsetWithinSection < (ONE * HighlightSectionSpacing)); } sectionOffset = HighlightSectionOffsetWithinSection >> 12; assert(sectionOffset >= 0); assert(sectionOffset < HighlightSectionSpacing); if (ValidID(StartDrawingSectionId) == 0) return; baseSection = GetRelativeSection(StartDrawingSectionId, sectionOffset, snakeDirection); assert(ValidID(baseSection)); numberDrawn = TheTunnelDescription.numberSectionsDrawn; data1 = TheTunnelDescription.highlightData1; for (i = 0; i < numberDrawn; i += HighlightSectionSpacing) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); TheTunnel[sectionID].miscFlags = data1; } } void HandleFullLengthSnakeOscillation (void) { int time, cycleRatio, speed; int polyIndex, baseSection, sectionID, snakeLength, data1, i; u_long tmdAddress; TMD_NORM *normal; int snakeDirection; int normalFlag; snakeDirection = GetSnakeDirection(); assert(SnakePeriod > 0); time = frameNumber % SnakePeriod; cycleRatio = (time * ONE) / SnakePeriod; if (cycleRatio < 2048) speed = SnakeMaxSpeed - ((SnakeMaxSpeed*cycleRatio)/1024); else speed = -SnakeMaxSpeed + ((SnakeMaxSpeed*(cycleRatio-2048))/1024); SnakeBaseSectionPolygonIndexCounter += speed; assert(NumberShapesPerSection > 0); assert(NumberShapesPerSection < MAX_NUMBER_SHAPES_PER_SECTION); if (SnakeBaseSectionPolygonIndexCounter < 0) { SnakeBaseSectionPolygonIndexCounter += (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter >= 0); } if (SnakeBaseSectionPolygonIndexCounter >= (ONE * NumberShapesPerSection)) { SnakeBaseSectionPolygonIndexCounter -= (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter < (ONE * NumberShapesPerSection)); } PreviousSnakeBaseSectionPolygonIndexCounter = SnakeBaseSectionPolygonIndexCounter; polyIndex = SnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); if (ValidID(StartDrawingSectionId) == 0) { return; } baseSection = StartDrawingSectionId; SnakePreviousBaseSection = baseSection; snakeLength = TheTunnelDescription.numberSectionsDrawn; SnakePreviousLength = snakeLength; data1 = TheTunnelDescription.highlightData1; PreviousSnakeDirection = snakeDirection; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < snakeLength; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); // firstly, store then change normal tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { normal = TMD_getNormAddr( (u_long*) tmdAddress, (u_char) polyIndex); setNORMAL( &FirstSnakeNormalStore[i], normal->nx, normal->ny, normal->nz); MakePolygonNormalChange(normal, data1); } else { assert(FALSE); // retexture at runtime causes crash MakePolygonTextureChange (tmdAddress, polyIndex, data1, sectionID); } // now update polyIndex for next section alteration switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); } } int PolygonEffectUsesNormal (int effectID) { switch(effectID) { case POLYGON_BRIGHTEN: case POLYGON_DARKEN: case POLY_ACCENTUATE_X: case POLY_ACCENTUATE_Y: case POLY_ACCENTUATE_Z: return TRUE; case POLY_NEW_RAND_FIXED_TEXTURE: case POLY_NEW_RAND_CHANGING_TEXTURE: return FALSE; default: assert(FALSE); } } int PolygonEffectUsesTexture (int effectID) { switch(effectID) { case POLYGON_BRIGHTEN: case POLYGON_DARKEN: case POLY_ACCENTUATE_X: case POLY_ACCENTUATE_Y: case POLY_ACCENTUATE_Z: return FALSE; case POLY_NEW_RAND_FIXED_TEXTURE: case POLY_NEW_RAND_CHANGING_TEXTURE: return TRUE; default: assert(FALSE); } } void MakePolygonNormalChange (TMD_NORM *normal, int change) { assert(normal != NULL); switch(change) { case POLYGON_BRIGHTEN: normal->nx *= 3; normal->ny *= 3; normal->nz *= 3; break; case POLYGON_DARKEN: normal->nx /= 4; normal->ny /= 4; normal->nz /= 4; break; case POLY_ACCENTUATE_X: normal->nx *= 4; break; case POLY_ACCENTUATE_Y: normal->ny *= 4; break; case POLY_ACCENTUATE_Z: normal->nz *= 4; break; default: assert(FALSE); } } void MakePolygonTextureChange (u_long addr, int index, int change, int sectionID) { int time, tPageMode; u_long *pointer; int tPageID, baseX, baseY, widthCompression; u_char u0, v0, u1, v1, u2, v2, u3, v3; assert(FALSE); // retexture at runtime causes crash assert(SnakePeriod > 0); time = frameNumber % SnakePeriod; // update texture if need be switch(change) { case POLY_NEW_RAND_FIXED_TEXTURE: break; case POLY_NEW_RAND_CHANGING_TEXTURE: if (time == 0) { SnakeTexture = GetNewSnakeTexture(sectionID); } break; default: assert(FALSE); } assert(SnakeTexture != NULL); pointer = &addr; assert(SnakeTexture != NULL); switch(SnakeTexture->pmode) { case 2: // 16 bit tPageMode = 2; widthCompression = 1; break; case 8: // 4 bit tPageMode = 0; TMD_setClut(pointer, index, (long) SnakeTexture->cx, (long) SnakeTexture->cy); widthCompression = 4; break; case 9: // 8 bit tPageMode = 1; TMD_setClut(pointer, index, (long) SnakeTexture->cx, (long) SnakeTexture->cy); widthCompression = 2; break; default: assert(FALSE); } TMD_setTPage(pointer, index, (long)tPageMode, 0, (long) SnakeTexture->px, (long) SnakeTexture->py); GetTexturePage(SnakeTexture, &tPageID, &baseX, &baseY); u0 = baseX; v0 = baseY; u1 = baseX + (SnakeTexture->pw * widthCompression) - 1; v1 = baseY; u2 = baseX; v2 = baseY + SnakeTexture->ph - 1; u3 = baseX + (SnakeTexture->pw * widthCompression) - 1; v3 = baseY + SnakeTexture->ph - 1; TMD_setUV4(pointer, index, u0, v0, u1, v1, u2, v2, u3, v3); } void RevertPolygonTextureChange (u_long addr, int index, int sectionID) { int tPageMode; u_long *pointer; int tPageID, baseX, baseY, widthCompression; u_char u0, v0, u1, v1, u2, v2, u3, v3; GsIMAGE *original; assert(FALSE); // retexture at runtime causes crash original = GetTextureInfoOnTunnelSection(sectionID); assert(original != NULL); pointer = &addr; assert(original != NULL); switch(original->pmode) { case 2: // 16 bit tPageMode = 2; widthCompression = 1; break; case 8: // 4 bit tPageMode = 0; TMD_setClut(pointer, index, (long) original->cx, (long) original->cy); widthCompression = 4; break; case 9: // 8 bit tPageMode = 1; TMD_setClut(pointer, index, (long) original->cx, (long) original->cy); widthCompression = 2; break; default: assert(FALSE); } TMD_setTPage(pointer, index, (long)tPageMode, 0, (long) original->px, (long) original->py); GetTexturePage(original, &tPageID, &baseX, &baseY); u0 = baseX; v0 = baseY; u1 = baseX + (original->pw * widthCompression) - 1; v1 = baseY; u2 = baseX; v2 = baseY + original->ph - 1; u3 = baseX + (original->pw * widthCompression) - 1; v3 = baseY + original->ph - 1; TMD_setUV4(pointer, index, u0, v0, u1, v1, u2, v2, u3, v3); } void ResetNormalsFromFullSnakeOscillation (void) { int polyIndex, baseSection, sectionID, snakeLength, snakeDirection, i; u_long tmdAddress; int data1, normalFlag; assert(NumberShapesPerSection > 0); assert(NumberShapesPerSection < MAX_NUMBER_SHAPES_PER_SECTION); polyIndex = PreviousSnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); baseSection = SnakePreviousBaseSection; snakeLength = SnakePreviousLength; snakeDirection = PreviousSnakeDirection; data1 = TheTunnelDescription.highlightData1; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < snakeLength; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); // re store normal tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { TMD_setNorm0( (u_long*) tmdAddress, (u_char) polyIndex, FirstSnakeNormalStore[i].nx, FirstSnakeNormalStore[i].ny, FirstSnakeNormalStore[i].nz); } else { assert(FALSE); // retexture at runtime causes crash RevertPolygonTextureChange (tmdAddress, polyIndex, sectionID); } switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); } } void HandleShortSnake (void) { int time, cycleRatio, speed; int polyIndex, baseSection, offset, sectionID, data1, i; int actualDistanceMovable; int snakeDirection, normalFlag; u_long tmdAddress; TMD_NORM *normal; snakeDirection = GetSnakeDirection(); assert(SnakePeriod > 0); time = frameNumber % SnakePeriod; cycleRatio = (time * ONE) / SnakePeriod; assert((TheTunnelDescription.numberSectionsDrawn - ShortSnakeLengthInSections) > 0); actualDistanceMovable = (TheTunnelDescription.numberSectionsDrawn - ShortSnakeLengthInSections) * ONE; assert(NumberShapesPerSection > 0); assert(NumberShapesPerSection < MAX_NUMBER_SHAPES_PER_SECTION); if (cycleRatio < 2048) { ShortSnakePositionCounter = (actualDistanceMovable * cycleRatio) / 2048; speed = SnakeMaxSpeed - ((SnakeMaxSpeed*cycleRatio)/1024); } else { ShortSnakePositionCounter = actualDistanceMovable - ((actualDistanceMovable * (cycleRatio-2048)) / 2048); speed = -SnakeMaxSpeed + ((SnakeMaxSpeed*(cycleRatio-2048))/1024); } assert(ShortSnakePositionCounter >= 0); assert(ShortSnakePositionCounter <= actualDistanceMovable); //assert(ValidID(StartDrawingSectionId)); if (ValidID(StartDrawingSectionId) == 0) { return; } offset = ShortSnakePositionCounter >> 12; baseSection = GetRelativeSection(StartDrawingSectionId, offset, snakeDirection); SnakePreviousBaseSection = baseSection; SnakeBaseSectionPolygonIndexCounter += speed; if (SnakeBaseSectionPolygonIndexCounter < 0) { SnakeBaseSectionPolygonIndexCounter += (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter >= 0); } if (SnakeBaseSectionPolygonIndexCounter >= (ONE * NumberShapesPerSection)) { SnakeBaseSectionPolygonIndexCounter -= (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter < (ONE * NumberShapesPerSection)); } PreviousSnakeBaseSectionPolygonIndexCounter = SnakeBaseSectionPolygonIndexCounter; polyIndex = SnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); data1 = TheTunnelDescription.highlightData1; SnakePreviousLength = ShortSnakeLengthInSections; PreviousSnakeDirection = snakeDirection; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < ShortSnakeLengthInSections; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); // firstly, store then change normal tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { normal = TMD_getNormAddr( (u_long*) tmdAddress, (u_char) polyIndex); setNORMAL( &FirstSnakeNormalStore[i], normal->nx, normal->ny, normal->nz); MakePolygonNormalChange(normal, data1); } else { assert(FALSE); // retexture at runtime causes crash MakePolygonTextureChange (tmdAddress, polyIndex, data1, sectionID); } // now update polyIndex for next section alteration switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); } } void ResetNormalsFromShortSnakeOscillation (void) { int time, cycleRatio; int polyIndex, baseSection, sectionID, data1, snakeDirection, i; u_long tmdAddress; int normalFlag; assert(SnakePeriod > 0); time = frameNumber % SnakePeriod; cycleRatio = (time * ONE) / SnakePeriod; assert(NumberShapesPerSection > 0); assert(NumberShapesPerSection < MAX_NUMBER_SHAPES_PER_SECTION); baseSection = SnakePreviousBaseSection; polyIndex = PreviousSnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); data1 = TheTunnelDescription.highlightData1; snakeDirection = PreviousSnakeDirection; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < SnakePreviousLength; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { // restoration TMD_setNorm0( (u_long*) tmdAddress, (u_char) polyIndex, FirstSnakeNormalStore[i].nx, FirstSnakeNormalStore[i].ny, FirstSnakeNormalStore[i].nz); } else { assert(FALSE); // retexture at runtime causes crash RevertPolygonTextureChange (tmdAddress, polyIndex, sectionID); } // now update polyIndex for next section alteration switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); } } void HandleTwoSnakesInHelix (void) { int time, cycleRatio, speed; int polyIndex, otherPolyIndex, baseSection, sectionID, snakeLength, data1, i; int snakeDirection, normalFlag; u_long tmdAddress; TMD_NORM *normal1, *normal2; snakeDirection = GetSnakeDirection(); assert(SnakePeriod > 0); time = frameNumber % SnakePeriod; cycleRatio = (time * ONE) / SnakePeriod; if (cycleRatio < 2048) speed = SnakeMaxSpeed - ((SnakeMaxSpeed*cycleRatio)/1024); else speed = -SnakeMaxSpeed + ((SnakeMaxSpeed*(cycleRatio-2048))/1024); SnakeBaseSectionPolygonIndexCounter += speed; assert(NumberShapesPerSection > 0); assert(NumberShapesPerSection < MAX_NUMBER_SHAPES_PER_SECTION); if (SnakeBaseSectionPolygonIndexCounter < 0) { SnakeBaseSectionPolygonIndexCounter += (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter >= 0); } if (SnakeBaseSectionPolygonIndexCounter >= (ONE * NumberShapesPerSection)) { SnakeBaseSectionPolygonIndexCounter -= (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter < (ONE * NumberShapesPerSection)); } PreviousSnakeBaseSectionPolygonIndexCounter = SnakeBaseSectionPolygonIndexCounter; polyIndex = SnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); // get poly opposite for second snake otherPolyIndex = polyIndex + (NumberShapesPerSection/2); if (otherPolyIndex >= NumberShapesPerSection) { otherPolyIndex -= NumberShapesPerSection; } assert(otherPolyIndex >= 0); assert(otherPolyIndex < NumberShapesPerSection); //assert(ValidID(StartDrawingSectionId)); if (ValidID(StartDrawingSectionId) == 0) { return; } baseSection = StartDrawingSectionId; SnakePreviousBaseSection = baseSection; snakeLength = TheTunnelDescription.numberSectionsDrawn; SnakePreviousLength = snakeLength; data1 = TheTunnelDescription.highlightData1; PreviousSnakeDirection = snakeDirection; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < snakeLength; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); // firstly, store then change normal tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { normal1 = TMD_getNormAddr( (u_long*) tmdAddress, (u_char) polyIndex); normal2 = TMD_getNormAddr( (u_long*) tmdAddress, (u_char) otherPolyIndex); setNORMAL( &FirstSnakeNormalStore[i], normal1->nx, normal1->ny, normal1->nz); setNORMAL( &SecondSnakeNormalStore[i], normal2->nx, normal2->ny, normal2->nz); MakePolygonNormalChange(normal1, data1); MakePolygonNormalChange(normal2, data1); } else { assert(FALSE); // retexture at runtime causes crash MakePolygonTextureChange (tmdAddress, polyIndex, data1, sectionID); MakePolygonTextureChange (tmdAddress, otherPolyIndex, data1, sectionID); } // now update polyIndex for next section alteration switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); otherPolyIndex = polyIndex + (NumberShapesPerSection/2); if (otherPolyIndex >= NumberShapesPerSection) { otherPolyIndex -= NumberShapesPerSection; } assert(otherPolyIndex >= 0); assert(otherPolyIndex < NumberShapesPerSection); } } void ResetNormalsFromTwoSnakeOscillation (void) { int polyIndex, otherPolyIndex, baseSection, sectionID, snakeLength, data1, i; u_long tmdAddress; int snakeDirection, normalFlag; assert(NumberShapesPerSection > 0); assert(NumberShapesPerSection < MAX_NUMBER_SHAPES_PER_SECTION); polyIndex = PreviousSnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); // get poly opposite for second snake otherPolyIndex = polyIndex + (NumberShapesPerSection/2); if (otherPolyIndex >= NumberShapesPerSection) { otherPolyIndex -= NumberShapesPerSection; } assert(otherPolyIndex >= 0); assert(otherPolyIndex < NumberShapesPerSection); baseSection = SnakePreviousBaseSection; snakeLength = SnakePreviousLength; data1 = TheTunnelDescription.highlightData1; snakeDirection = PreviousSnakeDirection; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < snakeLength; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { // restoration TMD_setNorm0( (u_long*) tmdAddress, (u_char) polyIndex, FirstSnakeNormalStore[i].nx, FirstSnakeNormalStore[i].ny, FirstSnakeNormalStore[i].nz); TMD_setNorm0( (u_long*) tmdAddress, (u_char) otherPolyIndex, SecondSnakeNormalStore[i].nx, SecondSnakeNormalStore[i].ny, SecondSnakeNormalStore[i].nz); } else { assert(FALSE); // retexture at runtime causes crash RevertPolygonTextureChange (tmdAddress, polyIndex, sectionID); RevertPolygonTextureChange (tmdAddress, otherPolyIndex, sectionID); } // now update polyIndex for next section switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); otherPolyIndex = polyIndex + (NumberShapesPerSection/2); if (otherPolyIndex >= NumberShapesPerSection) { otherPolyIndex -= NumberShapesPerSection; } assert(otherPolyIndex >= 0); assert(otherPolyIndex < NumberShapesPerSection); } } // need to sort it out for two-quarters-ness (several polys) // and the choice of texture void HandleTwoQuartersSnake (void) { int time, cycleRatio, speed; int polyIndex, baseSection, sectionID, snakeLength, data1, i, j; u_long tmdAddress; TMD_NORM *normal; int snakeDirection, snakeWidth, normalFlag, oppositePolyIndex, thisIndex; snakeDirection = GetSnakeDirection(); snakeWidth = NumberShapesPerSection / 4; assert(SnakePeriod > 0); time = frameNumber % SnakePeriod; cycleRatio = (time * ONE) / SnakePeriod; if (cycleRatio < 2048) speed = SnakeMaxSpeed - ((SnakeMaxSpeed*cycleRatio)/1024); else speed = -SnakeMaxSpeed + ((SnakeMaxSpeed*(cycleRatio-2048))/1024); SnakeBaseSectionPolygonIndexCounter += speed; assert(NumberShapesPerSection > 0); assert(NumberShapesPerSection < MAX_NUMBER_SHAPES_PER_SECTION); if (SnakeBaseSectionPolygonIndexCounter < 0) { SnakeBaseSectionPolygonIndexCounter += (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter >= 0); } if (SnakeBaseSectionPolygonIndexCounter >= (ONE * NumberShapesPerSection)) { SnakeBaseSectionPolygonIndexCounter -= (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter < (ONE * NumberShapesPerSection)); } PreviousSnakeBaseSectionPolygonIndexCounter = SnakeBaseSectionPolygonIndexCounter; polyIndex = SnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); oppositePolyIndex = polyIndex + (NumberShapesPerSection/2); if (oppositePolyIndex >= NumberShapesPerSection) { oppositePolyIndex -= NumberShapesPerSection; } assert(oppositePolyIndex >= 0); assert(oppositePolyIndex < NumberShapesPerSection); if (ValidID(StartDrawingSectionId) == 0) { return; } baseSection = StartDrawingSectionId; SnakePreviousBaseSection = baseSection; snakeLength = TheTunnelDescription.numberSectionsDrawn; SnakePreviousLength = snakeLength; data1 = TheTunnelDescription.highlightData1; PreviousSnakeDirection = snakeDirection; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < snakeLength; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); // firstly, store then change normal tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { for (j = 0; j < snakeWidth; j++) { thisIndex = polyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); normal = TMD_getNormAddr( (u_long*) tmdAddress, (u_char) thisIndex); setNORMAL( &GeneralSnakeNormalStore[(j*2)][i], normal->nx, normal->ny, normal->nz); MakePolygonNormalChange(normal, data1); thisIndex = oppositePolyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); normal = TMD_getNormAddr( (u_long*) tmdAddress, (u_char) thisIndex); setNORMAL( &GeneralSnakeNormalStore[((j*2)+1)][i], normal->nx, normal->ny, normal->nz); MakePolygonNormalChange(normal, data1); } } else { assert(FALSE); // retexture at runtime causes crash for (j = 0; j < snakeWidth; j++) { thisIndex = polyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); MakePolygonTextureChange (tmdAddress, thisIndex, data1, sectionID); thisIndex = oppositePolyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); MakePolygonTextureChange (tmdAddress, thisIndex, data1, sectionID); } } // now update polyIndex for next section alteration switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); oppositePolyIndex = polyIndex + (NumberShapesPerSection/2); if (oppositePolyIndex >= NumberShapesPerSection) oppositePolyIndex -= NumberShapesPerSection; assert(oppositePolyIndex >= 0); assert(oppositePolyIndex < NumberShapesPerSection); } } void ResetTextureDataFromTwoQuartersSnakeOscillation (void) { int polyIndex, otherPolyIndex, baseSection, sectionID, snakeLength, data1, i, j; u_long tmdAddress; int snakeDirection, normalFlag, thisIndex, snakeWidth; assert(NumberShapesPerSection > 0); assert(NumberShapesPerSection < MAX_NUMBER_SHAPES_PER_SECTION); snakeWidth = NumberShapesPerSection / 4; polyIndex = PreviousSnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); // get poly opposite for second snake otherPolyIndex = polyIndex + (NumberShapesPerSection/2); if (otherPolyIndex >= NumberShapesPerSection) { otherPolyIndex -= NumberShapesPerSection; } assert(otherPolyIndex >= 0); assert(otherPolyIndex < NumberShapesPerSection); baseSection = SnakePreviousBaseSection; snakeLength = SnakePreviousLength; data1 = TheTunnelDescription.highlightData1; snakeDirection = PreviousSnakeDirection; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < snakeLength; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { for (j = 0; j < snakeWidth; j++) { thisIndex = polyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); // restoration TMD_setNorm0( (u_long*) tmdAddress, (u_char) thisIndex, GeneralSnakeNormalStore[(j*2)][i].nx, GeneralSnakeNormalStore[(j*2)][i].ny, GeneralSnakeNormalStore[(j*2)][i].nz); thisIndex = otherPolyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); TMD_setNorm0( (u_long*) tmdAddress, (u_char) thisIndex, GeneralSnakeNormalStore[((j*2)+1)][i].nx, GeneralSnakeNormalStore[((j*2)+1)][i].ny, GeneralSnakeNormalStore[((j*2)+1)][i].nz); } } else { assert(FALSE); // retexture at runtime causes crash for (j = 0; j < snakeWidth; j++) { thisIndex = polyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); RevertPolygonTextureChange (tmdAddress, thisIndex, sectionID); thisIndex = otherPolyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); RevertPolygonTextureChange (tmdAddress, thisIndex, sectionID); } } // now update polyIndex for next section switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); otherPolyIndex = polyIndex + (NumberShapesPerSection/2); if (otherPolyIndex >= NumberShapesPerSection) { otherPolyIndex -= NumberShapesPerSection; } assert(otherPolyIndex >= 0); assert(otherPolyIndex < NumberShapesPerSection); } } void HandleSnakeGrowingAndShrinking (void) { int time, cycleRatio, speed; int polyIndex, baseSection, sectionID, snakeLength, data1, i, j; u_long tmdAddress; TMD_NORM *normal; int snakeDirection, normalFlag, oppositePolyIndex, thisIndex; snakeDirection = GetSnakeDirection(); assert(SnakePeriod > 0); time = frameNumber % SnakePeriod; cycleRatio = (time * ONE) / SnakePeriod; if (cycleRatio < 2048) { speed = SnakeMaxSpeed - ((SnakeMaxSpeed*cycleRatio)/1024); SnakeWidth = 1 + ((((NumberShapesPerSection/2)-1) * cycleRatio) / 2048); } else { speed = -SnakeMaxSpeed + ((SnakeMaxSpeed*(cycleRatio-2048))/1024); SnakeWidth = (NumberShapesPerSection/2) - ((((NumberShapesPerSection/2)-1) * (cycleRatio-2048)) / 2048); } assert(SnakeWidth >= 1); assert(SnakeWidth <= (NumberShapesPerSection/2)); PreviousSnakeWidth = SnakeWidth; SnakeBaseSectionPolygonIndexCounter += speed; assert(NumberShapesPerSection > 0); assert(NumberShapesPerSection < MAX_NUMBER_SHAPES_PER_SECTION); if (SnakeBaseSectionPolygonIndexCounter < 0) { SnakeBaseSectionPolygonIndexCounter += (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter >= 0); } if (SnakeBaseSectionPolygonIndexCounter >= (ONE * NumberShapesPerSection)) { SnakeBaseSectionPolygonIndexCounter -= (ONE * NumberShapesPerSection); assert(SnakeBaseSectionPolygonIndexCounter < (ONE * NumberShapesPerSection)); } PreviousSnakeBaseSectionPolygonIndexCounter = SnakeBaseSectionPolygonIndexCounter; polyIndex = SnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); oppositePolyIndex = polyIndex + (NumberShapesPerSection/2); if (oppositePolyIndex >= NumberShapesPerSection) { oppositePolyIndex -= NumberShapesPerSection; } assert(oppositePolyIndex >= 0); assert(oppositePolyIndex < NumberShapesPerSection); //assert(ValidID(StartDrawingSectionId)); if (ValidID(StartDrawingSectionId) == 0) { return; } baseSection = StartDrawingSectionId; SnakePreviousBaseSection = baseSection; snakeLength = TheTunnelDescription.numberSectionsDrawn; SnakePreviousLength = snakeLength; data1 = TheTunnelDescription.highlightData1; PreviousSnakeDirection = snakeDirection; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < snakeLength; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); // firstly, store then change normal tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { for (j = 0; j < SnakeWidth; j++) { thisIndex = polyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); normal = TMD_getNormAddr( (u_long*) tmdAddress, (u_char) thisIndex); setNORMAL( &GeneralSnakeNormalStore[(j*2)][i], normal->nx, normal->ny, normal->nz); MakePolygonNormalChange(normal, data1); thisIndex = oppositePolyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); normal = TMD_getNormAddr( (u_long*) tmdAddress, (u_char) thisIndex); setNORMAL( &GeneralSnakeNormalStore[((j*2)+1)][i], normal->nx, normal->ny, normal->nz); MakePolygonNormalChange(normal, data1); } } else { assert(FALSE); // retexture at runtime causes crash for (j = 0; j < SnakeWidth; j++) { thisIndex = polyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); MakePolygonTextureChange (tmdAddress, thisIndex, data1, sectionID); thisIndex = oppositePolyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); MakePolygonTextureChange (tmdAddress, thisIndex, data1, sectionID); } } // now update polyIndex for next section alteration switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); oppositePolyIndex = polyIndex + (NumberShapesPerSection/2); if (oppositePolyIndex >= NumberShapesPerSection) oppositePolyIndex -= NumberShapesPerSection; assert(oppositePolyIndex >= 0); assert(oppositePolyIndex < NumberShapesPerSection); } } void ResetTextureDataFromSnakeGrowingAndShrinking (void) { int polyIndex, baseSection, sectionID, snakeLength, data1, i, j; u_long tmdAddress; int snakeDirection, normalFlag, otherPolyIndex, thisIndex; snakeDirection = GetSnakeDirection(); SnakeWidth = PreviousSnakeWidth; assert(SnakeWidth >= 1); assert(SnakeWidth <= (NumberShapesPerSection/2)); polyIndex = PreviousSnakeBaseSectionPolygonIndexCounter >> 12; assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); // get poly opposite for second snake otherPolyIndex = polyIndex + (NumberShapesPerSection/2); if (otherPolyIndex >= NumberShapesPerSection) { otherPolyIndex -= NumberShapesPerSection; } assert(otherPolyIndex >= 0); assert(otherPolyIndex < NumberShapesPerSection); baseSection = SnakePreviousBaseSection; snakeLength = SnakePreviousLength; data1 = TheTunnelDescription.highlightData1; snakeDirection = PreviousSnakeDirection; normalFlag = PolygonEffectUsesNormal(data1); assert(ValidID(baseSection)); for (i = 0; i < snakeLength; i++) { sectionID = GetRelativeSection(baseSection, i, snakeDirection); // firstly, store then change normal tmdAddress = START_OF_CREATED_TMDS_STACK + (sectionID * SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); assert(tmdAddress >= START_OF_CREATED_TMDS_STACK); assert(tmdAddress < END_OF_CREATED_TMDS_STACK); if (normalFlag == TRUE) { for (j = 0; j < SnakeWidth; j++) { thisIndex = polyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); // restoration TMD_setNorm0( (u_long*) tmdAddress, (u_char) thisIndex, GeneralSnakeNormalStore[(j*2)][i].nx, GeneralSnakeNormalStore[(j*2)][i].ny, GeneralSnakeNormalStore[(j*2)][i].nz); thisIndex = otherPolyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); TMD_setNorm0( (u_long*) tmdAddress, (u_char) thisIndex, GeneralSnakeNormalStore[((j*2)+1)][i].nx, GeneralSnakeNormalStore[((j*2)+1)][i].ny, GeneralSnakeNormalStore[((j*2)+1)][i].nz); } } else { assert(FALSE); // retexture at runtime causes crash for (j = 0; j < SnakeWidth; j++) { thisIndex = polyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); RevertPolygonTextureChange (tmdAddress, thisIndex, sectionID); thisIndex = otherPolyIndex + j; if (thisIndex >= NumberShapesPerSection) thisIndex -= NumberShapesPerSection; assert(thisIndex >= 0); assert(thisIndex < NumberShapesPerSection); RevertPolygonTextureChange (tmdAddress, thisIndex, sectionID); } } // now update polyIndex for next section alteration switch(SnakeTwistingDirection) { case CLOCKWISE: polyIndex++; if (polyIndex >= NumberShapesPerSection) { polyIndex -= NumberShapesPerSection; } break; case ANTICLOCKWISE: polyIndex--; if (polyIndex < 0) { polyIndex += NumberShapesPerSection; } break; default: assert(FALSE); } assert(polyIndex >= 0); assert(polyIndex < NumberShapesPerSection); otherPolyIndex = polyIndex + (NumberShapesPerSection/2); if (otherPolyIndex >= NumberShapesPerSection) otherPolyIndex -= NumberShapesPerSection; assert(otherPolyIndex >= 0); assert(otherPolyIndex < NumberShapesPerSection); } } int GetSnakeDirection (void) { int direction, sectionID; int viewSection; sectionID = TheViewShip->tunnelSection; if (ValidID(sectionID) != TRUE) { PRINT("BAD sectionID %d: NOT drawing tunnel\n", sectionID); PRINT("ViewShip %08x\n", (int)(TheViewShip) ); return 0; } assert(ValidID(sectionID)); viewSection = FindViewPointSection(sectionID); if (ValidID(viewSection) != TRUE) { PRINT("BAD section %d\n", viewSection); assert(FALSE); } direction = FindWhichWayViewLooksInSection(viewSection); // snake always forwards or backwards if (direction == VIEW_IS_SIDEWAYS) direction = FORWARDS; return direction; }