diff --git a/.github/assets/controls.png b/.github/assets/controls.png index 83674a9..4e05a19 100644 Binary files a/.github/assets/controls.png and b/.github/assets/controls.png differ diff --git a/.github/assets/lightning.png b/.github/assets/lightning.png index 5d86e35..c03e8e0 100644 Binary files a/.github/assets/lightning.png and b/.github/assets/lightning.png differ diff --git a/.github/assets/night.png b/.github/assets/night.png index 9089175..9f64619 100644 Binary files a/.github/assets/night.png and b/.github/assets/night.png differ diff --git a/README.md b/README.md index 4042687..d3b450d 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ Click [here](https://labystudio.github.io/js-minecraft/) for a demo! - Underwater fog - Dynamic FOV - Third person + - First person hand + - First person item in hand - GUI - Screens - Loading Screen diff --git a/src/js/net/minecraft/client/Minecraft.js b/src/js/net/minecraft/client/Minecraft.js index f676f8f..a3f930d 100644 --- a/src/js/net/minecraft/client/Minecraft.js +++ b/src/js/net/minecraft/client/Minecraft.js @@ -287,6 +287,9 @@ export default class Minecraft { // Place block this.world.setBlockAt(x, y, z, typeId); + // Swing player arm + this.player.swingArm(); + // Handle block abilities let block = Block.getById(typeId); block.onBlockPlaced(this.world, x, y, z, hitResult.face); diff --git a/src/js/net/minecraft/client/gui/screens/GuiControls.js b/src/js/net/minecraft/client/gui/screens/GuiControls.js index c27e574..045e5b8 100644 --- a/src/js/net/minecraft/client/gui/screens/GuiControls.js +++ b/src/js/net/minecraft/client/gui/screens/GuiControls.js @@ -16,17 +16,17 @@ export default class GuiControls extends GuiScreen { let settings = this.minecraft.settings; let scope = this; - this.buttonList.push(new GuiKeyButton("Crouch", settings.crouching, this.width / 2 - 100, this.height / 2 - 20, 200, 20, function (key) { + this.buttonList.push(new GuiKeyButton("Crouch", settings.crouching, this.width / 2 - 100, this.height / 2 - 30, 200, 20, function (key) { settings.crouching = key; scope.init(); })); - this.buttonList.push(new GuiKeyButton("Sprint", settings.sprinting, this.width / 2 - 100, this.height / 2 + 5, 200, 20, function (key) { + this.buttonList.push(new GuiKeyButton("Sprint", settings.sprinting, this.width / 2 - 100, this.height / 2 - 5, 200, 20, function (key) { settings.sprinting = key; scope.init(); })); - this.buttonList.push(new GuiKeyButton("Toggle Perspective", settings.togglePerspective, this.width / 2 - 100, this.height / 2 + 30, 200, 20, function (key) { + this.buttonList.push(new GuiKeyButton("Toggle Perspective", settings.togglePerspective, this.width / 2 - 100, this.height / 2 + 20, 200, 20, function (key) { settings.togglePerspective = key; scope.init(); })); diff --git a/src/js/net/minecraft/client/render/BlockRenderer.js b/src/js/net/minecraft/client/render/BlockRenderer.js index 272fb54..ac77ced 100644 --- a/src/js/net/minecraft/client/render/BlockRenderer.js +++ b/src/js/net/minecraft/client/render/BlockRenderer.js @@ -287,6 +287,25 @@ export default class BlockRenderer { mesh.scale.z = 6; } + renderBlockInFirstPerson(group, block, brightness) { + this.tessellator.startDrawing(); + + // Render block + this.renderBlock(null, block, 0, 0, 0); + + // Change brightness + this.tessellator.transformBrightness(brightness); + + // Create mesh + let mesh = this.tessellator.draw(group); + mesh.geometry.center(); + + // Scale + mesh.scale.x = 16; + mesh.scale.y = 16; + mesh.scale.z = 16; + } + renderGuiBlock(group, block, x, y, size, brightness) { this.tessellator.startDrawing(); diff --git a/src/js/net/minecraft/client/render/WorldRenderer.js b/src/js/net/minecraft/client/render/WorldRenderer.js index e1a0b97..30d2da9 100644 --- a/src/js/net/minecraft/client/render/WorldRenderer.js +++ b/src/js/net/minecraft/client/render/WorldRenderer.js @@ -347,11 +347,15 @@ export default class WorldRenderer { renderHand(partialTicks) { // Hide hand before rendering let player = this.minecraft.player; - let stack = player.renderer.handGroup; + let stack = player.renderer.firstPersonGroup; stack.visible = false; + let firstPerson = this.minecraft.settings.thirdPersonView === 0; + let itemId = firstPerson ? this.itemToRender : player.inventory.getItemInSelectedSlot(); + let hasItem = itemId !== 0; + // Hide in third person - if (this.minecraft.settings.thirdPersonView !== 0) { + if (!firstPerson) { return; } @@ -369,14 +373,6 @@ export default class WorldRenderer { let pitchArm = player.prevRenderArmPitch + (player.renderArmPitch - player.prevRenderArmPitch) * partialTicks; let yawArm = player.prevRenderArmYaw + (player.renderArmYaw - player.prevRenderArmYaw) * partialTicks; - let factor = 0.8; - let zOffset = Math.sin(swingProgress * Math.PI); - let yOffset = Math.sin(Math.sqrt(swingProgress) * Math.PI * 2.0); - let xOffset = Math.sin(Math.sqrt(swingProgress) * Math.PI); - - let yRotation = Math.sin(Math.sqrt(swingProgress) * Math.PI); - let zRotation = Math.sin(swingProgress * swingProgress * Math.PI); - // Bobbing animation if (this.minecraft.settings.viewBobbing) { let walked = -(player.prevDistanceWalked + (player.distanceWalked - player.prevDistanceWalked) * partialTicks); @@ -393,31 +389,55 @@ export default class WorldRenderer { stack.rotateX(MathHelper.toRadians(pitch)); } + let factor = 0.8; + let zOffset = Math.sin(swingProgress * Math.PI); + let yOffset = Math.sin(Math.sqrt(swingProgress) * Math.PI * 2.0); + let xOffset = Math.sin(Math.sqrt(swingProgress) * Math.PI); + + let sqrtRotation = Math.sin(Math.sqrt(swingProgress) * Math.PI); + let powRotation = Math.sin(swingProgress * swingProgress * Math.PI); + // Camera rotation movement stack.rotateX(MathHelper.toRadians((player.rotationPitch - pitchArm) * 0.1)); stack.rotateY(MathHelper.toRadians((player.rotationYaw - yawArm) * 0.1)); - // Initial offset on screen - this.translate(stack, -xOffset * 0.3, yOffset * 0.4, -zOffset * 0.4); - this.translate(stack, 0.8 * factor, -0.75 * factor - (1.0 - equipProgress) * 0.6, -0.9 * factor); + if(hasItem) { + // Initial offset on screen + this.translate(stack, -xOffset * 0.4, yOffset * 0.2, -zOffset * 0.2); + this.translate(stack, 0.7 * factor, -0.65 * factor - (1.0 - equipProgress) * 0.6, -0.9 * factor); - // Rotation of hand - stack.rotateY(MathHelper.toRadians(45)); - stack.rotateY(MathHelper.toRadians(yRotation * 70)); - stack.rotateZ(MathHelper.toRadians(-zRotation * 20)); + // Rotation of hand + stack.rotateY(MathHelper.toRadians(45)); + stack.rotateY(MathHelper.toRadians(-powRotation * 20)); + stack.rotateZ(MathHelper.toRadians(-sqrtRotation * 20)); + stack.rotateX(MathHelper.toRadians(-sqrtRotation * 80)); - // Post transform - this.translate(stack, -1, 3.6, 3.5); - stack.rotateZ(MathHelper.toRadians(120)); - stack.rotateX(MathHelper.toRadians(200)); - stack.rotateY(MathHelper.toRadians(-135)); - this.translate(stack, 5.6, 0.0, 0.0); + // Scale down + stack.scale.x *= 0.4; + stack.scale.y *= 0.4; + stack.scale.z *= 0.4; - if (this.itemToRender === 0) { - // Render hand - player.renderer.renderRightArm(player, partialTicks); - } else { // Render item + player.renderer.updateFirstPerson(player); + } else { + // Initial offset on screen + this.translate(stack, -xOffset * 0.3, yOffset * 0.4, -zOffset * 0.4); + this.translate(stack, 0.8 * factor, -0.75 * factor - (1.0 - equipProgress) * 0.6, -0.9 * factor); + + // Rotation of hand + stack.rotateY(MathHelper.toRadians(45)); + stack.rotateY(MathHelper.toRadians(sqrtRotation * 70)); + stack.rotateZ(MathHelper.toRadians(-powRotation * 20)); + + // Post transform + this.translate(stack, -1, 3.6, 3.5); + stack.rotateZ(MathHelper.toRadians(120)); + stack.rotateX(MathHelper.toRadians(200)); + stack.rotateY(MathHelper.toRadians(-135)); + this.translate(stack, 5.6, 0.0, 0.0); + + // Render hand + player.renderer.renderRightHand(player, partialTicks); } } diff --git a/src/js/net/minecraft/client/render/entity/EntityRenderer.js b/src/js/net/minecraft/client/render/entity/EntityRenderer.js index 37a47b6..11c7349 100644 --- a/src/js/net/minecraft/client/render/entity/EntityRenderer.js +++ b/src/js/net/minecraft/client/render/entity/EntityRenderer.js @@ -26,7 +26,6 @@ export default class EntityRenderer { fillMeta(entity, meta) { meta.brightness = entity.getEntityBrightness(); - meta.itemInHand = entity.inventory.getItemInSelectedSlot(); } isRebuildRequired(entity) { diff --git a/src/js/net/minecraft/client/render/entity/entity/PlayerRenderer.js b/src/js/net/minecraft/client/render/entity/entity/PlayerRenderer.js index a4aff1b..2f38c34 100644 --- a/src/js/net/minecraft/client/render/entity/entity/PlayerRenderer.js +++ b/src/js/net/minecraft/client/render/entity/entity/PlayerRenderer.js @@ -16,33 +16,52 @@ export default class PlayerRenderer extends EntityRenderer { // First person right-hand holder this.handModel = null; - this.handGroup = new THREE.Object3D(); - this.worldRenderer.overlay.add(this.handGroup); + this.firstPersonGroup = new THREE.Object3D(); + this.worldRenderer.overlay.add(this.firstPersonGroup); } rebuild(entity) { - this.tessellator.bindTexture(this.textureCharacter); - super.rebuild(entity); + let firstPerson = this.worldRenderer.minecraft.settings.thirdPersonView === 0; + let itemId = firstPerson ? this.worldRenderer.itemToRender : entity.inventory.getItemInSelectedSlot(); + let hasItem = itemId !== 0; - // Render item in hand - let group = this.model.rightArm.bone; - let id = entity.inventory.getItemInSelectedSlot(); - if (id !== 0 && this.worldRenderer.minecraft.settings.thirdPersonView !== 0) { - let block = Block.getById(id); - this.worldRenderer.blockRenderer.renderBlockInHandThirdPerson(group, block, entity.getEntityBrightness()); + if (firstPerson && hasItem) { + super.rebuild(entity); + + // Create new item group and add it to the hand + this.firstPersonGroup.clear(); + let itemGroup = new THREE.Object3D(); + this.firstPersonGroup.add(itemGroup); + + // Render item in hand in first person + let block = Block.getById(itemId); + this.worldRenderer.blockRenderer.renderBlockInFirstPerson(itemGroup, block, entity.getEntityBrightness()); + + // Copy material and update depth test of the item to render it always in front + let mesh = itemGroup.children[0]; + mesh.material = mesh.material.clone(); + mesh.material.depthTest = false; + } else { + this.tessellator.bindTexture(this.textureCharacter); + super.rebuild(entity); + + // Render item in hand in third person + if (hasItem) { + let block = Block.getById(itemId); + let group = this.model.rightArm.bone; + this.worldRenderer.blockRenderer.renderBlockInHandThirdPerson(group, block, entity.getEntityBrightness()); + } + + // Create first person right hand and attach it to the holder + this.firstPersonGroup.clear(); + this.handModel = this.model.rightArm.clone(); + this.firstPersonGroup.add(this.handModel.bone); + + // Copy material and update depth test of the hand to render it always in front + let mesh = this.handModel.bone.children[0]; + mesh.material = mesh.material.clone(); + mesh.material.depthTest = false; } - - // Create first person right hand and attach it to the holder - this.handGroup.clear(); - this.handModel = this.model.rightArm.clone(); - this.handGroup.add(this.handModel.bone); - - // Copy material and update depth test of the hand - let mesh = this.handModel.bone.children[0]; - mesh.renerOrder = 999; - mesh.material = mesh.material.clone(); - mesh.material.depthTest = false; - mesh.material.depthWrite = false; } render(entity, partialTicks) { @@ -57,10 +76,17 @@ export default class PlayerRenderer extends EntityRenderer { super.render(entity, partialTicks); } - renderRightArm(player, partialTicks) { + updateFirstPerson(player) { // Make sure the model is created this.prepareModel(player); + // Make the group visible + this.firstPersonGroup.visible = true; + } + + renderRightHand(player, partialTicks) { + this.updateFirstPerson(player); + // Set transform of renderer this.model.swingProgress = 0; this.model.hasItemInHand = false; @@ -68,14 +94,17 @@ export default class PlayerRenderer extends EntityRenderer { this.model.setRotationAngles(player, 0, 0, 0, 0, 0, 0); this.handModel.copyTransformOf(this.model.rightArm); - // Render the model - this.handGroup.visible = true; + // Render hand model this.handModel.render(); } fillMeta(entity, meta) { super.fillMeta(entity, meta); - meta.itemInHand = entity.inventory.getItemInSelectedSlot(); + + let firstPerson = this.worldRenderer.minecraft.settings.thirdPersonView === 0; + + meta.firstPerson = firstPerson; + meta.itemInHand = firstPerson ? this.worldRenderer.itemToRender : entity.inventory.getItemInSelectedSlot(); } } \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/gui/ItemRenderer.js b/src/js/net/minecraft/client/render/gui/ItemRenderer.js index 42a5431..279e641 100644 --- a/src/js/net/minecraft/client/render/gui/ItemRenderer.js +++ b/src/js/net/minecraft/client/render/gui/ItemRenderer.js @@ -11,7 +11,7 @@ export default class ItemRenderer { // Create item camera this.camera = new THREE.OrthographicCamera(0, 0, 0, 0, 0, 300); this.camera.near = 0; - this.camera.far = 100; + this.camera.far = 15; this.camera.rotation.order = 'ZYX'; this.camera.up = new THREE.Vector3(0, 1, 0);