Bläddra i källkod

Initial working POC of a simple raycaster engine.

bart 1 år sedan
förälder
incheckning
0eba720aeb
1 ändrade filer med 271 tillägg och 0 borttagningar
  1. 271 0
      BCC/userBDOS/RAYCAST.C

+ 271 - 0
BCC/userBDOS/RAYCAST.C

@@ -0,0 +1,271 @@
+/*
+* Raycaster, based on lodev engine tutorial
+*/
+
+#define word char
+
+#include "LIB/MATH.C"
+#include "LIB/STDLIB.C"
+#include "LIB/SYS.C"
+#include "LIB/GFX.C"
+#include "LIB/FP.C"
+
+#define screenWidth 320
+#define screenHeight 240
+#define mapWidth 24
+#define mapHeight 24
+
+// Colors
+#define COLOR_RED         224
+#define COLOR_DARK_RED    96
+#define COLOR_GREEN       28
+#define COLOR_DARK_GREEN  12
+#define COLOR_BLUE        3
+#define COLOR_DARK_BLUE   2
+#define COLOR_WHITE       0xFF
+#define COLOR_GREY        0xB6
+#define COLOR_YELLOW      0xFC
+#define COLOR_DARK_YELLOW 0x90
+
+#define FB_ADDR 0xD00000
+
+// Framebuffer. fb[Y][X] (bottom right is [239][319])
+char (*fb)[screenWidth] = (char (*)[screenWidth]) FB_ADDR;
+
+word quitGame = 0;
+
+word worldMap[mapWidth][mapHeight]=
+{
+  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1},
+  {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1},
+  {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,5,0,0,0,0,0,0,1},
+  {1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
+};
+
+void RAYFX_drawVertLine(word x, word start, word end, char color)
+{
+  word y = 0;
+  while (y < screenHeight)
+  {
+    if (y >= start && y <= end)
+    {
+      fb[y][x] = color;
+    }
+    else
+    {
+      fb[y][x] = 0;
+    }
+    y++;
+  }
+}
+
+int main() 
+{
+  // clear screen from text
+  GFX_clearWindowtileTable();
+  GFX_clearWindowpaletteTable();
+  GFX_clearBGtileTable();
+  GFX_clearBGpaletteTable();
+
+  // tmp precalculated values for rotation
+  fixed_point_t sinval = FP_StringToFP("-0.0998");
+  fixed_point_t cosval = FP_StringToFP("0.995");
+
+  //x and y start position
+  fixed_point_t posX = FP_intToFP(12);
+  fixed_point_t posY = FP_intToFP(12);
+
+  //initial direction vector
+  fixed_point_t dirX = FP_intToFP(-1);
+  fixed_point_t dirY = 0;
+
+  //the 2d raycaster version of camera plane
+  fixed_point_t planeX = 0;
+  fixed_point_t planeY = FP_StringToFP("0.66");
+
+  fixed_point_t time = 0; //time of current frame
+  fixed_point_t oldTime = 0; //time of previous frame
+
+  while(!quitGame)
+  {
+    word x;
+    for(x = 0; x < screenWidth; x++)
+    {
+      //calculate ray position and direction
+      fixed_point_t cameraX = FP_Div(FP_intToFP(x<<1), FP_intToFP(screenWidth)) - FP_intToFP(1); //x-coordinate in camera space
+      fixed_point_t rayDirX = dirX + FP_Mult(planeX, cameraX);
+      fixed_point_t rayDirY = dirY + FP_Mult(planeY, cameraX);
+
+      //which box of the map we are in
+      word mapX = FP_FPtoInt(posX);
+      word mapY = FP_FPtoInt(posY);
+
+      //length of ray from current position to next x or y-side
+      fixed_point_t sideDistX;
+      fixed_point_t sideDistY;
+
+      //length of ray from one x or y-side to next x or y-side
+      //these are derived as:
+      //deltaDistX = sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
+      //deltaDistY = sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
+      //which can be simplified to abs(|rayDir| / rayDirX) and abs(|rayDir| / rayDirY)
+      //where |rayDir| is the length of the vector (rayDirX, rayDirY). Its length,
+      //unlike (dirX, dirY) is not 1, however this does not matter, only the
+      //ratio between deltaDistX and deltaDistY matters, due to the way the DDA
+      //stepping further below works. So the values can be computed as below.
+      // Division through zero is prevented by setting the result to a very high value
+      fixed_point_t deltaDistX = (rayDirX == 0) ? 1<<30 : MATH_abs( FP_Div(FP_intToFP(1), rayDirX));
+      fixed_point_t deltaDistY = (rayDirY == 0) ? 1<<30 : MATH_abs( FP_Div(FP_intToFP(1), rayDirY));
+
+      fixed_point_t perpWallDist;
+
+      //what direction to step in x or y-direction (either +1 or -1)
+      word stepX;
+      word stepY;
+
+      word hit = 0; //was there a wall hit?
+      word side; //was a NS or a EW wall hit?
+
+      //calculate step and initial sideDist
+      if(rayDirX < 0)
+      {
+        stepX = -1;
+        sideDistX = FP_Mult((posX - FP_intToFP(mapX)), deltaDistX);
+      }
+      else
+      {
+        stepX = 1;
+        sideDistX = FP_Mult((FP_intToFP(mapX + 1) - posX), deltaDistX);
+      }
+      if(rayDirY < 0)
+      {
+        stepY = -1;
+        sideDistY = FP_Mult((posY - FP_intToFP(mapY)), deltaDistY);
+      }
+      else
+      {
+        stepY = 1;
+        sideDistY = FP_Mult((FP_intToFP(mapY + 1) - posY), deltaDistY);
+      }
+
+      //perform DDA
+      while(hit == 0)
+      {
+        //jump to next map square, either in x-direction, or in y-direction
+        if(sideDistX < sideDistY)
+        {
+          sideDistX += deltaDistX;
+          mapX += stepX;
+          side = 0;
+        }
+        else
+        {
+          sideDistY += deltaDistY;
+          mapY += stepY;
+          side = 1;
+        }
+        //Check if ray has hit a wall
+        if(worldMap[mapX][mapY] > 0) hit = 1;
+      }
+
+      //Calculate distance projected on camera direction. This is the shortest distance from the point where the wall is
+      //hit to the camera plane. Euclidean to center camera point would give fisheye effect!
+      //This can be computed as (mapX - posX + (1 - stepX) / 2) / rayDirX for side == 0, or same formula with Y
+      //for size == 1, but can be simplified to the code below thanks to how sideDist and deltaDist are computed:
+      //because they were left scaled to |rayDir|. sideDist is the entire length of the ray above after the multiple
+      //steps, but we subtract deltaDist once because one step more into the wall was taken above.
+      if(side == 0) perpWallDist = (sideDistX - deltaDistX);
+      else          perpWallDist = (sideDistY - deltaDistY);
+
+      //Calculate height of line to draw on screen
+      word lineHeight = FP_FPtoInt(FP_Div(FP_intToFP(screenHeight), perpWallDist));
+
+      //calculate lowest and highest pixel to fill in current stripe
+      word drawStart = - (lineHeight >> 1) + (screenHeight >> 1);
+      if(drawStart < 0) drawStart = 0;
+      word drawEnd = (lineHeight >> 1) + (screenHeight >> 1);
+      if(drawEnd >= screenHeight) drawEnd = screenHeight - 1;
+
+      //choose wall color
+      //give x and y sides different brightness
+      char color;
+      switch(worldMap[mapX][mapY])
+      {
+        case 1:  color = (side == 1) ? COLOR_DARK_RED : COLOR_RED;    break;
+        case 2:  color = (side == 1) ? COLOR_DARK_GREEN : COLOR_GREEN;    break;
+        case 3:  color = (side == 1) ? COLOR_DARK_BLUE : COLOR_BLUE;   break;
+        case 4:  color = (side == 1) ? COLOR_GREY : COLOR_WHITE;  break;
+        default: color = (side == 1) ? COLOR_DARK_YELLOW : COLOR_YELLOW; break;
+      }
+
+      //draw the pixels of the stripe as a vertical line
+      RAYFX_drawVertLine(x, drawStart, drawEnd, color);
+    }
+
+    
+    // TMP go right
+    //both camera direction and camera plane must be rotated
+    fixed_point_t oldDirX = dirX;
+    dirX = FP_Mult(dirX, cosval) - FP_Mult(dirY , sinval);
+    dirY = FP_Mult(oldDirX, sinval) + FP_Mult(dirY, cosval);
+    fixed_point_t oldPlaneX = planeX;
+    planeX = FP_Mult(planeX, cosval) - FP_Mult(planeY, sinval);
+    planeY = FP_Mult(oldPlaneX, sinval) + FP_Mult(planeY, cosval);
+    
+  }
+
+  return 'q';
+}
+
+void interrupt()
+{
+  // handle all interrupts
+  word i = getIntID();
+  switch(i)
+  {
+    case INTID_TIMER1:
+      timer1Value = 1; // notify ending of timer1
+      break;
+
+    case INTID_TIMER2:
+      break;
+
+    case INTID_UART0:
+      break;
+
+    case INTID_GPU:
+      break;
+
+    case INTID_TIMER3:
+      break;
+
+    case INTID_PS2:
+      break;
+
+    case INTID_UART1:
+      break;
+
+    case INTID_UART2:
+      break;
+  }
+}