diff --git a/src/js/net/minecraft/client/Minecraft.js b/src/js/net/minecraft/client/Minecraft.js index a3f930d..6100609 100644 --- a/src/js/net/minecraft/client/Minecraft.js +++ b/src/js/net/minecraft/client/Minecraft.js @@ -13,6 +13,7 @@ import Block from "./world/block/Block.js"; import BoundingBox from "../util/BoundingBox.js"; import {BlockRegistry} from "./world/block/BlockRegistry.js"; import FontRenderer from "./render/gui/FontRenderer.js"; +import GrassColorizer from "./render/GrassColorizer.js"; export default class Minecraft { @@ -59,6 +60,9 @@ export default class Minecraft { // Create font renderer this.fontRenderer = new FontRenderer(this); + // Grass colorizer + this.grassColorizer = new GrassColorizer(this); + // Update window size this.window.updateWindowSize(); diff --git a/src/js/net/minecraft/client/gui/widgets/GuiKeyButton.js b/src/js/net/minecraft/client/gui/widgets/GuiKeyButton.js index a1131f3..b4a34a4 100644 --- a/src/js/net/minecraft/client/gui/widgets/GuiKeyButton.js +++ b/src/js/net/minecraft/client/gui/widgets/GuiKeyButton.js @@ -4,6 +4,8 @@ export default class GuiKeyButton extends GuiButton { constructor(name, key, x, y, width, height, callback) { super(name + ": " + key, x, y, width, height, _ => callback(this.key)); + + this.name = name; this.listening = false; } @@ -14,7 +16,7 @@ export default class GuiKeyButton extends GuiButton { keyTyped(key) { if (this.listening) { - this.string = name + ": " + key; + this.string = this.name + ": " + key; this.listening = false; this.key = key; this.callback(); diff --git a/src/js/net/minecraft/client/render/BlockRenderer.js b/src/js/net/minecraft/client/render/BlockRenderer.js index 696101d..51d931c 100644 --- a/src/js/net/minecraft/client/render/BlockRenderer.js +++ b/src/js/net/minecraft/client/render/BlockRenderer.js @@ -60,79 +60,85 @@ export default class BlockRenderer { minV = 1 - minV; maxV = 1 - maxV; + // Get color multiplier + let color = block.getColor(world, x, y, z, face); + let red = (color >> 16 & 255) / 255.0; + let green = (color >> 8 & 255) / 255.0; + let blue = (color & 255) / 255.0; + // Classic lightning if (!ambientOcclusion) { let level = world === null ? 15 : world.getTotalLightAt(minX + face.x, minY + face.y, minZ + face.z); let brightness = 0.9 / 15.0 * level + 0.1; - let color = brightness * face.getShading(); - this.tessellator.setColor(color, color, color); + let shade = brightness * face.getShading(); + this.tessellator.setColor(red * shade, green * shade, blue * shade); } // Set opacity of block (Using alpha channel in texture right now) // this.tessellator.setAlpha(1 - block.getTransparency()); // Add face to tessellator - this.addFace(world, face, ambientOcclusion, minX, minY, minZ, maxX, maxY, maxZ, minU, minV, maxU, maxV); + this.addFace(world, face, ambientOcclusion, minX, minY, minZ, maxX, maxY, maxZ, minU, minV, maxU, maxV, red, green, blue); } - addFace(world, face, ambientOcclusion, minX, minY, minZ, maxX, maxY, maxZ, minU, minV, maxU, maxV) { + addFace(world, face, ambientOcclusion, minX, minY, minZ, maxX, maxY, maxZ, minU, minV, maxU, maxV, red = 1, green = 1, blue = 1) { if (face === EnumBlockFace.BOTTOM) { - this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, maxZ, maxU, maxV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, minZ, maxU, minV); - this.addBlockCorner(world, face, ambientOcclusion, minX, minY, minZ, minU, minV); - this.addBlockCorner(world, face, ambientOcclusion, minX, minY, maxZ, minU, maxV); + this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, maxZ, maxU, maxV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, minZ, maxU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, minX, minY, minZ, minU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, minX, minY, maxZ, minU, maxV, red, green, blue); } if (face === EnumBlockFace.TOP) { - this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, maxZ, minU, maxV); - this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, minZ, minU, minV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, minZ, maxU, minV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, maxZ, maxU, maxV); + this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, maxZ, minU, maxV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, minZ, minU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, minZ, maxU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, maxZ, maxU, maxV, red, green, blue); } if (face === EnumBlockFace.NORTH) { - this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, minZ, minU, minV); - this.addBlockCorner(world, face, ambientOcclusion, minX, minY, minZ, minU, maxV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, minZ, maxU, maxV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, minZ, maxU, minV); + this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, minZ, minU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, minX, minY, minZ, minU, maxV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, minZ, maxU, maxV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, minZ, maxU, minV, red, green, blue); } if (face === EnumBlockFace.SOUTH) { - this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, maxZ, maxU, minV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, maxZ, minU, minV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, maxZ, minU, maxV); - this.addBlockCorner(world, face, ambientOcclusion, minX, minY, maxZ, maxU, maxV); + this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, maxZ, maxU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, maxZ, minU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, maxZ, minU, maxV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, minX, minY, maxZ, maxU, maxV, red, green, blue); } if (face === EnumBlockFace.WEST) { - this.addBlockCorner(world, face, ambientOcclusion, minX, minY, maxZ, minU, maxV); - this.addBlockCorner(world, face, ambientOcclusion, minX, minY, minZ, maxU, maxV); - this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, minZ, maxU, minV); - this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, maxZ, minU, minV); + this.addBlockCorner(world, face, ambientOcclusion, minX, minY, maxZ, minU, maxV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, minX, minY, minZ, maxU, maxV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, minZ, maxU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, minX, maxY, maxZ, minU, minV, red, green, blue); } if (face === EnumBlockFace.EAST) { - this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, maxZ, maxU, minV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, minZ, minU, minV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, minZ, minU, maxV); - this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, maxZ, maxU, maxV); + this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, maxZ, maxU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, maxY, minZ, minU, minV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, minZ, minU, maxV, red, green, blue); + this.addBlockCorner(world, face, ambientOcclusion, maxX, minY, maxZ, maxU, maxV, red, green, blue); } } - addBlockCorner(world, face, ambientOcclusion, x, y, z, u, v) { + addBlockCorner(world, face, ambientOcclusion, x, y, z, u, v, red, green, blue) { // Smooth lightning if (ambientOcclusion) { - this.setAverageColor(world, face, x, y, z); + this.setAverageBrightness(world, face, x, y, z, red, green, blue); } this.tessellator.addVertexWithUV(x, y, z, u, v); } - setAverageColor(world, face, x, y, z) { + setAverageBrightness(world, face, x, y, z, red = 1, green = 1, blue = 1) { // Get the average light level of all 4 blocks at this corner let lightLevelAtThisCorner = this.getAverageLightLevelAt(world, x, y, z); // Convert light level from [0 - 15] to [0.1 - 1.0] let brightness = 0.9 / 15.0 * lightLevelAtThisCorner + 0.1; - let color = brightness * face.getShading(); + let shading = brightness * face.getShading(); - // Set color with shading - this.tessellator.setColorRGB(color, color, color); + // Transform brightness of edge + this.tessellator.setColor(red * shading, green * shading, blue * shading); } getAverageLightLevelAt(world, x, y, z) { diff --git a/src/js/net/minecraft/client/render/GrassColorizer.js b/src/js/net/minecraft/client/render/GrassColorizer.js new file mode 100644 index 0000000..8569416 --- /dev/null +++ b/src/js/net/minecraft/client/render/GrassColorizer.js @@ -0,0 +1,35 @@ +export default class GrassColorizer { + + constructor(minecraft) { + this.texture = minecraft.resources["misc/grasscolor.png"]; + + this.bitMap = this.createBitMap(this.texture); + } + + getColor(temperature, humidity) { + humidity *= temperature; + + let x = Math.floor((1.0 - temperature) * 255); + let y = Math.floor((1.0 - humidity) * 255); + + let index = (x + y * this.texture.width) * 4 + if (index >= this.bitMap.length) { + return -65281; + } + + let red = this.bitMap[index]; + let green = this.bitMap[index + 1]; + let blue = this.bitMap[index + 2]; + + return red << 16 | green << 8 | blue; + } + + createBitMap(img) { + let canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height); + return canvas.getContext('2d').getImageData(0, 0, img.width, img.height).data; + } + +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/Tessellator.js b/src/js/net/minecraft/client/render/Tessellator.js index f2b1997..e45f224 100644 --- a/src/js/net/minecraft/client/render/Tessellator.js +++ b/src/js/net/minecraft/client/render/Tessellator.js @@ -36,6 +36,13 @@ export default class Tessellator { this.setAlpha(alpha); } + multiplyColor(red, green, blue, alpha = 1) { + this.red *= red; + this.green *= green; + this.blue *= blue; + this.alpha *= alpha; + } + setAlpha(alpha) { this.alpha = alpha; } diff --git a/src/js/net/minecraft/client/world/World.js b/src/js/net/minecraft/client/world/World.js index 9e48afe..2c5a853 100644 --- a/src/js/net/minecraft/client/world/World.js +++ b/src/js/net/minecraft/client/world/World.js @@ -485,8 +485,12 @@ export default class World { return MathHelper.calculateCelestialAngle(this.time, partialTicks); } - getTemperature(x, z) { - return 1.24; + getTemperature(x, y, z) { + return 0.75; // TODO implement biomes + } + + getHumidity(x, y, z) { + return 0.85; // TODO implement biomes } getSkyColor(x, z, partialTicks) { diff --git a/src/js/net/minecraft/client/world/block/Block.js b/src/js/net/minecraft/client/world/block/Block.js index 4636959..cfa1a5e 100644 --- a/src/js/net/minecraft/client/world/block/Block.js +++ b/src/js/net/minecraft/client/world/block/Block.js @@ -48,6 +48,10 @@ export default class Block { return typeId === 0 || !Block.getById(typeId).isSolid(); } + getColor(world, x, y, z, face) { + return 0xffffff; + } + getLightValue() { return 0; } diff --git a/src/js/net/minecraft/client/world/block/type/BlockGrass.js b/src/js/net/minecraft/client/world/block/type/BlockGrass.js index 7e59dbb..c099c03 100644 --- a/src/js/net/minecraft/client/world/block/type/BlockGrass.js +++ b/src/js/net/minecraft/client/world/block/type/BlockGrass.js @@ -10,6 +10,22 @@ export default class BlockGrass extends Block { this.sound = Block.sounds.grass; } + getColor(world, x, y, z, face) { + // Only top face has a biome color + if (face !== EnumBlockFace.TOP) { + return 0xFFFFFF; + } + + // Inventory items have a default color + if (world === null) { + return 0x7cbd6b; + } + + let temperature = world.getTemperature(x, y, z); + let humidity = world.getHumidity(x, y, z); + return world.minecraft.grassColorizer.getColor(temperature, humidity); + } + getTextureForFace(face) { switch (face) { case EnumBlockFace.TOP: diff --git a/src/js/net/minecraft/client/world/block/type/BlockLeave.js b/src/js/net/minecraft/client/world/block/type/BlockLeave.js index 3be1dac..a07e067 100644 --- a/src/js/net/minecraft/client/world/block/type/BlockLeave.js +++ b/src/js/net/minecraft/client/world/block/type/BlockLeave.js @@ -9,6 +9,17 @@ export default class BlockLeave extends Block { this.sound = Block.sounds.grass; } + getColor(world, x, y, z, face) { + // Inventory items have a default color + if (world === null) { + return 0 << 16 | 255 << 8 | 0; + } + + let temperature = world.getTemperature(x, y, z); + let humidity = world.getHumidity(x, y, z); + return world.minecraft.grassColorizer.getColor(temperature, humidity); + } + getOpacity() { return 0.3; } diff --git a/src/resources/misc/grasscolor.png b/src/resources/misc/grasscolor.png new file mode 100644 index 0000000..95de068 Binary files /dev/null and b/src/resources/misc/grasscolor.png differ diff --git a/src/resources/terrain/terrain.png b/src/resources/terrain/terrain.png index 91c9555..a530bfe 100644 Binary files a/src/resources/terrain/terrain.png and b/src/resources/terrain/terrain.png differ diff --git a/src/start.js b/src/start.js index 7439910..c0a9dbb 100644 --- a/src/start.js +++ b/src/start.js @@ -2,21 +2,6 @@ import Minecraft from './js/net/minecraft/client/Minecraft.js'; let resources = []; -// Browser test function -function isES6() { - try { - Function("() => {};"); - return true; - } catch (exception) { - return false; - } -} - -// Test for browser support -if (!isES6()) { - alert("Your browser isn't supported! Please use another one."); -} - // Script loader function loadScripts(scripts) { let total = scripts.length; @@ -71,6 +56,7 @@ function updatePreStatus(message) { // Load textures loadTexture([ + "misc/grasscolor.png", "gui/font.png", "gui/gui.png", "gui/background.png",