mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2024-12-15 02:59:55 +08:00
Pre-allocate voxel mesh buffer, ~3x faster
This commit is contained in:
parent
081b602493
commit
6dd471bc32
@ -1,7 +1,8 @@
|
||||
import { BasicBlockAssigner, OrderedDitheringBlockAssigner } from './block_assigner';
|
||||
import { Voxel, VoxelMesh } from './voxel_mesh';
|
||||
import { FACES_PER_VOXEL, VERTICES_PER_FACE, Voxel, VoxelMesh, VoxelMeshBufferComponentOffsets } from './voxel_mesh';
|
||||
import { BlockAtlas, BlockInfo } from './block_atlas';
|
||||
import { ColourSpace, AppError } from './util';
|
||||
import { ColourSpace, AppError, ASSERT } from './util';
|
||||
import { ComponentSize } from './buffer';
|
||||
import { Renderer } from './renderer';
|
||||
|
||||
interface Block {
|
||||
@ -70,21 +71,53 @@ export class BlockMesh {
|
||||
}
|
||||
|
||||
public createBuffer() {
|
||||
const buffer = Renderer.Get._voxelBuffer.copy();
|
||||
ASSERT(this._blocks.length === this._voxelMesh.getVoxelCount());
|
||||
|
||||
const blockTexcoords: number[] = [];
|
||||
for (const block of this._blocks) {
|
||||
const faceOrder = ['north', 'south', 'up', 'down', 'east', 'west'];
|
||||
for (const face of faceOrder) {
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
const texcoord = block.blockInfo.faces[face].texcoord;
|
||||
blockTexcoords.push(texcoord.u, texcoord.v);
|
||||
const numBlocks = this._blocks.length;
|
||||
const newBuffer = {
|
||||
position: {
|
||||
numComponents: ComponentSize.POSITION,
|
||||
data: Renderer.Get._voxelBufferRaw!.position.data,
|
||||
},
|
||||
colour: {
|
||||
numComponents: ComponentSize.COLOUR,
|
||||
data: Renderer.Get._voxelBufferRaw!.colour.data,
|
||||
},
|
||||
occlusion: {
|
||||
numComponents: ComponentSize.OCCLUSION,
|
||||
data: Renderer.Get._voxelBufferRaw!.occlusion.data,
|
||||
},
|
||||
texcoord: {
|
||||
numComponents: ComponentSize.TEXCOORD,
|
||||
data: Renderer.Get._voxelBufferRaw!.texcoord.data,
|
||||
},
|
||||
normal: {
|
||||
numComponents: ComponentSize.NORMAL,
|
||||
data: Renderer.Get._voxelBufferRaw!.normal.data,
|
||||
},
|
||||
indices: {
|
||||
numComponents: ComponentSize.INDICES,
|
||||
data: Renderer.Get._voxelBufferRaw!.indices.data,
|
||||
},
|
||||
blockTexcoord: {
|
||||
numComponents: ComponentSize.TEXCOORD,
|
||||
data: new Float32Array(numBlocks * VoxelMeshBufferComponentOffsets.TEXCOORD),
|
||||
},
|
||||
};
|
||||
|
||||
const faceOrder = ['north', 'south', 'up', 'down', 'east', 'west'];
|
||||
let insertIndex = 0;
|
||||
for (let i = 0; i < numBlocks; ++i) {
|
||||
for (let f = 0; f < FACES_PER_VOXEL; ++f) {
|
||||
const faceName = faceOrder[f];
|
||||
const texcoord = this._blocks[i].blockInfo.faces[faceName].texcoord;
|
||||
for (let v = 0; v < VERTICES_PER_FACE; ++v) {
|
||||
newBuffer.blockTexcoord.data[insertIndex++] = texcoord.u;
|
||||
newBuffer.blockTexcoord.data[insertIndex++] = texcoord.v;
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer.attachNewAttribute({ name: 'blockTexcoord', numComponents: 2 }, blockTexcoords);
|
||||
buffer.removeAttribute('colour');
|
||||
|
||||
return buffer;
|
||||
return newBuffer;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,15 @@ import { ASSERT } from './util';
|
||||
|
||||
import * as twgl from 'twgl.js';
|
||||
|
||||
export namespace ComponentSize {
|
||||
export const TEXCOORD = 2;
|
||||
export const POSITION = 3;
|
||||
export const COLOUR = 3;
|
||||
export const NORMAL = 3;
|
||||
export const INDICES = 3;
|
||||
export const OCCLUSION = 4;
|
||||
}
|
||||
|
||||
export interface Attribute {
|
||||
name: string,
|
||||
numComponents: number
|
||||
|
@ -48,8 +48,9 @@ export class Renderer {
|
||||
buffer: RenderBuffer,
|
||||
material: (SolidMaterial | (TexturedMaterial & { texture: WebGLTexture }))
|
||||
}>;
|
||||
public _voxelBuffer: RenderBuffer;
|
||||
private _blockBuffer: RenderBuffer;
|
||||
public _voxelBuffer?: twgl.BufferInfo;
|
||||
public _voxelBufferRaw?: any;
|
||||
private _blockBuffer?: twgl.BufferInfo;
|
||||
private _debugBuffers: { [meshType: string]: { [bufferComponent: string]: RenderBuffer } };
|
||||
|
||||
private _isGridComponentEnabled: { [bufferComponent: string]: boolean };
|
||||
@ -67,8 +68,6 @@ export class Renderer {
|
||||
|
||||
this._modelsAvailable = 0;
|
||||
this._materialBuffers = [];
|
||||
this._voxelBuffer = new RenderBuffer([]);
|
||||
this._blockBuffer = new RenderBuffer([]);
|
||||
|
||||
this._debugBuffers = {};
|
||||
this._debugBuffers[MeshType.None] = {};
|
||||
@ -191,7 +190,9 @@ export class Renderer {
|
||||
|
||||
LOG('Using voxel mesh');
|
||||
LOG(voxelMesh);
|
||||
this._voxelBuffer = voxelMesh.createBuffer(ambientOcclusionEnabled);
|
||||
|
||||
this._voxelBufferRaw = voxelMesh.createBuffer(ambientOcclusionEnabled);
|
||||
this._voxelBuffer = twgl.createBufferInfoFromArrays(this._gl, this._voxelBufferRaw);
|
||||
this._voxelSize = voxelMesh?.getVoxelSize();
|
||||
|
||||
// this._translate = new Vector3(0, voxelMesh.getBounds().getDimensions().y/2 * voxelMesh.getVoxelSize(), 0);
|
||||
@ -217,7 +218,7 @@ export class Renderer {
|
||||
|
||||
LOG('Using block mesh');
|
||||
LOG(blockMesh);
|
||||
this._blockBuffer = blockMesh.createBuffer();
|
||||
this._blockBuffer = twgl.createBufferInfoFromArrays(this._gl, blockMesh.createBuffer());
|
||||
this._voxelSize = blockMesh.getVoxelMesh().getVoxelSize();
|
||||
|
||||
this._atlasTexture = twgl.createTexture(this._gl, {
|
||||
@ -275,21 +276,35 @@ export class Renderer {
|
||||
}
|
||||
|
||||
private _drawVoxelMesh() {
|
||||
this._drawRegister(this._voxelBuffer, ShaderManager.Get.voxelProgram, {
|
||||
const shader = ShaderManager.Get.voxelProgram;
|
||||
const uniforms = {
|
||||
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
||||
u_voxelSize: this._voxelSize,
|
||||
u_gridOffset: this._gridOffset.toArray(),
|
||||
});
|
||||
};
|
||||
if (this._voxelBuffer) {
|
||||
this._gl.useProgram(shader.program);
|
||||
twgl.setBuffersAndAttributes(this._gl, shader, this._voxelBuffer);
|
||||
twgl.setUniforms(shader, uniforms);
|
||||
this._gl.drawElements(this._gl.TRIANGLES, this._voxelBuffer.numElements, this._gl.UNSIGNED_INT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private _drawBlockMesh() {
|
||||
this._drawRegister(this._blockBuffer, ShaderManager.Get.blockProgram, {
|
||||
const shader = ShaderManager.Get.blockProgram;
|
||||
const uniforms = {
|
||||
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
||||
u_texture: this._atlasTexture,
|
||||
u_voxelSize: this._voxelSize,
|
||||
u_atlasSize: BlockAtlas.Get.getAtlasSize(),
|
||||
u_gridOffset: this._gridOffset.toArray(),
|
||||
});
|
||||
};
|
||||
if (this._blockBuffer) {
|
||||
this._gl.useProgram(shader.program);
|
||||
twgl.setBuffersAndAttributes(this._gl, shader, this._blockBuffer);
|
||||
twgl.setUniforms(shader, uniforms);
|
||||
this._gl.drawElements(this._gl.TRIANGLES, this._blockBuffer.numElements, this._gl.UNSIGNED_INT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
@ -306,10 +321,6 @@ export class Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
private static _getNeighbourIndex(neighbour: Vector3) {
|
||||
return 9*(neighbour.x+1) + 3*(neighbour.y+1) + (neighbour.z+1);
|
||||
}
|
||||
|
||||
private _setupScene() {
|
||||
twgl.resizeCanvasToDisplaySize(<HTMLCanvasElement> this._gl.canvas);
|
||||
this._gl.viewport(0, 0, this._gl.canvas.width, this._gl.canvas.height);
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { RenderBuffer, AttributeData } from './buffer';
|
||||
import { AppConfig } from './config';
|
||||
import { RenderBuffer, AttributeData, ComponentSize } from './buffer';
|
||||
import { GeometryTemplates } from './geometry';
|
||||
import { HashMap } from './hash_map';
|
||||
import { Mesh } from './mesh';
|
||||
@ -8,6 +7,20 @@ import { TextureFiltering } from './texture';
|
||||
import { Bounds, RGB } from './util';
|
||||
import { Vector3 } from './vector';
|
||||
|
||||
export const FACES_PER_VOXEL = 6;
|
||||
export const VERTICES_PER_FACE = 4;
|
||||
const INDICES_PER_VOXEL = 24;
|
||||
const COMPONENT_PER_SIZE_OFFSET = FACES_PER_VOXEL * VERTICES_PER_FACE;
|
||||
|
||||
export namespace VoxelMeshBufferComponentOffsets {
|
||||
export const TEXCOORD = ComponentSize.TEXCOORD * COMPONENT_PER_SIZE_OFFSET;
|
||||
export const POSITION = ComponentSize.POSITION * COMPONENT_PER_SIZE_OFFSET;
|
||||
export const COLOUR = ComponentSize.COLOUR * COMPONENT_PER_SIZE_OFFSET;
|
||||
export const NORMAL = ComponentSize.NORMAL * COMPONENT_PER_SIZE_OFFSET;
|
||||
export const INDICES = ComponentSize.INDICES * COMPONENT_PER_SIZE_OFFSET;
|
||||
export const OCCLUSION = ComponentSize.OCCLUSION * COMPONENT_PER_SIZE_OFFSET;
|
||||
}
|
||||
|
||||
export interface Voxel {
|
||||
position: Vector3;
|
||||
colour: RGB;
|
||||
@ -94,57 +107,75 @@ export class VoxelMesh {
|
||||
return this._bounds;
|
||||
}
|
||||
|
||||
public getVoxelCount() {
|
||||
return this._voxels.length;
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public createBuffer(ambientOcclusionEnabled: boolean) {
|
||||
const buffer = new RenderBuffer([
|
||||
{ name: 'position', numComponents: 3 },
|
||||
{ name: 'colour', numComponents: 3 },
|
||||
{ name: 'occlusion', numComponents: 4 },
|
||||
{ name: 'texcoord', numComponents: 2 },
|
||||
{ name: 'normal', numComponents: 3 },
|
||||
]);
|
||||
const numVoxels = this._voxels.length;
|
||||
const newBuffer = {
|
||||
position: {
|
||||
numComponents: ComponentSize.POSITION,
|
||||
data: new Float32Array(numVoxels * VoxelMeshBufferComponentOffsets.POSITION),
|
||||
},
|
||||
colour: {
|
||||
numComponents: ComponentSize.COLOUR,
|
||||
data: new Float32Array(numVoxels * VoxelMeshBufferComponentOffsets.COLOUR),
|
||||
},
|
||||
occlusion: {
|
||||
numComponents: ComponentSize.OCCLUSION,
|
||||
data: new Float32Array(numVoxels * VoxelMeshBufferComponentOffsets.OCCLUSION).fill(1.0),
|
||||
},
|
||||
texcoord: {
|
||||
numComponents: ComponentSize.TEXCOORD,
|
||||
data: new Float32Array(numVoxels * VoxelMeshBufferComponentOffsets.TEXCOORD),
|
||||
},
|
||||
normal: {
|
||||
numComponents: ComponentSize.NORMAL,
|
||||
data: new Float32Array(numVoxels * VoxelMeshBufferComponentOffsets.NORMAL),
|
||||
},
|
||||
indices: {
|
||||
numComponents: ComponentSize.INDICES,
|
||||
data: new Uint32Array(numVoxels * VoxelMeshBufferComponentOffsets.INDICES),
|
||||
},
|
||||
};
|
||||
|
||||
for (const voxel of this._voxels) {
|
||||
// Each vertex of a face needs the occlusion data for the other 3 vertices
|
||||
// in it's face, not just itself. Also flatten occlusion data.
|
||||
let occlusions: number[];
|
||||
const cube: AttributeData = GeometryTemplates.getBoxBufferData(new Vector3(0, 0, 0));
|
||||
for (let i = 0; i < numVoxels; ++i) {
|
||||
const voxel = this._voxels[i];
|
||||
const voxelColourArray = voxel.colour.toArray();
|
||||
const voxelPositionArray = voxel.position.toArray();
|
||||
|
||||
for (let j = 0; j < VoxelMeshBufferComponentOffsets.POSITION; ++j) {
|
||||
newBuffer.position.data[i * VoxelMeshBufferComponentOffsets.POSITION + j] = cube.custom.position[j] + voxelPositionArray[j % 3];
|
||||
}
|
||||
|
||||
for (let j = 0; j < VoxelMeshBufferComponentOffsets.COLOUR; ++j) {
|
||||
newBuffer.colour.data[i * VoxelMeshBufferComponentOffsets.COLOUR + j] = voxelColourArray[j % 3];
|
||||
}
|
||||
|
||||
for (let j = 0; j < VoxelMeshBufferComponentOffsets.NORMAL; ++j) {
|
||||
newBuffer.normal.data[i * VoxelMeshBufferComponentOffsets.NORMAL + j] = cube.custom.normal[j];
|
||||
}
|
||||
|
||||
for (let j = 0; j < VoxelMeshBufferComponentOffsets.TEXCOORD; ++j) {
|
||||
newBuffer.texcoord.data[i * VoxelMeshBufferComponentOffsets.TEXCOORD + j] = cube.custom.texcoord[j];
|
||||
}
|
||||
|
||||
for (let j = 0; j < VoxelMeshBufferComponentOffsets.INDICES; ++j) {
|
||||
newBuffer.indices.data[i * VoxelMeshBufferComponentOffsets.INDICES + j] = cube.indices[j] + (i * INDICES_PER_VOXEL);
|
||||
}
|
||||
|
||||
if (ambientOcclusionEnabled) {
|
||||
occlusions = OcclusionManager.Get.getOcclusions(voxel.position, this);
|
||||
} else {
|
||||
occlusions = OcclusionManager.Get.getBlankOcclusions();
|
||||
}
|
||||
|
||||
const data: AttributeData = GeometryTemplates.getBoxBufferData(voxel.position);
|
||||
data.custom.occlusion = occlusions;
|
||||
|
||||
data.custom.colour = [];
|
||||
for (let i = 0; i < 24; ++i) {
|
||||
data.custom.colour.push(voxel.colour.r, voxel.colour.g, voxel.colour.b);
|
||||
}
|
||||
|
||||
const faceNormals = OcclusionManager.Get.getFaceNormals();
|
||||
if (AppConfig.FACE_CULLING) {
|
||||
// TODO: Optimise, enabling FACE_CULLING is slower than not bothering
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
if (!this.isVoxelAt(Vector3.add(voxel.position, faceNormals[i]))) {
|
||||
buffer.add({
|
||||
custom: {
|
||||
position: data.custom.position.slice(i * 12, (i+1) * 12),
|
||||
occlusion: data.custom.occlusion.slice(i * 16, (i+1) * 16),
|
||||
normal: data.custom.normal.slice(i * 12, (i+1) * 12),
|
||||
texcoord: data.custom.texcoord.slice(i * 8, (i+1) * 8),
|
||||
colour: data.custom.colour.slice(i * 12, (i+1) * 12),
|
||||
},
|
||||
indices: data.indices.slice(0, 6),
|
||||
});
|
||||
}
|
||||
const voxelOcclusionArray = OcclusionManager.Get.getOcclusions(voxel.position, this);
|
||||
for (let j = 0; j < VoxelMeshBufferComponentOffsets.OCCLUSION; ++j) {
|
||||
newBuffer.occlusion.data[i * VoxelMeshBufferComponentOffsets.OCCLUSION + j] = voxelOcclusionArray[j];
|
||||
}
|
||||
} else {
|
||||
buffer.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
return newBuffer;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user