/**************** ACK-3D ( Animation Construction Kit 3D ) *******************/ /* Engine Code */ /* Author: Lary Myers */ #include #include #include #include #include #include #include #include #include #include #include "ack3d.h" #include "ackext.h" /**************************************************************************** ** ** ****************************************************************************/ char xRay(int x,int y,int angle) { char Color; int i,j,mx,my; int TablePosn; int MapPosn,CurPosn; int xBeg; long xPos,xNext; int BitmapColumn; int xCenter,yCenter,xAdj; int ObjPosn; int oBegX,oBegY; long yPos; long yNext; long xd,yd,sy; long ObjDist; xBeg = x & 0xFFC0; /* Get upper left corner of square */ yNext = yNextTable[angle]; /* PreCalc'd value of 64 * Tan(angle) */ if (angle > INT_ANGLE_270 || angle < INT_ANGLE_90) { xPos = xBeg + GRID_SIZE; /* Looking to the right */ xNext = GRID_SIZE; /* Positive direction */ } else { xPos = xBeg; /* Looking to the left */ xNext = -GRID_SIZE; /* Negative direction */ yNext = -yNext; } /* Calculate the Y coordinate for the current square */ yPos = (((long)xPos - (long)x) * LongTanTable[angle]) + ((long)y << FP_SHIFT); while (1) { if (xPos < 0 || xPos > GRID_XMAX || yPos < 0 || yPos > GRID_YMAXLONG) break; /************** Fixed point Y/64 * 64 X / 64 ***********/ MapPosn = ((yPos >> FP_SHIFT) & 0xFFC0) + (xPos >> 6); /* First check for an object that has not been seen yet */ if ((Color = xObjGrid[MapPosn]) > 0 && !(ObjFlags[Color] & 3)) { ObjFlags[Color] |= 1; /* Indicate object has been seen */ xd = ObjList[Color].x - x; yd = ObjList[Color].y - y; ObjDist = (xd * xd) + (yd * yd); /* Calc relative distance */ /* Place the object in the array according to distance, with the ones closer */ /* being drawn last. This will allow closer objects to hide farther objects */ j = TotalObjects; for (i = 0; i < TotalObjects; i++) { if (ObjDist > ObjRelDist[i]) { for (j = TotalObjects; j > i; j--) { ObjRelDist[j] = ObjRelDist[j-1]; ObjNumber[j] = ObjNumber[j-1]; ObjMapPosn[j] = ObjMapPosn[j-1]; } j = i; i = TotalObjects; } } /* Hold onto relavant data for the object found */ ObjNumber[j] = Color; ObjRelDist[j] = ObjDist; ObjMapPosn[j] = MapPosn; TotalObjects++; ObjRelDist[TotalObjects] = 3200000; } /* Now check to see if a wall is being struck by the ray */ if ((Color = xGrid[MapPosn]) != 0) { xMapPosn = MapPosn; /* Hold onto the map location */ iLastX = xPos; LastY1 = yPos; if (Color == DOOR_XCODE) /* Is this a door? */ { yd = (yPos >> FP_SHIFT) & 0xFFC0; /* Get the left side */ xd = yd + GRID_SIZE; /* And the right side */ ObjDist = (yPos + (yNext >> 1)) >> FP_SHIFT; /* Calc door distance */ if (ObjDist < yd || ObjDist > xd) /* Is door visible? */ { xPos += xNext; /* Nope, continue casting */ yPos += yNext; /* the ray as before */ continue; } LastY1 = yPos + (yNext >> 1); /* Adjust the X,Y values so */ iLastX += (xNext >> 1); /* the door is halfway in sq. */ } if (Color == DOOR_SECRETCODE) { if (xSecretColumn != 0) { sy = xSecretColumn * LongTanTable[angle]; ObjDist = (yPos + sy) >> FP_SHIFT; yd = (yPos >> FP_SHIFT) & 0xFFC0; /* Get the left side */ xd = yd + GRID_SIZE; /* And the right side */ if (ObjDist < yd || ObjDist > xd) /* Is door visible? */ { xPos += xNext; /* Nope, continue casting */ yPos += yNext; /* the ray as before */ continue; } LastY1 = yPos + sy; iLastX += xSecretColumn; } } return(Color); } xPos += xNext; /* Next X coordinate (fixed at 64 or -64) */ yPos += yNext; /* Next calculated Y coord for a delta of X */ } return(0); /* Return that no wall was found */ } /**************************************************************************** ** ** ****************************************************************************/ char yRay(int x,int y,int angle) { char Color; int i,j,mx,my; int MapPosn; int yBeg; long yPos,yNext; int BitmapColumn; int xCenter,yCenter,yAdj; int ObjPosn; int oBegX; long xPos; long xNext; long xd,yd,ObjDist,sx; yBeg = y & 0xFFC0; /* Same as div 64 then mul 64 */ xNext = xNextTable[angle]; /* Pre-calc'd value of 64 / tan(angle) */ if (angle < INT_ANGLE_180) { yPos = yBeg + GRID_SIZE; /* Looking down */ yNext = GRID_SIZE; /* Positive direction */ } else { yPos = yBeg; /* Looking up */ yNext = -GRID_SIZE; /* Negative direction */ xNext = -xNext; } /* Calculate the X coordinate for the current square */ xPos = (((long)yPos - (long)y) * LongInvTanTable[angle]) + ((long)x << FP_SHIFT); oBegX = 0; while (1) { if (xPos < 0 || xPos > GRID_XMAXLONG || yPos < 0 || yPos > GRID_YMAX) break; /*********** Y/64 * 64 Fixed point and /64 ******/ MapPosn = (yPos & 0xFFC0) + (xPos >> (FP_SHIFT+6)); /* Check for unseen objects first */ if ((Color = yObjGrid[MapPosn]) > 0 && !(ObjFlags[Color] & 3)) { ObjFlags[Color] |= 2; /* Signal object seen on Y cast */ xd = ObjList[Color].x - x; yd = ObjList[Color].y - y; yd = (xd * xd) + (yd * yd); /* Calc relative distance to object */ /* Place the object in the array according to distance, with the ones closer */ /* being drawn last. This will allow closer objects to hide farther objects */ j = TotalObjects; for (i = 0; i < TotalObjects; i++) { if (yd > ObjRelDist[i]) { for (j = TotalObjects; j > i; j--) { ObjRelDist[j] = ObjRelDist[j-1]; ObjNumber[j] = ObjNumber[j-1]; ObjMapPosn[j] = ObjMapPosn[j-1]; } j = i; i = TotalObjects; } } ObjNumber[j] = Color; /* Hold onto object data for later */ ObjRelDist[j] = yd; ObjMapPosn[j] = MapPosn; TotalObjects++; ObjRelDist[TotalObjects] = 3200000; } /** Check for a wall being struck **/ if ((Color = yGrid[MapPosn]) != 0) { yMapPosn = MapPosn; /* Hold onto map position */ LastX1 = xPos; iLastY = yPos; if (Color == DOOR_YCODE) /* Is this a door? */ { yd = (xPos >> FP_SHIFT) & 0xFFC0; /* Calc top side of square */ xd = yd + GRID_SIZE; /* And bottom side of square */ ObjDist = (xPos + (xNext >> 1)) >> FP_SHIFT; if (ObjDist < yd || ObjDist > xd) /* Is door visible? */ { xPos += xNext; /* No, continue on with ray cast */ yPos += yNext; continue; } LastX1 = xPos + (xNext >> 1); /* Adjust coordinates so door is */ iLastY += (yNext >> 1); /* Halfway into wall */ } if (Color == DOOR_SECRETCODE) { if (ySecretColumn != 0) { sx = ySecretColumn * LongInvTanTable[angle]; ObjDist = (xPos + sx) >> FP_SHIFT; yd = (xPos >> FP_SHIFT) & 0xFFC0; /* Get the top side */ xd = yd + GRID_SIZE; /* And the bottom side */ if (ObjDist < yd || ObjDist > xd) /* Is door visible? */ { xPos += xNext; /* Nope, continue casting */ yPos += yNext; /* the ray as before */ continue; } LastX1 = xPos + sx; iLastY += ySecretColumn; } } return(Color); } xPos += xNext; /* Next calculated X value for delta Y */ yPos += yNext; /* Next fixed value of 64 or -64 */ if (++oBegX > 64) /* Check a maximum number of squares */ break; } return(0); /* Return here if no Y wall is found */ } /**************************************************************************** ** This is the heart of the engine. This routine will cast two rays for ** ** each column of the screen. The first ray will look for any walls that ** ** fall on the X boundaries (vertical walls). The second ray will look for ** ** walls on the Y boundaries (horizontal walls). The wall with the shorter ** ** distance will be used to draw a sliver of the bitmap at the current ** ** screen column (0-319). A 60 degree field of view was selected to give ** ** a realistic perspective to the walls. ** ** ** ****************************************************************************/ void DrawView(int xPlayer,int yPlayer,int PlayerAngle) { int i,j,index; unsigned char xBitmap,yBitmap,BitmapNumber; int ViewAngle; long xDistance,yDistance; long WallDistance; int distance; unsigned int BitmapColumn,yBitmapColumn; int WallCode,OldMapPosn,OldMapPosn1; long xd,yd; unsigned char *Video; unsigned int offset; /* Clear out the object seen flags so objects can be found */ memset(ObjFlags,0,MaxObjects); /* Begin looking 30 degrees to the left of our current angle */ ViewAngle = PlayerAngle - INT_ANGLE_30; if (ViewAngle < 0) ViewAngle += INT_ANGLE_360; /* Reset total objects found on last DrawView pass */ TotalObjects = 0; MaxDistance = 0; /* Cast two rays for each column of the video display */ for (i = 0; i < VIEW_WIDTH; i++) { WallDistance = 3000000; /* Set to a ridiculous distance */ BitmapColumn = -1; /* Set to no walls found */ WallCode = 0; /* Set to no shadowing */ /* Don't even cast an X ray if impossible to intersect the X walls */ if (ViewAngle != INT_ANGLE_90 && ViewAngle != INT_ANGLE_270) { yd = CLOCK_PTR; BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle); if (BitmapNumber) /* A wall was found */ { /* Use the Y intercept to determine the wall column */ BitmapColumn = (LastY1 >> FP_SHIFT) & 0x3F; /* Keep the orientation the same no matter which side we're on */ if ((int)iLastX < xPlayer) BitmapColumn = 63 - BitmapColumn; /* Did we strike a door? */ if (BitmapNumber == DOOR_XCODE) { index = FindDoor(xMapPosn); if (index >= 0) /* This is a valid door */ { j = Door[index].ColOffset; /* Get its current pos */ if ((int)iLastX > xPlayer) /* Handle orientation */ j = -j; BitmapColumn += j; /* Adjust column to show */ if (BitmapColumn > 63) /* Door is opening */ { /* Get the grid coordinates for this door */ OldMapPosn = Door[index].mPos; OldMapPosn1 = Door[index].mPos1; /* Fake the engine into thinking no door is there */ xGrid[OldMapPosn] = 0; xGrid[OldMapPosn1] = 0; /* Cast the ray to get walls beyond the door */ BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle); /* Put back the door codes if not fully open */ if (Door[index].ColOffset < 63) { xGrid[OldMapPosn] = Door[index].mCode; xGrid[OldMapPosn1] = Door[index].mCode1; } /* Calc the new bitmap column of wall behind door */ BitmapColumn = (LastY1 >> FP_SHIFT) & 0x3F; if ((int)iLastX < xPlayer) BitmapColumn = 63 - BitmapColumn; } } } /* Calculate the distance to the wall. Since the X position was */ /* fixed to move 64 or -64 we can use it to determine the actual */ /* wall distance. The InvCosTable values were stored with a fixed */ /* point of 20 decimal places. At this time we'll knock off 14 of */ /* them so we can later multiple with a fixed point value of 16 */ xd = iLastX - xPlayer; WallDistance = (xd * InvCosTable[ViewAngle]) >> 14; /* Still looking for the reason this may occur. But this check */ /* will force the distance to a ridiculous value so no wall is */ /* seen later on when the X and Y walls are compared. */ if (WallDistance < 0) WallDistance = 120000L; /* Walls that are not doors will have shadowing */ if (BitmapNumber < DOOR_XCODE) WallCode = 1; } txRay += CLOCK_PTR - yd; } /* Don't cast a Y ray if its impossible to intercept any Y walls */ if (ViewAngle != 0 && ViewAngle != INT_ANGLE_180) { xd = CLOCK_PTR; yBitmap = yRay(xPlayer,yPlayer,ViewAngle); if (yBitmap) /* A wall was found */ { /* Use the X intercept to determine the column of the bitmap */ yBitmapColumn = (LastX1 >> FP_SHIFT) & 0x3F; /* Handle orientation from either side of the wall */ if ((int)iLastY > yPlayer) yBitmapColumn = 63 - yBitmapColumn; /* Did we strike a door? */ if (yBitmap == DOOR_YCODE) { index = FindDoor(yMapPosn); if (index >= 0) /* This is a valid door */ { /* Get the current door column offset */ j = Door[index].ColOffset; /* Deal with orientation */ if ((int)iLastY < yPlayer) j = -j; yBitmapColumn += j; /* If beyond width of bitmap than cast again */ if (yBitmapColumn > 63) { /* Get the yGrid coordinates for this door */ OldMapPosn = Door[index].mPos; OldMapPosn1 = Door[index].mPos1; /* Fool the engine into thinking no door is there */ yGrid[OldMapPosn] = 0; yGrid[OldMapPosn1] = 0; /* Cast again for walls beyond the door */ yBitmap = yRay(xPlayer,yPlayer,ViewAngle); /* Put door code back if not fully open */ if (Door[index].ColOffset < 63) { yGrid[OldMapPosn] = Door[index].mCode; yGrid[OldMapPosn1] = Door[index].mCode1; } /* Get the bitmap column of wall beyond door */ yBitmapColumn = (LastX1 >> FP_SHIFT) & 0x3F; if ((int)iLastY > yPlayer) yBitmapColumn = 63 - yBitmapColumn; } } } /* Calculate the distance to the wall. Since the Y position was */ /* fixed to move 64 or -64 we can use it to determine the actual */ /* wall distance. The InvSinTable values were stored with a fixed */ /* point of 20 decimal places. At this time we'll knock off 14 of */ /* them so we can later multiple with a fixed point value of 16 */ yd = iLastY - yPlayer; yDistance = (yd * InvSinTable[ViewAngle]) >> 14; /* Don't know the reason but change negative value into ridiculous */ if (yDistance < 0) yDistance = 120000L; /* At this point check the distance to the Y wall against the X */ /* wall to see which one is closer. The closer one is the one */ /* we'll draw at this column of the screen. */ if (yDistance < WallDistance) { WallCode = 0; /* Turn off shadowing if on */ WallDistance = yDistance; BitmapNumber = yBitmap; BitmapColumn = yBitmapColumn; } } tyRay += CLOCK_PTR - xd; } if (BitmapColumn < 64) /* A wall was found (either X or Y) */ { /* To avoid a fishbowl affect we need to adjust the distance so */ /* it appears perpendicular to the center point of the display */ /* which is relative angle 0 from the players current angle. We */ /* started at -30 degrees for the first screen column and will */ /* cycle from -30 down to 0 then back up to +30 degrees. This */ /* cosine value was pre-calculated and placed in ViewCosTable. */ WallDistance *= ViewCosTable[i]; /* Now we strip off somemore decimal points and check round-up */ xd = WallDistance >> 14; if (WallDistance - (xd << 14) >= 8096) xd++; /* The last decimal points from the multiplication after the X and */ /* Y rays is stripped off and checked for round-up. */ WallDistance = xd >> 6; if (xd - (WallDistance << 6) >= 32) WallDistance++; /* Don't really need to, but put it into an integer for fast compare */ distance = WallDistance; /* This is an arbitrary minimum distance to look for */ if (distance < 10) distance = 10; /* Don't want it to go outside our table boundaries */ if (distance >= MAX_DISTANCE) distance = MAX_DISTANCE - 1; /* Save the wall data to display when done with entire screen */ Walls[i].Distance = distance; Walls[i].Number = (BitmapNumber & 0x3F) + WallCode; Walls[i].Column = BitmapColumn; if (distance > MaxDistance) MaxDistance = distance; } ViewAngle++; if (ViewAngle >= INT_ANGLE_360) ViewAngle -= INT_ANGLE_360; } xd = CLOCK_PTR; /* This routine gets the Walls structure and draws all of the walls */ DrawWalls(); tDOW += CLOCK_PTR - xd; /* Now we look at any objects that may be closer than the walls */ FindObject(xPlayer,yPlayer,PlayerAngle); xd = CLOCK_PTR; /* Reset mask to allow all planes then flip to the page we just drew to */ outp(0x3c5,255); flippage(); PageNum ^= 1; usepage(PageNum); tFlip += CLOCK_PTR - xd; } /**************************************************************************** ** This routine is used when the player moves location as well as by the ** ** moveable objects. Its purpose is to determine if a valid obstacle is ** ** in front of the player or object. ** ** ** ****************************************************************************/ int CheckHit(int xPlayer,int yPlayer,int ViewAngle) { int BitmapColumn,BitmapNumber,yBitmap,distance; int i,WallCode; long WallDistance,xd,yd,yDistance; long CheckDist; WallDistance = 3000000; /* Set to a ridiculous value */ WallCode = 3; /* Initialize to strike an object */ CheckDist = 48L; /* Initial minimum distance to look for */ BitmapNumber = 0; /* Initialize to no bitmap found */ /* Clear out any objects seen from previous passes */ memset(ObjFlags,0,MaxObjects); /* Set number of objects seen on this pass */ TotalObjects = 0; /* Don't allow one of these angles, causes either the X or Y ray to not be */ /* cast which gives a false reading about an obstacle. */ if (ViewAngle == INT_ANGLE_45 || ViewAngle == INT_ANGLE_135 || ViewAngle == INT_ANGLE_225 || ViewAngle == INT_ANGLE_315) ViewAngle++; /* Don't cast an X ray if no chance of striking a X wall */ if (ViewAngle != INT_ANGLE_90 && ViewAngle != INT_ANGLE_270) { BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle); if (BitmapNumber) { xd = iLastX - xPlayer; /* Use the delta X to determine the distance to the wall */ WallDistance = (xd * InvCosTable[ViewAngle]) >> 14; if (WallDistance < 0) WallDistance = 120000L; /* Set the wall struck code to an X wall */ WallCode = 1; } } /* Don't cast a Y ray if impossible to strike a Y wall */ if (ViewAngle != 0 && ViewAngle != INT_ANGLE_180) { yBitmap = yRay(xPlayer,yPlayer,ViewAngle); if (yBitmap) { yd = iLastY - yPlayer; /* Use the delta Y to determine distance to the wall */ yDistance = (yd * InvSinTable[ViewAngle]) >> 14; if (yDistance < 0) yDistance = 120000L; /* If Y wall closer than X wall then use Y wall data */ if (yDistance < WallDistance) { WallDistance = yDistance; /* Indicate the wall struck was a Y wall */ WallCode = 2; BitmapNumber = yBitmap; } } } /* Since doors appear in the middle of the wall, adjust the minimum distance */ /* to it. This handles walking up close to a door. */ if (BitmapNumber >= DOOR_XCODE) CheckDist += 34L; if (WallCode) { /* Adjust the distance based on the center of the screen */ WallDistance *= ViewCosTable[160]; /* Remove fixed point and round-up */ xd = WallDistance >> 14; if (WallDistance - (xd << 14) >= 8096) xd++; /* Remove initial fixed point and round-up */ WallDistance = xd >> 6; if (xd - (WallDistance << 6) >= 32) WallDistance++; /* Get minimum distance to any objects between player and wall */ yDistance = CheckObjects(xPlayer,yPlayer,ViewAngle); /* An object was found, if closer than use object as obstacle */ if (yDistance > 0L && yDistance < WallDistance) { /* Return code for an object struck */ WallCode = 3; /* Adjust minimum distance to keep object from getting too close */ CheckDist += 32L; WallDistance = yDistance; } /* If the wall or object is further than the minimum distance, we can */ /* continue moving in this direction. */ if (WallDistance > CheckDist) WallCode = 0; } return(WallCode); }