Add destroying blocks

This commit is contained in:
Paul Mathieu 2021-12-17 15:33:33 -08:00
parent 200efb3a3c
commit 4be09f8d45
2 changed files with 80 additions and 43 deletions

View File

@ -1,5 +1,5 @@
import { loadTexture, makeProgram } from "./gl"; import { loadTexture, makeProgram } from "./gl";
import { blockLookup, BlockType, generateMissingChunks, makeWorld, updateWorldGeometry } from './world'; import { blockLookup, BlockType, createChunkFace, generateMissingChunks, makeWorld, updateWorldGeometry } from './world';
import * as se3 from './se3'; import * as se3 from './se3';
import { makeBufferFromFaces, makeFace } from "./geometry"; import { makeBufferFromFaces, makeFace } from "./geometry";
@ -591,8 +591,18 @@ async function main() {
}; };
} }
canvas.onclick = e => { const canvasClickHandler = e => {
canvas.requestPointerLock(); canvas.requestPointerLock();
canvas.onclick = null;
const clickListener = e => {
switch(e.button) {
case 0: // left click
destroySelectedBlock(params);
break;
case 2: // right click
break;
}
};
const keyListener = e => { const keyListener = e => {
if (e.type === 'keydown') { if (e.type === 'keydown') {
params.keys.add(e.code); params.keys.add(e.code);
@ -617,16 +627,20 @@ async function main() {
if (document.pointerLockElement === canvas) { if (document.pointerLockElement === canvas) {
return; return;
} }
document.removeEventListener('pointerdown', clickListener);
document.removeEventListener('pointerlockchange', changeListener); document.removeEventListener('pointerlockchange', changeListener);
document.removeEventListener('pointermove', moveListener); document.removeEventListener('pointermove', moveListener);
document.removeEventListener('keydown', keyListener); document.removeEventListener('keydown', keyListener);
document.removeEventListener('keyup', keyListener); document.removeEventListener('keyup', keyListener);
canvas.onclick = canvasClickHandler;
}; };
document.addEventListener('pointerdown', clickListener);
document.addEventListener('pointerlockchange', changeListener); document.addEventListener('pointerlockchange', changeListener);
document.addEventListener('pointermove', moveListener); document.addEventListener('pointermove', moveListener);
document.addEventListener('keydown', keyListener); document.addEventListener('keydown', keyListener);
document.addEventListener('keyup', keyListener); document.addEventListener('keyup', keyListener);
}; };
canvas.onclick = canvasClickHandler;
requestAnimationFrame(time => tick(time, gl, params)); requestAnimationFrame(time => tick(time, gl, params));
} }

View File

@ -111,17 +111,47 @@ export function blockLookup(world, x, y, z) {
const j = Math.floor(midx - chunkj * 16); const j = Math.floor(midx - chunkj * 16);
const k = Math.floor(midy); const k = Math.floor(midy);
const blockIndex = 256 * (16*i + j) + k;
return { return {
type: chunk.data[256 * (16*i + j) + k], type: chunk.data[blockIndex],
centerPosition: [ centerPosition: [
Math.floor(midx), Math.floor(midx),
k, k,
Math.floor(midz), Math.floor(midz),
], ],
chunk,
blockIndex,
}; };
} }
function makeChunkBuffer(gl, data, z0, x0, blockLookup) { function faceTexture(type, dir) {
switch (type) {
case BlockType.GRASS:
switch (dir) {
case '+y': return [0, 15];
case '-y': return [2, 15];
default: return [1, 15];
}
case BlockType.DIRT: return [2, 15];
case BlockType.STONE: return [3, 15];
default: return [0, 0];
}
}
function faceCenter(blockCenter, dir) {
const [x, y, z] = blockCenter;
switch (dir) {
case '+x': return [x + 0.5, y, z];
case '-x': return [x - 0.5, y, z];
case '+y': return [x, y + 0.5, z];
case '-y': return [x, y - 0.5, z];
case '+z': return [x, y, z + 0.5];
case '-z': return [x, y, z - 0.5];
}
}
function makeFaceList(data, chunkz, chunkx, blockLookup) {
const lookup = (i, j, k) => { const lookup = (i, j, k) => {
if (i < 0 || j < 0 || i > 15 || j > 15) { if (i < 0 || j < 0 || i > 15 || j > 15) {
return blockLookup(i, j, k); return blockLookup(i, j, k);
@ -131,12 +161,13 @@ function makeChunkBuffer(gl, data, z0, x0, blockLookup) {
} }
return data[256*(16*i + j) + k]; return data[256*(16*i + j) + k];
}; };
const sideNeighbors = (i, j, k) => const neighbors = (i, j, k) => [
[ { block: lookup(i - 1, j, k), dir: '-z', faceCenter: [chunkx + j, k, chunkz + i - 0.5] },
{ block: lookup(i - 1, j, k), dir: '-z', faceCenter: [x0 + j, k, z0 + i - 0.5] }, { block: lookup(i + 1, j, k), dir: '+z', faceCenter: [chunkx + j, k, chunkz + i + 0.5] },
{ block: lookup(i + 1, j, k), dir: '+z', faceCenter: [x0 + j, k, z0 + i + 0.5] }, { block: lookup(i, j - 1, k), dir: '-x', faceCenter: [chunkx + j - 0.5, k, chunkz + i] },
{ block: lookup(i, j - 1, k), dir: '-x', faceCenter: [x0 + j - 0.5, k, z0 + i] }, { block: lookup(i, j + 1, k), dir: '+x', faceCenter: [chunkx + j + 0.5, k, chunkz + i] },
{ block: lookup(i, j + 1, k), dir: '+x', faceCenter: [x0 + j + 0.5, k, z0 + i] }, { block: lookup(i, j, k - 1), dir: '-y', faceCenter: [chunkx + j, k - 0.5, chunkz + i] },
{ block: lookup(i, j, k + 1), dir: '+y', faceCenter: [chunkx + j, k + 0.5, chunkz + i] },
]; ];
const faces = []; const faces = [];
@ -145,37 +176,20 @@ function makeChunkBuffer(gl, data, z0, x0, blockLookup) {
for (let j = 0; j < 16; j++) { for (let j = 0; j < 16; j++) {
let bi = i * 16 * 256 + j * 256; let bi = i * 16 * 256 + j * 256;
for (let k = 0; k < 256; k++, bi++) { for (let k = 0; k < 256; k++, bi++) {
const upTexture = (() => { if (data[bi] === BlockType.AIR) {
switch (data[bi - 1]) { continue;
case BlockType.GRASS: return [0, 15];
case BlockType.DIRT: return [2, 15];
case BlockType.STONE: return [3, 15];
}
})();
if (data[bi] == BlockType.AIR) {
faces.push(makeFace('+y', upTexture, [x0 + j, k - 0.5, z0 + i]));
break;
}
const sideTexture = (() => {
switch (data[bi]) {
case BlockType.GRASS: return [1, 15];
case BlockType.DIRT: return [2, 15];
case BlockType.STONE: return [3, 15];
}
})();
for (let {block, dir, faceCenter} of sideNeighbors(i, j, k)) {
if (block === BlockType.AIR) {
faces.push(makeFace(dir, sideTexture, faceCenter));
} }
for (const {block, dir, faceCenter} of neighbors(i, j, k).filter(({block}) => block === BlockType.AIR)) {
faces.push({
blockIndex: bi,
face: makeFace(dir, faceTexture(data[bi], dir), faceCenter),
});
} }
} }
} }
} }
return makeBufferFromFaces(gl, faces); return faces;
} }
// - data <-- need to generate the first time, update when m&p // - data <-- need to generate the first time, update when m&p
@ -251,17 +265,22 @@ function invalidateChunkGeometry(world, i, j) {
delete chunk.buffer; delete chunk.buffer;
} }
export function createChunkFace(block, dir) {
return {
blockIndex: block.blockIndex,
face: makeFace(dir, faceTexture(block.type, dir), faceCenter(block.centerPosition, dir)),
};
}
/** Generates geometry for all visible chunks. */ /** Generates geometry for all visible chunks. */
export function updateWorldGeometry(gl, world, z, x, timeLimit = 10000) { export function updateWorldGeometry(gl, world, z, x, timeLimit = 10000) {
const ic = Math.floor(z / 16); const ic = Math.floor(z / 16);
const jc = Math.floor(x / 16); const jc = Math.floor(x / 16);
const blockIndex = (i, j, k) => 256 * (i*16 +j) + k;
const start = performance.now(); const start = performance.now();
// k. Now, generate buffers for all chunks // k. Now, generate buffers for all chunks
for (let radius = 0; radius < 8; radius++) { for (let radius = 1; radius < 8; radius++) {
for (let i = ic - radius; i < ic + radius; i++) { for (let i = ic - radius; i < ic + radius; i++) {
for (let j = jc - radius; j < jc + radius; j++) { for (let j = jc - radius; j < jc + radius; j++) {
const chunk = world.chunkMap.get(i, j); const chunk = world.chunkMap.get(i, j);
@ -270,11 +289,15 @@ export function updateWorldGeometry(gl, world, z, x, timeLimit = 10000) {
continue; continue;
} }
if(chunk.faces === undefined) {
const chunkz = 16 * i; const chunkz = 16 * i;
const chunkx = 16 * j; const chunkx = 16 * j;
const lookup = (i, j, k) => blockLookup(world, j + chunkx, k, i + chunkz).type; const lookup = (i, j, k) => blockLookup(world, j + chunkx, k, i + chunkz).type;
chunk.buffer = makeChunkBuffer(gl, chunk.data, chunk.position.z, chunk.position.x, lookup); chunk.faces = makeFaceList(chunk.data, chunk.position.z, chunk.position.x, lookup);
}
chunk.buffer = makeBufferFromFaces(gl, chunk.faces.map(f => f.face));
// throttle this for fluidity // throttle this for fluidity
if (performance.now() - start > timeLimit) { if (performance.now() - start > timeLimit) {