Minor performance refactor
- turns out the old makeFaceList was probably a bit faster
This commit is contained in:
parent
a38c7f7a76
commit
c5370bf057
69
game.js
69
game.js
@ -123,34 +123,46 @@ function handleKeys(params) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getObjects(world, z, x, glContext) {
|
function memoize(f) {
|
||||||
const drawedChunks = world.chunks
|
const memo = {};
|
||||||
.filter(chunk => {
|
|
||||||
if (chunk.position.z < z - 8 * 16) return false;
|
|
||||||
if (chunk.position.z > z + 7 * 16) return false;
|
|
||||||
if (chunk.position.x < x - 8 * 16) return false;
|
|
||||||
if (chunk.position.x > x + 7 * 16) return false;
|
|
||||||
|
|
||||||
if (chunk.buffer === undefined) {
|
function g(...args) {
|
||||||
return false;
|
if (!(args in memo)) {
|
||||||
|
memo[args] = f(...args);
|
||||||
|
}
|
||||||
|
return memo[args];
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return g;
|
||||||
})
|
}
|
||||||
|
|
||||||
|
function _getChunkOrder(chunki, chunkj) {
|
||||||
|
return [...function* () {
|
||||||
|
for (let i = chunki - 8; i < chunki + 8; i++) {
|
||||||
|
for (let j = chunkj - 8; j < chunkj + 8; j++) {
|
||||||
|
yield [i, j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()]
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const zz = Math.floor(z / 16);
|
const d = (i, j) => {
|
||||||
const xx = Math.floor(x / 16);
|
const di = i - chunki;
|
||||||
const azz = Math.floor(a.position.z / 16) - zz;
|
const dj = j - chunkj;
|
||||||
const axx = Math.floor(a.position.x / 16) - xx;
|
return Math.sqrt(di * di + dj * dj);
|
||||||
const bzz = Math.floor(b.position.z / 16) - zz;
|
};
|
||||||
const bxx = Math.floor(b.position.x / 16) - xx;
|
return d(...b) - d(...a);
|
||||||
const da = Math.abs(azz) + Math.abs(axx);
|
|
||||||
const db = Math.abs(bzz) + Math.abs(bxx);
|
|
||||||
return db - da;
|
|
||||||
});
|
});
|
||||||
const buffers = drawedChunks
|
}
|
||||||
.map(chunk => chunk.buffer)
|
const getChunkOrder = memoize(_getChunkOrder);
|
||||||
.concat(drawedChunks.map(chunk => chunk.transparentBuffer));
|
|
||||||
|
function getObjects(world, z, x, glContext) {
|
||||||
|
const chunki = Math.floor(z / 16);
|
||||||
|
const chunkj = Math.floor(x / 16);
|
||||||
|
const chunks = getChunkOrder(chunki, chunkj)
|
||||||
|
.map(([i, j]) => world.chunkMap.get(i, j))
|
||||||
|
.filter(chunk => chunk.buffer !== undefined);
|
||||||
|
const buffers = chunks.map(chunk => chunk.buffer)
|
||||||
|
.concat(chunks.map(chunk => chunk.transparentBuffer));
|
||||||
return buffers.map(buffer => ({
|
return buffers.map(buffer => ({
|
||||||
position: [0.0, 0.0, 0.0],
|
position: [0.0, 0.0, 0.0],
|
||||||
orientation: [0.0, 0.0, 0.0],
|
orientation: [0.0, 0.0, 0.0],
|
||||||
@ -308,12 +320,13 @@ export function initUiListeners(params, canvas) {
|
|||||||
};
|
};
|
||||||
const keyListener = e => {
|
const keyListener = e => {
|
||||||
if (e.type === 'keydown') {
|
if (e.type === 'keydown') {
|
||||||
|
if (e.repeat) return;
|
||||||
params.keys.add(e.code);
|
params.keys.add(e.code);
|
||||||
|
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case 'KeyF':
|
case 'KeyF':
|
||||||
params.flying = !params.flying;
|
params.flying = !params.flying;
|
||||||
break;
|
return false;
|
||||||
case 'Space':
|
case 'Space':
|
||||||
if (!params.flying) {
|
if (!params.flying) {
|
||||||
if (params.jumpAmount > 0) {
|
if (params.jumpAmount > 0) {
|
||||||
@ -322,7 +335,7 @@ export function initUiListeners(params, canvas) {
|
|||||||
params.jumpAmount -= 1;
|
params.jumpAmount -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
params.keys.delete(e.code);
|
params.keys.delete(e.code);
|
||||||
@ -355,13 +368,11 @@ export function initUiListeners(params, canvas) {
|
|||||||
};
|
};
|
||||||
canvas.onclick = canvasClickHandler;
|
canvas.onclick = canvasClickHandler;
|
||||||
document.addEventListener('keydown', e => {
|
document.addEventListener('keydown', e => {
|
||||||
|
if (e.repeat) return;
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case 'F11':
|
case 'F11':
|
||||||
canvas.requestFullscreen().then(() => {
|
canvas.requestFullscreen();
|
||||||
console.log('full screen!!');
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
230
world.js
230
world.js
@ -218,16 +218,9 @@ function isTransparent(type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeFaceList(data, chunkz, chunkx, blockLookup) {
|
function makeFaceList(chunk, lookup) {
|
||||||
const lookup = (i, j, k) => {
|
const chunkz = chunk.position.z;
|
||||||
if (i < 0 || j < 0 || i > 15 || j > 15) {
|
const chunkx = chunk.position.x;
|
||||||
return blockLookup(i, j, k);
|
|
||||||
}
|
|
||||||
if (k < 0 || k > 255) {
|
|
||||||
return BlockType.UNDEFINED;
|
|
||||||
}
|
|
||||||
return data[256*(16*i + j) + k];
|
|
||||||
};
|
|
||||||
const neighbors = (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: [chunkx + j, k, chunkz + 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: [chunkx + j, k, chunkz + i + 0.5] },
|
||||||
@ -237,48 +230,20 @@ function makeFaceList(data, chunkz, chunkx, blockLookup) {
|
|||||||
{ 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 solidFaces = [];
|
function* blocks() {
|
||||||
const transparentFaces = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < 16; i++) {
|
for (let i = 0; i < 16; i++) {
|
||||||
for (let j = 0; j < 16; j++) {
|
for (let j = 0; j < 16; j++) {
|
||||||
let bi = i * 16 * 256 + j * 256;
|
for (let k = 0; k < 256; k++) {
|
||||||
for (let k = 0; k < 256; k++, bi++) {
|
yield [i, j, k];
|
||||||
if (data[bi] === BlockType.AIR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isTransparent(data[bi])) {
|
|
||||||
for (const { dir, faceCenter } of neighbors(i, j, k)
|
|
||||||
.filter(({ block }) => block === BlockType.AIR)
|
|
||||||
) {
|
|
||||||
if (data[bi] === BlockType.WATER) {
|
|
||||||
faceCenter[1] -= 0.15;
|
|
||||||
}
|
|
||||||
transparentFaces.push({
|
|
||||||
dir,
|
|
||||||
face: makeFace(dir, faceTexture(data[bi], dir), faceCenter),
|
|
||||||
blockIndex: bi,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const { dir, faceCenter } of neighbors(i, j, k)
|
|
||||||
.filter(({ block }) => isTransparent(block))
|
|
||||||
) {
|
|
||||||
solidFaces.push({
|
|
||||||
dir,
|
|
||||||
face: makeFace(dir, faceTexture(data[bi], dir), faceCenter),
|
|
||||||
blockIndex: bi,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
chunk.faces = [];
|
||||||
solidFaces,
|
chunk.transparentFaces = [];
|
||||||
transparentFaces,
|
|
||||||
};
|
return makeFaces(chunk, blocks, neighbors);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - data <-- need to generate the first time, update when m&p
|
// - data <-- need to generate the first time, update when m&p
|
||||||
@ -286,6 +251,10 @@ function makeFaceList(data, chunkz, chunkx, blockLookup) {
|
|||||||
// - buffers <-- need to generate every time geometry changes?
|
// - buffers <-- need to generate every time geometry changes?
|
||||||
// --> could also render chunks 1 by 1
|
// --> could also render chunks 1 by 1
|
||||||
|
|
||||||
|
const undefinedChunk = {data: new Proxy({}, {
|
||||||
|
get: () => BlockType.UNDEFINED,
|
||||||
|
})};
|
||||||
|
|
||||||
class ChunkMap {
|
class ChunkMap {
|
||||||
map = {};
|
map = {};
|
||||||
|
|
||||||
@ -295,7 +264,8 @@ class ChunkMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(i, j) {
|
get(i, j) {
|
||||||
return this.map[this.key(i, j)];
|
const out = this.map[this.key(i, j)];
|
||||||
|
return out ?? undefinedChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
set(i, j, x) {
|
set(i, j, x) {
|
||||||
@ -312,6 +282,109 @@ export function makeWorld() {
|
|||||||
return {chunks: [], chunkMap: new ChunkMap()};
|
return {chunks: [], chunkMap: new ChunkMap()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeFaces(chunk, blocks, neighbors) {
|
||||||
|
if (chunk === undefined || chunk.faces === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let geometryChanged = false;
|
||||||
|
|
||||||
|
for (const [i, j, k] of blocks()) {
|
||||||
|
const bi = 256 * (16 * i + j) + k;
|
||||||
|
if (chunk.data[bi] === BlockType.AIR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isTransparent(chunk.data[bi])) {
|
||||||
|
for (const { dir, faceCenter } of neighbors(i, j, k)
|
||||||
|
.filter(({ block }) => block === BlockType.AIR)
|
||||||
|
) {
|
||||||
|
if (chunk.data[bi] === BlockType.WATER) {
|
||||||
|
faceCenter[1] -= 0.15;
|
||||||
|
}
|
||||||
|
chunk.transparentFaces.push({
|
||||||
|
dir,
|
||||||
|
face: makeFace(dir, faceTexture(chunk.data[bi], dir), faceCenter),
|
||||||
|
blockIndex: bi,
|
||||||
|
});
|
||||||
|
geometryChanged = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const { dir, faceCenter } of neighbors(i, j, k)
|
||||||
|
.filter(({ block }) => isTransparent(block))
|
||||||
|
) {
|
||||||
|
chunk.faces.push({
|
||||||
|
dir,
|
||||||
|
face: makeFace(dir, faceTexture(chunk.data[bi], dir), faceCenter),
|
||||||
|
blockIndex: bi,
|
||||||
|
});
|
||||||
|
geometryChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (geometryChanged && chunk.buffer !== undefined) {
|
||||||
|
chunk.buffer.delete();
|
||||||
|
delete chunk.buffer;
|
||||||
|
chunk.transparentBuffer.delete();
|
||||||
|
delete chunk.transparentBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMissingFaces(world, chunk) {
|
||||||
|
const chunkz = chunk.position.z;
|
||||||
|
const chunkx = chunk.position.x;
|
||||||
|
const chunki = Math.floor(chunkz / 16);
|
||||||
|
const chunkj = Math.floor(chunkx / 16);
|
||||||
|
|
||||||
|
makeFaces(world.chunkMap.get(chunki - 1, chunkj),
|
||||||
|
function* () {
|
||||||
|
for (let j = 0; j < 16; j++) {
|
||||||
|
for (let k = 0; k < 256; k++) {
|
||||||
|
yield [15, j, k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (i, j, k) => [{
|
||||||
|
block: chunk.data[256 * (16 * 0 + j) + k],
|
||||||
|
dir: '+z',
|
||||||
|
faceCenter: [chunkx + j, k, chunkz - 0.5],
|
||||||
|
}]);
|
||||||
|
makeFaces(world.chunkMap.get(chunki + 1, chunkj),
|
||||||
|
function* () {
|
||||||
|
for (let j = 0; j < 16; j++) {
|
||||||
|
for (let k = 0; k < 256; k++) {
|
||||||
|
yield [0, j, k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (i, j, k) => [{
|
||||||
|
block: chunk.data[256 * (15 * 16 + j) + k],
|
||||||
|
dir: '-z',
|
||||||
|
faceCenter: [chunkx + j, k, chunkz + 16 - 0.5],
|
||||||
|
}]);
|
||||||
|
makeFaces(world.chunkMap.get(chunki, chunkj - 1),
|
||||||
|
function* () {
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
for (let k = 0; k < 256; k++) {
|
||||||
|
yield [i, 15, k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (i, j, k) => [{
|
||||||
|
block: chunk.data[256 * (16 * i + 0) + k],
|
||||||
|
dir: '+x',
|
||||||
|
faceCenter: [chunkx - 0.5, k, chunkz + i],
|
||||||
|
}]);
|
||||||
|
makeFaces(world.chunkMap.get(chunki, chunkj + 1),
|
||||||
|
function* () {
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
for (let k = 0; k < 256; k++) {
|
||||||
|
yield [i, 0, k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (i, j, k) => [{
|
||||||
|
block: chunk.data[256 * (16 * i + 15) + k],
|
||||||
|
dir: '-x',
|
||||||
|
faceCenter: [chunkx + 16 - 0.5, k, chunkz + i],
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
/** Update the world, generating missing chunks if necessary. */
|
/** Update the world, generating missing chunks if necessary. */
|
||||||
export function generateMissingChunks(world, z, x, timeLimit = 10000) {
|
export function generateMissingChunks(world, z, x, timeLimit = 10000) {
|
||||||
const ic = Math.floor(z / 16);
|
const ic = Math.floor(z / 16);
|
||||||
@ -320,46 +393,27 @@ export function generateMissingChunks(world, z, x, timeLimit = 10000) {
|
|||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
|
|
||||||
for (let radius = 1; 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++) {
|
||||||
if (world.chunkMap.has(i, j)) {
|
if (world.chunkMap.has(i, j)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const chunk = makeChunk(i * 16, j * 16);
|
|
||||||
world.chunks.push(chunk);
|
|
||||||
world.chunkMap.set(i, j, chunk);
|
|
||||||
|
|
||||||
invalidateChunkGeometry(world, i - 1, j);
|
|
||||||
invalidateChunkGeometry(world, i + 1, j);
|
|
||||||
invalidateChunkGeometry(world, i, j - 1);
|
|
||||||
invalidateChunkGeometry(world, i, j + 1);
|
|
||||||
|
|
||||||
if (radius > 2 && performance.now() - start > timeLimit) {
|
if (radius > 2 && performance.now() - start > timeLimit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chunk = makeChunk(i * 16, j * 16);
|
||||||
|
world.chunks.push(chunk);
|
||||||
|
world.chunkMap.set(i, j, chunk);
|
||||||
|
|
||||||
|
updateMissingFaces(world, chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
function invalidateChunkGeometry(world, i, j) {
|
|
||||||
const chunk = world.chunkMap.get(i, j);
|
|
||||||
if (chunk === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (chunk.faces === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
delete chunk.faces;
|
|
||||||
if (chunk.buffer === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
chunk.buffer.delete();
|
|
||||||
delete chunk.buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFace(chunk, block, dir) {
|
function addFace(chunk, block, dir) {
|
||||||
const face = createChunkFace(block, dir);
|
const face = createChunkFace(block, dir);
|
||||||
if (isTransparent(block.type)) {
|
if (isTransparent(block.type)) {
|
||||||
@ -369,7 +423,7 @@ function addFace(chunk, block, dir) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createChunkFace(block, dir, center = null) {
|
function createChunkFace(block, dir, center = null) {
|
||||||
center = center ?? faceCenter(block.centerPosition, dir);
|
center = center ?? faceCenter(block.centerPosition, dir);
|
||||||
if (block.type === BlockType.WATER && dir === '+y') {
|
if (block.type === BlockType.WATER && dir === '+y') {
|
||||||
center[1] -= 0.15;
|
center[1] -= 0.15;
|
||||||
@ -398,23 +452,31 @@ export function updateWorldGeometry(gl, world, z, x, timeLimit = 10000) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(chunk.faces === undefined) {
|
if (radius > 2 && performance.now() - start > timeLimit) {
|
||||||
const chunkz = 16 * i;
|
return;
|
||||||
const chunkx = 16 * j;
|
}
|
||||||
const lookup = (i, j, k) => blockLookup(world, j + chunkx, k, i + chunkz).type;
|
|
||||||
|
|
||||||
const faces = makeFaceList(chunk.data, chunk.position.z, chunk.position.x, lookup);
|
if(chunk.faces === undefined) {
|
||||||
chunk.faces = faces.solidFaces;
|
const neighborChunks = [
|
||||||
chunk.transparentFaces = faces.transparentFaces;
|
world.chunkMap.get(i - 1, j),
|
||||||
|
world.chunkMap.get(i + 1, j),
|
||||||
|
world.chunkMap.get(i, j - 1),
|
||||||
|
world.chunkMap.get(i, j + 1),
|
||||||
|
];
|
||||||
|
const lookup = (i, j, k) => {
|
||||||
|
if (i < 0) { return neighborChunks[0].data[256 * (16 * (i + 16) + j) + k]; }
|
||||||
|
if (i > 15) { return neighborChunks[1].data[256 * (16 * (i - 16) + j) + k]; }
|
||||||
|
if (j < 0) { return neighborChunks[2].data[256 * (16 * i + j + 16) + k]; }
|
||||||
|
if (j > 15) { return neighborChunks[3].data[256 * (16 * i + j - 16) + k]; }
|
||||||
|
if (k < 0 || k > 255) { return BlockType.UNDEFINED; }
|
||||||
|
return chunk.data[256*(16*i + j) + k];
|
||||||
|
};
|
||||||
|
|
||||||
|
makeFaceList(chunk, lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk.buffer = makeBufferFromFaces(gl, chunk.faces.map(f => f.face));
|
chunk.buffer = makeBufferFromFaces(gl, chunk.faces.map(f => f.face));
|
||||||
chunk.transparentBuffer = makeBufferFromFaces(gl, chunk.transparentFaces.map(f => f.face));
|
chunk.transparentBuffer = makeBufferFromFaces(gl, chunk.transparentFaces.map(f => f.face));
|
||||||
|
|
||||||
// throttle this for fluidity
|
|
||||||
if (radius > 2 && performance.now() - start > timeLimit) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user