/************************************************************ * * * tunnel.c * * * * * LPGE 1997 * * * * Copyright (C) 1996 Sony Computer Entertainment Inc. * * All Rights Reserved * * * ***********************************************************/ /**************************************************************************** includes ****************************************************************************/ #include "tunnel.h" #include "tunnel2.h" #include "trig.h" #include "camera.h" /**************************************************************************** globals ****************************************************************************/ int NumberOfCreatedTMDs; u_long CurrentTMDStackAddress; int NumberTunnelSections; int TunnelSectionShapeAngle; int TunnelSectionLength; int TunnelOuterRadius; int TunnelMiddleRadius; int TunnelInnerRadius; int NumberShapesPerSection; int TunnelSideAppearanceFlag; int TunnelSectionClippingFlag; int NumberTunnelSectionsDrawn; int NumberTunnelSectionsSubdivided; int StartDrawingSectionId; int EndDrawingSectionId; int StartDrawingSectionBaseShift; int StartSubdividingSectionId; int EndSubdividingSectionId; int TunnelBaseDescription; SVECTOR TunnelSectionRotations[MAX_TUNNEL_SECTIONS]; // starting conditions VECTOR TunnelStartPoint; MATRIX TunnelInitialOrientation; int TunnelTotalDistance; VECTOR TunnelCentrePoint; // average centre point of whole track TunnelSection TheTunnel[MAX_TUNNEL_SECTIONS]; TunnelDescription TheTunnelDescription; /**************************************************************************** macros ****************************************************************************/ #define setNORMAL(normal, x, y, z) \ (normal)->nx = (x), (normal)->ny = (y), (normal)->nz = (z) #define setVERTEX(vertex, x, y, z) \ (vertex)->vx = (x), (vertex)->vy = (y), (vertex)->vz = (z) #define ValidID(id) ( (id >= 0) && (id < NumberTunnelSections) ) #define ValidPolygonID(id) ( (id >= 0) && (id < MAX_POLYGONS) ) #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 max(a,b) ( ((a) > (b)) ? (a) : (b) ) #define min(a,b) ( ((a) < (b)) ? (a) : (b) ) #define abs(x) ( ((x) >= 0) ? (x) : (-(x)) ) /**************************************************************************** local prototypes ****************************************************************************/ void DrawTunnelFromSide (int viewSectionID, TunnelDescription *description, GsOT *ot, int otShift); /**************************************************************************** functions ****************************************************************************/ // clear all tunnel data void InitTunnelToVoid (void) { int i, j; TunnelSection* thisSection; TunnelBaseDescription = -1; NumberTunnelSections = -1; TunnelSectionShapeAngle = -1; TunnelSectionLength = -1; TunnelOuterRadius = -1; // start at world origin with standard orientation setVECTOR( &TunnelStartPoint, 0, 0, 0); InitMatrix( &TunnelInitialOrientation); InitDescription(&TheTunnelDescription); TunnelTotalDistance = -1; for (i = 0; i < MAX_TUNNEL_SECTIONS; i++) { thisSection = &TheTunnel[i]; setVECTOR( &thisSection->splinePoint, 0, 0, 0); setVECTOR( &thisSection->sectionCentre, 0, 0, 0); thisSection->thetaX = 0; thisSection->thetaY = 0; thisSection->thetaZ = 0; thisSection->totalAngle = 0; thisSection->miscFlags = 0; GsInitCoordinate2( WORLD, &thisSection->coordinateData); for (j = 0; j < MAX_NUMBER_SHAPES_PER_SECTION; j++) { setVECTOR( &thisSection->pointsOnCircle[j], 0, 0, 0); } LinkObjectHandlerToSingleTMD( &thisSection->singleHandler, CUBE_MODEL_ADDRESS); thisSection->singleHandler.coord2 = WORLD; setVECTOR( &TunnelSectionRotations[i], 0, 0, 0); } NumberOfCreatedTMDs = 0; CurrentTMDStackAddress = START_OF_CREATED_TMDS_STACK; } void SetBasicTunnelParameters (void) { NumberShapesPerSection = DEFAULT_NUMBER_SHAPES_PER_SECTION; assert(NumberShapesPerSection != 0); // number polygons per section determines angular smoothness TunnelSectionShapeAngle = ONE / NumberShapesPerSection; assert(TunnelSectionShapeAngle < ONE/4); } // working out where the points on the circle are // these will be the vertices of the polygons that // make up the tunnel walls void CalculateCirclePointsOfTunnel (void) { int i, j; VECTOR xAxis, yAxis; int angle; int circleX, circleY; VECTOR worldPosition; GsCOORDINATE2* coord; SVECTOR circleRotation; GsCOORDINATE2 circleCoords; VECTOR *point; TunnelSection *thisSection; for (i = 0; i < NumberTunnelSections; i++) { thisSection = &TheTunnel[i]; coord = &thisSection->coordinateData; setVECTOR( &circleRotation, -thisSection->thetaX/2, -thisSection->thetaY/2, -thisSection->thetaZ/2); assert(thisSection->thetaZ == 0); assert(thisSection->thetaX == 0 || thisSection->thetaY == 0); RotateCoordinateSystem(coord, &circleRotation, &circleCoords); // get the x and y axes of this tunnel section, in world coord terms setVECTOR( &xAxis, circleCoords.coord.m[0][0], circleCoords.coord.m[0][1], circleCoords.coord.m[0][2]); setVECTOR( &yAxis, circleCoords.coord.m[1][0], circleCoords.coord.m[1][1], circleCoords.coord.m[1][2]); // find x and y on circle, know x and y axes, hence get circle points in world for (j = 0; j < NumberShapesPerSection; j++) { angle = j * TunnelSectionShapeAngle; point = &thisSection->pointsOnCircle[j]; circleX = (TunnelOuterRadius * rcos(angle)) >> 12; // x = r.cos(theta) circleY = (TunnelOuterRadius * rsin(angle)) >> 12; // y = r.sin(theta) worldPosition.vx = thisSection->splinePoint.vx + ( ((circleX * xAxis.vx) + (circleY * yAxis.vx)) >> 12); worldPosition.vy = thisSection->splinePoint.vy + ( ((circleX * xAxis.vy) + (circleY * yAxis.vy)) >> 12); worldPosition.vz = thisSection->splinePoint.vz + ( ((circleX * xAxis.vz) + (circleY * yAxis.vz)) >> 12); setVECTOR(point, worldPosition.vx, worldPosition.vy, worldPosition.vz); } } } #if 0 // OLD // from our viewpoint, next spline point on neither x or y axes, // find thetaY and thetaX such that those rotations (order: y then x) // will take us to that point // Note: only approximate // NOTE: rewrite this: can find the angles MUCH more easily // using arcsin and arccos void FindCompoundRotationAngles (int dx, int dy, int dz, int* thetaX, int* thetaY) { int angleX = 0, angleY = 0; int newDX, newDY; GsCOORDINATE2 coord, temp, semiFixed; SVECTOR rotationVector; VECTOR originalPosition, oldPosition, newPosition; // rotate firstly y, then x; i.e. we firstly rotate left/right until point // is roughly in our y axis, then rotate up/down until it is nearly dead ahead assert(dx != 0 && dy != 0); GsInitCoordinate2(WORLD, &coord); GsInitCoordinate2(WORLD, &temp); GsInitCoordinate2(WORLD, &semiFixed); setVECTOR( &originalPosition, dx, dy, dz); //printf("original position:\n"); //dumpVECTOR(&originalPosition); // firstly: rotate left/right until point is close to / nearly on our y axis if (dx > 0) // tilt to the right, i.e. thetaY is -ve { newDX = originalPosition.vx; for (;;) { if (abs(newDX) < X_ROTATION_TOLERANCE_ERROR) // close enough break; angleY -= ANGLE_CHANGE; // very slow to change by 1 setVECTOR( &rotationVector, 0, angleY, 0); GsInitCoordinate2(WORLD, &coord); // start centred DeriveNewCoordSystemFromRotation( &coord, &rotationVector, &temp); CopyCoordinateSystem( &temp, &coord); // find new dx under our tentative rotation ExpressSuperPointInSub (&originalPosition, &coord, &newPosition); newDX = newPosition.vx; //printf("newDX: %d, old DX: %d\n", newDX, originalPosition.vx); assert(newDX <= originalPosition.vx); // must be getting closer } } else // dx < 0, ==> tilt to the left, i.e. thetaY is +ve { newDX = originalPosition.vx; for (;;) { if (abs(newDX) < X_ROTATION_TOLERANCE_ERROR) // close enough break; angleY += ANGLE_CHANGE; setVECTOR( &rotationVector, 0, angleY, 0); GsInitCoordinate2(WORLD, &coord); // start centred DeriveNewCoordSystemFromRotation( &coord, &rotationVector, &temp); CopyCoordinateSystem( &temp, &coord); // find new dx under our tentative rotation ExpressSuperPointInSub (&originalPosition, &coord, &newPosition); newDX = newPosition.vx; //printf("newDX: %d, old DX: %d\n", newDX, originalPosition.vx); assert(newDX >= originalPosition.vx); // must be getting closer } } // now our first rotation is fixed; // semiFixed holds our *thetaY = angleY; //printf("angleY: %d\n", angleY); setVECTOR( &rotationVector, 0, *thetaY, 0); GsInitCoordinate2(WORLD, &coord); // start centred DeriveNewCoordSystemFromRotation( &coord, &rotationVector, &semiFixed); //printf("semiFixed coord:\n"); //dumpCOORD2(&semiFixed); // 'old'position: where point appears to be from our interim state semiFixed, // before we try thetaX rotations ExpressSuperPointInSub (&originalPosition, &semiFixed, &oldPosition); //printf("original position:\n"); //dumpVECTOR(&originalPosition); //printf("oldPosition:\n"); //dumpVECTOR(&oldPosition); // now, after thetaY rotation, point is close to / nearly on our (new) y axis // i.e. rotate up/down if (dy > 0) { newDY = oldPosition.vy; for (;;) { if (abs(newDY) < Y_ROTATION_TOLERANCE_ERROR) // close enough break; angleX += ANGLE_CHANGE; setVECTOR( &rotationVector, angleX, 0, 0); CopyCoordinateSystem( &semiFixed, &coord); // start from just thetaY rotation DeriveNewCoordSystemFromRotation( &coord, &rotationVector, &temp); CopyCoordinateSystem( &temp, &coord); // find new dx under our tentative rotation ExpressSuperPointInSub (&oldPosition, &coord, &newPosition); newDY = newPosition.vy; //printf("newDY: %d, old DY: %d\n", newDY, oldPosition.vy); assert(newDY <= oldPosition.vy); // must be getting closer } } else { newDY = oldPosition.vy; for (;;) { if (abs(newDY) < Y_ROTATION_TOLERANCE_ERROR) // close enough break; angleX -= ANGLE_CHANGE; setVECTOR( &rotationVector, angleX, 0, 0); CopyCoordinateSystem( &semiFixed, &coord); // start from just thetaY rotation DeriveNewCoordSystemFromRotation( &coord, &rotationVector, &temp); CopyCoordinateSystem( &temp, &coord); // find new dx under our tentative rotation ExpressSuperPointInSub (&oldPosition, &coord, &newPosition); newDY = newPosition.vy; //printf("newDY: %d, old DY: %d\n", newDY, oldPosition.vy); assert(newDY >= oldPosition.vy); // must be getting closer } } *thetaX = angleX; //printf("angleX: %d\n", angleX); } #endif // OLD void FindSplineCentralPoints (void) { int i; VECTOR *currentPoint, *nextPoint; for (i = 0; i < NumberTunnelSections; i++) { currentPoint = &TheTunnel[i].splinePoint; if (i == NumberTunnelSections-1) { nextPoint = &TheTunnel[0].splinePoint; // wrap around } else { nextPoint = &TheTunnel[i+1].splinePoint; } setVECTOR( &TheTunnel[i].sectionCentre, currentPoint->vx + ((nextPoint->vx - currentPoint->vx)/2), currentPoint->vy + ((nextPoint->vy - currentPoint->vy)/2), currentPoint->vz + ((nextPoint->vz - currentPoint->vz)/2) ); } } // create the TMDs dynamically: // each one a single textured polygon // As it stands: only fit 8 created-tmds into 1K, ie 128 bytes each, // with 12 shapes/section that's 1.5K per section of track // ie with TMD scratchpad of 320K, that's just over 200 sections of track void CreateTheTunnel2 (void) { int i, j; TunnelSection *thisSection, *nextSection; VECTOR *p1, *p2, *p3, *p4; u_long address; VECTOR *polygonNormal, *sectionCentre; VECTOR polygonCentre; VECTOR normalsToPolygons[MAX_NUMBER_SHAPES_PER_SECTION]; GsDOBJ2 *polygonHandler; for (i = 0; i < NumberTunnelSections; i++) { thisSection = &TheTunnel[i]; if (i == NumberTunnelSections - 1) nextSection = &TheTunnel[0]; else nextSection = &TheTunnel[i+1]; sectionCentre = &thisSection->sectionCentre; for (j = 0; j < NumberShapesPerSection; j++) { polygonNormal = &normalsToPolygons[j]; if (j == NumberShapesPerSection-1) { p1 = &thisSection->pointsOnCircle[j]; p2 = &thisSection->pointsOnCircle[0]; p3 = &nextSection->pointsOnCircle[j]; p4 = &nextSection->pointsOnCircle[0]; } else { p1 = &thisSection->pointsOnCircle[j]; p2 = &thisSection->pointsOnCircle[j+1]; p3 = &nextSection->pointsOnCircle[j]; p4 = &nextSection->pointsOnCircle[j+1]; } // find centre of polygon setVECTOR(&polygonCentre, ((p1->vx + p2->vx + p3->vx + p4->vx) >> 2), ((p1->vy + p2->vy + p3->vy + p4->vy) >> 2), ((p1->vz + p2->vz + p3->vz + p4->vz) >> 2) ); // calculate normals: from centre of poly to centre of section // ==> poly always visible from inside section setVECTOR(polygonNormal, sectionCentre->vx - polygonCentre.vx, sectionCentre->vy - polygonCentre.vy, sectionCentre->vz - polygonCentre.vz); switch(TunnelSideAppearanceFlag) { case VISIBLE_FROM_INSIDE: // do nowt, normals correct as they are break; case VISIBLE_FROM_OUTSIDE: // reverse normals polygonNormal->vx = -polygonNormal->vx; polygonNormal->vy = -polygonNormal->vy; polygonNormal->vz = -polygonNormal->vz; break; default: assert(FALSE); } } address = CurrentTMDStackAddress; assert(address < END_OF_CREATED_TMDS_STACK); CreateWholeSectionTMD ( (u_long*)address, i, NumberShapesPerSection, normalsToPolygons); polygonHandler = &thisSection->singleHandler; LinkObjectHandlerToSingleTMD(polygonHandler, address); NumberOfCreatedTMDs++; CurrentTMDStackAddress += (SIZEOF_SINGLE_POLYGON_TMD * NumberShapesPerSection); } } void CreateWholeSectionTMD (u_long* address, int sectionID, int numberShapesInThisSection, VECTOR *normalsToPolygons) { int i; int nextSectionID; TunnelSection *thisSection, *nextSection; VECTOR *p1, *p2, *p3, *p4; VECTOR vector1, vector2, vector3, vector4, temp; int clutX, clutY; int tPageID, pixelMode, tPageMode; u_char u0, v0, u1, v1, u2, v2, u3, v3; u_char temp1, temp2, temp3, temp4; int imageSystemID; GsIMAGE *image; int twistFlag, baseTwistFlag, flipFlag; RECT *rect; int baseX, baseY; int chunkID, imageID; int tunnelTwistOffset; int widthCompression; int polygonRef; int totalNumberSections; assert(ValidID(sectionID)); assert(numberShapesInThisSection >= 4); assert(numberShapesInThisSection <= MAX_NUMBER_SHAPES_PER_SECTION); assert(normalsToPolygons != NULL); nextSectionID = GetRelativeSection(sectionID, 1, FORWARDS); thisSection = &TheTunnel[sectionID]; nextSection = &TheTunnel[nextSectionID]; TMD_start(address); // sets up TMD header, etc for (i = 0; i < numberShapesInThisSection; i++) { TMD_addPolyFT4(); } TMD_end(); // now specify details of each polygon for (i = 0; i < numberShapesInThisSection; i++) { if (i == NumberShapesPerSection-1) { p1 = &thisSection->pointsOnCircle[i]; p2 = &thisSection->pointsOnCircle[0]; p3 = &nextSection->pointsOnCircle[i]; p4 = &nextSection->pointsOnCircle[0]; } else { p1 = &thisSection->pointsOnCircle[i]; p2 = &thisSection->pointsOnCircle[i+1]; p3 = &nextSection->pointsOnCircle[i]; p4 = &nextSection->pointsOnCircle[i+1]; } setVECTOR( &vector1, p1->vx, p1->vy, p1->vz); setVECTOR( &vector2, p2->vx, p2->vy, p2->vz); setVECTOR( &vector3, p3->vx, p3->vy, p3->vz); setVECTOR( &vector4, p4->vx, p4->vy, p4->vz); switch(TunnelSideAppearanceFlag) // make everything much smaller { // when viewing whole track in one go case VISIBLE_FROM_INSIDE: // leave as it is break; case VISIBLE_FROM_OUTSIDE: // flip entirely: swap v1 with v2, and v3 with v4 setVECTOR( &temp, vector2.vx, vector2.vy, vector2.vz); vector2.vx = vector1.vx; vector2.vy = vector1.vy; vector2.vz = vector1.vz; vector1.vx = temp.vx; vector1.vy = temp.vy; vector1.vz = temp.vz; setVECTOR( &temp, vector3.vx, vector3.vy, vector3.vz); vector3.vx = vector4.vx; vector3.vy = vector4.vy; vector3.vz = vector4.vz; vector4.vx = temp.vx; vector4.vy = temp.vy; vector4.vz = temp.vz; break; default: assert(FALSE); } polygonRef = i; // firstly, get handles for image and offset rectangle switch(TheTunnelDescription.texturingType) { case SINGLE_TUNNEL_TEXTURE: // just grab first in list of images imageID = 0; break; case ONE_TEXTURE_PER_CHUNK: // use chunkID as textureID chunkID = WhichChunkInDescription( &TheTunnelDescription, sectionID); imageID = chunkID; break; case ONE_TEXTURE_PER_N_SECTIONS: assert(TheTunnelDescription.textureNumber != 0); // division by zero imageID = sectionID / TheTunnelDescription.textureNumber; assert(imageID >= 0); assert(imageID < TheTunnelDescription.numberImages); break; case ONE_TEXTURE_PER_N_POLYGONS: if (TheTunnelDescription.textureNumber & FULL_MAP) { tunnelTwistOffset = (TheTunnelDescription.textureNumber >> 8) * sectionID; imageID = polygonRef + tunnelTwistOffset; while (imageID >= TheTunnelDescription.numberImages) imageID -= TheTunnelDescription.numberImages; } else { tunnelTwistOffset = (TheTunnelDescription.textureNumber >> 8) * sectionID; polygonRef += tunnelTwistOffset; while (polygonRef >= NumberShapesPerSection) polygonRef -= NumberShapesPerSection; assert( (TheTunnelDescription.textureNumber & 0xff) != 0); imageID = polygonRef / (TheTunnelDescription.textureNumber & 0xff); } break; case SPREAD_N_TEXTURES_EVENLY: totalNumberSections = NumberSectionsInTunnelDescription( &TheTunnelDescription); assert(totalNumberSections != 0); imageID = (TheTunnelDescription.numberImages * sectionID) / totalNumberSections; break; default: printf("BAD value is %d\n", TheTunnelDescription.texturingType); assert(FALSE); } assert(imageID >= 0); assert(imageID < TheTunnelDescription.numberImages); imageSystemID = TheTunnelDescription.listOfImageIds[imageID]; image = GetImageGivenID(imageSystemID); twistFlag = TheTunnelDescription.listOfTwistFlags[imageID]; rect = &TheTunnelDescription.listOfOffsets[imageID]; clutX = image->cx; clutY = image->cy; switch(image->pmode) { case 2: // 16-bit pixelMode = 16; tPageMode = 2; widthCompression = 1; break; case 8: // 4-bit pixelMode = 4; tPageMode = 0; widthCompression = 4; break; case 9: // 8-bit pixelMode = 8; tPageMode = 1; widthCompression = 2; break; default: PRINT("BAD image pmode\n"); dumpIMAGE(image); assert(FALSE); } GetTexturePage (image, &tPageID, &baseX, &baseY); // order of vertices: topleft, topright, bottomleft, bottomright // when offset rect is <0,0,0,0>, just use the texture as it is if (rect->x == 0 && rect->y == 0 && rect->w == 0 && rect->h == 0) { u0 = baseX; v0 = baseY; u1 = baseX + (image->pw * widthCompression) - 1; v1 = baseY; u2 = baseX; v2 = baseY + image->ph - 1; u3 = baseX + (image->pw * widthCompression) - 1; v3 = baseY + image->ph - 1; } else { u0 = baseX + rect->x; v0 = baseY + rect->y; u1 = baseX + rect->x + rect->w - 1; v1 = baseY + rect->y; u2 = baseX + rect->x; v2 = baseY + rect->y + rect->h - 1; u3 = baseX + rect->x + rect->w - 1; v3 = baseY + rect->y + rect->h - 1; } baseTwistFlag = twistFlag & 0xffff; // twist angle only part flipFlag = twistFlag >> 16; // flipping boolean part if (flipFlag) // swap 1st and 2nd points of texture on VRAM with each other, { // and 3rd and 4th points temp1 = u1; temp2 = v1; u1 = u0; v1 = v0; u0 = temp1; v0 = temp2; temp1 = u3; temp2 = v3; u3 = u2; v3 = v2; u2 = temp1; v2 = temp2; } // accounting for baseTwistFla switch(baseTwistFlag) { case FALSE: // leave them as they are break; case TWIST_NINETY_DEGREES: temp1 = u0; temp2 = v0; temp3 = u1; temp4 = v1; u0 = u2; v0 = v2; u1 = temp1; v1 = temp2; temp1 = u2; temp2 = v2; u2 = u3; v2 = v3; u3 = temp3; v3 = temp4; break; case TWIST_ONE_EIGHTY_DEGREES: temp1 = u0; temp2 = v0; temp3 = u1; temp4 = v1; u0 = u3; v0 = v3; u1 = u2; v1 = v2; u2 = temp3; v2 = temp4; u3 = temp1; v3 = temp2; break; case TWIST_TWO_SEVENTY_DEGREES: temp1 = u0; temp2 = v0; u0 = u1; v0 = v1; u1 = u3; v1 = v3; temp3 = u2; temp4 = v2; u2 = temp1; v2 = temp2; u3 = temp3; v3 = temp4; break; default: PRINT("BAD baseTwistFlag %d\n", baseTwistFlag); PRINT("BAD twistFlag %d\n", twistFlag); //PrintTunnelDescription( &TheTunnelDescription); assert(FALSE); } // specify poly FT4 TMD_setUV4(address, i, u0, v0, u1, v1, u2, v2, u3, v3); TMD_setClut(address, i, (long)clutX, (long)clutY); TMD_setTPage(address, i, (long)tPageMode, 0, (long)image->px, (long)image->py); TMD_setVert0(address, i, vector1.vx, vector1.vy, vector1.vz); TMD_setVert1(address, i, vector2.vx, vector2.vy, vector2.vz); TMD_setVert2(address, i, vector3.vx, vector3.vy, vector3.vz); TMD_setVert3(address, i, vector4.vx, vector4.vy, vector4.vz); TMD_setNorm0(address, i, normalsToPolygons[i].vx, normalsToPolygons[i].vy, normalsToPolygons[i].vz); } } void UpdateTheTunnel (void) { switch(TheTunnelDescription.tunnelLightingEffect) { case FALSE: // do nowt break; case THREE_COLOURED_LIGHTS: // do nowt break; case CYCLE_THREE_LIGHTS_AROUND_AXES: CycleLightsAroundAxes(); break; case CYCLE_THREE_LIGHTS_BRIGHTLY_AROUND_AXES: CycleLightsBrightlyAroundAxes(); break; case CYCLE_LIGHTS_BRIGHTNESS: CycleLightsBrightness(); break; case CYCLE_THREE_LIGHTS_DIRECTION_AND_COLOUR: CycleLightsDirectionAndColour(); break; case THREE_WHITE_LIGHTS: // do nowt break; default: assert(FALSE); } if (TheTunnelDescription.tunnelHighlightFlag == TRUE) { HandleHighlightingForTrack(); } if (TheTunnelDescription.numberClutAnimations > 0) { HandleTunnelClutAnimations(); } if (TheTunnelDescription.numberDrawProcesses > 0) { HandleTunnelDrawProcesses(); } if (TheTunnelDescription.fogEffectFlag != FALSE) { HandleTunnelFogEffect(); } if (TheTunnelDescription.ambientEffectFlag != FALSE) { HandleTunnelAmbientEffect(); } } void CycleLightsAroundAxes (void) { int cyclePoint, theta; assert(TheTunnelDescription.tunnelLightingEffect == CYCLE_THREE_LIGHTS_AROUND_AXES); cyclePoint = (frameNumber % TheTunnelDescription.lightEffectPeriod); assert(TheTunnelDescription.lightEffectPeriod != 0); // div by zero theta = (ONE * cyclePoint) / TheTunnelDescription.lightEffectPeriod; // not very sorted, but will do TheLights[0].vx = rcos(theta); TheLights[0].vy = rsin(theta); GsSetFlatLight(0, &TheLights[0]); TheLights[1].vz = rcos(theta); TheLights[1].vx = rsin(theta); GsSetFlatLight(1, &TheLights[1]); TheLights[2].vy = rcos(theta); TheLights[2].vz = rsin(theta); GsSetFlatLight(2, &TheLights[2]); } void CycleLightsBrightlyAroundAxes (void) { int cyclePoint, theta; assert(TheTunnelDescription.tunnelLightingEffect == CYCLE_THREE_LIGHTS_BRIGHTLY_AROUND_AXES); cyclePoint = (frameNumber % TheTunnelDescription.lightEffectPeriod); assert(TheTunnelDescription.lightEffectPeriod != 0); // div by zero theta = ONE * cyclePoint / TheTunnelDescription.lightEffectPeriod; // not very sorted, but will do TheLights[0].vx = rcos(theta); TheLights[0].vy = rsin(theta); TheLights[0].vz = 0; TheLights[0].r = 255; TheLights[0].g = 0; TheLights[0].b = 0; GsSetFlatLight(0, &TheLights[0]); TheLights[1].vz = rcos(theta); TheLights[1].vx = rsin(theta); TheLights[1].vy = 0; TheLights[1].g = 255; TheLights[1].r = 0; TheLights[1].b = 0; GsSetFlatLight(1, &TheLights[1]); TheLights[2].vy = rcos(theta); TheLights[2].vz = rsin(theta); TheLights[2].vx = 0; TheLights[2].b = 255; TheLights[2].r = 255; TheLights[2].g = 255; GsSetFlatLight(2, &TheLights[2]); } void CycleLightsBrightness (void) { int cyclePoint, brightness; assert(TheTunnelDescription.tunnelLightingEffect == CYCLE_LIGHTS_BRIGHTNESS); cyclePoint = (frameNumber % TheTunnelDescription.lightEffectPeriod); assert(TheTunnelDescription.lightEffectPeriod != 0); // div by zero if (cyclePoint < (TheTunnelDescription.lightEffectPeriod/2)) { brightness = (cyclePoint << 13) / TheTunnelDescription.lightEffectPeriod; } else { cyclePoint = TheTunnelDescription.lightEffectPeriod - cyclePoint; brightness = (cyclePoint << 13) / TheTunnelDescription.lightEffectPeriod; } assert(brightness >= 0); assert(brightness <= ONE); GsSetAmbient(brightness, brightness, brightness); } void CycleLightsDirectionAndColour (void) { int cyclePoint, theta, phase, subTime; int oneThirdTime; assert(TheTunnelDescription.tunnelLightingEffect == CYCLE_THREE_LIGHTS_DIRECTION_AND_COLOUR); cyclePoint = (frameNumber % TheTunnelDescription.lightEffectPeriod); assert(TheTunnelDescription.lightEffectPeriod != 0); // div by zero theta = (cyclePoint << 12) / TheTunnelDescription.lightEffectPeriod; oneThirdTime = TheTunnelDescription.lightEffectPeriod/3; phase = cyclePoint / oneThirdTime; switch(phase) { case 0: subTime = cyclePoint; InitiallyRed.r = 128 - ((subTime * 128) / oneThirdTime); InitiallyRed.g = (subTime * 128) / oneThirdTime; InitiallyRed.b = 0; break; case 1: subTime = cyclePoint - oneThirdTime; InitiallyRed.r = 0; InitiallyRed.g = 128 - ((subTime * 128) / oneThirdTime); InitiallyRed.b = (subTime * 128) / oneThirdTime; break; case 2: subTime = cyclePoint - (2*oneThirdTime); InitiallyRed.r = (subTime * 128) / oneThirdTime; InitiallyRed.g = 0; InitiallyRed.b = 128 - ((subTime * 128) / oneThirdTime); break; default: assert(FALSE); } InitiallyGreen.g = InitiallyRed.r; InitiallyGreen.b = InitiallyRed.g; InitiallyGreen.r = InitiallyRed.b; InitiallyBlue.b = InitiallyRed.r; InitiallyBlue.r = InitiallyRed.g; InitiallyBlue.g = InitiallyRed.b; // not very sorted, but will do TheLights[0].vx = rcos(theta); TheLights[0].vy = rsin(theta); TheLights[0].vz = 0; TheLights[0].r = InitiallyRed.r; TheLights[0].g = InitiallyRed.g; TheLights[0].b = InitiallyRed.b; GsSetFlatLight(0, &TheLights[0]); TheLights[1].vz = rcos(theta); TheLights[1].vx = rsin(theta); TheLights[1].vy = 0; TheLights[1].g = InitiallyGreen.g; TheLights[1].r = InitiallyGreen.r; TheLights[1].b = InitiallyGreen.b; GsSetFlatLight(1, &TheLights[1]); TheLights[2].vx = 0; TheLights[2].vy = rcos(theta); TheLights[2].vz = rsin(theta); TheLights[2].b = InitiallyBlue.b; TheLights[2].g = InitiallyBlue.g; TheLights[2].r = InitiallyBlue.r; GsSetFlatLight(2, &TheLights[2]); } // flesh it out from a single base description void BuildTheTunnel (void) { assert(NumberTunnelSections >= 0); assert(NumberTunnelSections < MAX_TUNNEL_SECTIONS); // used to describe tunnel in one of four ways // now always use section rotations switch (TunnelBaseDescription) { case SPLINE_POINTS: assert(FALSE); // OLD break; case SPLINE_VECTORS: assert(FALSE); // OLD break; case SECTION_ROTATIONS: VerifySectionRotationDescription(); SortTunnelMainDescriptionsFromRotationsList(); break; case SPLINE_POINTS_AND_ROTATIONS: assert(FALSE); // OLD break; default: assert(FALSE); } //PrintTunnelSplinePoints(); //PrintTunnelSplineVectors(); //PrintTunnelSectionRotations(); //assert(TestIfTunnelIsCircular() == TRUE); // thirdly: calculate all other section data FleshOutTunnelFromItsDescriptions(); //PrintTunnelSectionAngles(); // PrintTunnelSplineCurve(); //PrintTunnelDescription( &TheTunnelDescription); } // we have only a tunnel description in terms of rotations; // find the spline vectors, spline points and coordinate systems void SortTunnelMainDescriptionsFromRotationsList (void) { int i; GsCOORDINATE2 coord, temp, circleCoord; // tunnel coord system: moving ship SVECTOR* thisRotation; // one section to the next SVECTOR circleRotation; VECTOR *thisSplinePoint, *nextSplinePoint; // world coords VECTOR thisSplineVector; // from above-former to above-latter, world coords TunnelSection *thisSection; GsInitCoordinate2(WORLD, &coord); GsInitCoordinate2(WORLD, &temp); GsInitCoordinate2(WORLD, &circleCoord); // set the starting point for the entire tunnel setVECTOR( &TheTunnel[0].splinePoint, TunnelStartPoint.vx, TunnelStartPoint.vy, TunnelStartPoint.vz); // sort out the starting matrix: starting orientation CopyMatrix( &TunnelInitialOrientation, &coord.coord); // go through from section to section, // find coord system of next section, for (i = 0; i < NumberTunnelSections; i++) { thisSection = &TheTunnel[i]; thisRotation = &TunnelSectionRotations[i]; thisSection->thetaX = thisRotation->vx; thisSection->thetaY = thisRotation->vy; thisSection->thetaZ = thisRotation->vz; thisSplinePoint = &thisSection->splinePoint; // circle at half-way tilt setVECTOR( &circleRotation, thisRotation->vx / 2, thisRotation->vy / 2, thisRotation->vz / 2); RotateCoordinateSystem (&coord, &circleRotation, &circleCoord); // find next section's coordinate system from rotations // order of rotations is zyx RotateCoordinateSystem (&coord, thisRotation, &temp); CopyCoordinateSystem( &temp, &coord); // copy into Tunnel itself CopyCoordinateSystem( &coord, &thisSection->coordinateData); // the next spline point is TunnelSectionLength away, // directly ahead // i.e. relative vector is always (0, 0, TunnelSectionLength); // here we find this vector in world coord system setVECTOR( &thisSplineVector, ((TunnelSectionLength * coord.coord.m[2][0]) >> 12), ((TunnelSectionLength * coord.coord.m[2][1]) >> 12), ((TunnelSectionLength * coord.coord.m[2][2]) >> 12)); if (i != NumberTunnelSections-1) { nextSplinePoint = &TheTunnel[i+1].splinePoint; // now we know current point plus vector to next point: // hence find next point itself setVECTOR(nextSplinePoint, thisSplinePoint->vx + thisSplineVector.vx, thisSplinePoint->vy + thisSplineVector.vy, thisSplinePoint->vz + thisSplineVector.vz); } thisSection->coordinateData.coord.t[0] = thisSplinePoint->vx; thisSection->coordinateData.coord.t[1] = thisSplinePoint->vy; thisSection->coordinateData.coord.t[2] = thisSplinePoint->vz; thisSection->coordinateData.flg = 0; } } // we have only a tunnel description in terms of rotations; // find the spline vectors, spline points and coordinate systems void CreateSplineListFromRotationList (int numberOfSections, SVECTOR* rotationList, VECTOR* outputSpline, VECTOR* startPoint, MATRIX* initialOrientation, int sectionLength) { int i; GsCOORDINATE2 coord, temp; // tunnel coord system: moving ship SVECTOR* thisRotation; // one section to the next VECTOR *thisSplinePoint, *nextSplinePoint; // world coords VECTOR thisSplineVector; // from former to latter assert(numberOfSections > 1); assert(sectionLength > 1); GsInitCoordinate2(WORLD, &coord); GsInitCoordinate2(WORLD, &temp); // set the starting point for the entire tunnel setVECTOR( &outputSpline[0], startPoint->vx, startPoint->vy, startPoint->vz); // sort out the starting matrix: starting orientation CopyMatrix( initialOrientation, &coord.coord); // go through from section to section, // find coord system of next section for (i = 0; i < numberOfSections; i++) { thisRotation = &rotationList[i]; thisSplinePoint = &outputSpline[i]; // find next section's coordinate system from rotations // order of rotations is Y then X DeriveNewCoordSystemFromRotation (&coord, thisRotation, &temp); CopyCoordinateSystem( &temp, &coord); // the next spline point is sectionLength away, // directly ahead // i.e. relative vector is always (0, 0, sectionLength); // here we find this vector in world coord system setVECTOR( &thisSplineVector, ((sectionLength * coord.coord.m[2][0]) >> 12), ((sectionLength * coord.coord.m[2][1]) >> 12), ((sectionLength * coord.coord.m[2][2]) >> 12)); if (i != numberOfSections-1) { nextSplinePoint = &outputSpline[i+1]; // now we know current point plus vector to next point: // hence find next point itself setVECTOR(nextSplinePoint, thisSplinePoint->vx + thisSplineVector.vx, thisSplinePoint->vy + thisSplineVector.vy, thisSplinePoint->vz + thisSplineVector.vz); } } } void VerifySectionRotationDescription (void) { int i; SVECTOR *thisRotationVector; for (i = 0; i < NumberTunnelSections; i++) { thisRotationVector = &TunnelSectionRotations[i]; // z rotation isn't meaningful in quasi-cylindrical tunnel assert(thisRotationVector->vz == 0); if (i == 0) // first section must be a straight { assert(thisRotationVector->vx == 0); assert(thisRotationVector->vy == 0); } else { if (abs(thisRotationVector->vx) < X_ROTATION_TOLERANCE_ERROR) { // no thetaX assert(abs(thisRotationVector->vy) <= MAXIMUM_PURE_Y_ROTATION); } else if (abs(thisRotationVector->vy) < Y_ROTATION_TOLERANCE_ERROR) { // no thetaY assert(abs(thisRotationVector->vx) <= MAXIMUM_PURE_X_ROTATION); } else { assert(abs(thisRotationVector->vx) <= MAXIMUM_MIXED_X_ROTATION); assert(abs(thisRotationVector->vy) <= MAXIMUM_MIXED_Y_ROTATION); } } } //PRINT("OK\n\n\n"); } int TestIfTunnelIsCircular (void) { TunnelSection* last; VECTOR tunnelStartPoint, relativePosition; last = &TheTunnel[NumberTunnelSections-1]; // looking from last section, see where next point is setVECTOR( &tunnelStartPoint, TheTunnel[0].splinePoint.vx, TheTunnel[0].splinePoint.vy, TheTunnel[0].splinePoint.vz); tunnelStartPoint.vx -= last->splinePoint.vx; tunnelStartPoint.vy -= last->splinePoint.vy; tunnelStartPoint.vz -= last->splinePoint.vz; ExpressSuperPointInSub( &tunnelStartPoint, &last->coordinateData, &relativePosition); // it should be dead ahead if (abs(relativePosition.vx) > SPLINE_POINT_DISTANCE_TOLERANCE) return FALSE; if (abs(relativePosition.vy) > SPLINE_POINT_DISTANCE_TOLERANCE) return FALSE; if (abs(relativePosition.vz - TunnelSectionLength) > SPLINE_POINT_DISTANCE_TOLERANCE) return FALSE; return TRUE; } void PrintTunnelSplinePoints (void) { #if (DEVELOPMENT_ENVIRONMENT==YAROZE) int i; VECTOR *thisSplinePoint; printf("\n\nHere are the tunnel spline points\n"); for (i = 0; i < NumberTunnelSections; i++) { thisSplinePoint = &TheTunnel[i].splinePoint; dumpVECTOR(thisSplinePoint); } printf("\n\n\n"); #endif } void FindSectionSplineVector (int sectionID, VECTOR *splineVector) { int nextSectionID; VECTOR *thisPoint, *nextPoint; assert(ValidID(sectionID)); if (sectionID == NumberTunnelSections-1) nextSectionID = 0; else nextSectionID = sectionID+1; assert(ValidID(nextSectionID)); thisPoint = &TheTunnel[sectionID].splinePoint; nextPoint = &TheTunnel[nextSectionID].splinePoint; setVECTOR( splineVector, nextPoint->vx - thisPoint->vx, nextPoint->vy - thisPoint->vy, nextPoint->vz - thisPoint->vz); } void PrintTunnelSplineVectors (void) { #if (DEVELOPMENT_ENVIRONMENT==YAROZE) int i; VECTOR splineVector; printf("\n\nHere are the tunnel spline vectors\n"); for (i = 0; i < NumberTunnelSections; i++) { FindSectionSplineVector(i, &splineVector); dumpVECTOR( &splineVector); } printf("\n\n\n"); #endif } void PrintTunnelSectionRotations (void) { #if (DEVELOPMENT_ENVIRONMENT==YAROZE) int i; int thetaX, thetaY, thetaZ; printf("\n\nHere are the tunnel rotations\n"); for (i = 0; i < NumberTunnelSections; i++) { thetaX = TheTunnel[i].thetaX; thetaY = TheTunnel[i].thetaY; thetaZ = TheTunnel[i].thetaZ; printf("thetaX %d, thetaY %d, thetaZ %d\n", thetaX, thetaY, thetaZ); } printf("\n\n\n"); #endif } // here, we know spline points and vectors, // rotation angles and coordinate systems, for all sections; // hence we can precalculate all other useful data void FleshOutTunnelFromItsDescriptions (void) { FindSplineCentralPoints(); CalculateCirclePointsOfTunnel(); CalculateEachSectionsTotalAngle(); TunnelTotalDistance = NumberTunnelSections * TunnelSectionLength; FindCentreOfTunnel(); InferSoundToGraphicUseFromTunnelDescription(); } void FindCentreOfTunnel (void) { int i; setVECTOR( &TunnelCentrePoint, 0, 0, 0); for (i = 0; i < NumberTunnelSections; i++) { TunnelCentrePoint.vx += TheTunnel[i].splinePoint.vx; TunnelCentrePoint.vy += TheTunnel[i].splinePoint.vy; TunnelCentrePoint.vz += TheTunnel[i].splinePoint.vz; } TunnelCentrePoint.vx /= NumberTunnelSections; TunnelCentrePoint.vy /= NumberTunnelSections; TunnelCentrePoint.vz /= NumberTunnelSections; } void PutObjectInTunnel (ObjectHandler *object, int sectionID, VECTOR *relativeVector, MATRIX *relativeMatrix) { TunnelSection* section; VECTOR offsetInWorld; VECTOR xAxis, yAxis, zAxis; VECTOR xAxisInWorld, yAxisInWorld, zAxisInWorld; assert(ValidID(sectionID)); assert(relativeVector->vx > -TunnelMiddleRadius); assert(relativeVector->vx < TunnelMiddleRadius); assert(relativeVector->vy > -TunnelMiddleRadius); assert(relativeVector->vy < TunnelMiddleRadius); assert(relativeVector->vz >= 0); assert(relativeVector->vz < TunnelSectionLength); section = &TheTunnel[sectionID]; // sort ship position in world ExpressSubPointInSuper(relativeVector, §ion->coordinateData, &offsetInWorld); setVECTOR( &object->position, section->splinePoint.vx + offsetInWorld.vx, section->splinePoint.vy + offsetInWorld.vy, section->splinePoint.vz + offsetInWorld.vz); // sort ship orientation setVECTOR( &xAxis, relativeMatrix->m[0][0], relativeMatrix->m[1][0], relativeMatrix->m[2][0]); setVECTOR( &yAxis, relativeMatrix->m[0][1], relativeMatrix->m[1][1], relativeMatrix->m[2][1]); setVECTOR( &zAxis, relativeMatrix->m[0][2], relativeMatrix->m[1][2], relativeMatrix->m[2][2]); ExpressSubPointInSuper( &xAxis, §ion->coordinateData, &xAxisInWorld); ExpressSubPointInSuper(&yAxis, §ion->coordinateData, &yAxisInWorld); ExpressSubPointInSuper(&zAxis, §ion->coordinateData, &zAxisInWorld); if (object->modelFlag == TMD_WRONG_WAY) { //printf("reversing now\n"); xAxisInWorld.vx = -xAxisInWorld.vx; xAxisInWorld.vy = -xAxisInWorld.vy; xAxisInWorld.vz = -xAxisInWorld.vz; zAxisInWorld.vx = -zAxisInWorld.vx; zAxisInWorld.vy = -zAxisInWorld.vy; zAxisInWorld.vz = -zAxisInWorld.vz; } object->angle = 0; object->tilt = 0; SortMatrixFromAngle(object); SortCoordFromMatrix(object); object->tunnelSection = sectionID; // STATIONARY when goes into tunnel setVECTOR( &object->velocity, 0, 0, 0); setVECTOR( &object->twist, 0, 0, 0); // fixing it for the 3rd blagger fliers object->startFrame = 1; // untrue, but fixed 3rd blagger's rewrite assert(relativeVector->vx == 0); assert(relativeVector->vy == 0); assert(relativeVector->vz == TunnelSectionLength/2); object->currentPositionIndex = (sectionID * object->speedFactor) + (object->speedFactor/2); } void SortShipAdvancing (ObjectHandler* object, int sectionBefore, int sectionAfter) { int distance, direction; int thisLapIndex; assert(sectionBefore != sectionAfter); FindShortestDistance (sectionBefore, sectionAfter, &distance, &direction); // not allowed to move faster than this //assert(distance <= 1); if (distance >= 2) { printf("sectionBefore %d\n", sectionBefore); printf("sectionAfter %d\n", sectionAfter); printf("distance %d\n", distance); printf("direction %d\n", direction); //assert(FALSE); } // update the ship's current section object->tunnelSection = sectionAfter; if (direction == FORWARDS) { if (sectionBefore == object->furthestSection) // section progress { if (sectionAfter == 0) // lap progress { // record when ship finished this lap thisLapIndex = object->furthestLap+1; assert(thisLapIndex >= 1); assert(thisLapIndex < MAX_LAPS_PER_OBJECT); object->framesWhenLapsEnd[thisLapIndex] = RaceFrameCounter; // could print the lap time for a few secs // onto next lap object->furthestLap++; object->lapProgressFlag = TRUE; #if (RECORDING_FREE_PRACTICE_FLAG==1) { if (MainMenuChoice == FREE_PRACTICE && OverallGameState == DURING_THE_RACE) { if (RecordingThisLapFlag == TRUE) { // record first lap if (object->furthestLap == 1) { PRINT("ending recording\n"); CurrentlyRecordingNowFlag = FALSE; RecordingFrameIndex = 0; } } } } #endif assert(object->furthestLap <= NumberOfLapsInRace); // has this ship finished the race? if (object->furthestLap == NumberOfLapsInRace) { HandleShipFinishingRace(object); } object->furthestSection = 0; } else { object->furthestSection++; object->lapProgressFlag = FALSE; } } object->advancedFlag = TRUE; } else // going backwards, no progress { object->advancedFlag = FALSE; object->lapProgressFlag = FALSE; } } void HandleShipFinishingRace (ObjectHandler* object) { int numberLeft; int i; int endOfRaceFlag = FALSE; object->racingFlag = FALSE; object->displayFlag = FALSE; if (object == &PlayerOnesShip || object == &PlayerTwosShip) { endOfRaceFlag = TRUE; } // how many ships still racing? numberLeft = 0; if (PlayerOnesShip.racingFlag == TRUE) numberLeft++; if (PlayerTwosShip.racingFlag == TRUE) numberLeft++; for (i = 0; i < MAX_OTHER_SHIPS; i++) { if (OtherShips[i].alive == TRUE) { if (OtherShips[i].racingFlag == TRUE) numberLeft++; } } if (numberLeft == 0) { endOfRaceFlag = TRUE; } if (endOfRaceFlag == TRUE) HandleEndOfRace(); } void HandleEndOfRace (void) { EndRaceNowFlag = TRUE; } // only tests 3 sections: main, one before, one ahead int FindSectionPointIsIn (VECTOR* point, int mainGuess) { int section, behindID, forwardID; int behindDistance, forwardDistance, distance; VECTOR* sectionCentre; int x, y, z; assert(ValidID(mainGuess)); sectionCentre = &TheTunnel[mainGuess].sectionCentre; x = sectionCentre->vx - point->vx; y = sectionCentre->vy - point->vy; z = sectionCentre->vz - point->vz; distance = (x * x) + (y * y) + (z * z); section = mainGuess; behindID = GetRelativeSection(mainGuess, 1, BACKWARDS); assert(ValidID(behindID)); sectionCentre = &TheTunnel[behindID].sectionCentre; x = sectionCentre->vx - point->vx; y = sectionCentre->vy - point->vy; z = sectionCentre->vz - point->vz; behindDistance = (x * x) + (y * y) + (z * z); forwardID = GetRelativeSection(mainGuess, 1, FORWARDS); assert(ValidID(forwardID)); sectionCentre = &TheTunnel[forwardID].sectionCentre; x = sectionCentre->vx - point->vx; y = sectionCentre->vy - point->vy; z = sectionCentre->vz - point->vz; forwardDistance = (x * x) + (y * y) + (z * z); if (distance < forwardDistance) // forward further away than current or behind { if (distance <= behindDistance) section = mainGuess; else section = behindID; } else // forward closer than current, must be closest { section = forwardID; } return section; } int FindViewPointSection (int guess) { int viewSection; VECTOR viewPoint, viewVector; assert(ValidID(guess)); FindPositionAndDirectionOfView( &viewPoint, &viewVector); viewSection = FindSectionPointIsIn( &viewPoint, guess); return viewSection; } void FindPositionAndDirectionOfView (VECTOR *position, VECTOR *direction) { switch(ViewTypeFlag) { case REFERENCE_VIEW: { VECTOR vpToVr, vp; GsCOORDINATE2 *coordSystem; setVECTOR( &vpToVr, TheView.vrx - TheView.vpx, TheView.vry - TheView.vpy, TheView.vrz - TheView.vpz); setVECTOR( &vp, TheView.vpx, TheView.vpy, TheView.vpz); coordSystem = TheView.super; if (coordSystem == WORLD) { setVECTOR( position, vp.vx, vp.vy, vp.vz); setVECTOR( direction, vpToVr.vx, vpToVr.vy, vpToVr.vz); } else { VECTOR vP, vR, tmpVp, tmpVr; GsCOORDINATE2 *tempCoord; tempCoord = TheView.super; setVECTOR( &vP, TheView.vpx, TheView.vpy, TheView.vpz); setVECTOR( &vR, TheView.vrx, TheView.vry, TheView.vrz); while (tempCoord != WORLD) { ExpressSubPointInSuper( &vP, tempCoord, &tmpVp); ExpressSubPointInSuper( &vR, tempCoord, &tmpVr); setVECTOR( &vP, tmpVp.vx, tmpVp.vy, tmpVp.vz); vP.vx += tempCoord->coord.t[0]; vP.vy += tempCoord->coord.t[1]; vP.vz += tempCoord->coord.t[2]; setVECTOR( &vR, tmpVr.vx, tmpVr.vy, tmpVr.vz); vR.vx += tempCoord->coord.t[0]; vR.vy += tempCoord->coord.t[1]; vR.vz += tempCoord->coord.t[2]; tempCoord = tempCoord->super; } setVECTOR( position, vP.vx, vP.vy, vP.vz); setVECTOR( direction, vR.vx - vP.vx, vR.vy - vP.vy, vR.vz - vP.vz); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: direction->vx = -direction->vx; break; case Y_Z_PLANE_ONLY: direction->vy = -direction->vy; break; default: assert(FALSE); } } } break; case MATRIX_VIEW: { GsCOORDINATE2 *coordSystem; coordSystem = TheMatrixView.super; if (coordSystem == WORLD) { VECTOR relativeTranslation; setVECTOR( &relativeTranslation, TheMatrixView.view.t[0], TheMatrixView.view.t[1], TheMatrixView.view.t[2]); ExpressSubPointInSuperMatrix( &relativeTranslation, &TheMatrixView.view, position); // UNUSUAL DIRECTION OF MATRIX EXPRESSION setVECTOR(direction, TheMatrixView.view.m[2][0], TheMatrixView.view.m[2][1], TheMatrixView.view.m[2][2]); } else { VECTOR pos1, dir1, pos2, dir2; GsCOORDINATE2 *tempCoord; setVECTOR( &pos2, TheMatrixView.view.t[0], TheMatrixView.view.t[1], TheMatrixView.view.t[2]); ExpressSubPointInSuperMatrix( &pos2, &TheMatrixView.view, &pos1); setVECTOR( &pos1, pos2.vx, pos2.vy, pos2.vz); // UNUSUAL DIRECTION OF MATRIX EXPRESSION setVECTOR( &dir1, TheMatrixView.view.m[2][0], TheMatrixView.view.m[2][1], TheMatrixView.view.m[2][2]); tempCoord = TheMatrixView.super; while(tempCoord != WORLD) { ExpressSubPointInSuper( &pos1, tempCoord, &pos2); pos2.vx += tempCoord->coord.t[0]; pos2.vy += tempCoord->coord.t[1]; pos2.vz += tempCoord->coord.t[2]; ExpressSubPointInSuper( &dir1, tempCoord, &dir2); dir2.vx += tempCoord->coord.t[0]; dir2.vy += tempCoord->coord.t[1]; dir2.vz += tempCoord->coord.t[2]; setVECTOR( &pos1, pos2.vx, pos2.vy, pos2.vz); setVECTOR( &dir1, dir2.vx, dir2.vy, dir2.vz); tempCoord = tempCoord->super; } setVECTOR( position, pos1.vx, pos1.vy, pos1.vz); setVECTOR( direction, dir1.vx, dir1.vy, dir1.vz); } } break; default: assert(FALSE); } } int FindWhichWayViewLooksInSection (int section) { int direction; VECTOR viewDirection, viewPos, relativeDirection; assert(ValidID(section)); FindPositionAndDirectionOfView( &viewPos, &viewDirection); ExpressSuperPointInSub( &viewDirection, &TheTunnel[section].coordinateData, &relativeDirection); #if 1 // simplest: just forwards or backwards if (relativeDirection.vz > 0) direction = FORWARDS; else direction = BACKWARDS; #endif #if 0 // test for sideways also { VECTOR forwards; int dotProduct, sizeOfRelativeDirection; setVECTOR( &forwards, 0, 0, ONE); dotProduct = (relativeDirection.vx * forwards.vx) + (relativeDirection.vy * forwards.vy) + (relativeDirection.vz * forwards.vz); if (dotProduct < 0) dotProduct = -dotProduct; sizeOfRelativeDirection = SizeOfVector2( &relativeDirection); if (dotProduct < ((ONE * sizeOfRelativeDirection) / 3)) { direction = VIEW_IS_SIDEWAYS; } else { if (relativeDirection.vz > 0) direction = FORWARDS; else direction = BACKWARDS; } } #endif return direction; } // FORWARDS or BACKWARDS int FindWhichWayVectorGoesInSection (VECTOR *vector, int sectionID) { int direction; VECTOR relativeVector; assert(ValidID(sectionID)); assert(vector != NULL); ExpressSuperPointInSub(vector, &TheTunnel[sectionID].coordinateData, &relativeVector); if (relativeVector.vz >= 0) direction = FORWARDS; else direction = BACKWARDS; return direction; } int GetRelativeSection (int first, int numberOn, int direction) { int final; assert(ValidID(first)); switch (direction) { case FORWARDS: final = first + numberOn; break; case BACKWARDS: final = first - numberOn; break; default: { printf("BAD value %d at line %d\n", direction, __LINE__); assert(FALSE); } } final = ConvertIdToRealID(final); assert(ValidID(final)); return final; } // wrap into valid list index int ConvertIdToRealID (int id) { int realID = id; while (realID >= NumberTunnelSections) realID -= NumberTunnelSections; while (realID < 0) realID += NumberTunnelSections; assert(ValidID(realID)); return realID; } void FindShortestDistance (int first, int second, int* distance, int* direction) { int simpleDist; int halfDistance = NumberTunnelSections/2; assert(ValidID(first)); assert(ValidID(second)); simpleDist = first - second; if (simpleDist == 0) // same section { *distance = 0; *direction = -1; return; } else if (simpleDist > 0) // first larger { if (simpleDist >= halfDistance) { *distance = NumberTunnelSections - simpleDist; *direction = FORWARDS; } else { *distance = first - second; *direction = BACKWARDS; } } else // second larger { if (-simpleDist >= halfDistance) { *distance = NumberTunnelSections + simpleDist; *direction = BACKWARDS; } else { *distance = -simpleDist; *direction = FORWARDS; } } assert(*distance > 0); assert(*distance <= halfDistance); assert((*direction == FORWARDS) || (*direction == BACKWARDS)); } int SectionBetweenLimits (int sectionID, int start, int end) { int distance, direction; int inBetweenFlag = FALSE; assert(ValidID(sectionID)); assert(ValidID(start)); assert(ValidID(end)); assert(start != end); FindShortestDistance (start, end, &distance, &direction); //assert(distance <= MAX_NUMBER_SECTIONS_DRAWN); if (distance > MAX_NUMBER_SECTIONS_DRAWN) { printf("distance %d\n", distance); printf("direction %d\n", direction); printf("sectionID %d\n", sectionID); printf("start %d\n", start); printf("end %d\n", end); return FALSE; } switch(direction) { case FORWARDS: if (start < end) { if (sectionID >= start && sectionID <= end) inBetweenFlag = TRUE; } else { if (sectionID >= start || sectionID <= end) inBetweenFlag = TRUE; } break; case BACKWARDS: if (start < end) { if (sectionID <= start || sectionID >= end) inBetweenFlag = TRUE; } else { if (sectionID <= start && sectionID >= end) inBetweenFlag = TRUE; } break; default: assert(FALSE); } return inBetweenFlag; } void DrawTheTunnelPolygons (GsOT* ot, int otShift) { register int i; TunnelSection* section; MATRIX matrix; GsCOORDINATE2 world; register GsDOBJ2 *handler = NULL; int sectionID, thisSectionID; int viewSection, viewDirection, viewAntiDirection; int drawBackOffset; int subdivisionBackOffset, subdivisionExtent; int thisSectionSubdivisionFlag; TunnelDescription *description; GsInitCoordinate2( WORLD, &world); InitMatrix( &matrix); GsGetLs(&world, &matrix); GsSetLightMatrix(&matrix); GsSetLsMatrix(&matrix); description = &TheTunnelDescription; switch(TunnelSectionClippingFlag) { case TRUE: sectionID = TheViewShip->tunnelSection; if (ValidID(sectionID) != TRUE) { PRINT("BAD sectionID %d: NOT drawing tunnel\n", sectionID); PRINT("ViewShip %08x\n", (int)(TheViewShip) ); return; } assert(ValidID(sectionID)); viewSection = FindViewPointSection(sectionID); if (ValidID(viewSection) != TRUE) { PRINT("BAD section %d\n", viewSection); assert(FALSE); } viewDirection = FindWhichWayViewLooksInSection(viewSection); #if 0 // not that much better if (viewDirection == VIEW_IS_SIDEWAYS) { DrawTunnelFromSide(viewSection, description, ot, otShift); return; } #endif viewAntiDirection = ANTI_DIRECTION(viewDirection); NumberTunnelSectionsDrawn = description->numberSectionsDrawn; drawBackOffset = description->drawBackOffset; #if 0 // read from track data, each track has its own settings NumberTunnelSectionsSubdivided = description->numberSectionsSubdivided; subdivisionBackOffset = description->subdivisionBackOffset; subdivisionExtent = description->subdivisionExtent; #endif #if 1 // always use defaults NumberTunnelSectionsSubdivided = DEFAULT_NUMBER_SECTIONS_SUBDIVIDED; subdivisionBackOffset = DEFAULT_SUBDIVISION_BACK_OFFSET; subdivisionExtent = DEFAULT_SUBDIVISION_EXTENT; #endif assert(NumberTunnelSectionsDrawn <= MAX_NUMBER_SECTIONS_DRAWN); assert(NumberTunnelSectionsSubdivided <= MAX_NUMBER_SECTIONS_SUBDIVIDED); // allow some camera control functions to set a section shift // eg static flyby StartDrawingSectionId = GetRelativeSection(viewSection, -drawBackOffset, viewDirection); StartDrawingSectionId = GetRelativeSection(StartDrawingSectionId, StartDrawingSectionBaseShift, FORWARDS); EndDrawingSectionId = GetRelativeSection(viewSection, -drawBackOffset + NumberTunnelSectionsDrawn - 1, viewDirection); EndDrawingSectionId = GetRelativeSection(EndDrawingSectionId, StartDrawingSectionBaseShift, FORWARDS); StartSubdividingSectionId = GetRelativeSection(viewSection, -subdivisionBackOffset, viewDirection); StartSubdividingSectionId = GetRelativeSection(StartSubdividingSectionId, StartDrawingSectionBaseShift, FORWARDS); EndSubdividingSectionId = GetRelativeSection(viewSection, -subdivisionBackOffset + NumberTunnelSectionsSubdivided - 1, viewDirection); EndSubdividingSectionId = GetRelativeSection(EndSubdividingSectionId, StartDrawingSectionBaseShift, FORWARDS); for (i = 0; i < NumberTunnelSectionsDrawn; i++) { thisSectionID = GetRelativeSection(StartDrawingSectionId, i, viewDirection); section = &TheTunnel[thisSectionID]; if (SectionBetweenLimits(thisSectionID, StartSubdividingSectionId, EndSubdividingSectionId) == TRUE) { thisSectionSubdivisionFlag = TRUE; } else thisSectionSubdivisionFlag = FALSE; handler = §ion->singleHandler; assert(handler != NULL); if (thisSectionSubdivisionFlag == TRUE) { SetHandlerSubdivision(handler, subdivisionExtent); } GsGetLs(&world, &matrix); GsSetLsMatrix(&matrix); HandleSectionHighlightEffect(section, &matrix); GsSortObject4(handler, ot, otShift, getScratchAddr(0)); if (thisSectionSubdivisionFlag == TRUE) { SetHandlerSubdivision(handler, 0); } } break; case FALSE: StartDrawingSectionId = 0; EndDrawingSectionId = NumberTunnelSections-1; section = &TheTunnel[0]; for (i = 0; i < NumberTunnelSections; i++) { handler = §ion->singleHandler; assert(handler != NULL); GsSortObject4(handler, ot, otShift, getScratchAddr(0)); section++; } break; default: assert(FALSE); } } void DrawTunnelFromSide (int viewSectionID, TunnelDescription *description, GsOT *ot, int otShift) { register int i; TunnelSection* section; MATRIX matrix; GsCOORDINATE2 world; register GsDOBJ2 *handler = NULL; int thisSectionID; int drawBackOffset; int subdivisionBackOffset, subdivisionExtent; int thisSectionSubdivisionFlag; int halfNumSectionsDrawn; assert(ValidID(viewSectionID)); assert(description != NULL); GsInitCoordinate2( WORLD, &world); InitMatrix( &matrix); GsGetLs(&world, &matrix); GsSetLightMatrix(&matrix); GsSetLsMatrix(&matrix); NumberTunnelSectionsDrawn = description->numberSectionsDrawn; drawBackOffset = description->drawBackOffset; #if 0 // read from track data, each track has its own settings NumberTunnelSectionsSubdivided = description->numberSectionsSubdivided; subdivisionBackOffset = description->subdivisionBackOffset; subdivisionExtent = description->subdivisionExtent; #endif #if 1 // always use defaults NumberTunnelSectionsSubdivided = DEFAULT_NUMBER_SECTIONS_SUBDIVIDED; subdivisionBackOffset = DEFAULT_SUBDIVISION_BACK_OFFSET; subdivisionExtent = DEFAULT_SUBDIVISION_EXTENT; #endif assert(NumberTunnelSectionsDrawn <= MAX_NUMBER_SECTIONS_DRAWN); assert(NumberTunnelSectionsSubdivided <= MAX_NUMBER_SECTIONS_SUBDIVIDED); halfNumSectionsDrawn = NumberTunnelSectionsDrawn / 2; // viewDirection: replace with FORWARDS // allow some camera control functions to set a section shift // eg static flyby StartDrawingSectionId = GetRelativeSection(viewSectionID, -drawBackOffset-halfNumSectionsDrawn, FORWARDS); StartDrawingSectionId = GetRelativeSection(StartDrawingSectionId, StartDrawingSectionBaseShift, FORWARDS); EndDrawingSectionId = GetRelativeSection(viewSectionID, -drawBackOffset-halfNumSectionsDrawn + NumberTunnelSectionsDrawn - 1, FORWARDS); EndDrawingSectionId = GetRelativeSection(EndDrawingSectionId, StartDrawingSectionBaseShift, FORWARDS); StartSubdividingSectionId = GetRelativeSection(viewSectionID, -subdivisionBackOffset-halfNumSectionsDrawn, FORWARDS); StartSubdividingSectionId = GetRelativeSection(StartSubdividingSectionId, StartDrawingSectionBaseShift, FORWARDS); EndSubdividingSectionId = GetRelativeSection(viewSectionID, -subdivisionBackOffset-halfNumSectionsDrawn + NumberTunnelSectionsSubdivided - 1, FORWARDS); EndSubdividingSectionId = GetRelativeSection(EndSubdividingSectionId, StartDrawingSectionBaseShift, FORWARDS); for (i = 0; i < NumberTunnelSectionsDrawn; i++) { thisSectionID = GetRelativeSection(StartDrawingSectionId, i, FORWARDS); section = &TheTunnel[thisSectionID]; if (SectionBetweenLimits(thisSectionID, StartSubdividingSectionId, EndSubdividingSectionId) == TRUE) { thisSectionSubdivisionFlag = TRUE; } else thisSectionSubdivisionFlag = FALSE; handler = §ion->singleHandler; assert(handler != NULL); if (thisSectionSubdivisionFlag == TRUE) { SetHandlerSubdivision(handler, subdivisionExtent); } GsGetLs(&world, &matrix); GsSetLsMatrix(&matrix); HandleSectionHighlightEffect(section, &matrix); GsSortObject4(handler, ot, otShift, getScratchAddr(0)); if (thisSectionSubdivisionFlag == TRUE) { SetHandlerSubdivision(handler, 0); } } } void HandleSectionHighlightEffect (TunnelSection *section, MATRIX *lightMatrix) { int flag, i, j; flag = section->miscFlags; // default: multiply all by 4 for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { lightMatrix->m[i][j] <<= 2; } switch(flag) { case 0: // do nowt break; case SECTION_BRIGHTENING: // brighten by doubling for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { lightMatrix->m[i][j] <<= 1; } break; case SECTION_DARKENING: // darken by halving for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { while (abs(lightMatrix->m[i][j]) > 128) { lightMatrix->m[i][j] /= 2; } } break; case SECTION_ACCENTUATE_X: lightMatrix->m[0][0] <<= 2; lightMatrix->m[1][0] <<= 2; lightMatrix->m[2][0] <<= 2; break; case SECTION_ACCENTUATE_Y: lightMatrix->m[0][1] <<= 2; lightMatrix->m[1][1] <<= 2; lightMatrix->m[2][1] <<= 2; break; case SECTION_ACCENTUATE_Z: lightMatrix->m[0][2] <<= 2; lightMatrix->m[1][2] <<= 2; lightMatrix->m[2][2] <<= 2; break; default: assert(FALSE); } // ALWAYS TURN OFF IMMEDIATELY section->miscFlags = 0; GsSetLightMatrix(lightMatrix); } void DetectAndHandleCollisionWithTunnel (ObjectHandler* object, int newSection, VECTOR* oldPos, VECTOR* newPos, VECTOR* worldVelocity) { int collisionFlag = FALSE; VECTOR newWorldPos, *splinePoint; VECTOR relativeNewPos; int effectiveRadius; TunnelSection* section; assert(ValidID(newSection)); section = &TheTunnel[newSection]; splinePoint = §ion->splinePoint; // express both ship positions 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 if (abs(relativeNewPos.vx) >= MAX_VECTOR_ELEMENT_SIZE) { PRINT("relativeNewPos.vx %d\n", (int) relativeNewPos.vx); PRINT("MAX_VECTOR_ELEMENT_SIZE %d\n", MAX_VECTOR_ELEMENT_SIZE); assert(FALSE); } if (abs(relativeNewPos.vy) >= MAX_VECTOR_ELEMENT_SIZE) { PRINT("relativeNewPos.vy %d\n", (int) relativeNewPos.vy); PRINT("MAX_VECTOR_ELEMENT_SIZE %d\n", MAX_VECTOR_ELEMENT_SIZE); assert(FALSE); } // 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; } if (collisionFlag == TRUE) { HandleTheCollision4(object, section, oldPos, worldVelocity); } else { HandleLackOfCollision(object, newPos); } } void HandleLackOfCollision (ObjectHandler* object, VECTOR* newPosition) { // just set new position setVECTOR (&object->position, newPosition->vx, newPosition->vy, newPosition->vz); object->matrix.t[0] = object->position.vx; object->matrix.t[1] = object->position.vy; object->matrix.t[2] = object->position.vz; } // crude collision handler: stop ship dead, stop twist; // optional reposition in centre of tunnel; // leave it pointing the same way as before void HandleTheCollision3 (ObjectHandler* object, TunnelSection* section) { setVECTOR( &object->position, section->sectionCentre.vx, section->sectionCentre.vy, section->sectionCentre.vz); setVECTOR( &object->twist, 0, 0, 0); setVECTOR( &object->velocity, 0, 0, 0); object->matrix.t[0] = object->position.vx; object->matrix.t[1] = object->position.vy; object->matrix.t[2] = object->position.vz; } // currently: halve speed each time (used to be 2048) #define SHIP_TUNNEL_COLLISION_SPEED_REDUCTION_FACTOR (3072) // possible addition: spin-knock for disorientation as extra price for collision void HandleTheCollision4 (ObjectHandler *object, TunnelSection *section, VECTOR *oldPos, VECTOR *worldVelocity) { VECTOR velocityInTunnel, positionInTunnel, oldPosCopy; int position, delta; int effectiveRadius; int speed; int newDelta; int extraDelta; ExpressSuperPointInSub( worldVelocity, §ion->coordinateData, &velocityInTunnel); setVECTOR( &oldPosCopy, oldPos->vx, oldPos->vy, oldPos->vz); oldPosCopy.vx -= section->splinePoint.vx; oldPosCopy.vy -= section->splinePoint.vy; oldPosCopy.vz -= section->splinePoint.vz; ExpressSuperPointInSub( &oldPosCopy, §ion->coordinateData, &positionInTunnel); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: delta = velocityInTunnel.vx; position = positionInTunnel.vx; break; case Y_Z_PLANE_ONLY: delta = velocityInTunnel.vy; position = positionInTunnel.vy; break; default: assert(FALSE); } effectiveRadius = TunnelMiddleRadius - object->collisionRadius; // if delta is wrong sign, force it to be right sign if (position >= 0) if (delta < 0) delta = -delta; else if (delta > 0) delta = -delta; speed = abs(object->velocity.vz); assert(object->maximumSpeed != 0); newDelta = (-delta * TunnelMiddleRadius) / object->maximumSpeed; // moving forcibly away from tunnel edge extraDelta = -position; while( (abs(extraDelta)) > 40) extraDelta >>= 1; newDelta += extraDelta; switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: setVECTOR( &positionInTunnel, positionInTunnel.vx + newDelta, positionInTunnel.vy, positionInTunnel.vz); break; case Y_Z_PLANE_ONLY: setVECTOR( &positionInTunnel, positionInTunnel.vx, positionInTunnel.vy + newDelta, positionInTunnel.vz); break; default: assert(FALSE); } ExpressSubPointInSuper( &positionInTunnel, §ion->coordinateData, &object->position); object->position.vx += section->splinePoint.vx; object->position.vy += section->splinePoint.vy; object->position.vz += section->splinePoint.vz; object->matrix.t[0] = object->position.vx; object->matrix.t[1] = object->position.vy; object->matrix.t[2] = object->position.vz; object->velocity.vx = (object->velocity.vx * SHIP_TUNNEL_COLLISION_SPEED_REDUCTION_FACTOR) >> 12; object->velocity.vy = (object->velocity.vy * SHIP_TUNNEL_COLLISION_SPEED_REDUCTION_FACTOR) >> 12; object->velocity.vz = (object->velocity.vz * SHIP_TUNNEL_COLLISION_SPEED_REDUCTION_FACTOR) >> 12; }