diff --git a/README.md b/README.md index e46d593..b2dbcf5 100755 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ - Features textures, a mini-map, procedural/maze generation, doors, fully implemented first person controller (including strafe and up/down mouselook), and more! - Uses modern javascript and browser features such as classes, modules, JSDoc (mostly), PointerLock API, etc. -*This project started as a quest to improve a much older raycaster I made a long time ago, and the original code plus its README can be found in the `Version 1` directory.* + # Credits Much of the work here is inspired by Lode Vandevenne's excellent tutorials on raycasting. diff --git a/raycaster.js b/raycaster.js index 24e5c7a..e24b6c8 100755 --- a/raycaster.js +++ b/raycaster.js @@ -6,6 +6,7 @@ import { Timer } from "./src/Timer.js"; import { MiniMap } from "./src/MiniMap.js"; import { OptionsHandler } from "./src/OptionsHandler.js"; import { Player } from "./src/Player.js"; +import { Sprite } from "./src/Sprite.js"; import * as Utils from "./src/Utils.js"; @@ -45,6 +46,13 @@ var playerSize = Utils.castToInt(padding * 4/5); var miniMap = new MiniMap(blockSize, playerSize, padding); +// Sprite setup +var sprites = [new Sprite(new Utils.Vector2(2, 1.5), barrelTexture)]; +var zBuffer = [] +for (let z = 0; z < screenWidth; z++) { + zBuffer.push(0); +} + // Main loop function main() { // Calculate delta time @@ -254,12 +262,12 @@ function main() { let dimFactor = 0.9 + (0.2 * (currentDistance)); let fogPercentage = (optionsHandler.getOption("fog")) ? 0.1 * currentDistance : 0; - let pixelindex = (ceilingTex.y * textureWidth + ceilingTex.x) * 4; - red = ceilingTexture.data[pixelindex] / dimFactor; + let pixelIndex = (ceilingTex.y * textureWidth + ceilingTex.x) * 4; + red = ceilingTexture.data[pixelIndex] / dimFactor; red = red * (1 - fogPercentage) + fogPercentage * 0.1; - green = ceilingTexture.data[pixelindex+1] / dimFactor; + green = ceilingTexture.data[pixelIndex+1] / dimFactor; green = green * (1 - fogPercentage) + fogPercentage * 0.1; - blue = ceilingTexture.data[pixelindex+2] / dimFactor; + blue = ceilingTexture.data[pixelIndex+2] / dimFactor; blue = blue * (1 - fogPercentage) + fogPercentage * 0.1; } @@ -276,26 +284,88 @@ function main() { let weight = currentDistance / perpendicularWallDistance; - let currentFloor = new Utils.Vector2(weight * floorCeilingWallPos.x + (1.0 - weight) * player.position.x, - weight * floorCeilingWallPos.y + (1.0 - weight) * player.position.y) + let currentFloor = new Utils.Vector2( + weight * floorCeilingWallPos.x + (1.0 - weight) * player.position.x, + weight * floorCeilingWallPos.y + (1.0 - weight) * player.position.y + ); - let floorTex = new Utils.Vector2(Utils.castToInt(currentFloor.x * textureWidth) % textureWidth, - Utils.castToInt(currentFloor.y * textureHeight) % textureHeight); + let floorTex = new Utils.Vector2( + Utils.castToInt(currentFloor.x * textureWidth) % textureWidth, + Utils.castToInt(currentFloor.y * textureHeight) % textureHeight + ); let dimFactor = 0.9 + (0.2 * (currentDistance)); let fogPercentage = (optionsHandler.getOption("fog")) ? 0.1 * currentDistance : 0; - let pixelindex = (floorTex.y * textureWidth + floorTex.x) * 4; - red = groundTexture.data[pixelindex] / dimFactor; + let pixelIndex = (floorTex.y * textureWidth + floorTex.x) * 4; + red = groundTexture.data[pixelIndex] / dimFactor; red = red * (1 - fogPercentage) + fogPercentage * 0.1; - green = groundTexture.data[pixelindex+1] / dimFactor; + green = groundTexture.data[pixelIndex+1] / dimFactor; green = green * (1 - fogPercentage) + fogPercentage * 0.1; - blue = groundTexture.data[pixelindex+2] / dimFactor; + blue = groundTexture.data[pixelIndex+2] / dimFactor; blue = blue * (1 - fogPercentage) + fogPercentage * 0.1; } renderer.drawPixel(x, y, red, green, blue); } + + // Set zBuffer for sprite casting + zBuffer[x] = perpendicularWallDistance; + for (const sprite of sprites) { + sprite.distance = ((player.position.x - sprite.position.x) * (player.position.x - sprite.position.x) + (player.position.y - sprite.position.y) * (player.position.y - sprite.position.y)); + } + sprites.sort((a, b) => a.distance - b.distance); + } + + for (const sprite of sprites) { + let relSpritePosition = new Utils.Vector2( + sprite.position.x - player.position.x, + sprite.position.y - player.position.y + ); + + let invDet = 1.0 / (player.plane.x * player.direction.y - player.direction.x * player.plane.y); + let transformPosition = new Utils.Vector2( + invDet * (player.direction.y * relSpritePosition.x - player.direction.x * relSpritePosition.y), + invDet * (-player.plane.y * relSpritePosition.x + player.plane.x * relSpritePosition.y) + ); + + let spriteScreenX = Utils.castToInt((screenWidth / 2) * (1 + transformPosition.x / transformPosition.y)); + + let spriteHeight = Math.abs(Utils.castToInt(screenHeight / (transformPosition.y))); + let drawStartY = -spriteHeight / 2 + screenHeight / 2; + if (drawStartY < 0) drawStartY = 0; + let drawEndY = spriteHeight / 2 + screenHeight / 2; + if (drawEndY >= screenHeight) drawEndY = screenHeight - 1; + + let spriteWidth = Math.abs(Utils.castToInt(screenHeight / (transformPosition.y))); + let drawStartX = -spriteWidth / 2 + spriteScreenX; + if (drawStartX < 0) drawStartX = 0; + let drawEndX = spriteWidth / 2 + spriteScreenX; + if (drawEndX >= screenWidth) drawEndX = screenWidth - 1; + + for (let stripe = drawStartX; stripe < drawEndX; stripe++) { + let texX = Utils.castToInt((stripe - (-spriteWidth / 2 + spriteScreenX)) * textureWidth / spriteWidth); + // the conditions in the if are: + // 1) it's in front of camera plane so you don't see things behind you + // 2) it's on the screen (left) + // 3) it's on the screen (right) + // 4) ZBuffer, with perpendicular distance + if (transformPosition.y > 0 && stripe > 0 && stripe < screenWidth && transformPosition.y < zBuffer[stripe]) { + for (let y = drawStartY; y < drawEndY; y++) { + let d = (y) - screenHeight + spriteHeight; + let texY = ((d * textureHeight) / spriteHeight); + let pixelIndex = (textureWidth * texY + texX) * 4; + let red = barrelTexture.data[pixelIndex] / 1; + // red = red * (1 - fogPercentage) + fogPercentage * 0.1; + let green = barrelTexture.data[pixelIndex+1] / 1; + // green = green * (1 - fogPercentage) + fogPercentage * 0.1; + let blue = barrelTexture.data[pixelIndex+2] / 1; + // blue = blue * (1 - fogPercentage) + fogPercentage * 0.1; + if (Utils.castToInt(red) != 0 && Utils.castToInt(green) != 0 && Utils.castToInt(blue) != 0) // Transparency for black + renderer.drawPixel(stripe, y, red, green, blue); + } + } + } } // Render minimap @@ -319,12 +389,13 @@ function main() { // Textures const textureWidth = 64; const textureHeight = 64; -const textureUrls = ["textures/bricks.png", "textures/tiles.png", "textures/tiles.png", "textures/door.png"]; +const textureUrls = ["textures/bricks.png", "textures/tiles.png", "textures/tiles.png", "textures/door.png", "textures/barrel 2.png"]; var wallTexture; var groundTexture; var ceilingTexture; var doorTexture; +var barrelTexture; Utils.loadImages(textureUrls).then(textures => { ctx.drawImage(textures[0], 0, 0); @@ -338,6 +409,9 @@ Utils.loadImages(textureUrls).then(textures => { ctx.drawImage(textures[3], 0, 0); doorTexture = ctx.getImageData(0, 0, textureWidth, textureHeight); - + + ctx.drawImage(textures[4], 0, 0); + barrelTexture = ctx.getImageData(0, 0, textureWidth, textureHeight); + requestAnimationFrame(main); }); \ No newline at end of file diff --git a/src/Player.js b/src/Player.js index 5947ad3..f5016c6 100644 --- a/src/Player.js +++ b/src/Player.js @@ -9,7 +9,7 @@ export class Player { * Creates an instance of Player. */ constructor() { - this.position = new Utils.Vector2(1.5, 1.5); + this.position = new Utils.Vector2(3.5, 1.5); this.direction = new Utils.Vector2(1, 0); this.plane = new Utils.Vector2(0, 0.66); this.walkTime = 0.0; @@ -78,7 +78,7 @@ export class Player { if (moved) { this.walkTime += timer.getDeltaTime(); - this.pitch += (Math.cos(10 * this.walkTime) / 2 * 8); + this.pitch += (Math.cos(10 * this.walkTime) / 2 * 7); } } diff --git a/src/Sprite.js b/src/Sprite.js new file mode 100644 index 0000000..2d4ebfc --- /dev/null +++ b/src/Sprite.js @@ -0,0 +1,8 @@ +export class Sprite { + constructor(position, texture) { + this.distance = 0.0; + + this.position = position; + this.texture = texture; + } +} \ No newline at end of file diff --git a/textures/barrel 2.png b/textures/barrel 2.png new file mode 100755 index 0000000..eea212d Binary files /dev/null and b/textures/barrel 2.png differ diff --git a/textures/barrel.png b/textures/barrel.png new file mode 100644 index 0000000..a39547c Binary files /dev/null and b/textures/barrel.png differ