Minor performance refactor
- turns out the old makeFaceList was probably a bit faster
This commit is contained in:
234
world.js
234
world.js
@@ -218,16 +218,9 @@ function isTransparent(type) {
|
||||
}
|
||||
}
|
||||
|
||||
function makeFaceList(data, chunkz, chunkx, blockLookup) {
|
||||
const lookup = (i, j, k) => {
|
||||
if (i < 0 || j < 0 || i > 15 || j > 15) {
|
||||
return blockLookup(i, j, k);
|
||||
}
|
||||
if (k < 0 || k > 255) {
|
||||
return BlockType.UNDEFINED;
|
||||
}
|
||||
return data[256*(16*i + j) + k];
|
||||
};
|
||||
function makeFaceList(chunk, lookup) {
|
||||
const chunkz = chunk.position.z;
|
||||
const chunkx = chunk.position.x;
|
||||
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] },
|
||||
@@ -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] },
|
||||
];
|
||||
|
||||
const solidFaces = [];
|
||||
const transparentFaces = [];
|
||||
|
||||
for (let i = 0; i < 16; i++) {
|
||||
for (let j = 0; j < 16; j++) {
|
||||
let bi = i * 16 * 256 + j * 256;
|
||||
for (let k = 0; k < 256; k++, bi++) {
|
||||
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,
|
||||
});
|
||||
}
|
||||
function* blocks() {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
for (let j = 0; j < 16; j++) {
|
||||
for (let k = 0; k < 256; k++) {
|
||||
yield [i, j, k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
solidFaces,
|
||||
transparentFaces,
|
||||
};
|
||||
chunk.faces = [];
|
||||
chunk.transparentFaces = [];
|
||||
|
||||
return makeFaces(chunk, blocks, neighbors);
|
||||
}
|
||||
|
||||
// - 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?
|
||||
// --> could also render chunks 1 by 1
|
||||
|
||||
const undefinedChunk = {data: new Proxy({}, {
|
||||
get: () => BlockType.UNDEFINED,
|
||||
})};
|
||||
|
||||
class ChunkMap {
|
||||
map = {};
|
||||
|
||||
@@ -295,7 +264,8 @@ class ChunkMap {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -312,6 +282,109 @@ export function makeWorld() {
|
||||
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. */
|
||||
export function generateMissingChunks(world, z, x, timeLimit = 10000) {
|
||||
const ic = Math.floor(z / 16);
|
||||
@@ -320,46 +393,27 @@ export function generateMissingChunks(world, z, x, timeLimit = 10000) {
|
||||
const start = performance.now();
|
||||
|
||||
for (let radius = 1; radius < 8; radius++) {
|
||||
|
||||
for (let i = ic - radius; i < ic + radius; i++) {
|
||||
for (let j = jc - radius; j < jc + radius; j++) {
|
||||
if (world.chunkMap.has(i, j)) {
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chunk = makeChunk(i * 16, j * 16);
|
||||
world.chunks.push(chunk);
|
||||
world.chunkMap.set(i, j, chunk);
|
||||
|
||||
updateMissingFaces(world, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
const face = createChunkFace(block, dir);
|
||||
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);
|
||||
if (block.type === BlockType.WATER && dir === '+y') {
|
||||
center[1] -= 0.15;
|
||||
@@ -398,23 +452,31 @@ export function updateWorldGeometry(gl, world, z, x, timeLimit = 10000) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(chunk.faces === undefined) {
|
||||
const chunkz = 16 * i;
|
||||
const chunkx = 16 * j;
|
||||
const lookup = (i, j, k) => blockLookup(world, j + chunkx, k, i + chunkz).type;
|
||||
if (radius > 2 && performance.now() - start > timeLimit) {
|
||||
return;
|
||||
}
|
||||
|
||||
const faces = makeFaceList(chunk.data, chunk.position.z, chunk.position.x, lookup);
|
||||
chunk.faces = faces.solidFaces;
|
||||
chunk.transparentFaces = faces.transparentFaces;
|
||||
if(chunk.faces === undefined) {
|
||||
const neighborChunks = [
|
||||
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.transparentBuffer = makeBufferFromFaces(gl, chunk.transparentFaces.map(f => f.face));
|
||||
|
||||
// throttle this for fluidity
|
||||
if (radius > 2 && performance.now() - start > timeLimit) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user