123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697 |
- /*
- * Raycaster, based on lodev engine tutorial
- * Plan:
- * - Improve collision detection:
- * - During movement function,
- * - Check if new pos has distance from possible wall hor and ver
- * - Make rendering variables global with prefix
- * - Convert rendering function to assembly
- * - Add sprites (as this is done after walls)
- */
- #define word char
- #include "lib/math.c"
- #include "lib/stdlib.c"
- #include "lib/sys.c"
- #include "lib/gfx.c"
- #include "lib/fp.c"
- // Note: these are also hardcoded in the render assembly, so update there as well!
- #define FB_ADDR 0xD00000
- #define screenWidth 160
- #define screenHeight 120
- #define texWidth 64
- #define texHeight 64
- #define mapWidth 24
- #define mapHeight 24
- // Input
- #define BTN_LEFT 256
- #define BTN_RIGHT 257
- #define BTN_UP 258
- #define BTN_DOWN 259
- // 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
- // Data
- /*
- - LUTdirX
- - LUTdirY
- - LUTplaneX
- - LUTplaney
- - texture
- */
- #include "data/raydat.c"
- // Framebuffer. fb[Y][X] (bottom right is [239][319])
- char (*fb)[320] = (char (*)[320])FB_ADDR;
- word worldMap[mapWidth][mapHeight] = {
- {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 7, 7, 7},
- {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 7},
- {4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7},
- {4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7},
- {4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 7},
- {4, 0, 4, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 0, 7, 7, 7, 7, 7},
- {4, 0, 5, 0, 0, 0, 0, 2, 0, 1, 0, 1, 0, 1, 0, 2, 7, 0, 0, 0, 7, 7, 7, 1},
- {4, 0, 6, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 7, 0, 0, 0, 0, 0, 0, 8},
- {4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 1},
- {4, 0, 8, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 0, 8},
- {4, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 7, 7, 7, 1},
- {4, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 0, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 1},
- {6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6},
- {8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4},
- {6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6},
- {4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 6, 0, 6, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3},
- {4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2},
- {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 5, 0, 0, 2, 0, 0, 0, 2},
- {4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2},
- {4, 0, 6, 0, 6, 0, 0, 0, 0, 4, 6, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 2},
- {4, 0, 0, 5, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2},
- {4, 0, 6, 0, 6, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 5, 0, 0, 2, 0, 0, 0, 2},
- {4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2},
- {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3}};
- word frameDone = 0;
- // Global render variables
- word drawStart = 0;
- word drawEnd = 0;
- word texNum = 0;
- word side = 0; // was a NorthSouth or a EastWest wall hit?
- // x and y start position
- fixed_point_t RAY_posX;
- fixed_point_t RAY_posY;
- // initial direction vector
- fixed_point_t RAY_dirX;
- fixed_point_t RAY_dirY;
- // the 2d raycaster version of camera plane
- fixed_point_t RAY_planeX;
- fixed_point_t RAY_planeY;
- // Render entire screen
- // Registers (global):
- // r1 x current vertical line (x of screen)
- void RAYFX_renderScreen()
- {
- // reg 4 5 6 7 (args) and 2 3 (retval) are safe
- asm(
- // backdup registers
- "push r1\n"
- "push r8\n"
- "push r9\n"
- "push r10\n"
- "push r11\n"
- "push r12\n"
- "push r13\n"
- "push r14\n"
- "push r15\n"
- );
- asm(
-
- // r1: current vertical line (x of screen)
- "load32 0 r1\n"
- // for each vertical line (x)
- "RAYFX_screenXloop:\n"
- // cameraX
- "addr2reg LUTcameraX r15\n" // r15 = LUTcameraX
- "add r15 r1 r15\n" // r15 = LUTcameraX[x] addr
- "read 0 r15 r15\n" // r15 = LUTcameraX[x] value
- // r2 rayDirx [FP] x dir of ray
- // r3 rayDiry [FP] y dir of ray
- // r2: rayDirX
- "addr2reg RAY_dirX r14\n" // r14 = RAY_dirX addr
- "read 0 r14 r14\n" // r14 = RAY_dirX value
- "addr2reg RAY_planeX r13\n" // r13 = RAY_planeX addr
- "read 0 r13 r13\n" // r13 = RAY_planeX value
- "multfp r13 r15 r2\n" // r2 = FP_Mult(RAY_planeX, cameraX)
- "add r2 r14 r2\n" // r2 += RAY_dirX
-
- // r3: rayDirY
- "addr2reg RAY_dirY r14\n" // r14 = RAY_dirY addr
- "read 0 r14 r14\n" // r14 = RAY_dirY value
- "addr2reg RAY_planeY r13\n" // r13 = RAY_planeY addr
- "read 0 r13 r13\n" // r13 = RAY_planeY value
- "multfp r13 r15 r3\n" // r3 = FP_Mult(RAY_planeY, cameraX)
- "add r3 r14 r3\n" // r3 += RAY_dirY
- // r4: deltaDistX
- "load32 1 r15\n" // r15 = 1
- "shiftl r15 16 r15\n" // r15 = FP(1)
- "load32 0xC02742 r14\n" // r14 = addr fpdiv_writea
- "write 0 r14 r15\n" // write a (fp1) to divider
- "write 1 r14 r2\n" // write b (rayDirX) to divider and perform division
- "read 1 r14 r4\n" // read result to r4
- "shiftrs r4 31 r13\n" // r13 = y = x >>(signed) 31 (start of abs)
- "xor r4 r13 r4\n" // r4 = (x ^ y)
- "sub r4 r13 r4\n" // r4 -= y (result of abs)
- // r5: deltaDistY (uses r14 and r15 from above)
- "write 0 r14 r15\n" // write a (fp1) to divider
- "write 1 r14 r3\n" // write b (rayDirY) to divider and perform division
- "read 1 r14 r5\n" // read result to r5
- "shiftrs r5 31 r13\n" // r13 = y = x >>(signed) 31 (start of abs)
- "xor r5 r13 r5\n" // r5 = (x ^ y)
- "sub r5 r13 r5\n" // r5 -= y (result of abs)
- // if (rayDirX < 0), else
- // r6: stepX
- // r7: stepY
- // r8: sideDistX
- // r9: sideDistY
- // r10: mapX
- // r11: mapY
- "blts r2 r0 2\n" // if (rayDirX < 0) skip next line
- "jump RAYFX_rayDirXge0\n" // goto RAYFX_rayDirXge0
- "load32 -1 r6\n" // r6 = stepX = -1
- "addr2reg RAY_posX r15\n" // r15 = RAY_posX addr
- "read 0 r15 r15\n" // r15 = RAY_posX value
- "shiftrs r15 16 r10\n" // mapX = int(RAY_posX)
- "load32 0xFFFF r14\n" // r14 = decimal part mask
- "and r15 r14 r15\n" // r15 = RAY_posX decimal part
- "multfp r15 r4 r8\n" // sideDistX = RAY_posX decimal part * deltaDistX
- "jump RAYFX_rayDirXltEnd\n" // skip the else part
- "RAYFX_rayDirXge0:\n"
- "load32 1 r6\n" // stepX = 1
- "addr2reg RAY_posX r15\n" // r15 = RAY_posX addr
- "read 0 r15 r15\n" // r15 = RAY_posX value
- "shiftrs r15 16 r10\n" // r10 (mapX) = r15 >> 16 (to int)
- "add r10 1 r14\n" // r14 = int(RAY_posX) + 1
- "shiftl r14 16 r14\n" // r14 <<=16 (to FP)
- "sub r14 r15 r15\n" // r15 = r14 - RAY_posX
- "multfp r15 r4 r8\n" // sideDistX = RAY_posX-ish * deltaDistX
- "RAYFX_rayDirXltEnd:\n"
- // if (rayDirY < 0), else
- "blts r3 r0 2\n" // if (rayDirY < 0) skip next line
- "jump RAYFX_rayDirYge0\n" // goto RAYFX_rayDirYge0
- "load32 -1 r7\n" // r7 = stepY = -1
- "addr2reg RAY_posY r15\n" // r15 = RAY_posY addr
- "read 0 r15 r15\n" // r15 = RAY_posY value
- "shiftrs r15 16 r11\n" // mapY = int(RAY_posY)
- "load32 0xFFFF r14\n" // r14 = decimal part mask
- "and r15 r14 r15\n" // r15 = RAY_posY decimal part
- "multfp r15 r5 r9\n" // sideDistY = RAY_posY decimal part * deltaDistY
- "jump RAYFX_rayDirYltEnd\n" // skip the else part
- "RAYFX_rayDirYge0:\n"
- "load32 1 r7\n" // stepY = 1
- "addr2reg RAY_posY r15\n" // r15 = RAY_posY addr
- "read 0 r15 r15\n" // r15 = RAY_posY value
- "shiftrs r15 16 r11\n" // r11 (mapY) = r15 >> 16 (to int)
- "add r11 1 r14\n" // r14 = int(RAY_posY) + 1
- "shiftl r14 16 r14\n" // r14 <<=16 (to FP)
- "sub r14 r15 r15\n" // r15 = r14 - RAY_posX
- "multfp r15 r5 r9\n" // sideDistY = RAY_posX-ish * deltaDistY
- "RAYFX_rayDirYltEnd:\n"
- // DDA (uses tmp r15)
- "load32 0 r15\n" // hit = 0
- "RAYFX_DDAwhileNoHit:\n" // while (hit == 0)
- "beq r15 r0 2\n" // while check
- "jump RAYFX_DDAwhileNoHitDone\n"
- // if (sideDistX < sideDistY), else
- "blts r8 r9 2\n" // if (sideDistX < sideDistY) skip next line
- "jump RAYFX_sideDistXltY\n" // goto RAYFX_sideDistXltY
- "add r8 r4 r8\n" // sideDistX += deltaDistX;
- "add r10 r6 r10\n" // mapX += stepX;
- "addr2reg side r14\n" // side;
- "write 0 r14 r0\n" // side = 0;
- "jump RAYFX_sideDistXltYend\n" // skip the else part
- "RAYFX_sideDistXltY:\n"
- "add r9 r5 r9\n" // sideDistY += deltaDistY;
- "add r11 r7 r11\n" // mapY += stepY;
- "addr2reg side r14\n" // side;
- "load32 1 r13\n" // 1
- "write 0 r14 r13\n" // side = 1;
- "RAYFX_sideDistXltYend:\n"
- "addr2reg worldMap r14\n" // r14 = worldMap addr;
- "multu r10 24 r13\n" // r13 = mapX offset using mapHeight
- "add r13 r11 r13\n" // r13 += mapY offset
- "add r13 r14 r14\n" // r14 = worldMap[mapX][mapY] addr
- "read 0 r14 r14\n" // r14 = worldMap[mapX][mapY] value
- "bles r14 r0 2\n" // skip next instruction if worldMap[mapX][mapY] <= 0
- "load 1 r15\n" // hit = 1
- "jump RAYFX_DDAwhileNoHit\n" // return to while loop start
- "RAYFX_DDAwhileNoHitDone:\n"
- // From this point, r10 r11 mapx mapy (and r15) are free
- // texNum = worldMap[mapX][mapY] - 1;
- // assumes worldMap[mapX][mapY] value is still in r14
- "addr2reg texNum r13\n" // r13 = texNum addr;
- "sub r14 1 r14\n" // r14 = worldMap[mapX][mapY] - 1
- "write 0 r13 r14\n" // write texNum;
- // r10: perpWallDist (FP)
- "addr2reg side r15\n" // side addr
- "read 0 r15 r15\n" // side value
- // if (side == 0), else
- "bne r15 r0 3\n" // if (side != 0) skip next two lines
- "sub r8 r4 r10\n" // perpWallDist = (sideDistX - deltaDistX); (match)
- "jumpo 2\n" // skip else part
- "sub r9 r5 r10\n" // perpWallDist = (sideDistY - deltaDistY); (else)
- // From this point sideDistX&Y and deltaDistX&Y are free
- // this frees up regs 4 5 8 and 9
- // leaving to use: 4, 5, 8, 9, 11, 12, 13, 14, 15
- // r4: lineHeight
- "load32 120 r15\n" // r15 = screenHeight
- "shiftl r15 16 r15\n" // r15 = FP(screenHeight)
- "load32 0xC02742 r14\n" // r14 = addr fpdiv_writea
- "write 0 r14 r15\n" // write a (FP(screenHeight) to divider
- "write 1 r14 r10\n" // write b (perpWallDist) to divider and perform division
- "read 1 r14 r4\n" // read result to r4
- "shiftrs r4 16 r4\n" // r4 = lineHeight = int(r4)
- "shiftrs r4 1 r15\n" // r15 = lineHeight >> 1
- "load32 -1 r14\n" // r14 = -1
- "mults r15 r14 r15\n" // r15 = -r15
- "load32 60 r14\n" // r14 = screenHeight >> 1
- "add r14 r15 r15\n" // r15 = drawStart value (r14+r15)
- "bges r15 r0 2\n" // skip next line if drawStart >= 0
- "load 0 r15\n" // set drawStart to 0 if < 0
- "addr2reg drawStart r14\n" // r14 = drawStart addr
- "write 0 r14 r15\n" // write drawStart value
- // skip render if start > 118
- "load32 118 r14\n"
- "blts r15 r14 2 \n"
- "jump RAYFX_skipRenderLine\n"
- "shiftrs r4 1 r15\n" // r15 = lineHeight >> 1
- "load32 60 r14\n" // r14 = screenHeight >> 1
- "add r14 r15 r15\n" // r15 = drawEnd value (r14+r15)
- "load32 120 r14\n" // r14 = screenHeight
- "blts r15 r14 2\n" // skip next line if drawEnd < screenHeight
- "load 120 r15\n" // set drawEnd to screenHeight - 1
- "addr2reg drawEnd r14\n" // r14 = drawEnd addr
- "write 0 r14 r15\n" // write drawEnd value
- // texture calculations
- // r8: side
- // r5: wallX
- "addr2reg side r8\n" // r8 = side addr
- "read 0 r8 r8\n" // r8 = side value
- "bne r8 r0 7\n" // if side != 0, goto else part (addr2reg is two instructions!)
- "addr2reg RAY_posY r15\n" // r15 = RAY_posY addr
- "read 0 r15 r15\n" // r15 = RAY_posY value
- "multfp r10 r3 r14\n" // r14 = perpWallDist * rayDirY
- "add r14 r15 r5\n" // r5 = wallX = r14 + RAY_posY
- "jumpo 5\n" // skip else
- // else
- "addr2reg RAY_posX r15\n" // r15 = RAY_posX addr
- "read 0 r15 r15\n" // r15 = RAY_posX value
- "multfp r10 r2 r14\n" // r14 = perpWallDist, rayDirX
- "add r14 r15 r5\n" // r5 = wallX = r14 + RAY_posX
- "load32 0xFFFF r15\n" // r15 = floormask
- "and r5 r15 r5\n" // wallX-=floor(wallX)
- // r6: texX
- "load32 64 r15\n" // r15 = texWidth
- "shiftl r15 16 r14\n" // r14 = FP(texWidth)
- "multfp r14 r5 r6\n" // r6 = FP_Mult(wallX, FP_intToFP(texWidth))
- "shiftrs r6 16 r6\n" // r6 = int(r6)
- "bne r8 r0 4\n" // skip if side != 0
- "bles r2 r0 3\n" // skip if rayDirX <= 0
- "sub r15 r6 r6\n" // texX = texWidth - texX
- "sub r6 1 r6\n" // texX -= 1
- "beq r8 r0 4\n" // skip if side == 0
- "bges r3 r0 3\n" // skip if rayDirY >= 0
- "sub r15 r6 r6\n" // texX = texWidth - texX
- "sub r6 1 r6\n" // texX -= 1
- // r2 and r3 are free now (no rayDirX&Y needed)
- // r5: step
- "load32 64 r15\n" // r15 = texHeight
- "shiftl r15 16 r15\n" // r15 = FP(texHeight)
- "shiftl r4 16 r14\n" // r14 = FP(lineHeight)
- "load32 0xC02742 r13\n" // r13 = addr fpdiv_writea
- "write 0 r13 r15\n" // write a (FP(texHeight) to divider
- "write 1 r13 r14\n" // write b (FP(lineHeight)) to divider and perform division
- "read 1 r13 r5\n" // read result to r5
- // r4: texPos
- "addr2reg drawStart r15\n" // r15 = drawStart addr
- "read 0 r15 r15\n" // r15 = drawStart value
- "sub r15 60 r15\n" // r15 -= (screenHeight >> 1)
- "shiftrs r4 1 r14\n" // r14 = lineHeight >> 1
- "add r15 r14 r15\n" // r15 += lineHeight >> 1
- "shiftl r15 16 r15\n" // r15 = FP(r15)
- "multfp r15 r5 r4\n" // r4 = r15 * step
- "shiftl r1 0 r7\n" // move x to r7
- // Render vertical line in pixel plane with textures
- // Registers:
- // r1 first pixel addr VRAM addr of first pixel in line (top pixel)
- // r2 (a2r) drawStart Starting pixel of wall
- // r3 (a2r) drawEnd Ending pixel of wall
- // r4 texPos Starting texture coordinate
- // r5 step How much to increase the texture coordinate per screen pixel
- // r6 texX X coordinate on the texture
- // r7 x X position of line to render
- // r8 current FB pos Current framebuffer position in VRAM (top to bottom)
- // r9 end loop FB pos Last framebuffer position in VRAM of current loop
- // r10 texY Y coordinate on the texture
- // r11 gp Used as temp reg in calculations
- // r12 texture[texNum] Texture array of texture to draw
- // r13 color Pixel color
- // r14 ceil or floor col Ceiling or floor color
- // r15 side North South or East West wall side
- "push r1\n" // backup x loop var
- "addr2reg drawStart r2 ; r2 = drawStart addr\n"
- "read 0 r2 r2 ; r2 = drawStart value\n"
- "addr2reg drawEnd r3 ; r3 = drawEnd addr\n"
- "read 0 r3 r3 ; r3 = drawEnd value\n"
- "load32 0xD00000 r1 ; r1 = framebuffer addr\n"
- "add r1 r7 r1 ; r1 = first pixel in line (fb+x)\n"
- "or r0 r1 r8 ; r8 = current pixel\n"
- "multu r2 320 r9 ; r9 = drawStart VRAM offset\n"
- "add r9 r1 r9 ; r9 = last FB pos of before wall\n"
- "addr2reg texNum r12 ; r12 = texNum addr\n"
- "read 0 r12 r12 ; r12 = texNum value\n"
- "multu r12 4096 r12 ; r12 = texture offset (64*64 per texture)\n"
- "addr2reg texture r11 ; r11 = texture array\n"
- "add r12 r11 r12 ; r12 = texture[texNum]\n"
- "load32 0x0082F0 r14 ; r14 = ceiling color\n"
- "addr2reg side r15 ; r15 = side addr\n"
- "read 0 r15 r15 ; r15 = side value\n"
- "ble r2 r0 5 ; skip ceiling if wall starts at first pixel\n"
- // draw until start
- "RAYFX_drawVlineLoopCeiling:\n"
- " write 0 r8 r14 ; write ceiling pixel\n"
- " add r8 320 r8 ; go to next line pixel\n"
- " bge r8 r9 2 ; keep looping until reached wall\n"
- " jump RAYFX_drawVlineLoopCeiling\n"
- "multu r3 320 r9 ; r9 = drawEnd VRAM offset\n"
- "add r9 r1 r9 ; r9 = last FB pos of wall\n"
- "load32 8355711 r2 ; r2 = mask for darken color\n"
- // draw until floor
- "RAYFX_drawVlineLoopWall:\n"
- " shiftrs r4 16 r11 ; r11 = texY = FPtoInt(texPos)\n"
- " and r11 63 r11 ; r11 = r11 & (texHeight-1)\n"
- " add r4 r5 r4 ; texPos += step\n"
- " multu r11 64 r11 ; r11 = texHeight * texY \n"
- " add r11 r6 r11 ; r11 += texX\n"
- " add r11 r12 r13 ; r13 = addr of color in texture array\n"
- " read 0 r13 r13 ; r13 = pixel color\n"
- " beq r15 r0 3 ; skip next two lines if not side of wall is hit\n"
- " shiftrs r13 1 r13\n" // r13 >> 1
- " and r13 r2 r13 ; r13 & darken color mask\n"
- " write 0 r8 r13 ; write texture pixel\n"
- " add r8 320 r8 ; go to next line pixel\n"
- " bge r8 r9 2 ; keep looping until reached floor\n"
- " jump RAYFX_drawVlineLoopWall\n"
- "load32 120 r11\n"
- "bge r3 r11 10 ; skip floor if wall ends at bottom of screen\n"
- "load32 119 r9 ; r9 = last y position\n"
- "multu r9 320 r9 ; r9 = screen end VRAM offset\n"
- "add r9 r1 r9 ; r9 = last FB pos of line\n"
- "load32 0x9E9E9E r14 ; r14 = floor color\n"
- "; draw until end of screen\n"
- "RAYFX_drawVlineLoopFloor:\n"
- " write 0 r8 r14 ; write floor pixel\n"
- " add r8 320 r8 ; go to next line pixel\n"
- " bgt r8 r9 2 ; keep looping until reached end of screen\n"
- " jump RAYFX_drawVlineLoopFloor\n"
- "pop r1\n" // restore x loop var
- "RAYFX_skipRenderLine:\n"
-
- "add r1 1 r1\n" // r1 = x++
- "load32 160 r15\n" // r15 = stop x loop (screenWidth)
- "bge r1 r15 2\n" // keep looping until reached final vline (x) of screen
- "jump RAYFX_screenXloop\n"
- );
- asm(
- // restore registers
- "pop r15\n"
- "pop r14\n"
- "pop r13\n"
- "pop r12\n"
- "pop r11\n"
- "pop r10\n"
- "pop r9\n"
- "pop r8\n"
- "pop r1\n"
- );
- }
- int main() {
- // clear screen from text
- GFX_clearWindowtileTable();
- GFX_clearWindowpaletteTable();
- GFX_clearBGtileTable();
- GFX_clearBGpaletteTable();
- GFX_setHalfRes(1);
- // x and y start position
- RAY_posX = FP_intToFP(15);
- RAY_posY = FP_StringToFP("11.5");
- // initial direction vector
- RAY_dirX = LUTdirX[0];
- RAY_dirY = LUTdirY[0];
- // the 2d raycaster version of camera plane
- RAY_planeX = LUTplaneX[0];
- RAY_planeY = LUTplaneY[0];
- // rotation angle (loops at 360)
- word rotationAngle = 0;
- word rotationSpeed = 0; // degrees per frame
- fixed_point_t moveSpeed = 0;
- fixed_point_t movePadding = FP_StringToFP("0.15");
- word quitGame = 0;
- word prevMillis = millis();
- while (!quitGame) {
- // wait until at least one frame has passed
- while (!frameDone);
- frameDone = 0;
- // render screen
- RAYFX_renderScreen();
- // calculate and draw FPS
- word currentMillis = millis();
- word renderMillis = currentMillis - prevMillis;
- prevMillis = currentMillis;
- word fps = FP_FPtoInt(FP_Div(FP_intToFP(1000), FP_intToFP(renderMillis)));
- char buffer[11];
- itoa(fps, buffer);
- // clear fps digits (assumes fps <= 999)
- asm(
- "push r1\n"
- "load32 0xC01420 r1 ; r1 = vram addr\n"
- "write 0 r1 r0 ; write char to vram\n"
- "write 1 r1 r0 ; write char to vram\n"
- "write 2 r1 r0 ; write char to vram\n"
- "pop r1\n"
- );
-
- GFX_printWindowColored(buffer, strlen(buffer), 0, 0);
- // adjust movement and rotation speed based on FPS
- moveSpeed = FP_Mult(FP_Div(FP_intToFP(renderMillis), FP_intToFP(1000)), FP_intToFP(3));
- rotationSpeed = renderMillis >> 1;
- // check which button is held
- if (bdos_usbkey_held('a')) {
- // both camera direction and camera plane must be rotated
- rotationAngle -= rotationSpeed;
- if (rotationAngle < 0) {
- rotationAngle += 1440;
- }
- RAY_dirX = LUTdirX[rotationAngle];
- RAY_dirY = LUTdirY[rotationAngle];
- RAY_planeX = LUTplaneX[rotationAngle];
- RAY_planeY = LUTplaneY[rotationAngle];
- } else if (bdos_usbkey_held('d')) {
- // both camera direction and camera plane must be rotated
- rotationAngle += rotationSpeed;
- if (rotationAngle >= 1440) {
- rotationAngle -= 1440;
- }
- RAY_dirX = LUTdirX[rotationAngle];
- RAY_dirY = LUTdirY[rotationAngle];
- RAY_planeX = LUTplaneX[rotationAngle];
- RAY_planeY = LUTplaneY[rotationAngle];
- }
- if (bdos_usbkey_held('w')) {
- word worldMapX = FP_FPtoInt(RAY_posX + FP_Mult(RAY_dirX, moveSpeed + movePadding));
- word worldMapY = FP_FPtoInt(RAY_posY);
- if (worldMap[worldMapX][worldMapY] == 0) {
- RAY_posX += FP_Mult(RAY_dirX, moveSpeed);
- }
- worldMapX = FP_FPtoInt(RAY_posX);
- worldMapY = FP_FPtoInt(RAY_posY + FP_Mult(RAY_dirY, moveSpeed + movePadding));
- if (worldMap[worldMapX][worldMapY] == 0) {
- RAY_posY += FP_Mult(RAY_dirY, moveSpeed);
- }
- } else if (bdos_usbkey_held('s')) {
- word worldMapX = FP_FPtoInt(RAY_posX - FP_Mult(RAY_dirX, moveSpeed + movePadding));
- word worldMapY = FP_FPtoInt(RAY_posY);
- if (worldMap[worldMapX][worldMapY] == 0) {
- RAY_posX -= FP_Mult(RAY_dirX, moveSpeed);
- }
- worldMapX = FP_FPtoInt(RAY_posX);
- worldMapY = FP_FPtoInt(RAY_posY - FP_Mult(RAY_dirY, moveSpeed + movePadding));
- if (worldMap[worldMapX][worldMapY] == 0) {
- RAY_posY -= FP_Mult(RAY_dirY, moveSpeed);
- }
- } else if (bdos_usbkey_held('e')) {
- word worldMapX = FP_FPtoInt(RAY_posX + FP_Mult(RAY_planeX, moveSpeed + movePadding));
- word worldMapY = FP_FPtoInt(RAY_posY);
- if (worldMap[worldMapX][worldMapY] == 0) {
- RAY_posX += FP_Mult(RAY_planeX, moveSpeed);
- }
- worldMapX = FP_FPtoInt(RAY_posX);
- worldMapY = FP_FPtoInt(RAY_posY + FP_Mult(RAY_planeY, moveSpeed + movePadding));
- if (worldMap[worldMapX][worldMapY] == 0) {
- RAY_posY += FP_Mult(RAY_planeY, moveSpeed);
- }
- } else if (bdos_usbkey_held('q')) {
- word worldMapX = FP_FPtoInt(RAY_posX - FP_Mult(RAY_planeX, moveSpeed + movePadding));
- word worldMapY = FP_FPtoInt(RAY_posY);
- if (worldMap[worldMapX][worldMapY] == 0) {
- RAY_posX -= FP_Mult(RAY_planeX, moveSpeed);
- }
- worldMapX = FP_FPtoInt(RAY_posX);
- worldMapY = FP_FPtoInt(RAY_posY - FP_Mult(RAY_planeY, moveSpeed + movePadding));
- if (worldMap[worldMapX][worldMapY] == 0) {
- RAY_posY -= FP_Mult(RAY_planeY, moveSpeed);
- }
- }
- if (hid_checkfifo()) {
- word c = hid_fiforead();
- if (c == 27) // escape
- {
- // clean up and exit
- GFX_clearPXframebuffer();
- GFX_clearWindowtileTable();
- GFX_clearWindowpaletteTable();
- GFX_clearBGtileTable();
- GFX_clearBGpaletteTable();
- GFX_setHalfRes(0);
- quitGame = 1;
- }
- else if (c == '1')
- {
- GFX_setHalfRes(0);
- }
- else if (c == '2')
- {
- GFX_setHalfRes(1);
- }
- }
- }
- return 'q';
- }
- void interrupt() {
- // Handle all interrupts
- word i = get_int_id();
- switch (i) {
- case INTID_TIMER1:
- timer1Value = 1; // Notify ending of timer1
- break;
- case INTID_GPU:
- frameDone++;
- break;
- }
- }
|