/* PTUNNEL.C * * S2 The Party '94 64kb intro * -- Polygon tunnel * * Copyright 1995 Petteri Kangaslampi and Jarno Paananen * * This file is part of the MIDAS Sound System, and may only be * used, modified and distributed under the terms of the MIDAS * Sound System license, LICENSE.TXT. By continuing to use, * modify or distribute this file you indicate that you have * read the license and understand and accept it fully. */ /* SLOW! Besides, there appears to be a bug somewhere, which causes the filler to fill a polygon with something like 32767x32767 dimensions completely outside the screen. At least the occasional pauses suggest something like that. */ #include #include #include #include #include #include #include #include "intro.h" #define VZSPEED -8 #define VXSPEED 2 #define VYSPEED 3 #define SCRWIDTH 320 #define SCRHEIGHT 200 //#define ELEMSIZE 256 #define ELEMSIZE 320 #define ELEMANG 36 #define ELEMANG2 (90-ELEMANG) #define ELEMDIST 256 #define VIEWDIST 16 #define MAXDETAIL 1 /* Perspective projection multiplier: */ #define PROJMULT -2*160 /* Aspect ratio: (xx/100) */ #define ASPECTRATIO 83 /* NOTE! ALL 2D ROTATION IS COUNTERCLOCKWISE! */ /****************************************************************************\ * struct Element * -------------- * Description: One tunnel element \****************************************************************************/ typedef struct { int x, y, z; /* world coordinates for element center */ int visible; /* 1 if the element is visible */ int rotation; /* rotation angle (around z axis) */ int color; /* element base color */ int scrX[4]; /* corner screen X-coordinates */ int scrY[4]; /* corner screen Y-coordinates */ } Element; /****************************************************************************\ * struct PolyVertex * ----------------- * Description: One Gouraud-shaded polygon vertex \****************************************************************************/ typedef struct { int x, y; /* vertex coordinates */ int color; /* vertex color, in 8.8 fixed point format */ } PolyVertex; void FillTriangle(PolyVertex *vertices); static Element *tunnel; /* all tunnel elements */ static int numElements; /* number of elements in tunnel */ static int viewX, viewY, viewZ; /* viewer coordinates */ static int vZrot; /* viewer rotation angle about z axis */ static int elemZ; /* z coordinate for next tunnel element */ static int elemColor; /* color for next tunnel element */ static int elemRot; /* rotation for next tunnel element */ unsigned actSeg; /* active screen page segment */ static int movePhase; /* movement phase */ extern uchar ptPalette[]; /* palette for the part */ static int aang1 = 0; static int px, py; #define NUMPHASES 1 /****************************************************************************\ * * Function: int MultDiv(int a, int b, int c) * * Description: Calculates (a*b) / c using 32-bit temporary value * * Input: int a multiplicant * int b multiplier * int c divisor * * Returns: (a * b) / c * \****************************************************************************/ int MultDiv(int a, int b, int c); #pragma aux MultDiv = \ "imul dx" \ "idiv bx" \ parm [ax] [dx] [bx] \ modify exact [ax dx] \ value [ax]; void Add1(int num) { Element *element; element = &tunnel[num]; element->x = iSinMult(aang1 / 2, ELEMSIZE); element->y = iSinMult(aang1, ELEMSIZE); element->z = elemZ; element->rotation = elemRot; element->color = 128 * elemColor; elemZ -= ELEMDIST; elemColor ^= 1; UpdInfo(); if ( (info->pos >= 0x09) ) { aang1 += 16; if ( aang1 > 360 ) aang1 -= 360; } if ( (info->pos >= 0x0B) ) { elemRot += 5; if ( elemRot > 360 ) elemRot -= 360; } } void Move1(void) { viewZ += VZSPEED; } /****************************************************************************\ * * Function: void MoveTunnel(void) * * Description: Moves the tunnel * \****************************************************************************/ void MoveTunnel(void) { int i, vdx, vdy; Move1(); if ( viewZ <= (tunnel[0].z + 8) ) { for ( i = 0; i < (numElements - 1); i++ ) memcpy(&tunnel[i], &tunnel[i+1], sizeof(Element)); if ( (info->pos < 0x10) || ((info->pos <= 0x10) && (info->row < 0x20)) ) { Add1(VIEWDIST-1); } else { if ( numElements > 0 ) numElements--; } } /* * viewX = px + MultDiv(tunnel[0].x - px, ELEMSIZE-i, ELEMSIZE); viewY = py + MultDiv(tunnel[0].y - py, ELEMSIZE-i, ELEMSIZE); } else { i = viewZ - tunnel[0].z; viewX = tunnel[0].x + MultDiv(tunnel[1].x - tunnel[0].x, ELEMSIZE - i, ELEMSIZE); viewY = tunnel[0].y + MultDiv(tunnel[1].y - tunnel[0].y, ELEMSIZE - i, ELEMSIZE); */ if ( (viewZ - tunnel[0].z) >= ELEMSIZE ) { i = (viewZ - tunnel[0].z) - ELEMSIZE; vdx = (px + MultDiv(tunnel[0].x - px, ELEMSIZE-i, ELEMSIZE)) - viewX; vdy = (py + MultDiv(tunnel[0].y - py, ELEMSIZE-i, ELEMSIZE)) - viewY; } else { i = viewZ - tunnel[0].z; vdx = ( tunnel[0].x + MultDiv(tunnel[1].x - tunnel[0].x, ELEMSIZE - i, ELEMSIZE) ) - viewX; vdy = ( tunnel[0].y + MultDiv(tunnel[1].y - tunnel[0].y, ELEMSIZE - i, ELEMSIZE) ) - viewY; px = tunnel[0].x; py = tunnel[0].y; } if ( vdx < -VXSPEED ) vdx = -VXSPEED; if ( vdx > VXSPEED ) vdx = VXSPEED; if ( vdy < -VYSPEED ) vdy = -VYSPEED; if ( vdy > VYSPEED ) vdy = VYSPEED; viewX += vdx; viewY += vdy; } /****************************************************************************\ * * Function: void ProjectTunnel(void) * * Description: Projects tunnel element coordinates to the screen * \****************************************************************************/ void ProjectTunnel(void) { int i; int vx, vy, vz; /* view space coordinates for element center */ int sx, sy; /* screen coordinates for element center */ int srx, sry; /* element radius in screen coords */ int tempX, tempY; int ang; Element *element; for ( i = 0; i < numElements; i++ ) { element = &tunnel[i]; /* Transform element center to view space: */ tempX = element->x - viewX; tempY = element->y - viewY; vx = iCosMult(vZrot, tempX) - iSinMult(vZrot, tempY); vy = iSinMult(vZrot, tempX) + iCosMult(vZrot, tempY); vz = element->z - viewZ; /* Project element only if it's in front of the viewer: */ if ( vz < 0 ) { element->visible = 1; /* Project element center to the screen: */ sx = 160 + MultDiv(PROJMULT, vx, vz); sy = 100 + MultDiv(PROJMULT * ASPECTRATIO / 100, vy, vz); /* Project element radius to the screen: */ srx = MultDiv(PROJMULT, ELEMSIZE, vz); sry = MultDiv(PROJMULT * ASPECTRATIO / 100, ELEMSIZE, vz); /* Calculate the coordinates for all of the corners of the element, using the projected radius and center: */ ang = element->rotation - vZrot; element->scrX[0] = sx - iSinMult(ang + ELEMANG2, srx); element->scrX[1] = sx + iCosMult(ang + ELEMANG, srx); element->scrX[2] = sx + iSinMult(ang + ELEMANG2, srx); element->scrX[3] = sx - iCosMult(ang + ELEMANG, srx); element->scrY[0] = sy - iCosMult(ang + ELEMANG2, sry); element->scrY[1] = sy - iSinMult(ang + ELEMANG, sry); element->scrY[2] = sy + iCosMult(ang + ELEMANG2, sry); element->scrY[3] = sy + iSinMult(ang + ELEMANG, sry); } else element->visible = 0; } } /****************************************************************************\ * * Function: void DrawTunnel(void) * * Description: Draws the tunnel to the screen * \****************************************************************************/ void DrawTunnel(void) { int i; int color1, color2; Element *elem1, *elem2; PolyVertex face[3]; int vnum; int x0, x1, x2, x3; int y0, y1, y2, y3; for ( i = (numElements-1); i > 0; i-- ) { elem1 = &tunnel[i-1]; elem2 = &tunnel[i]; if ( (elem1->visible) && (elem2->visible) ) { color1 = (viewZ - elem1->z) / (ELEMDIST / 4); if ( color1 > 63 ) color1 = 63; if ( color1 < 0 ) color1 = 0; color2 = (viewZ - elem2->z) / (ELEMDIST / 4); if ( color2 > 63 ) color2 = 63; if ( color2 < 0 ) color2 = 0; color1 = (elem1->color << 8) + ((63-color1) << 8); color2 = (elem1->color << 8) + ((63-color2) << 8); for ( vnum = 0; vnum < 4; vnum++ ) { x0 = elem1->scrX[vnum]; y0 = elem1->scrY[vnum]; x1 = elem1->scrX[(vnum+1) & 3]; y1 = elem1->scrY[(vnum+1) & 3]; x2 = elem2->scrX[vnum]; y2 = elem2->scrY[vnum]; x3 = elem2->scrX[(vnum+1) & 3]; y3 = elem2->scrY[(vnum+1) & 3]; if ( (((long) (x1-x0)) * ((long) (y2-y0))) > (((long) (y1-y0)) * ((long) (x2-x0))) ) { face[0].x = x0; face[0].y = y0; face[0].color = color1; face[1].x = x1; face[1].y = y1; face[1].color = color1; face[2].x = x2; face[2].y = y2; face[2].color = color2; FillTriangle(&face[0]); face[0].x = x1; face[0].y = y1; face[0].color = color1; face[1].x = x3; face[1].y = y3; face[1].color = color2; face[2].x = x2; face[2].y = y2; face[2].color = color2; FillTriangle(&face[0]); } color1 ^= 0x4000; color2 ^= 0x4000; } } } } void ClearScreen(unsigned segment); #pragma aux ClearScreen = \ "mov es,ax" \ "mov dx,03C4h" \ "mov ax,0F02h" \ "out dx,ax" \ "xor di,di" \ "mov cx,4000" \ "xor eax,eax" \ "cld" \ "rep stosd" \ parm [ax] \ modify exact [ax cx dx es di]; /****************************************************************************\ * * Function: void PolygonTunnel(void) * * Description: The Polygon tunnel part * \****************************************************************************/ void PolygonTunnel(void) { int error, i; int page = 0; int stop = 0; ulong skipFrames, oldFrameCount; /* allocate memory for tunnel data: */ if ( (error = memAlloc(MAXDETAIL * 2*VIEWDIST * sizeof(Element), (void**) &tunnel)) != OK ) Error(errorMsg[error]); /* set starting position for viewpoint: */ viewX = 0; viewY = 0; viewZ = 32000; /* build initial tunnel: */ elemZ = viewZ - VIEWDIST * ELEMDIST; // elemZ = viewZ - ELEMDIST; elemColor = 0; elemRot = 0; numElements = VIEWDIST; for ( i = 0; i < VIEWDIST; i++ ) Add1(i); /* Set the correct palette: */ SetPalette(&ptPalette[0], 0, 256); actSeg = 0xA000; ClearScreen(actSeg); oldFrameCount = frameCount; movePhase = 0; /* run the tunnel: */ while ( !stop ) { UpdInfo(); if ( (info->pos > 0x11) || ((info->pos == 0x11) && (info->row > 0x3C)) ) stop = 1; scrStart = 16000 * page; WaitFrame(); page ^= 1; actSeg = 0xA000 + (16000/16) * page; skipFrames = frameCount - oldFrameCount; oldFrameCount = frameCount; // skipFrames = 1; while ( skipFrames ) { MoveTunnel(); skipFrames--; } ProjectTunnel(); ClearScreen(actSeg); DrawTunnel(); } /* deallocate tunnel data: */ if ( (error = memFree(tunnel)) != OK ) Error(errorMsg[error]); }