From 52427e610dc27fd6466bbda83d41b1d847f15e30 Mon Sep 17 00:00:00 2001 From: LabyStudio Date: Sat, 5 Feb 2022 16:18:31 +0100 Subject: [PATCH] implement item renderer, add debug information to overlay --- src/js/net/minecraft/client/GameWindow.js | 18 ++++- src/js/net/minecraft/client/Minecraft.js | 29 +++++--- .../net/minecraft/client/gui/IngameOverlay.js | 29 ++++---- .../minecraft/client/render/BlockRenderer.js | 33 ++++++++- .../minecraft/client/render/Tessellator.js | 4 +- .../minecraft/client/render/WorldRenderer.js | 4 + .../client/render/gui/ItemRenderer.js | 73 +++++++++++++++++++ .../minecraft/client/world/ChunkSection.js | 2 +- src/start.js | 1 + 9 files changed, 160 insertions(+), 33 deletions(-) create mode 100644 src/js/net/minecraft/client/render/gui/ItemRenderer.js diff --git a/src/js/net/minecraft/client/GameWindow.js b/src/js/net/minecraft/client/GameWindow.js index 2551e60..ae6d35c 100644 --- a/src/js/net/minecraft/client/GameWindow.js +++ b/src/js/net/minecraft/client/GameWindow.js @@ -25,9 +25,15 @@ window.GameWindow = class { this.wrapper.appendChild(this.canvasItems); // Stats - this.stats = new Stats() - this.stats.showPanel(0); - this.wrapper.appendChild(this.stats.dom); + this.statsFps = new Stats() + this.statsFps.showPanel(0); + this.statsFps.domElement.style.cssText = 'position:absolute;top:0px;right:80px;float:right'; + this.wrapper.appendChild(this.statsFps.dom); + + this.statsMs = new Stats() + this.statsMs.showPanel(1); + this.statsMs.domElement.style.cssText = 'position:absolute;top:0px;right:160px;float:right'; + this.wrapper.appendChild(this.statsMs.dom); // On resize let scope = this; @@ -112,12 +118,18 @@ window.GameWindow = class { let wrapperHeight = this.height * this.scaleFactor; let worldRenderer = this.minecraft.worldRenderer; + let itemRenderer = this.minecraft.itemRenderer; // Update world renderer size and camera worldRenderer.camera.aspect = this.width / this.height; worldRenderer.camera.updateProjectionMatrix(); worldRenderer.webRenderer.setSize(wrapperWidth, wrapperHeight); + // Update item renderer size and camera + itemRenderer.camera.aspect = this.width / this.height; + itemRenderer.camera.updateProjectionMatrix(); + itemRenderer.webRenderer.setSize(wrapperWidth, wrapperHeight); + // Update canvas 2d size this.canvas2d.style.width = wrapperWidth + "px"; this.canvas2d.style.height = wrapperHeight + "px"; diff --git a/src/js/net/minecraft/client/Minecraft.js b/src/js/net/minecraft/client/Minecraft.js index d2279d8..afdffa3 100644 --- a/src/js/net/minecraft/client/Minecraft.js +++ b/src/js/net/minecraft/client/Minecraft.js @@ -7,24 +7,23 @@ window.Minecraft = class { this.currentScreen = null; this.loadingScreen = null; - // Create window and world renderer - this.window = new GameWindow(this, canvasWrapperId); - this.worldRenderer = new WorldRenderer(this, this.window); + // Tick timer this.timer = new Timer(20); - // Create screen renderer + // Create window and world renderer + this.window = new GameWindow(this, canvasWrapperId); + + // Create renderers + this.worldRenderer = new WorldRenderer(this, this.window); this.screenRenderer = new ScreenRenderer(this, this.window); + this.itemRenderer = new ItemRenderer(this, this.window); // Create current screen and overlay this.ingameOverlay = new IngameOverlay(this, this.window); - // Update window size - this.window.updateWindowSize(); - // Display loading screen this.loadingScreen = new GuiLoadingScreen(); this.loadingScreen.setTitle("Building terrain..."); - this.displayScreen(this.loadingScreen); this.frames = 0; this.lastTime = Date.now(); @@ -32,6 +31,11 @@ window.Minecraft = class { // Create all blocks Block.create(); + this.itemRenderer.initialize(); + + // Update window size + this.window.updateWindowSize(); + // Create world this.world = new World(this); this.worldRenderer.scene.add(this.world.group); @@ -40,6 +44,8 @@ window.Minecraft = class { this.player = new Player(this, this.world); this.inventory = new Inventory(); + this.displayScreen(this.loadingScreen); + // Initialize this.init(); } @@ -69,7 +75,8 @@ window.Minecraft = class { } onLoop() { - this.window.stats.begin(); + this.window.statsFps.begin(); + this.window.statsMs.begin(); // Update the timer this.timer.advanceTime(); @@ -93,7 +100,8 @@ window.Minecraft = class { this.frames = 0; } - this.window.stats.end(); + this.window.statsFps.end(); + this.window.statsMs.end(); } onRender(partialTicks) { @@ -113,6 +121,7 @@ window.Minecraft = class { // Render the game this.worldRenderer.render(partialTicks); this.screenRenderer.render(partialTicks); + this.itemRenderer.render(partialTicks); } displayScreen(screen) { diff --git a/src/js/net/minecraft/client/gui/IngameOverlay.js b/src/js/net/minecraft/client/gui/IngameOverlay.js index 75bac8a..cc4050b 100644 --- a/src/js/net/minecraft/client/gui/IngameOverlay.js +++ b/src/js/net/minecraft/client/gui/IngameOverlay.js @@ -7,7 +7,6 @@ window.IngameOverlay = class extends Gui { this.textureCrosshair = Gui.loadTexture("icons.png"); this.textureHotbar = Gui.loadTexture("gui.png"); - this.textureTerrain = Gui.loadTexture("terrain.png"); } render(stack, mouseX, mouseY, partialTicks) { @@ -15,7 +14,14 @@ window.IngameOverlay = class extends Gui { this.renderCrosshair(stack, this.window.width / 2, this.window.height / 2) } - this.renderHotbar(stack, this.window.width / 2 - 100, this.window.height - 22); + this.renderHotbar(stack, this.window.width / 2 - 91, this.window.height - 22); + + // Debug + this.drawString(stack, Math.floor(this.minecraft.timer.fps) + " fps," + + " " + this.minecraft.world.lightUpdateQueue.length + " light updates," + + " " + this.minecraft.worldRenderer.chunkSectionUpdateQueue.length + " chunk updates", 1, 1); + this.drawString(stack, Math.floor(this.minecraft.player.x) + ", " + Math.floor(this.minecraft.player.y) + ", " + Math.floor(this.minecraft.player.z) + + " (" + Math.floor(this.minecraft.player.x >> 4) + ", " + Math.floor(this.minecraft.player.y >> 4) + ", " + Math.floor(this.minecraft.player.z >> 4) + ")", 1, 1 + 9); } renderCrosshair(stack, x, y) { @@ -24,6 +30,7 @@ window.IngameOverlay = class extends Gui { } renderHotbar(stack, x, y) { + // Render background this.drawSprite(stack, this.textureHotbar, 0, 0, 200, 22, x, y, 200, 22) this.drawSprite( stack, @@ -34,23 +41,13 @@ window.IngameOverlay = class extends Gui { 24, 24 ) + // Render items for (let i = 0; i < 9; i++) { let typeId = this.minecraft.inventory.getItemInSlot(i); - if (typeId !== 0) { - /*this.renderBlock( - stack, - this.textureTerrain, Block.getById(typeId), - x + i * 20 + 11, - y + 9 - );*/ - - // UV Mapping - let textureIndex = Block.getById(typeId).getTextureForFace(EnumBlockFace.NORTH); - let minU = (textureIndex % 16) / 16.0; - let minV = Math.floor(textureIndex / 16) / 16.0; - - this.drawSprite(stack, this.textureTerrain, minU * 256, minV, 16, 16, x + 3 + i * 20, y + 3, 16, 16) + let renderId = "hotbar" + i; + let block = Block.getById(typeId); + this.minecraft.itemRenderer.renderItemInGui(renderId, block, x + i * 20 + 11, y + 11); } } } diff --git a/src/js/net/minecraft/client/render/BlockRenderer.js b/src/js/net/minecraft/client/render/BlockRenderer.js index fcb7380..2840f4d 100644 --- a/src/js/net/minecraft/client/render/BlockRenderer.js +++ b/src/js/net/minecraft/client/render/BlockRenderer.js @@ -8,7 +8,7 @@ window.BlockRenderer = class { this.tessellator.bindTexture(worldRenderer.terrainTexture); } - renderBlock(world, group, block, x, y, z) { + renderBlock(world, block, x, y, z) { let boundingBox = block.getBoundingBox(world, x, y, z); // Render all faces @@ -47,7 +47,8 @@ window.BlockRenderer = class { // Classic lightning if (BlockRenderer.CLASSIC_LIGHTNING) { - let brightness = 0.9 / 15.0 * world.getTotalLightAt(minX + face.x, minY + face.y, minZ + face.z) + 0.1; + 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); } @@ -112,6 +113,10 @@ window.BlockRenderer = class { } getAverageLightLevelAt(world, x, y, z) { + if (world === null) { + return 15; + } + let totalLightLevel = 0; let totalBlocks = 0; @@ -135,4 +140,28 @@ window.BlockRenderer = class { // Calculate the average light level of all surrounding blocks return totalBlocks === 0 ? 0 : totalLightLevel / totalBlocks; } + + renderGuiBlock(group, block, x, y, size) { + this.tessellator.startDrawing(); + + let boundingBox = block.getBoundingBox(null, 0, 0, 0); + this.renderFace(null, block, boundingBox, EnumBlockFace.TOP, 0, 0, 0); + this.renderFace(null, block, boundingBox, EnumBlockFace.NORTH, 0, 0, 0); + this.renderFace(null, block, boundingBox, EnumBlockFace.EAST, 0, 0, 0); + + let mesh = this.tessellator.draw(group); + mesh.geometry.center(); + + mesh.rotation.x = -MathHelper.toRadians(45 / 1.5); + mesh.rotation.y = MathHelper.toRadians(45); + + mesh.position.x = x; + mesh.position.y = -y; + mesh.position.z = -3; + + //let scale = Math.cos(Date.now() / 1000) * 100; + mesh.scale.x = size; + mesh.scale.y = size; + mesh.scale.z = size; + } } \ 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 a5ff94a..e9db17a 100644 --- a/src/js/net/minecraft/client/render/Tessellator.js +++ b/src/js/net/minecraft/client/render/Tessellator.js @@ -3,7 +3,8 @@ window.Tessellator = class { constructor() { this.material = new THREE.MeshBasicMaterial({ vertexColors: THREE.VertexColors, - side: THREE.BackSide, + //side: THREE.BackSide, + side: THREE.DoubleSide, transparent: true, depthTest: true }); @@ -66,6 +67,7 @@ window.Tessellator = class { let mesh = new THREE.Mesh(geometry, this.material); group.matrixAutoUpdate = false; group.add(mesh); + return mesh; } } \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/WorldRenderer.js b/src/js/net/minecraft/client/render/WorldRenderer.js index 5be26dd..df5b11e 100644 --- a/src/js/net/minecraft/client/render/WorldRenderer.js +++ b/src/js/net/minecraft/client/render/WorldRenderer.js @@ -15,6 +15,10 @@ window.WorldRenderer = class { // Block Renderer this.blockRenderer = new BlockRenderer(this); + this.initialize(); + } + + initialize() { // Create world camera this.camera = new THREE.PerspectiveCamera(0, 1, 0.001, 1000); this.camera.rotation.order = 'ZYX'; diff --git a/src/js/net/minecraft/client/render/gui/ItemRenderer.js b/src/js/net/minecraft/client/render/gui/ItemRenderer.js new file mode 100644 index 0000000..9ae08e0 --- /dev/null +++ b/src/js/net/minecraft/client/render/gui/ItemRenderer.js @@ -0,0 +1,73 @@ +window.ItemRenderer = class { + + constructor(minecraft, window) { + this.minecraft = minecraft; + this.window = window; + + this.items = []; + } + + initialize() { + // Create item camera + this.camera = new THREE.OrthographicCamera(0, 0, 0, 0, 0, 300); + this.camera.near = 0; + this.camera.far = 15; + this.camera.rotation.order = 'ZYX'; + this.camera.up = new THREE.Vector3(0, 1, 0); + + // Create scene + this.scene = new THREE.Scene(); + this.scene.matrixAutoUpdate = false; + + // Create web renderer + this.webRenderer = new THREE.WebGLRenderer({ + canvas: this.window.canvasItems, + antialias: true + }); + + // Settings + this.webRenderer.setSize(this.window.width, this.window.height); + this.webRenderer.shadowMap.enabled = true; + this.webRenderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap + this.webRenderer.autoClear = false; + this.webRenderer.sortObjects = false; + this.webRenderer.setClearColor(0x000000, 0); + this.webRenderer.clear(); + } + + render(partialTicks) { + // Update camera + this.camera.left = -this.window.width / 2; + this.camera.right = this.window.width / 2; + this.camera.top = this.window.height / 2; + this.camera.bottom = -this.window.height / 2; + this.camera.setViewOffset(this.window.width, this.window.height, this.window.width / 2, this.window.height / 2, this.window.width, this.window.height); + this.camera.updateProjectionMatrix(); + + // Render scene + this.webRenderer.render(this.scene, this.camera); + } + + renderItemInGui(renderId, block, x, y) { + let meta = this.items[renderId]; + if (typeof meta === "undefined") { + let meta = {}; + + let group = new THREE.Group(); + this.minecraft.worldRenderer.blockRenderer.renderGuiBlock(group, block, x, y, 10); + this.scene.add(group); + + meta.group = group; + meta.typeId = block.getId(); + meta.x = x; + meta.y = y; + this.items[renderId] = meta; + } else { + if (meta.typeId !== block.getId() || meta.x !== x || meta.y !== y) { + this.scene.remove(meta.group); + delete this.items[renderId]; + this.renderItemInGui(renderId, block, x, y); + } + } + } +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/world/ChunkSection.js b/src/js/net/minecraft/client/world/ChunkSection.js index 153dd1f..427aec3 100644 --- a/src/js/net/minecraft/client/world/ChunkSection.js +++ b/src/js/net/minecraft/client/world/ChunkSection.js @@ -62,7 +62,7 @@ window.ChunkSection = class { let absoluteZ = this.z * ChunkSection.SIZE + z; let block = Block.getById(typeId); - renderer.blockRenderer.renderBlock(this.world, this.group, block, absoluteX, absoluteY, absoluteZ); + renderer.blockRenderer.renderBlock(this.world, block, absoluteX, absoluteY, absoluteZ); } } } diff --git a/src/start.js b/src/start.js index 94ef97c..e140cac 100644 --- a/src/start.js +++ b/src/start.js @@ -91,6 +91,7 @@ loadScripts([ "src/js/net/minecraft/client/render/isometric/Triangle.js", "src/js/net/minecraft/client/render/gui/FontRenderer.js", "src/js/net/minecraft/client/render/gui/ScreenRenderer.js", + "src/js/net/minecraft/client/render/gui/ItemRenderer.js", "src/js/net/minecraft/client/render/Tessellator.js", "src/js/net/minecraft/client/render/WorldRenderer.js", "src/js/net/minecraft/client/render/BlockRenderer.js"