RAYCAST.C 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. /*
  2. * Raycaster, based on lodev engine tutorial
  3. * Plan:
  4. * - Improve collision detection:
  5. * - During movement function,
  6. * - Check if new pos has distance from possible wall hor and ver
  7. * - Make rendering variables global with prefix
  8. * - Convert rendering function to assembly
  9. * - Add sprites (as this is done after walls)
  10. */
  11. #define word char
  12. #include "LIB/MATH.C"
  13. #include "LIB/STDLIB.C"
  14. #include "LIB/SYS.C"
  15. #include "LIB/GFX.C"
  16. #include "LIB/FP.C"
  17. // Note: these are also hardcoded in the render assembly, so update there as well!
  18. #define FB_ADDR 0xD00000
  19. #define screenWidth 320
  20. #define screenHeight 240
  21. #define texWidth 64
  22. #define texHeight 64
  23. #define mapWidth 24
  24. #define mapHeight 24
  25. // Input
  26. #define BTN_LEFT 256
  27. #define BTN_RIGHT 257
  28. #define BTN_UP 258
  29. #define BTN_DOWN 259
  30. // Colors
  31. #define COLOR_RED 224
  32. #define COLOR_DARK_RED 96
  33. #define COLOR_GREEN 28
  34. #define COLOR_DARK_GREEN 12
  35. #define COLOR_BLUE 3
  36. #define COLOR_DARK_BLUE 2
  37. #define COLOR_WHITE 0xFF
  38. #define COLOR_GREY 0xB6
  39. #define COLOR_YELLOW 0xFC
  40. #define COLOR_DARK_YELLOW 0x90
  41. // Data
  42. /*
  43. - LUTdirX
  44. - LUTdirY
  45. - LUTplaneX
  46. - LUTplaney
  47. - texture
  48. */
  49. #include "DATA/RAYDAT.C"
  50. // Framebuffer. fb[Y][X] (bottom right is [239][319])
  51. char (*fb)[screenWidth] = (char (*)[screenWidth])FB_ADDR;
  52. word worldMap[mapWidth][mapHeight] = {
  53. {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 7, 7, 7},
  54. {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 7},
  55. {4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7},
  56. {4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7},
  57. {4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 7},
  58. {4, 0, 4, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 0, 7, 7, 7, 7, 7},
  59. {4, 0, 5, 0, 0, 0, 0, 2, 0, 1, 0, 1, 0, 1, 0, 2, 7, 0, 0, 0, 7, 7, 7, 1},
  60. {4, 0, 6, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 7, 0, 0, 0, 0, 0, 0, 8},
  61. {4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 1},
  62. {4, 0, 8, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 0, 8},
  63. {4, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 7, 7, 7, 1},
  64. {4, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 0, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 1},
  65. {6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6},
  66. {8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4},
  67. {6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6},
  68. {4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 6, 0, 6, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3},
  69. {4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2},
  70. {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 5, 0, 0, 2, 0, 0, 0, 2},
  71. {4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2},
  72. {4, 0, 6, 0, 6, 0, 0, 0, 0, 4, 6, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 2},
  73. {4, 0, 0, 5, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2},
  74. {4, 0, 6, 0, 6, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 5, 0, 0, 2, 0, 0, 0, 2},
  75. {4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2},
  76. {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3}};
  77. word frameCounter = 0;
  78. word frameCounterLoop = 0;
  79. // Global render variables
  80. word drawStart = 0;
  81. word drawEnd = 0;
  82. word texNum = 0;
  83. word side = 0; // was a NorthSouth or a EastWest wall hit?
  84. // x and y start position
  85. fixed_point_t RAY_posX;
  86. fixed_point_t RAY_posY;
  87. // initial direction vector
  88. fixed_point_t RAY_dirX;
  89. fixed_point_t RAY_dirY;
  90. // the 2d raycaster version of camera plane
  91. fixed_point_t RAY_planeX;
  92. fixed_point_t RAY_planeY;
  93. // Render entire screen
  94. // Registers (global):
  95. // r1 x current vertical line (x of screen)
  96. void RAYFX_renderScreen()
  97. {
  98. // reg 4 5 6 7 (args) and 2 3 (retval) are safe
  99. asm(
  100. // backdup registers
  101. "push r1\n"
  102. "push r8\n"
  103. "push r9\n"
  104. "push r10\n"
  105. "push r11\n"
  106. "push r12\n"
  107. "push r13\n"
  108. "push r14\n"
  109. "push r15\n"
  110. );
  111. asm(
  112. // r1: current vertical line (x of screen)
  113. "load32 0 r1\n"
  114. // for each vertical line (x)
  115. "RAYFX_screenXloop:\n"
  116. // cameraX
  117. "addr2reg LUTcameraX r15\n" // r15 = LUTcameraX
  118. "add r15 r1 r15\n" // r15 = LUTcameraX[x] addr
  119. "read 0 r15 r15\n" // r15 = LUTcameraX[x] value
  120. // r2 rayDirx [FP] x dir of ray
  121. // r3 rayDiry [FP] y dir of ray
  122. // r2: rayDirX
  123. "addr2reg RAY_dirX r14\n" // r14 = RAY_dirX addr
  124. "read 0 r14 r14\n" // r14 = RAY_dirX value
  125. "addr2reg RAY_planeX r13\n" // r13 = RAY_planeX addr
  126. "read 0 r13 r13\n" // r13 = RAY_planeX value
  127. "multfp r13 r15 r2\n" // r2 = FP_Mult(RAY_planeX, cameraX)
  128. "add r2 r14 r2\n" // r2 += RAY_dirX
  129. // r3: rayDirY
  130. "addr2reg RAY_dirY r14\n" // r14 = RAY_dirY addr
  131. "read 0 r14 r14\n" // r14 = RAY_dirY value
  132. "addr2reg RAY_planeY r13\n" // r13 = RAY_planeY addr
  133. "read 0 r13 r13\n" // r13 = RAY_planeY value
  134. "multfp r13 r15 r3\n" // r3 = FP_Mult(RAY_planeY, cameraX)
  135. "add r3 r14 r3\n" // r3 += RAY_dirY
  136. // r4: deltaDistX
  137. "load32 1 r15\n" // r15 = 1
  138. "shiftl r15 16 r15\n" // r15 = FP(1)
  139. "load32 0xC02742 r14\n" // r14 = addr fpdiv_writea
  140. "write 0 r14 r15\n" // write a (fp1) to divider
  141. "write 1 r14 r2\n" // write b (rayDirX) to divider and perform division
  142. "read 1 r14 r4\n" // read result to r4
  143. "shiftrs r4 31 r13\n" // r13 = y = x >>(signed) 31 (start of abs)
  144. "xor r4 r13 r4\n" // r4 = (x ^ y)
  145. "sub r4 r13 r4\n" // r4 -= y (result of abs)
  146. // r5: deltaDistY (uses r14 and r15 from above)
  147. "write 0 r14 r15\n" // write a (fp1) to divider
  148. "write 1 r14 r3\n" // write b (rayDirY) to divider and perform division
  149. "read 1 r14 r5\n" // read result to r5
  150. "shiftrs r5 31 r13\n" // r13 = y = x >>(signed) 31 (start of abs)
  151. "xor r5 r13 r5\n" // r5 = (x ^ y)
  152. "sub r5 r13 r5\n" // r5 -= y (result of abs)
  153. // if (rayDirX < 0), else
  154. // r6: stepX
  155. // r7: stepY
  156. // r8: sideDistX
  157. // r9: sideDistY
  158. // r10: mapX
  159. // r11: mapY
  160. "blts r2 r0 2\n" // if (rayDirX < 0) skip next line
  161. "jump RAYFX_rayDirXge0\n" // goto RAYFX_rayDirXge0
  162. "load32 -1 r6\n" // r6 = stepX = -1
  163. "addr2reg RAY_posX r15\n" // r15 = RAY_posX addr
  164. "read 0 r15 r15\n" // r15 = RAY_posX value
  165. "shiftrs r15 16 r10\n" // mapX = int(RAY_posX)
  166. "load32 0xFFFF r14\n" // r14 = decimal part mask
  167. "and r15 r14 r15\n" // r15 = RAY_posX decimal part
  168. "multfp r15 r4 r8\n" // sideDistX = RAY_posX decimal part * deltaDistX
  169. "jump RAYFX_rayDirXltEnd\n" // skip the else part
  170. "RAYFX_rayDirXge0:\n"
  171. "load32 1 r6\n" // stepX = 1
  172. "addr2reg RAY_posX r15\n" // r15 = RAY_posX addr
  173. "read 0 r15 r15\n" // r15 = RAY_posX value
  174. "shiftrs r15 16 r10\n" // r10 (mapX) = r15 >> 16 (to int)
  175. "add r10 1 r14\n" // r14 = int(RAY_posX) + 1
  176. "shiftl r14 16 r14\n" // r14 <<=16 (to FP)
  177. "sub r14 r15 r15\n" // r15 = r14 - RAY_posX
  178. "multfp r15 r4 r8\n" // sideDistX = RAY_posX-ish * deltaDistX
  179. "RAYFX_rayDirXltEnd:\n"
  180. // if (rayDirY < 0), else
  181. "blts r3 r0 2\n" // if (rayDirY < 0) skip next line
  182. "jump RAYFX_rayDirYge0\n" // goto RAYFX_rayDirYge0
  183. "load32 -1 r7\n" // r7 = stepY = -1
  184. "addr2reg RAY_posY r15\n" // r15 = RAY_posY addr
  185. "read 0 r15 r15\n" // r15 = RAY_posY value
  186. "shiftrs r15 16 r11\n" // mapY = int(RAY_posY)
  187. "load32 0xFFFF r14\n" // r14 = decimal part mask
  188. "and r15 r14 r15\n" // r15 = RAY_posY decimal part
  189. "multfp r15 r5 r9\n" // sideDistY = RAY_posY decimal part * deltaDistY
  190. "jump RAYFX_rayDirYltEnd\n" // skip the else part
  191. "RAYFX_rayDirYge0:\n"
  192. "load32 1 r7\n" // stepY = 1
  193. "addr2reg RAY_posY r15\n" // r15 = RAY_posY addr
  194. "read 0 r15 r15\n" // r15 = RAY_posY value
  195. "shiftrs r15 16 r11\n" // r11 (mapY) = r15 >> 16 (to int)
  196. "add r11 1 r14\n" // r14 = int(RAY_posY) + 1
  197. "shiftl r14 16 r14\n" // r14 <<=16 (to FP)
  198. "sub r14 r15 r15\n" // r15 = r14 - RAY_posX
  199. "multfp r15 r5 r9\n" // sideDistY = RAY_posX-ish * deltaDistY
  200. "RAYFX_rayDirYltEnd:\n"
  201. // DDA (uses tmp r15)
  202. "load32 0 r15\n" // hit = 0
  203. "RAYFX_DDAwhileNoHit:\n" // while (hit == 0)
  204. "beq r15 r0 2\n" // while check
  205. "jump RAYFX_DDAwhileNoHitDone\n"
  206. // if (sideDistX < sideDistY), else
  207. "blts r8 r9 2\n" // if (sideDistX < sideDistY) skip next line
  208. "jump RAYFX_sideDistXltY\n" // goto RAYFX_sideDistXltY
  209. "add r8 r4 r8\n" // sideDistX += deltaDistX;
  210. "add r10 r6 r10\n" // mapX += stepX;
  211. "addr2reg side r14\n" // side;
  212. "write 0 r14 r0\n" // side = 0;
  213. "jump RAYFX_sideDistXltYend\n" // skip the else part
  214. "RAYFX_sideDistXltY:\n"
  215. "add r9 r5 r9\n" // sideDistY += deltaDistY;
  216. "add r11 r7 r11\n" // mapY += stepY;
  217. "addr2reg side r14\n" // side;
  218. "load32 1 r13\n" // 1
  219. "write 0 r14 r13\n" // side = 1;
  220. "RAYFX_sideDistXltYend:\n"
  221. "addr2reg worldMap r14\n" // r14 = worldMap addr;
  222. "multu r10 24 r13\n" // r13 = mapX offset using mapHeight
  223. "add r13 r11 r13\n" // r13 += mapY offset
  224. "add r13 r14 r14\n" // r14 = worldMap[mapX][mapY] addr
  225. "read 0 r14 r14\n" // r14 = worldMap[mapX][mapY] value
  226. "bles r14 r0 2\n" // skip next instruction if worldMap[mapX][mapY] <= 0
  227. "load 1 r15\n" // hit = 1
  228. "jump RAYFX_DDAwhileNoHit\n" // return to while loop start
  229. "RAYFX_DDAwhileNoHitDone:\n"
  230. // From this point, r10 r11 mapx mapy (and r15) are free
  231. // texNum = worldMap[mapX][mapY] - 1;
  232. // assumes worldMap[mapX][mapY] value is still in r14
  233. "addr2reg texNum r13\n" // r13 = texNum addr;
  234. "sub r14 1 r14\n" // r14 = worldMap[mapX][mapY] - 1
  235. "write 0 r13 r14\n" // write texNum;
  236. // r10: perpWallDist (FP)
  237. "addr2reg side r15\n" // side addr
  238. "read 0 r15 r15\n" // side value
  239. // if (side == 0), else
  240. "bne r15 r0 3\n" // if (side != 0) skip next two lines
  241. "sub r8 r4 r10\n" // perpWallDist = (sideDistX - deltaDistX); (match)
  242. "jumpo 2\n" // skip else part
  243. "sub r9 r5 r10\n" // perpWallDist = (sideDistY - deltaDistY); (else)
  244. // From this point sideDistX&Y and deltaDistX&Y are free
  245. // this frees up regs 4 5 8 and 9
  246. // leaving to use: 4, 5, 8, 9, 11, 12, 13, 14, 15
  247. // r4: lineHeight
  248. "load32 240 r15\n" // r15 = screenHeight
  249. "shiftl r15 16 r15\n" // r15 = FP(screenHeight)
  250. "load32 0xC02742 r14\n" // r14 = addr fpdiv_writea
  251. "write 0 r14 r15\n" // write a (FP(screenHeight) to divider
  252. "write 1 r14 r10\n" // write b (perpWallDist) to divider and perform division
  253. "read 1 r14 r4\n" // read result to r4
  254. "shiftrs r4 16 r4\n" // r4 = lineHeight = int(r4)
  255. "shiftrs r4 1 r15\n" // r15 = lineHeight >> 1
  256. "load32 -1 r14\n" // r14 = -1
  257. "mults r15 r14 r15\n" // r15 = -r15
  258. "load32 120 r14\n" // r14 = screenHeight >> 1
  259. "add r14 r15 r15\n" // r15 = drawStart value (r14+r15)
  260. "bges r15 r0 2\n" // skip next line if drawStart >= 0
  261. "load 0 r15\n" // set drawStart to 0 if < 0
  262. "addr2reg drawStart r14\n" // r14 = drawStart addr
  263. "write 0 r14 r15\n" // write drawStart value
  264. // skip render if start > 238
  265. "load32 238 r14\n"
  266. "blts r15 r14 2 \n"
  267. "jump RAYFX_skipRenderLine\n"
  268. "shiftrs r4 1 r15\n" // r15 = lineHeight >> 1
  269. "load32 120 r14\n" // r14 = screenHeight >> 1
  270. "add r14 r15 r15\n" // r15 = drawEnd value (r14+r15)
  271. "load32 240 r14\n" // r14 = screenHeight
  272. "blts r15 r14 2\n" // skip next line if drawEnd < screenHeight
  273. "load 239 r15\n" // set drawEnd to screenHeight - 1
  274. "addr2reg drawEnd r14\n" // r14 = drawEnd addr
  275. "write 0 r14 r15\n" // write drawEnd value
  276. // texture calculations
  277. // r8: side
  278. // r5: wallX
  279. "addr2reg side r8\n" // r8 = side addr
  280. "read 0 r8 r8\n" // r8 = side value
  281. "bne r8 r0 7\n" // if side != 0, goto else part (addr2reg is two instructions!)
  282. "addr2reg RAY_posY r15\n" // r15 = RAY_posY addr
  283. "read 0 r15 r15\n" // r15 = RAY_posY value
  284. "multfp r10 r3 r14\n" // r14 = perpWallDist * rayDirY
  285. "add r14 r15 r5\n" // r5 = wallX = r14 + RAY_posY
  286. "jumpo 5\n" // skip else
  287. // else
  288. "addr2reg RAY_posX r15\n" // r15 = RAY_posX addr
  289. "read 0 r15 r15\n" // r15 = RAY_posX value
  290. "multfp r10 r2 r14\n" // r14 = perpWallDist, rayDirX
  291. "add r14 r15 r5\n" // r5 = wallX = r14 + RAY_posX
  292. "load32 0xFFFF r15\n" // r15 = floormask
  293. "and r5 r15 r5\n" // wallX-=floor(wallX)
  294. // r6: texX
  295. "load32 64 r15\n" // r15 = texWidth
  296. "shiftl r15 16 r14\n" // r14 = FP(texWidth)
  297. "multfp r14 r5 r6\n" // r6 = FP_Mult(wallX, FP_intToFP(texWidth))
  298. "shiftrs r6 16 r6\n" // r6 = int(r6)
  299. "bne r8 r0 4\n" // skip if side != 0
  300. "bles r2 r0 3\n" // skip if rayDirX <= 0
  301. "sub r15 r6 r6\n" // texX = texWidth - texX
  302. "sub r6 1 r6\n" // texX -= 1
  303. "beq r8 r0 4\n" // skip if side == 0
  304. "bges r3 r0 3\n" // skip if rayDirY >= 0
  305. "sub r15 r6 r6\n" // texX = texWidth - texX
  306. "sub r6 1 r6\n" // texX -= 1
  307. // r2 and r3 are free now (no rayDirX&Y needed)
  308. // r5: step
  309. "load32 64 r15\n" // r15 = texHeight
  310. "shiftl r15 16 r15\n" // r15 = FP(texHeight)
  311. "shiftl r4 16 r14\n" // r14 = FP(lineHeight)
  312. "load32 0xC02742 r13\n" // r13 = addr fpdiv_writea
  313. "write 0 r13 r15\n" // write a (FP(screenHeight) to divider
  314. "write 1 r13 r14\n" // write b (FP(lineHeight)) to divider and perform division
  315. "read 1 r13 r5\n" // read result to r5
  316. // r4: texPos
  317. "addr2reg drawStart r15\n" // r15 = drawStart addr
  318. "read 0 r15 r15\n" // r15 = drawStart value
  319. "sub r15 120 r15\n" // r15 -= (screenHeight >> 1)
  320. "shiftrs r4 1 r14\n" // r14 = lineHeight >> 1
  321. "add r15 r14 r15\n" // r15 += lineHeight >> 1
  322. "shiftl r15 16 r15\n" // r15 = FP(r15)
  323. "multfp r15 r5 r4\n" // r4 = r15 * step
  324. "or r1 r0 r7\n" // move x to r7
  325. // Render vertical line in pixel plane with textures
  326. // Registers:
  327. // r1 first pixel addr VRAM addr of first pixel in line (top pixel)
  328. // r2 (a2r) drawStart Starting pixel of wall
  329. // r3 (a2r) drawEnd Ending pixel of wall
  330. // r4 texPos Starting texture coordinate
  331. // r5 step How much to increase the texture coordinate per screen pixel
  332. // r6 texX X coordinate on the texture
  333. // r7 x X position of line to render
  334. // r8 current FB pos Current framebuffer position in VRAM (top to bottom)
  335. // r9 end loop FB pos Last framebuffer position in VRAM of current loop
  336. // r10 texY Y coordinate on the texture
  337. // r11 gp Used as temp reg in calculations
  338. // r12 texture[texNum] Texture array of texture to draw
  339. // r13 color Pixel color
  340. // r14 ceil or floor col Ceiling or floor color
  341. // r15 side North South or East West wall side
  342. "push r1\n" // backup x loop var
  343. "addr2reg drawStart r2 ; r2 = drawStart addr\n"
  344. "read 0 r2 r2 ; r2 = drawStart value\n"
  345. "addr2reg drawEnd r3 ; r3 = drawEnd addr\n"
  346. "read 0 r3 r3 ; r3 = drawEnd value\n"
  347. "load32 0xD00000 r1 ; r1 = framebuffer addr\n"
  348. "add r1 r7 r1 ; r1 = first pixel in line (fb+x)\n"
  349. "or r0 r1 r8 ; r8 = current pixel\n"
  350. "multu r2 320 r9 ; r9 = drawStart VRAM offset\n"
  351. "add r9 r1 r9 ; r9 = last FB pos of before wall\n"
  352. "addr2reg texNum r12 ; r12 = texNum addr\n"
  353. "read 0 r12 r12 ; r12 = texNum value\n"
  354. "multu r12 4096 r12 ; r12 = texture offset (64*64 per texture)\n"
  355. "addr2reg texture r11 ; r11 = texture array\n"
  356. "add r12 r11 r12 ; r12 = texture[texNum]\n"
  357. "load32 0x0082F0 r14 ; r14 = ceiling color\n"
  358. "addr2reg side r15 ; r15 = side addr\n"
  359. "read 0 r15 r15 ; r15 = side value\n"
  360. // draw until start
  361. "RAYFX_drawVlineLoopCeiling:\n"
  362. " write 0 r8 r14 ; write ceiling pixel\n"
  363. " add r8 320 r8 ; go to next line pixel\n"
  364. " bge r8 r9 2 ; keep looping until reached wall\n"
  365. " jump RAYFX_drawVlineLoopCeiling\n"
  366. "multu r3 320 r9 ; r9 = drawEnd VRAM offset\n"
  367. "add r9 r1 r9 ; r9 = last FB pos of wall\n"
  368. "load32 8355711 r2 ; r2 = mask for darken color\n"
  369. // draw until floor
  370. "RAYFX_drawVlineLoopWall:\n"
  371. " shiftrs r4 16 r11 ; r11 = texY = FPtoInt(texPos)\n"
  372. " and r11 63 r11 ; r11 = r11 & (texHeight-1)\n"
  373. " add r4 r5 r4 ; texPos += step\n"
  374. " multu r11 64 r11 ; r11 = texHeight * texY \n"
  375. " add r11 r6 r11 ; r11 += texX\n"
  376. " add r11 r12 r13 ; r13 = addr of color in texture array\n"
  377. " read 0 r13 r13 ; r13 = pixel color\n"
  378. " beq r15 r0 3 ; skip next two lines if not side of wall is hit\n"
  379. " shiftrs r13 1 r13\n" // r13 >> 1
  380. " and r13 r2 r13 ; r13 & darken color mask\n"
  381. " write 0 r8 r13 ; write texture pixel\n"
  382. " add r8 320 r8 ; go to next line pixel\n"
  383. " bge r8 r9 2 ; keep looping until reached floor\n"
  384. " jump RAYFX_drawVlineLoopWall\n"
  385. "load32 239 r9 ; r9 = last y position\n"
  386. "multu r9 320 r9 ; r9 = screen end VRAM offset\n"
  387. "add r9 r1 r9 ; r9 = last FB pos of line\n"
  388. "load32 0x9E9E9E r14 ; r14 = floor color\n"
  389. "; draw until end of screen\n"
  390. "RAYFX_drawVlineLoopFloor:\n"
  391. " write 0 r8 r14 ; write floor pixel\n"
  392. " add r8 320 r8 ; go to next line pixel\n"
  393. " bgt r8 r9 2 ; keep looping until reached end of screen\n"
  394. " jump RAYFX_drawVlineLoopFloor\n"
  395. "pop r1\n" // restore x loop var
  396. "RAYFX_skipRenderLine:\n"
  397. "add r1 1 r1\n" // r1 = x++
  398. "load32 320 r15\n" // r15 = stop x loop (screenWidth)
  399. "bge r1 r15 2\n" // keep looping until reached final vline (x) of screen
  400. "jump RAYFX_screenXloop\n"
  401. );
  402. asm(
  403. // restore registers
  404. "pop r15\n"
  405. "pop r14\n"
  406. "pop r13\n"
  407. "pop r12\n"
  408. "pop r11\n"
  409. "pop r10\n"
  410. "pop r9\n"
  411. "pop r8\n"
  412. "pop r1\n"
  413. );
  414. }
  415. int main() {
  416. // clear screen from text
  417. GFX_clearWindowtileTable();
  418. GFX_clearWindowpaletteTable();
  419. GFX_clearBGtileTable();
  420. GFX_clearBGpaletteTable();
  421. // x and y start position
  422. RAY_posX = FP_intToFP(15);
  423. RAY_posY = FP_StringToFP("11.5");
  424. // initial direction vector
  425. RAY_dirX = LUTdirX[0];
  426. RAY_dirY = LUTdirY[0];
  427. // the 2d raycaster version of camera plane
  428. RAY_planeX = LUTplaneX[0];
  429. RAY_planeY = LUTplaneY[0];
  430. // rotation angle (loops at 360)
  431. word rotationAngle = 0;
  432. word rotationSpeed = 5; // degrees per frame
  433. fixed_point_t moveSpeed = FP_StringToFP("0.15");
  434. fixed_point_t movePadding = 0;//FP_StringToFP("0.3");
  435. word quitGame = 0;
  436. while (!quitGame) {
  437. // render screen
  438. RAYFX_renderScreen();
  439. // calculate and draw FPS
  440. if (frameCounterLoop == 10)
  441. {
  442. word fps = MATH_div(600, frameCounter);
  443. frameCounter = 0;
  444. char buffer[11];
  445. itoa(fps, buffer);
  446. // clear fps digits (assumes fps <= 999)
  447. asm(
  448. "push r1\n"
  449. "load32 0xC01420 r1 ; r1 = vram addr\n"
  450. "write 0 r1 r0 ; write char to vram\n"
  451. "write 1 r1 r0 ; write char to vram\n"
  452. "write 2 r1 r0 ; write char to vram\n"
  453. "pop r1\n"
  454. );
  455. GFX_printWindowColored(buffer, strlen(buffer), 0, 0);
  456. frameCounterLoop = 0;
  457. }
  458. else
  459. {
  460. frameCounterLoop++;
  461. }
  462. // check which button is held
  463. if (BDOS_USBkeyHeld(BTN_LEFT)) {
  464. // both camera direction and camera plane must be rotated
  465. rotationAngle -= rotationSpeed;
  466. if (rotationAngle < 0) {
  467. rotationAngle += 360;
  468. }
  469. RAY_dirX = LUTdirX[rotationAngle];
  470. RAY_dirY = LUTdirY[rotationAngle];
  471. RAY_planeX = LUTplaneX[rotationAngle];
  472. RAY_planeY = LUTplaneY[rotationAngle];
  473. } else if (BDOS_USBkeyHeld(BTN_RIGHT)) {
  474. // both camera direction and camera plane must be rotated
  475. rotationAngle += rotationSpeed;
  476. if (rotationAngle >= 360) {
  477. rotationAngle -= 360;
  478. }
  479. RAY_dirX = LUTdirX[rotationAngle];
  480. RAY_dirY = LUTdirY[rotationAngle];
  481. RAY_planeX = LUTplaneX[rotationAngle];
  482. RAY_planeY = LUTplaneY[rotationAngle];
  483. }
  484. if (BDOS_USBkeyHeld(BTN_UP)) {
  485. word worldMapX = FP_FPtoInt(RAY_posX + FP_Mult(RAY_dirX, moveSpeed + movePadding));
  486. word worldMapY = FP_FPtoInt(RAY_posY);
  487. if (worldMap[worldMapX][worldMapY] == 0) {
  488. RAY_posX += FP_Mult(RAY_dirX, moveSpeed);
  489. }
  490. worldMapX = FP_FPtoInt(RAY_posX);
  491. worldMapY = FP_FPtoInt(RAY_posY + FP_Mult(RAY_dirY, moveSpeed + movePadding));
  492. if (worldMap[worldMapX][worldMapY] == 0) {
  493. RAY_posY += FP_Mult(RAY_dirY, moveSpeed);
  494. }
  495. } else if (BDOS_USBkeyHeld(BTN_DOWN)) {
  496. word worldMapX = FP_FPtoInt(RAY_posX - FP_Mult(RAY_dirX, moveSpeed + movePadding));
  497. word worldMapY = FP_FPtoInt(RAY_posY);
  498. if (worldMap[worldMapX][worldMapY] == 0) {
  499. RAY_posX -= FP_Mult(RAY_dirX, moveSpeed);
  500. }
  501. worldMapX = FP_FPtoInt(RAY_posX);
  502. worldMapY = FP_FPtoInt(RAY_posY - FP_Mult(RAY_dirY, moveSpeed + movePadding));
  503. if (worldMap[worldMapX][worldMapY] == 0) {
  504. RAY_posY -= FP_Mult(RAY_dirY, moveSpeed);
  505. }
  506. }
  507. if (HID_FifoAvailable()) {
  508. word c = HID_FifoRead();
  509. if (c == 27) // escape
  510. {
  511. // clean up and exit
  512. GFX_clearPXframebuffer();
  513. GFX_clearWindowtileTable();
  514. GFX_clearWindowpaletteTable();
  515. GFX_clearBGtileTable();
  516. GFX_clearBGpaletteTable();
  517. quitGame = 1;
  518. }
  519. }
  520. }
  521. return 'q';
  522. }
  523. void interrupt() {
  524. // handle all interrupts
  525. word i = getIntID();
  526. switch (i) {
  527. case INTID_TIMER1:
  528. timer1Value = 1; // notify ending of timer1
  529. break;
  530. case INTID_TIMER2:
  531. break;
  532. case INTID_UART0:
  533. break;
  534. case INTID_GPU:
  535. frameCounter++;
  536. break;
  537. case INTID_TIMER3:
  538. break;
  539. case INTID_PS2:
  540. break;
  541. case INTID_UART1:
  542. break;
  543. case INTID_UART2:
  544. break;
  545. }
  546. }