renderer.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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. FOV_RAD : 75 * Math.PI / 180,
  28. ISCHROME : /chrome/.test(navigator.userAgent.toLowerCase()),
  29. ISSAFARI : /safari/.test(navigator.userAgent.toLowerCase()),
  30. ISFIREFOX : /firefox/.test(navigator.userAgent.toLowerCase()),
  31. ISXP : /windows nt 5\./.test(navigator.userAgent.toLowerCase()),
  32. ISWEBKIT : /webkit/.test(navigator.userAgent.toLowerCase())
  33. });
  34. Wolf.setConsts({
  35. VIEW_DIST : (Wolf.XRES / 2) / Math.tan((Wolf.FOV_RAD / 2)),
  36. TEXTURERESOLUTION : Wolf.ISCHROME ? 128 : 64
  37. });
  38. Wolf.Renderer = (function() {
  39. var slices = [],
  40. useBackgroundImage = Wolf.ISWEBKIT,
  41. texturePath = "art/walls-shaded/" + Wolf.TEXTURERESOLUTION + "/",
  42. spritePath = "art/sprites/" + Wolf.TEXTURERESOLUTION + "/",
  43. sprites = [],
  44. maxDistZ = 64 * 0x10000,
  45. hasInit = false;
  46. visibleSprites = [];
  47. var TILESHIFT = Wolf.TILESHIFT,
  48. TILEGLOBAL = Wolf.TILEGLOBAL,
  49. TRACE_HIT_VERT = Wolf.TRACE_HIT_VERT,
  50. TRACE_HIT_DOOR = Wolf.TRACE_HIT_DOOR,
  51. WALL_TILE = Wolf.WALL_TILE,
  52. DOOR_TILE = Wolf.DOOR_TILE,
  53. TEX_PLATE = Wolf.TEX_PLATE,
  54. TILE2POS = Wolf.TILE2POS,
  55. POS2TILE = Wolf.POS2TILE,
  56. VIEW_DIST = Wolf.VIEW_DIST,
  57. SLICE_WIDTH = Wolf.SLICE_WIDTH,
  58. WALL_TEXTURE_WIDTH = Wolf.WALL_TEXTURE_WIDTH,
  59. FINE2RAD = Wolf.FINE2RAD,
  60. XRES = Wolf.XRES,
  61. YRES = Wolf.YRES,
  62. MINDIST = Wolf.MINDIST,
  63. cos = Math.cos,
  64. sin = Math.sin,
  65. tan = Math.tan,
  66. atan2 = Math.atan2,
  67. round = Math.round,
  68. sqrt = Math.sqrt;
  69. function init() {
  70. var image, slice, x;
  71. if (hasInit) {
  72. return;
  73. }
  74. hasInit = true;
  75. $("#game .renderer")
  76. .width(Wolf.XRES + "px")
  77. .height(Wolf.YRES + "px");
  78. for (x=0; x<Wolf.XRES; x += Wolf.SLICE_WIDTH) {
  79. slice = $("<div>");
  80. slice.css({
  81. position : "absolute",
  82. width : Wolf.SLICE_WIDTH + "px",
  83. height : Wolf.YRES + "px",
  84. left : x + "px",
  85. top : 0,
  86. overflow : "hidden"
  87. });
  88. slice.appendTo("#game .renderer");
  89. image = useBackgroundImage ? $("<div>") : $("<img>");
  90. image.css({
  91. position : "absolute",
  92. display : "block",
  93. top : 0,
  94. height : 0,
  95. width : Wolf.SLICE_WIDTH * Wolf.WALL_TEXTURE_WIDTH + "px",
  96. backgroundSize : "100% 100%"
  97. });
  98. var sliceElement = slice[0];
  99. sliceElement.texture = image[0];
  100. sliceElement.appendChild(sliceElement.texture);
  101. slices.push(sliceElement);
  102. }
  103. }
  104. function reset() {
  105. $("#game .renderer .sprite").remove();
  106. sprites = [];
  107. visibleSprites = [];
  108. }
  109. function processTrace(viewport, tracePoint) {
  110. var x = tracePoint.x,
  111. y = tracePoint.y,
  112. vx = viewport.x,
  113. vy = viewport.y,
  114. dx = viewport.x - tracePoint.x,
  115. dy = viewport.y - tracePoint.y,
  116. dist = Math.sqrt(dx*dx + dy*dy),
  117. frac,
  118. h, w, offset;
  119. // correct for fisheye
  120. dist = dist * cos(FINE2RAD(tracePoint.angle - viewport.angle));
  121. w = WALL_TEXTURE_WIDTH * SLICE_WIDTH;
  122. h = (VIEW_DIST / dist * TILEGLOBAL) >> 0;
  123. if (tracePoint.flags & TRACE_HIT_DOOR) {
  124. if (tracePoint.flags & TRACE_HIT_VERT) {
  125. if (x < vx) {
  126. frac = tracePoint.frac;
  127. } else {
  128. frac = 1 - tracePoint.frac;
  129. }
  130. } else {
  131. if (y < vy) {
  132. frac = 1 - tracePoint.frac;
  133. } else {
  134. frac = tracePoint.frac;
  135. }
  136. }
  137. } else {
  138. frac = 1 - tracePoint.frac;
  139. }
  140. offset = frac * w;
  141. if (offset > w - SLICE_WIDTH) {
  142. offset = w - SLICE_WIDTH;
  143. }
  144. offset = round(offset / SLICE_WIDTH) * SLICE_WIDTH;
  145. if (offset < 0) {
  146. offset = 0;
  147. }
  148. return {
  149. w : w,
  150. h : h,
  151. dist : dist,
  152. vert : tracePoint.flags & TRACE_HIT_VERT,
  153. offset : offset
  154. };
  155. }
  156. function clear() {
  157. var n, sprite;
  158. for (n=0;n<visibleSprites.length;n++) {
  159. sprite = visibleSprites[n].sprite;
  160. if (sprite && sprite.div) {
  161. sprite.div.style.display = "none";
  162. }
  163. }
  164. }
  165. function draw(viewport, level, tracers, visibleTiles) {
  166. var n, tracePoint;
  167. for (var n=0,len=tracers.length;n<len;++n) {
  168. tracePoint = tracers[n];
  169. if (!tracePoint.oob) {
  170. if (tracePoint.flags & Wolf.TRACE_HIT_DOOR) {
  171. drawDoor(n, viewport, tracePoint, level);
  172. } else {
  173. drawWall(n, viewport, tracePoint, level);
  174. }
  175. }
  176. }
  177. drawSprites(viewport, level, visibleTiles);
  178. }
  179. function updateSlice(n, textureSrc, proc) {
  180. var slice = slices[n],
  181. image = slice.texture,
  182. sliceStyle = slice.style,
  183. imgStyle = image.style,
  184. top = (Wolf.YRES - proc.h) / 2,
  185. left = -(proc.offset) >> 0,
  186. height = proc.h,
  187. z = (maxDistZ - proc.dist) >> 0,
  188. itop;
  189. if (Wolf.ISXP && Wolf.ISFIREFOX) {
  190. itop = (proc.texture % 2) ? 0 : -height;
  191. } else {
  192. itop = -(proc.texture-1) * height;
  193. textureSrc = "art/walls-shaded/64/walls.png";
  194. }
  195. if (image._src != textureSrc) {
  196. image._src = textureSrc;
  197. if (useBackgroundImage) {
  198. imgStyle.backgroundImage = "url(" + textureSrc + ")";
  199. } else {
  200. image.src = textureSrc;
  201. }
  202. }
  203. if (slice._zIndex != z) {
  204. sliceStyle.zIndex = slice._zIndex = z;
  205. }
  206. if (image._height != height) {
  207. sliceStyle.height = (image._height = height) + "px";
  208. if (Wolf.ISXP && Wolf.ISFIREFOX) {
  209. imgStyle.height = (height * 2) + "px";
  210. } else {
  211. imgStyle.height = (height * 120) + "px";
  212. }
  213. }
  214. if (image._itop != itop) {
  215. imgStyle.top = (image._itop = itop) + "px";
  216. }
  217. if (image._top != top) {
  218. sliceStyle.top = (image._top = top) + "px";
  219. }
  220. if (image._left != left) {
  221. imgStyle.left = (image._left = left) + "px";
  222. }
  223. }
  224. function drawWall(n, viewport, tracePoint, level) {
  225. var x = tracePoint.tileX,
  226. y = tracePoint.tileY,
  227. vx = POS2TILE(viewport.x),
  228. vy = POS2TILE(viewport.y),
  229. tileMap = level.tileMap,
  230. proc = processTrace(viewport, tracePoint),
  231. texture = proc.vert ? level.wallTexX[x][y] : level.wallTexY[x][y],
  232. textureSrc;
  233. // door sides
  234. if (tracePoint.flags & TRACE_HIT_VERT) {
  235. if (x >= vx && tileMap[x-1][y] & DOOR_TILE) {
  236. texture = TEX_PLATE;
  237. }
  238. if (x < vx && tileMap[x+1][y] & DOOR_TILE) {
  239. texture = TEX_PLATE;
  240. }
  241. } else {
  242. if (y >= vy && tileMap[x][y-1] & DOOR_TILE) {
  243. texture = TEX_PLATE;
  244. }
  245. if (y < vy && tileMap[x][y+1] & DOOR_TILE) {
  246. texture = TEX_PLATE;
  247. }
  248. }
  249. texture++;
  250. proc.texture = texture;
  251. if (texture % 2 == 0) {
  252. texture--;
  253. }
  254. textureSrc = texturePath + "w_" + texture + ".png";
  255. updateSlice(n, textureSrc, proc);
  256. }
  257. function drawDoor(n, viewport, tracePoint, level) {
  258. var proc = processTrace(viewport, tracePoint),
  259. texture, textureSrc;
  260. //texture = Wolf.TEX_DDOOR + 1;
  261. texture = level.state.doorMap[tracePoint.tileX][tracePoint.tileY].texture + 1;
  262. proc.texture = texture;
  263. if (texture % 2 == 0) {
  264. texture -= 1;
  265. }
  266. textureSrc = texturePath + "w_" + texture + ".png";
  267. updateSlice(n, textureSrc, proc);
  268. }
  269. function drawSprites(viewport, level, visibleTiles) {
  270. var vis, n,
  271. dist, dx, dy, angle,
  272. z, width, size,
  273. div, image,
  274. divStyle, imgStyle;
  275. // build visible sprites list
  276. visibleSprites = Wolf.Sprites.createVisList(viewport, level, visibleTiles);
  277. for (n = 0; n < visibleSprites.length; ++n ){
  278. vis = visibleSprites[n];
  279. dist = vis.dist;
  280. if (dist < MINDIST / 2 ) {
  281. //continue; // little hack to save speed & z-buffer
  282. }
  283. // make sure sprite is loaded
  284. if (!vis.sprite.div) {
  285. loadSprite(vis.sprite)
  286. }
  287. div = vis.sprite.div;
  288. divStyle = div.style;
  289. image = div.image;
  290. imgStyle = image.style;
  291. dx = vis.sprite.x - viewport.x;
  292. dy = vis.sprite.y - viewport.y;
  293. angle = atan2(dy, dx) - FINE2RAD(viewport.angle);
  294. //dist = dist * Math.cos(angle);
  295. size = (VIEW_DIST / dist * TILEGLOBAL) >> 0;
  296. divStyle.display = "block";
  297. divStyle.width = size + "px";
  298. divStyle.height = size + "px";
  299. divStyle.left = (XRES / 2 - size / 2 - tan(angle) * VIEW_DIST) + "px";
  300. divStyle.top = (YRES / 2 - size / 2) + "px";
  301. texture = Wolf.Sprites.getTexture(vis.sprite.tex[0]);
  302. textureSrc = spritePath + texture.sheet;
  303. if (image._src != textureSrc) {
  304. image._src = textureSrc;
  305. if (useBackgroundImage) {
  306. imgStyle.backgroundImage = "url(" + textureSrc + ")";
  307. } else {
  308. image.src = textureSrc;
  309. }
  310. }
  311. z = (maxDistZ - dist) >> 0;
  312. width = texture.num * size;
  313. left = -texture.idx * size;
  314. if (div._zIndex != z) {
  315. divStyle.zIndex = div._zIndex = z;
  316. }
  317. if (image._width != width) {
  318. imgStyle.width = (image._width = width) + "px";
  319. }
  320. if (image._height != size) {
  321. imgStyle.height = (image._height = size) + "px";
  322. }
  323. if (image._left != left) {
  324. imgStyle.left = (image._left = left) + "px";
  325. }
  326. }
  327. }
  328. function unloadSprite(sprite) {
  329. if (sprite.div) {
  330. $(sprite.div).remove();
  331. sprite.div = null;
  332. }
  333. }
  334. function loadSprite(sprite) {
  335. var div = document.createElement("div"),
  336. image;
  337. div.style.display = "none";
  338. div.style.position = "absolute";
  339. div.style.width = "128px";
  340. div.style.height = "128px";
  341. div.style.overflow = "hidden";
  342. div.className = "sprite";
  343. image = useBackgroundImage ? $("<div>") : $("<img>");
  344. image.css({
  345. position : "absolute",
  346. display : "block",
  347. top : 0,
  348. height : "100%",
  349. width : "100%",
  350. backgroundSize : "100%",
  351. backgroundRepeat : "no-repeat"
  352. });
  353. div.image = image[0];
  354. div.appendChild(div.image);
  355. sprite.div = div;
  356. $("#game .renderer").append(div);
  357. }
  358. return {
  359. init : init,
  360. draw : draw,
  361. clear : clear,
  362. loadSprite : loadSprite,
  363. unloadSprite : unloadSprite,
  364. reset : reset
  365. };
  366. })();