Optimised voxel and block mesh buffer generation

This commit is contained in:
Lucas Dower 2023-06-10 15:35:48 +01:00
parent 968f90f436
commit b8cc31d75a
No known key found for this signature in database
GPG Key ID: B3EE6B8499593605
6 changed files with 84 additions and 26 deletions

View File

@ -6,6 +6,7 @@ import { Mesh, SolidMaterial, TexturedMaterial } from './mesh';
import { OcclusionManager } from './occlusion';
import { ProgressManager } from './progress';
import { AttributeData } from './render_buffer';
import { AppUtil } from './util';
import { ASSERT } from './util/error_util';
import { Vector3 } from './vector';
import { VoxelMesh } from './voxel_mesh';
@ -71,38 +72,56 @@ export class ChunkedBufferGenerator {
const cube: AttributeData = GeometryTemplates.getBoxBufferData(new Vector3(0, 0, 0));
const voxels = voxelMesh.getVoxels();
// Build position buffer
for (let i = 0; i < numBufferVoxels; ++i) {
const voxelIndex = i + voxelsStartIndex;
const voxel = voxels[voxelIndex];
const voxelColourArray = [voxel.colour.r, voxel.colour.g, voxel.colour.b, voxel.colour.a];
const voxel = voxels[i + voxelsStartIndex];
const voxelPositionArray = voxel.position.toArray();
for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.POSITION; ++j) {
newBuffer.position.data[i * AppConstants.VoxelMeshBufferComponentOffsets.POSITION + j] = cube.custom.position[j] + voxelPositionArray[j % 3];
}
}
for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.COLOUR; ++j) {
newBuffer.colour.data[i * AppConstants.VoxelMeshBufferComponentOffsets.COLOUR + j] = voxelColourArray[j % 4];
}
// Build colour buffer
for (let i = 0; i < numBufferVoxels; ++i) {
const voxel = voxels[i + voxelsStartIndex];
newBuffer.colour.data[i * 96 + 0] = voxel.colour.r;
newBuffer.colour.data[i * 96 + 1] = voxel.colour.g;
newBuffer.colour.data[i * 96 + 2] = voxel.colour.b;
newBuffer.colour.data[i * 96 + 3] = voxel.colour.a;
for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.NORMAL; ++j) {
newBuffer.normal.data[i * AppConstants.VoxelMeshBufferComponentOffsets.NORMAL + j] = cube.custom.normal[j];
}
AppUtil.Array.repeatedFill(newBuffer.colour.data, i * 96, 4, 24);
}
for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.TEXCOORD; ++j) {
newBuffer.texcoord.data[i * AppConstants.VoxelMeshBufferComponentOffsets.TEXCOORD + j] = cube.custom.texcoord[j];
}
// Build normal buffer
{
newBuffer.normal.data.set(cube.custom.normal, 0);
AppUtil.Array.repeatedFill(newBuffer.normal.data, 0, 72, numBufferVoxels);
}
// Build texcoord buffer
{
newBuffer.texcoord.data.set(cube.custom.texcoord, 0);
AppUtil.Array.repeatedFill(newBuffer.texcoord.data, 0, 48, numBufferVoxels);
}
// Build indices buffer
for (let i = 0; i < numBufferVoxels; ++i) {
for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.INDICES; ++j) {
newBuffer.indices.data[i * AppConstants.VoxelMeshBufferComponentOffsets.INDICES + j] = cube.indices[j] + (i * AppConstants.INDICES_PER_VOXEL);
}
}
if (params.enableAmbientOcclusion) {
const voxelOcclusionArray = OcclusionManager.Get.getOcclusions(voxel.position, voxelMesh);
for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.OCCLUSION; ++j) {
newBuffer.occlusion.data[i * AppConstants.VoxelMeshBufferComponentOffsets.OCCLUSION + j] = voxelOcclusionArray[j];
}
// Build occlusion buffer
if (params.enableAmbientOcclusion) {
const voxelOcclusionArray = new Float32Array(96);
for (let i = 0; i < numBufferVoxels; ++i) {
const voxel = voxels[i + voxelsStartIndex];
OcclusionManager.Get.getOcclusions(voxelOcclusionArray, voxel.position, voxelMesh);
newBuffer.occlusion.data.set(voxelOcclusionArray, i * AppConstants.VoxelMeshBufferComponentOffsets.OCCLUSION);
}
}
@ -149,6 +168,7 @@ export class ChunkedBufferGenerator {
let insertIndex = 0;
let lightingInsertIndex = 0;
//const blockPositionArray = new Float32Array(3);
for (let i = 0; i < numBufferBlocks; ++i) {
const blockIndex = i + blocksStartIndex;
const blockLighting = blockMesh.getBlockLighting(blocks[blockIndex].voxel.position);
@ -166,10 +186,13 @@ export class ChunkedBufferGenerator {
}
}
const blockPosition = blocks[blockIndex].voxel.position.toArray();
for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.POSITION; ++j) {
newBuffer.blockPosition.data[i * AppConstants.VoxelMeshBufferComponentOffsets.POSITION + j] = blockPosition[j % 3];
}
//const blockPosition = blocks[blockIndex].voxel.position.toArray();
//blocks[blockIndex].voxel.position.intoArray(blockPositionArray, 0);
newBuffer.blockPosition.data[i * 72 + 0] = blocks[blockIndex].voxel.position.x;
newBuffer.blockPosition.data[i * 72 + 1] = blocks[blockIndex].voxel.position.y;
newBuffer.blockPosition.data[i * 72 + 2] = blocks[blockIndex].voxel.position.z;
AppUtil.Array.repeatedFill(newBuffer.blockPosition.data, i * 72, 3, 24);
}
return {

View File

@ -16,7 +16,7 @@ export class AppConfig {
public readonly MINECRAFT_VERSION = '1.19.4';
public readonly LOCALE = 'en_GB';
public readonly VOXEL_BUFFER_CHUNK_SIZE = 5_000;
public readonly VOXEL_BUFFER_CHUNK_SIZE = 50_000;
public readonly AMBIENT_OCCLUSION_OVERRIDE_CORNER = true;
public readonly USE_WORKER_THREAD = true;
public readonly MULTISAMPLE_COUNT = 16;

View File

@ -26,6 +26,10 @@ export namespace AppMath {
export function uint8(decimal: number) {
return Math.floor(decimal * 255);
}
export function largestPowerOfTwoLessThanN(n: number) {
return Math.floor(Math.log2(n));
}
}
export const argMax = (array: [number]) => {

View File

@ -25,12 +25,14 @@ export class OcclusionManager {
return new Array<number>(96).fill(1.0);
}
public getOcclusions(centre: Vector3, voxelMesh: VoxelMesh) {
// Assume's buffer is of length 96
public getOcclusions(buffer: Float32Array, centre: Vector3, voxelMesh: VoxelMesh): void {
// Cache local neighbours
const neighbourData = voxelMesh.getNeighbours(centre);
if (neighbourData === undefined) {
// This voxel has no neighbours within a 1-block radius
return this.getBlankOcclusions();
buffer.fill(0.0);
return;
}
for (let i = 0; i < 27; ++i) {
@ -69,7 +71,8 @@ export class OcclusionManager {
}
}
return this._occlusions;
buffer.set(this._occlusions, 0);
return;
}
public static getNeighbourIndex(neighbour: Vector3) {

View File

@ -1,3 +1,5 @@
import { AppMath } from "./math";
export namespace AppUtil {
export namespace Text {
export function capitaliseFirstLetter(text: string) {
@ -17,6 +19,26 @@ export namespace AppUtil {
return blockName.includes(':');
}
}
export namespace Array {
/**
* An optimised function for repeating a subarray contained within a buffer multiple times by
* repeatedly doubling the subarray's length.
*/
export function repeatedFill(buffer: Float32Array, start: number, startLength: number, desiredCount: number) {
const pow = AppMath.largestPowerOfTwoLessThanN(desiredCount);
let len = startLength;
for (let i = 0; i < pow; ++i) {
buffer.copyWithin(start + len, start, start + len);
len *= 2;
}
const finalLength = desiredCount * startLength;
buffer.copyWithin(start + len, start, start + finalLength - len);
}
}
}
/* eslint-disable */

View File

@ -249,6 +249,12 @@ export class Vector3 implements IHashable {
public stringify() {
return `${this.x}_${this.y}_${this.z}`;
}
public intoArray(array: Float32Array, start: number) {
array[start + 0] = this.x;
array[start + 1] = this.y;
array[start + 2] = this.z;
}
}
export const fastCrossXAxis = (vec: Vector3) => {