raycaster.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. * ===========================================================================
  3. *
  4. * Wolf3D Browser Version GPL Source Code
  5. * Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
  6. *
  7. * This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
  8. *
  9. * Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * Wolf3D Browser Source Code is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License version 2
  20. * along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. * If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  23. *
  24. * ===========================================================================
  25. */
  26. Wolf.setConsts({
  27. UPPERZCOORD : 0.6,
  28. LOWERZCOORD : -0.6,
  29. // marks
  30. TRACE_MARK_MAP : 1, // marks traced area in 'AM_AutoMap.vis' array
  31. // obstacle levels
  32. TRACE_SIGHT : 2, // player sight
  33. TRACE_SIGHT_AI : 4, // enemy sight
  34. TRACE_BULLET : 8, // bullet
  35. TRACE_OBJECT : 16, // object
  36. TRACE_HIT_VERT : 32, // vertical wall was hit
  37. TRACE_HIT_DOOR : 64, // door was hit
  38. TRACE_HIT_PWALL : 128 // pushwall was hit
  39. });
  40. Wolf.Raycaster = (function() {
  41. var x_tile_step = [ 1, -1, -1, 1 ],
  42. y_tile_step = [ 1, 1, -1, -1 ];
  43. var TILESHIFT = Wolf.TILESHIFT,
  44. TRACE_HIT_VERT = Wolf.TRACE_HIT_VERT,
  45. TILEGLOBAL = Wolf.TILEGLOBAL,
  46. WALL_TILE = Wolf.WALL_TILE,
  47. DOOR_TILE = Wolf.DOOR_TILE,
  48. TILE2POS = Wolf.TILE2POS,
  49. POS2TILE = Wolf.POS2TILE,
  50. FINE2RAD = Wolf.FINE2RAD,
  51. TRACE_HIT_DOOR = Wolf.TRACE_HIT_DOOR,
  52. PUSHWALL_TILE = Wolf.PUSHWALL_TILE,
  53. TRACE_HIT_PWALL = Wolf.TRACE_HIT_PWALL,
  54. DOOR_FULLOPEN = Wolf.DOOR_FULLOPEN,
  55. XnextTable = Wolf.Math.XnextTable,
  56. YnextTable = Wolf.Math.YnextTable,
  57. getQuadrant = Wolf.Math.getQuadrant,
  58. TanTable = Wolf.Math.TanTable;
  59. function traceCheck(tileMap, doorMap, visibleTiles, x, y, frac, dfrac, vert, flip, tracePoint) {
  60. var door;
  61. if (tileMap[x][y] & WALL_TILE) {
  62. if (vert) {
  63. tracePoint.x = (x << TILESHIFT) + (flip ? TILEGLOBAL : 0);
  64. tracePoint.y = (y << TILESHIFT) + frac;
  65. tracePoint.flags |= TRACE_HIT_VERT;
  66. } else {
  67. tracePoint.x = (x << TILESHIFT) + frac;
  68. tracePoint.y = (y << TILESHIFT) + (flip ? TILEGLOBAL : 0);
  69. tracePoint.flags &= ~TRACE_HIT_VERT;
  70. }
  71. tracePoint.tileX = x;
  72. tracePoint.tileY = y;
  73. tracePoint.frac = frac / TILEGLOBAL;
  74. return true; // wall, stop tracing
  75. }
  76. if (visibleTiles) {
  77. visibleTiles[x][y] = true; // this tile is visible
  78. }
  79. if (tileMap[x][y] & DOOR_TILE && doorMap[x][y].action != Wolf.dr_open) {
  80. door = doorMap[x][y];
  81. frac += dfrac >> 1;
  82. if (POS2TILE(frac)) {
  83. return false;
  84. }
  85. if (vert) {
  86. if (door.action != Wolf.dr_closed && (frac >> 10) > DOOR_FULLOPEN - Wolf.Doors.opened(door)) {
  87. return false; // opened enough
  88. }
  89. tracePoint.x = TILE2POS(x);
  90. tracePoint.y = (y << TILESHIFT) + frac;
  91. tracePoint.flags |= TRACE_HIT_VERT;
  92. tracePoint.frac = frac / TILEGLOBAL;
  93. } else {
  94. if (door.action != Wolf.dr_closed && (frac >> 10) < Wolf.Doors.opened(door)) {
  95. return false; // opened enough
  96. }
  97. tracePoint.y = TILE2POS(y);
  98. tracePoint.x = (x << TILESHIFT) + frac;
  99. tracePoint.flags &= ~TRACE_HIT_VERT;
  100. tracePoint.frac = 1 - frac / TILEGLOBAL;
  101. }
  102. tracePoint.flags |= TRACE_HIT_DOOR;
  103. tracePoint.tileX = x;
  104. tracePoint.tileY = y;
  105. tracePoint.frac += Wolf.Doors.opened(door) / DOOR_FULLOPEN;
  106. return true; // closed door, stop tracing
  107. }
  108. if (tileMap[x][y] & PUSHWALL_TILE) {
  109. var pwall = Wolf.PushWall.get(),
  110. offset = pwall.pointsMoved / 128;
  111. frac += dfrac * offset;
  112. if (POS2TILE(frac)) {
  113. return false;
  114. }
  115. if (vert) {
  116. tracePoint.x = (x << TILESHIFT) + (flip ? TILEGLOBAL : 0) + offset * TILEGLOBAL * (flip ? -1 : 1);
  117. tracePoint.y = (y << TILESHIFT) + frac;
  118. tracePoint.flags |= TRACE_HIT_VERT;
  119. } else {
  120. tracePoint.x = (x << TILESHIFT) + frac;
  121. tracePoint.y = (y << TILESHIFT) + (flip ? TILEGLOBAL : 0) + offset * TILEGLOBAL * (flip ? -1 : 1);
  122. tracePoint.flags &= ~TRACE_HIT_VERT;
  123. }
  124. tracePoint.flags |= TRACE_HIT_PWALL;
  125. tracePoint.tileX = x;
  126. tracePoint.tileY = y;
  127. tracePoint.frac = frac / TILEGLOBAL;
  128. return true;
  129. }
  130. return false; // no intersection, go on!
  131. }
  132. function trace(level, visibleTiles, tracePoint) {
  133. var xtilestep, ytilestep,
  134. xstep, ystep,
  135. xtile, ytile,
  136. xintercept, yintercept,
  137. YmapPos, XmapPos,
  138. tileMap = level.tileMap,
  139. doorMap = level.state.doorMap,
  140. q;
  141. // Setup for ray casting
  142. q = getQuadrant(FINE2RAD(tracePoint.angle));
  143. xtilestep = x_tile_step[q];
  144. ytilestep = y_tile_step[q];
  145. xtile = POS2TILE(tracePoint.x) + xtilestep;
  146. ytile = POS2TILE(tracePoint.y) + ytilestep;
  147. xstep = ytilestep * XnextTable[tracePoint.angle];
  148. ystep = xtilestep * YnextTable[tracePoint.angle];
  149. xintercept = (((((ytilestep == -1 ? ytile+1 : ytile) << TILESHIFT) - tracePoint.y)
  150. / TanTable[tracePoint.angle])>>0) + tracePoint.x;
  151. yintercept = (((((xtilestep == -1 ? xtile+1 : xtile) << TILESHIFT) - tracePoint.x)
  152. * TanTable[tracePoint.angle])>>0) + tracePoint.y;
  153. YmapPos = yintercept >> TILESHIFT; // toXray
  154. XmapPos = xintercept >> TILESHIFT; // toYray
  155. if (visibleTiles) {
  156. // this tile is visible
  157. visibleTiles[POS2TILE(tracePoint.x)][POS2TILE(tracePoint.y)] = true;
  158. }
  159. var traceCount = 0;
  160. // Start of ray-casting
  161. while (1) {
  162. traceCount++;
  163. // Vertical loop // an analogue for X-Ray
  164. while (!(ytilestep == -1 && YmapPos <= ytile) && !(ytilestep == 1 && YmapPos >= ytile)) {
  165. if (xtile < 0 || xtile >= 64 || YmapPos < 0 || YmapPos >= 64) {
  166. tracePoint.oob = true;
  167. return;
  168. }
  169. if (traceCheck(tileMap, doorMap, visibleTiles, xtile, YmapPos, yintercept % TILEGLOBAL, ystep, true, (xtilestep == -1), tracePoint)) {
  170. if (xstep < 0) {
  171. tracePoint.frac = 1 - tracePoint.frac;
  172. }
  173. return;
  174. }
  175. // prepare for next step
  176. xtile += xtilestep;
  177. yintercept += ystep;
  178. YmapPos = yintercept >> TILESHIFT;
  179. }
  180. // Horizontal loop // an analogue for Y-Ray
  181. while (!(xtilestep == -1 && XmapPos <= xtile) && !(xtilestep == 1 && XmapPos >= xtile)) {
  182. if (ytile < 0 || ytile >= 64 || XmapPos < 0 || XmapPos >= 64) {
  183. tracePoint.oob = true;
  184. return;
  185. }
  186. if (traceCheck(tileMap, doorMap, visibleTiles, XmapPos, ytile, xintercept % TILEGLOBAL, xstep, false, (ytilestep == -1), tracePoint)) {
  187. if (ystep > 0) {
  188. tracePoint.frac = 1 - tracePoint.frac;
  189. }
  190. return;
  191. }
  192. // prepare for next step
  193. ytile += ytilestep;
  194. xintercept += xstep;
  195. XmapPos = xintercept >> TILESHIFT;
  196. }
  197. if (traceCount > 1000) {
  198. return;
  199. }
  200. } // end of while( 1 )
  201. }
  202. function traceRays(viewport, level) {
  203. var n, i, j,
  204. tileMap = level.tileMap,
  205. tracePoint,
  206. visibleTiles = [],
  207. numRays = Wolf.XRES / Wolf.SLICE_WIDTH,
  208. tracers = [];
  209. for (i=0;i<64;i++) {
  210. visibleTiles[i] = [];
  211. for (j=0;j<64;j++) {
  212. visibleTiles[i][j] = 0;
  213. }
  214. }
  215. // Ray casting
  216. for (n = 0 ; n < numRays ; ++n) {
  217. tracePoint = {
  218. x : viewport.x,
  219. y : viewport.y,
  220. angle : Wolf.Math.normalizeAngle(viewport.angle - Wolf.Math.ColumnAngle[n * Wolf.SLICE_WIDTH]),
  221. flags : Wolf.TRACE_SIGHT | Wolf.TRACE_MARK_MAP,
  222. oob : false
  223. };
  224. trace(level, visibleTiles, tracePoint);
  225. tracers[n] = tracePoint;
  226. // Ugly hack to get rid of "blank slice" glitch due to out-of-bounds raycasting.
  227. // We simply re-use the previous slice if possible.
  228. if (tracePoint.oob) {
  229. if (n > 0 && !tracers[n-1].oob) {
  230. tracers[n] = tracers[n-1];
  231. }
  232. }
  233. }
  234. return {
  235. visibleTiles : visibleTiles,
  236. tracers : tracers
  237. };
  238. }
  239. return {
  240. traceRays : traceRays,
  241. trace : trace
  242. };
  243. })();