refactor project structure, implement tessellator
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
window.BlockRenderer = class {
|
||||
|
||||
constructor(worldRenderer) {
|
||||
this.worldRenderer = worldRenderer;
|
||||
this.tessellator = new Tessellator();
|
||||
this.tessellator.bindTexture(worldRenderer.terrainTexture);
|
||||
}
|
||||
|
||||
renderBlock(world, group, typeId, x, y, z) {
|
||||
let boundingBox = new BoundingBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
|
||||
|
||||
for (let face = 0; face < 6; face++) {
|
||||
this.tessellator.startDrawing();
|
||||
this.renderFace(world, typeId, boundingBox, face, x, y, z);
|
||||
this.tessellator.draw(group);
|
||||
}
|
||||
}
|
||||
|
||||
renderFace(world, typeId, boundingBox, face, x, y, z) {
|
||||
// Vertex mappings
|
||||
let minX = x + boundingBox.minX;
|
||||
let minY = y + boundingBox.minY;
|
||||
let minZ = z + boundingBox.minZ;
|
||||
let maxX = x + boundingBox.maxX;
|
||||
let maxY = y + boundingBox.maxY;
|
||||
let maxZ = z + boundingBox.maxZ;
|
||||
|
||||
// UV Mapping
|
||||
let textureIndex = typeId;
|
||||
let minU = (textureIndex % 16) / 16.0;
|
||||
let maxU = minU + (16 / 256);
|
||||
let minV = parseInt(textureIndex / 16); // TODO Math.round
|
||||
let maxV = minV + (16 / 256);
|
||||
|
||||
// Flip V
|
||||
minV = 1 - minV;
|
||||
maxV = 1 - maxV;
|
||||
|
||||
if (face === 0) {
|
||||
this.addBlockCorner(world, face, minX, minY, maxZ, minU, maxV);
|
||||
this.addBlockCorner(world, face, minX, minY, minZ, minU, minV);
|
||||
this.addBlockCorner(world, face, maxX, minY, minZ, maxU, minV);
|
||||
this.addBlockCorner(world, face, maxX, minY, maxZ, maxU, maxV);
|
||||
}
|
||||
if (face === 1) {
|
||||
this.addBlockCorner(world, face, maxX, maxY, maxZ, maxU, maxV);
|
||||
this.addBlockCorner(world, face, maxX, maxY, minZ, maxU, minV);
|
||||
this.addBlockCorner(world, face, minX, maxY, minZ, minU, minV);
|
||||
this.addBlockCorner(world, face, minX, maxY, maxZ, minU, maxV);
|
||||
}
|
||||
if (face === 2) {
|
||||
this.addBlockCorner(world, face, minX, maxY, minZ, maxU, minV);
|
||||
this.addBlockCorner(world, face, maxX, maxY, minZ, minU, minV);
|
||||
this.addBlockCorner(world, face, maxX, minY, minZ, minU, maxV);
|
||||
this.addBlockCorner(world, face, minX, minY, minZ, maxU, maxV);
|
||||
}
|
||||
if (face === 3) {
|
||||
this.addBlockCorner(world, face, minX, maxY, maxZ, minU, minV);
|
||||
this.addBlockCorner(world, face, minX, minY, maxZ, minU, maxV);
|
||||
this.addBlockCorner(world, face, maxX, minY, maxZ, maxU, maxV);
|
||||
this.addBlockCorner(world, face, maxX, maxY, maxZ, maxU, minV);
|
||||
}
|
||||
if (face === 4) {
|
||||
this.addBlockCorner(world, face, minX, maxY, maxZ, maxU, minV);
|
||||
this.addBlockCorner(world, face, minX, maxY, minZ, minU, minV);
|
||||
this.addBlockCorner(world, face, minX, minY, minZ, minU, maxV);
|
||||
this.addBlockCorner(world, face, minX, minY, maxZ, maxU, maxV);
|
||||
}
|
||||
if (face === 5) {
|
||||
this.addBlockCorner(world, face, maxX, minY, maxZ, minU, maxV);
|
||||
this.addBlockCorner(world, face, maxX, minY, minZ, maxU, maxV);
|
||||
this.addBlockCorner(world, face, maxX, maxY, minZ, maxU, minV);
|
||||
this.addBlockCorner(world, face, maxX, maxY, maxZ, minU, minV);
|
||||
}
|
||||
}
|
||||
|
||||
addBlockCorner(world, face, x, y, z, u, v) {
|
||||
this.tessellator.addVertexWithUV(x, y, z, u, v);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
window.Tessellator = class {
|
||||
|
||||
constructor() {
|
||||
this.material = new THREE.MeshBasicMaterial({
|
||||
color: 0xffffff,
|
||||
side: THREE.BackSide
|
||||
});
|
||||
}
|
||||
|
||||
bindTexture(texture) {
|
||||
this.material.map = texture;
|
||||
}
|
||||
|
||||
startDrawing() {
|
||||
this.verticies = [];
|
||||
this.uv = [];
|
||||
}
|
||||
|
||||
addVertexWithUV(x, y, z, u, v) {
|
||||
this.verticies.push(x);
|
||||
this.verticies.push(y);
|
||||
this.verticies.push(z);
|
||||
|
||||
this.uv.push(u);
|
||||
this.uv.push(v);
|
||||
}
|
||||
|
||||
draw(group) {
|
||||
let geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(this.verticies), 3));
|
||||
geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(this.uv), 2));
|
||||
geometry.setIndex(new THREE.BufferAttribute(new Uint32Array([0, 2, 1, 0, 3, 2]), 1));
|
||||
|
||||
let mesh = new THREE.Mesh(geometry, this.material);
|
||||
group.add(mesh);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
window.WorldRenderer = class {
|
||||
|
||||
constructor(minecraft) {
|
||||
this.minecraft = minecraft;
|
||||
|
||||
this.supportWebGL = !!WebGLRenderingContext
|
||||
&& (!!document.createElement('canvas').getContext('experimental-webgl')
|
||||
|| !!document.createElement('canvas').getContext('webgl'));
|
||||
|
||||
// Create cameras
|
||||
this.camera = new THREE.PerspectiveCamera(0, 1, 0.001, 10000);
|
||||
this.camera.rotation.order = 'ZYX';
|
||||
this.camera.up = new THREE.Vector3(0, 0, 1);
|
||||
|
||||
// Create scene
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
// Create web renderer
|
||||
this.canvasElement = document.createElement('canvas')
|
||||
this.webRenderer = this.supportWebGL ? new THREE.WebGLRenderer({
|
||||
canvas: this.canvasElement,
|
||||
antialias: true
|
||||
}) : new THREE.CanvasRenderer({
|
||||
canvas: this.canvasElement,
|
||||
antialias: true
|
||||
});
|
||||
|
||||
// Settings
|
||||
this.webRenderer.shadowMap.enabled = true;
|
||||
this.webRenderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
|
||||
this.webRenderer.autoClear = false;
|
||||
this.webRenderer.setClearColor(0x000000, 0);
|
||||
this.webRenderer.clear();
|
||||
|
||||
// Add lights
|
||||
const nightLight = new THREE.AmbientLight(0x888888, 1.0);
|
||||
this.scene.add(nightLight);
|
||||
|
||||
// Load terrain
|
||||
this.terrainTexture = new THREE.TextureLoader().load( 'src/resources/terrain.png' );
|
||||
this.terrainTexture.magFilter = THREE.NearestFilter;
|
||||
this.terrainTexture.minFilter = THREE.LinearFilter;
|
||||
|
||||
// Block Renderer
|
||||
this.blockRenderer = new BlockRenderer(this);
|
||||
}
|
||||
|
||||
render(partialTicks) {
|
||||
// Setup camera
|
||||
this.orientCamera(partialTicks);
|
||||
|
||||
// Render chunks
|
||||
this.renderChunks(this, partialTicks);
|
||||
|
||||
// Render window
|
||||
this.webRenderer.render(this.scene, this.camera);
|
||||
}
|
||||
|
||||
orientCamera(partialTicks) {
|
||||
let player = this.minecraft.player;
|
||||
|
||||
// Rotation
|
||||
this.camera.rotation.y = player.yaw * (Math.PI / 180);
|
||||
this.camera.rotation.x = player.pitch * (Math.PI / 180);
|
||||
|
||||
// Position
|
||||
let x = player.prevX + (player.x - player.prevX) * partialTicks;
|
||||
let y = player.prevY + (player.y - player.prevY) * partialTicks;
|
||||
let z = player.prevZ + (player.z - player.prevZ) * partialTicks;
|
||||
this.camera.position.set(x, y + player.getEyeHeight(), z);
|
||||
|
||||
// Update FOV
|
||||
this.camera.fov = 85 + player.getFOVModifier();
|
||||
this.camera.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
renderChunks(renderer, partialTicks) {
|
||||
let world = this.minecraft.world;
|
||||
|
||||
const xKeys = Object.keys(world.chunks)
|
||||
for (let x = 0; x < xKeys.length; x++) {
|
||||
|
||||
let zArray = world.chunks[xKeys[x]];
|
||||
const zKeys = Object.keys(zArray)
|
||||
|
||||
for (let z = 0; z < zKeys.length; z++) {
|
||||
let chunk = zArray[zKeys[z]];
|
||||
|
||||
for (let y = 0; y < chunk.sections.length; y++) {
|
||||
let section = chunk.sections[y];
|
||||
|
||||
if (section.dirty) {
|
||||
section.rebuild(renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user