diff --git a/src/js/net/minecraft/client/Minecraft.js b/src/js/net/minecraft/client/Minecraft.js index f1fea17..c09b53d 100644 --- a/src/js/net/minecraft/client/Minecraft.js +++ b/src/js/net/minecraft/client/Minecraft.js @@ -49,7 +49,8 @@ window.Minecraft = class { this.soundManager = new SoundManager(); // Create player - this.player = new Player(this, this.world); + this.player = new PlayerEntity(this, this.world); + this.world.addEntity(this.player); this.inventory = new Inventory(); this.displayScreen(this.loadingScreen); diff --git a/src/js/net/minecraft/client/entity/Entity.js b/src/js/net/minecraft/client/entity/Entity.js new file mode 100644 index 0000000..0f601ed --- /dev/null +++ b/src/js/net/minecraft/client/entity/Entity.js @@ -0,0 +1,7 @@ +window.Entity = class { + + constructor() { + this.group = new THREE.Object3D(); + } + +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/entity/Player.js b/src/js/net/minecraft/client/entity/PlayerEntity.js similarity index 99% rename from src/js/net/minecraft/client/entity/Player.js rename to src/js/net/minecraft/client/entity/PlayerEntity.js index 54c17e0..e572002 100644 --- a/src/js/net/minecraft/client/entity/Player.js +++ b/src/js/net/minecraft/client/entity/PlayerEntity.js @@ -1,6 +1,10 @@ -window.Player = class { +window.PlayerEntity = class extends Entity { + + static name = "PlayerEntity"; constructor(minecraft, world) { + super(); + this.minecraft = minecraft; this.world = world; diff --git a/src/js/net/minecraft/client/render/WorldRenderer.js b/src/js/net/minecraft/client/render/WorldRenderer.js index 66f2f9d..6898955 100644 --- a/src/js/net/minecraft/client/render/WorldRenderer.js +++ b/src/js/net/minecraft/client/render/WorldRenderer.js @@ -25,6 +25,9 @@ window.WorldRenderer = class { // Block Renderer this.blockRenderer = new BlockRenderer(this); + // Entity render manager + this.entityRenderManager = new EntityRenderManager(); + this.initialize(); } @@ -85,6 +88,11 @@ window.WorldRenderer = class { // Render target block this.renderBlockHitBox(player, partialTicks); + // Render entities + for (let entity of this.minecraft.world.entities) { + this.renderEntity(entity); + } + // Render actual scene this.webRenderer.render(this.scene, this.camera); } @@ -287,4 +295,9 @@ window.WorldRenderer = class { } } } + + renderEntity(entity) { + let entityRenderer = this.entityRenderManager.getEntityRendererByEntity(entity); + entityRenderer.render(entity); + } } \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/entity/EntityRenderManager.js b/src/js/net/minecraft/client/render/entity/EntityRenderManager.js new file mode 100644 index 0000000..d0f2640 --- /dev/null +++ b/src/js/net/minecraft/client/render/entity/EntityRenderManager.js @@ -0,0 +1,15 @@ +window.EntityRenderManager = class { + + constructor() { + this.renderers = []; + this.push(PlayerEntity, new PlayerRenderer()); + } + + push(entityType, entityRenderer) { + this.renderers[entityType.name] = entityRenderer; + } + + getEntityRendererByEntity(entity) { + return this.renderers[entity.constructor.name]; + } +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/entity/EntityRenderer.js b/src/js/net/minecraft/client/render/entity/EntityRenderer.js new file mode 100644 index 0000000..e79975f --- /dev/null +++ b/src/js/net/minecraft/client/render/entity/EntityRenderer.js @@ -0,0 +1,15 @@ +window.EntityRenderer = class { + + constructor(model) { + this.model = model; + } + + rebuild(tessellator, entity) { + this.model.rebuild(tessellator, entity); + } + + render(entity) { + this.model.render(0); + } + +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/entity/entity/PlayerRenderer.js b/src/js/net/minecraft/client/render/entity/entity/PlayerRenderer.js new file mode 100644 index 0000000..8d3a736 --- /dev/null +++ b/src/js/net/minecraft/client/render/entity/entity/PlayerRenderer.js @@ -0,0 +1,22 @@ +window.PlayerRenderer = class extends EntityRenderer { + + constructor() { + super(new ModelPlayer()); + + // Load character texture + this.textureCharacter = new THREE.TextureLoader().load('src/resources/char.png'); + this.textureCharacter.magFilter = THREE.NearestFilter; + this.textureCharacter.minFilter = THREE.NearestFilter; + } + + rebuild(tessellator, entity) { + tessellator.bindTexture(this.textureCharacter); + super.rebuild(tessellator, entity); + } + + render(entity) { + super.render(entity); + + } + +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/model/ModelBase.js b/src/js/net/minecraft/client/render/model/ModelBase.js new file mode 100644 index 0000000..d6b5230 --- /dev/null +++ b/src/js/net/minecraft/client/render/model/ModelBase.js @@ -0,0 +1,21 @@ +window.ModelBase = class { + + /** + * Rebuild the model + * + * @param tessellator Tessellator to render vertices + */ + rebuild(tessellator, entity) { + + } + + /** + * Render the model + * + * @param time Animation offset + */ + render(time) { + + } + +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/model/model/ModelPlayer.js b/src/js/net/minecraft/client/render/model/model/ModelPlayer.js new file mode 100644 index 0000000..e3a7015 --- /dev/null +++ b/src/js/net/minecraft/client/render/model/model/ModelPlayer.js @@ -0,0 +1,72 @@ +window.ModelPlayer = class extends ModelBase { + + /** + * Create cubes for the zombie model + */ + constructor() { + super(); + + // Create head ModelRenderer + this.head = new ModelRenderer(0, 0) + .setBox(-4.0, -8.0, -4.0, 8, 8, 8); + + // Create body ModelRenderer + this.body = new ModelRenderer(16, 16) + .setBox(-4.0, 0.0, -2.0, 8, 12, 4); + + // Right arm ModelRenderer + this.rightArm = new ModelRenderer(40, 16) + .setBox(-3.0, -2.0, -2.0, 4, 12, 4); + this.rightArm.setPosition(-5.0, 2.0, 0.0); + + // Left arm ModelRenderer + this.leftArm = new ModelRenderer(40, 16) + .setBox(-1.0, -2.0, -2.0, 4, 12, 4); + this.leftArm.setPosition(5.0, 2.0, 0.0); + + // Right Legs ModelRenderer + this.rightLeg = new ModelRenderer(0, 16) + .setBox(-2.0, 0.0, -2.0, 4, 12, 4); + this.rightLeg.setPosition(-2.0, 12.0, 0.0); + + // Left leg ModelRenderer + this.leftLeg = new ModelRenderer(0, 16) + .setBox(-2.0, 0.0, -2.0, 4, 12, 4); + this.leftLeg.setPosition(2.0, 12.0, 0.0); + } + + rebuild(tessellator, entity) { + this.head.rebuild(tessellator, entity); + this.body.rebuild(tessellator, entity); + this.leftArm.rebuild(tessellator, entity); + this.rightArm.rebuild(tessellator, entity); + this.leftLeg.rebuild(tessellator, entity); + this.rightLeg.rebuild(tessellator, entity); + } + + /** + * Render the model + * + * @param time Animation offset + */ + render(entity, time) { + // Set rotation of cubes + this.head.yRotation = Math.sin(time * 0.83); + this.head.xRotation = Math.sin(time) * 0.8; + this.rightArm.xRotation = Math.sin(time * 0.6662 + Math.PI) * 2.0; + this.rightArm.zRotation = (Math.sin(time * 0.2312) + 1.0); + this.leftArm.xRotation = Math.sin(time * 0.6662) * 2.0; + this.leftArm.zRotation = (Math.sin(time * 0.2812) - 1.0); + this.rightLeg.xRotation = Math.sin(time * 0.6662) * 1.4; + this.leftLeg.xRotation = Math.sin(time * 0.6662 + Math.PI) * 1.4; + + // Render cubes + this.head.render(entity); + this.body.render(entity); + this.rightArm.render(entity); + this.leftArm.render(entity); + this.rightLeg.render(entity); + this.leftLeg.render(entity); + } + +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/model/renderer/ModelRenderer.js b/src/js/net/minecraft/client/render/model/renderer/ModelRenderer.js new file mode 100644 index 0000000..bdb471b --- /dev/null +++ b/src/js/net/minecraft/client/render/model/renderer/ModelRenderer.js @@ -0,0 +1,156 @@ +window.ModelRenderer = class { + + /** + * Create cube object + * + * @param textureOffsetX x offset position on the texture + * @param textureOffsetY y offset position on the texture + */ + constructor(textureOffsetX, textureOffsetY) { + this.textureOffsetX = textureOffsetX; + this.textureOffsetY = textureOffsetY; + + this.xRotation = 0; + this.yRotation = 0; + this.zRotation = 0; + + this.x = 0; + this.y = 0; + this.z = 0; + } + + /** + * Set the texture offset position of the cube + * + * @param textureOffsetX Offset position x + * @param textureOffsetY Offset position y + */ + setTextureOffset(textureOffsetX, textureOffsetY) { + this.textureOffsetX = textureOffsetX; + this.textureOffsetY = textureOffsetY; + + this.xRotation = 0; + this.yRotation = 0; + this.zRotation = 0; + } + + /** + * Create box using offset position and width, height and depth + * + * @param offsetX X offset of the render position + * @param offsetY Y offset of the render position + * @param offsetZ Z offset of the render position + * @param width Cube width + * @param height Cube height + * @param depth Cube depth + */ + setBox(offsetX, offsetY, offsetZ, width, height, depth) { + this.polygons = []; + + let x = offsetX + width; + let y = offsetY + height; + let z = offsetZ + depth; + + // Create bottom vertex points of cube + let vertexBottom1 = new Vertex(offsetX, offsetY, offsetZ, 0.0, 0.0); + let vertexBottom2 = new Vertex(x, offsetY, offsetZ, 0.0, 8.0); + let vertexBottom3 = new Vertex(offsetX, offsetY, z, 0.0, 0.0); + let vertexBottom4 = new Vertex(x, offsetY, z, 0.0, 8.0); + + // Create top vertex points of cube + let vertexTop1 = new Vertex(x, y, z, 8.0, 8.0); + let vertexTop2 = new Vertex(offsetX, y, z, 8.0, 0.0); + let vertexTop3 = new Vertex(x, y, offsetZ, 8.0, 8.0); + let vertexTop4 = new Vertex(offsetX, y, offsetZ, 8.0, 0.0); + + // Create polygons for each cube side + this.polygons[0] = new Polygon( + [vertexBottom4, vertexBottom2, vertexTop3, vertexTop1], + this.textureOffsetX + depth + width, + this.textureOffsetY + depth, + this.textureOffsetX + depth + width + depth, + this.textureOffsetY + depth + height + ); + + this.polygons[1] = new Polygon( + [vertexBottom1, vertexBottom3, vertexTop2, vertexTop4], + this.textureOffsetX, + this.textureOffsetY + depth, + this.textureOffsetX + depth, + this.textureOffsetY + depth + height + ); + + this.polygons[2] = new Polygon( + [vertexBottom4, vertexBottom3, vertexBottom1, vertexBottom2], + this.textureOffsetX + depth, + this.textureOffsetY, + this.textureOffsetX + depth + width, + this.textureOffsetY + depth + ); + + this.polygons[3] = new Polygon( + [vertexTop3, vertexTop4, vertexTop2, vertexTop1], + this.textureOffsetX + depth + width, + this.textureOffsetY, + this.textureOffsetX + depth + width + width, + this.textureOffsetY + depth + ); + + this.polygons[4] = new Polygon( + [vertexBottom2, vertexBottom1, vertexTop4, vertexTop3], + this.textureOffsetX + depth, + this.textureOffsetY + depth, + this.textureOffsetX + depth + width, + this.textureOffsetY + depth + height + ); + + this.polygons[5] = new Polygon( + [vertexBottom3, vertexBottom4, vertexTop1, vertexTop2], + this.textureOffsetX + depth + width + depth, + this.textureOffsetY + depth, + this.textureOffsetX + depth + width + depth + width, + this.textureOffsetY + depth + height + ); + + return this; + } + + /** + * Set the absolute position of the cube + * + * @param x Absolute x position of cube + * @param y Absolute y position of cube + * @param z Absolute z position of cube + */ + setPosition(x, y, z) { + this.x = x; + this.y = y; + this.z = z; + } + + rebuild(tessellator, entity) { + // Start drawing + tessellator.startDrawing(); + + // Render polygons + for (let i = 0; i < 6; i++) { + let polygon = this.polygons[i]; + polygon.render(tessellator); + } + + // Finish drawing + tessellator.draw(entity.group); + } + + render(entity) { + entity.group.position.setX(this.x); + entity.group.position.setY(this.y); + entity.group.position.setZ(this.z); + + entity.group.rotation.order = 'ZYX'; + entity.group.rotation.x = this.xRotation; + entity.group.rotation.y = this.yRotation; + entity.group.rotation.z = this.zRotation; + } + +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/model/renderer/Polygon.js b/src/js/net/minecraft/client/render/model/renderer/Polygon.js new file mode 100644 index 0000000..52c5cc6 --- /dev/null +++ b/src/js/net/minecraft/client/render/model/renderer/Polygon.js @@ -0,0 +1,41 @@ +window.Polygon = class { + + /** + * Bind UV mappings on the vertices + * + * @param vertices Vertex array + * @param minU Minimum U coordinate + * @param minV Minimum V coordinate + * @param maxU Maximum U coordinate + * @param maxV Maximum V coordinate + */ + constructor(vertices, minU, minV, maxU, maxV) { + this.vertices = vertices; + this.vertexCount = vertices.length; + + // Map UV on vertices + vertices[0] = vertices[0].remap(maxU, minV); + vertices[1] = vertices[1].remap(minU, minV); + vertices[2] = vertices[2].remap(minU, maxV); + vertices[3] = vertices[3].remap(maxU, maxV); + } + + render(tessellator) { + // Set color of polygon + tessellator.setColor(1, 1, 1); + + // Render all vertices + for (let i = 3; i >= 0; i--) { + let vertex = this.vertices[i]; + + // Bind UV mappings and render vertex + tessellator.addVertexWithUV( + vertex.position.x, + vertex.position.y, + vertex.position.z, + vertex.u / 64.0, + vertex.v / 32.0 + ); + } + } +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/render/model/renderer/Vertex.js b/src/js/net/minecraft/client/render/model/renderer/Vertex.js new file mode 100644 index 0000000..c0b1144 --- /dev/null +++ b/src/js/net/minecraft/client/render/model/renderer/Vertex.js @@ -0,0 +1,29 @@ +window.Vertex = class { + + /** + * A vertex contains a 3 float vector position and UV coordinates + * + * @param x X position + * @param y Y position + * @param z Z position + * @param u U mapping + * @param v V mapping + */ + constructor(x, y, z, u, v) { + this.position = new Vector3(x, y, z); + this.u = u; + this.v = v; + } + + /** + * Create a new vertex of the current one with different UV mappings + * + * @param u New U mapping + * @param v New V mapping + * @return New vertex with the vector position of the current one + */ + remap(u, v) { + return new Vertex(this.position.x, this.position.y, this.position.z, u, v); + } + +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/world/World.js b/src/js/net/minecraft/client/world/World.js index fc37468..78a365d 100644 --- a/src/js/net/minecraft/client/world/World.js +++ b/src/js/net/minecraft/client/world/World.js @@ -5,6 +5,8 @@ window.World = class { constructor(minecraft) { this.minecraft = minecraft; + this.entities = []; + this.group = new THREE.Object3D(); this.group.matrixAutoUpdate = false; @@ -500,4 +502,31 @@ window.World = class { } return Math.floor(level * 11); } + + addEntity(entity) { + this.entities.push(entity); + this.group.add(entity.group); + + let tessellator = new Tessellator(); + + // Rebuild entity model + let worldRenderer = this.minecraft.worldRenderer; + let renderer = worldRenderer.entityRenderManager.getEntityRendererByEntity(entity); + renderer.rebuild(tessellator, entity); + } + + removeEntityById(id) { + let entity = this.getEntityById(id); + this.entities.remove(entity); + this.group.remove(entity.group); + } + + getEntityById(id) { + for (let entity of this.entities) { + if (entity.id === id) { + return entity; + } + } + return null; + } } \ No newline at end of file diff --git a/src/js/net/minecraft/util/Vector3.js b/src/js/net/minecraft/util/Vector3.js index 43dc77f..0d0cc8b 100644 --- a/src/js/net/minecraft/util/Vector3.js +++ b/src/js/net/minecraft/util/Vector3.js @@ -6,7 +6,6 @@ window.Vector3 = class { this.z = z; } - addVector(x, y, z) { return new Vector3(this.x + x, this.y + y, this.z + z); } @@ -69,4 +68,19 @@ window.Vector3 = class { } } + /** + * Create an interpolated vector from the current vector position to the given one + * + * @param vector The end vector + * @param partialTicks Interpolation progress + * @return Interpolated vector between the two positions + */ + interpolateTo(vector, partialTicks) { + let interpolatedX = this.x + (vector.x - this.x) * partialTicks; + let interpolatedY = this.y + (vector.y - this.y) * partialTicks; + let interpolatedZ = this.z + (vector.z - this.z) * partialTicks; + + return new Vector3(interpolatedX, interpolatedY, interpolatedZ); + } + } \ No newline at end of file diff --git a/src/resources/char.png b/src/resources/char.png new file mode 100644 index 0000000..1590f43 Binary files /dev/null and b/src/resources/char.png differ diff --git a/src/start.js b/src/start.js index c0ad66b..d2b1aa5 100644 --- a/src/start.js +++ b/src/start.js @@ -75,7 +75,8 @@ loadTexture([ "gui/icons.png", "terrain/terrain.png", "terrain/sun.png", - "terrain/moon.png" + "terrain/moon.png", + "char.png" ]).then(() => { // Load scripts loadScripts([ @@ -125,7 +126,8 @@ loadTexture([ "src/js/net/minecraft/client/world/generator/noise/NoiseGeneratorOctaves.js", "src/js/net/minecraft/client/world/generator/noise/NoiseGeneratorCombined.js", "src/js/net/minecraft/client/world/generator/WorldGenerator.js", - "src/js/net/minecraft/client/entity/Player.js", + "src/js/net/minecraft/client/entity/Entity.js", + "src/js/net/minecraft/client/entity/PlayerEntity.js", "src/js/net/minecraft/client/inventory/Inventory.js", "src/js/net/minecraft/client/GameSettings.js", "src/js/net/minecraft/client/Minecraft.js", @@ -137,6 +139,14 @@ loadTexture([ "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/model/ModelBase.js", + "src/js/net/minecraft/client/render/model/renderer/Vertex.js", + "src/js/net/minecraft/client/render/model/renderer/Polygon.js", + "src/js/net/minecraft/client/render/model/renderer/ModelRenderer.js", + "src/js/net/minecraft/client/render/model/model/ModelPlayer.js", + "src/js/net/minecraft/client/render/entity/EntityRenderer.js", + "src/js/net/minecraft/client/render/entity/entity/PlayerRenderer.js", + "src/js/net/minecraft/client/render/entity/EntityRenderManager.js", "src/js/net/minecraft/client/render/WorldRenderer.js", "src/js/net/minecraft/client/render/BlockRenderer.js" ]).then(() => {