mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2025-03-07 14:06:41 +08:00
Various buffer optimisations
This commit is contained in:
parent
8c364ae06b
commit
2038e036fe
@ -1,5 +1,6 @@
|
||||
uniform mat4 u_worldViewProjection;
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_voxelSize;
|
||||
|
||||
attribute vec4 position;
|
||||
attribute vec3 normal;
|
||||
@ -20,5 +21,5 @@ void main() {
|
||||
//float lighting = 0.2 * occlusion;
|
||||
//v_colour = vec4(abs(normal), 1.0);
|
||||
v_lighting = dot(light, abs(normal));
|
||||
gl_Position = u_worldViewProjection * vec4(position.xyz, 1.0);
|
||||
gl_Position = u_worldViewProjection * vec4(position.xyz * u_voxelSize, 1.0);
|
||||
}
|
||||
|
127
src/buffer.js
127
src/buffer.js
@ -25,6 +25,8 @@ class SegmentedBuffer {
|
||||
this._indicesInsertIndex = 0;
|
||||
this._maxIndex = 0;
|
||||
|
||||
this._sanityCheck = false;
|
||||
|
||||
this._getNewBuffer();
|
||||
}
|
||||
|
||||
@ -63,7 +65,7 @@ class SegmentedBuffer {
|
||||
}
|
||||
|
||||
for (const attr in this._attributes) {
|
||||
if (data[attr].length > this._bufferSize) {
|
||||
if (this._sanityCheck && data[attr].length > this._bufferSize) {
|
||||
// TODO: Automatically partition into smaller segments
|
||||
throw Error(`Data for ${attr} does not fit within the buffer.`);
|
||||
}
|
||||
@ -107,10 +109,13 @@ class SegmentedBuffer {
|
||||
if (this._compiled) {
|
||||
throw Error("Buffer already compiled, cannot add more data");
|
||||
}
|
||||
|
||||
this._checkDataMatchesAttributes(data);
|
||||
|
||||
if (this._sanityCheck) {
|
||||
this._checkDataMatchesAttributes(data);
|
||||
}
|
||||
|
||||
if (this._willOverflow(data)) {
|
||||
console.log("Cycling buffer...");
|
||||
this._cycle();
|
||||
}
|
||||
|
||||
@ -150,4 +155,120 @@ class SegmentedBuffer {
|
||||
}
|
||||
|
||||
|
||||
class BottomlessBuffer {
|
||||
|
||||
constructor(attributes) {
|
||||
this._completeBuffers = [];
|
||||
|
||||
this._compiled = false;
|
||||
this.WebGLBuffers = [];
|
||||
|
||||
this._attributes = {};
|
||||
for (const attr of attributes) {
|
||||
this._attributes[attr.name] = {
|
||||
numComponents: attr.numComponents
|
||||
};
|
||||
}
|
||||
|
||||
this._maxIndex = 0;
|
||||
this._sanityCheck = false;
|
||||
|
||||
this._getNewBuffer();
|
||||
}
|
||||
|
||||
_getNewBuffer() {
|
||||
this._buffer = {
|
||||
indices: {numComponents: 1, data: []}
|
||||
};
|
||||
for (const attr in this._attributes) {
|
||||
this._buffer[attr] = {
|
||||
numComponents: this._attributes[attr].numComponents,
|
||||
data: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
_cycle() {
|
||||
this._completeBuffers.push({
|
||||
buffer: this._buffer,
|
||||
numElements: this._buffer.indices.data.length,
|
||||
});
|
||||
this._getNewBuffer();
|
||||
|
||||
this._maxIndex = 0;
|
||||
}
|
||||
|
||||
_willOverflow(data) {
|
||||
// Check for indices Uint16 overflow
|
||||
const dataMaxIndex = Math.max(...data.indices);
|
||||
return ((this._maxIndex + dataMaxIndex) > 65535);
|
||||
}
|
||||
|
||||
_checkDataMatchesAttributes(data) {
|
||||
if (!('indices'in data)) {
|
||||
throw `Given data does not have indices data`;
|
||||
}
|
||||
const setsRequired = Math.max(...data.indices) + 1;
|
||||
for (const attr in this._attributes) {
|
||||
if (!(attr in data)) {
|
||||
throw Error(`Given data does not have ${attr} data`);
|
||||
}
|
||||
if (data[attr].length % this._attributes[attr].numComponents != 0) {
|
||||
throw Error(`Not enough/too much ${attr} data given`);
|
||||
}
|
||||
const numSets = data[attr].length / this._attributes[attr].numComponents;
|
||||
if (numSets != setsRequired) {
|
||||
//throw `Number of indices does not match number of ${attr} components given`;
|
||||
throw Error(`Expected ${setsRequired * this._attributes[attr].numComponents} values for ${attr}, got ${data[attr].length}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
add(data) {
|
||||
if (this._compiled) {
|
||||
throw Error("Buffer already compiled, cannot add more data");
|
||||
}
|
||||
|
||||
if (this._sanityCheck) {
|
||||
this._checkDataMatchesAttributes(data);
|
||||
}
|
||||
|
||||
if (this._willOverflow(data)) {
|
||||
console.log("Cycling buffer...");
|
||||
this._cycle();
|
||||
}
|
||||
|
||||
// Add the new indices data
|
||||
this._buffer.indices.data.push(...data.indices.map(x => x + this._maxIndex));
|
||||
this._maxIndex += 1 + Math.max(...data.indices);
|
||||
|
||||
for (const attr in this._attributes) {
|
||||
this._buffer[attr].data.push(...data[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
compile(gl) {
|
||||
if (this._compiled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._cycle();
|
||||
|
||||
this.WebGLBuffers = new Array(this._completeBuffers.length);
|
||||
|
||||
this._completeBuffers.forEach((buffer, i) => {
|
||||
this.WebGLBuffers[i] = {
|
||||
buffer: twgl.createBufferInfoFromArrays(gl, buffer.buffer),
|
||||
numElements: buffer.numElements
|
||||
};
|
||||
});
|
||||
|
||||
this._compiled = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
module.exports.SegmentedBuffer = SegmentedBuffer;
|
||||
module.exports.BottomlessBuffer = BottomlessBuffer;
|
@ -51,6 +51,7 @@ $("#loadBtn").on("click", () => {
|
||||
|
||||
renderer.registerMesh(loadedMesh);
|
||||
renderer.compile();
|
||||
console.log(renderer);
|
||||
|
||||
$('#voxelInput').prop('disabled', false);
|
||||
$('#voxelBtn').prop('disabled', false);
|
||||
@ -73,9 +74,9 @@ $("#voxelBtn").on("click", () => {
|
||||
voxelManager.clear();
|
||||
voxelManager.setVoxelSize(voxelSize);
|
||||
voxelManager.voxeliseMesh(loadedMesh);
|
||||
//console.log(voxelManager.voxels);
|
||||
|
||||
renderer.clear();
|
||||
//renderer.setDebug(true);
|
||||
renderer.registerVoxelMesh(voxelManager);
|
||||
|
||||
/*
|
||||
@ -163,7 +164,7 @@ $(document).resize(function() {
|
||||
});
|
||||
|
||||
function render(time) {
|
||||
renderer.draw();
|
||||
renderer.draw(voxelManager._voxelSize);
|
||||
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
@ -43,9 +43,11 @@ class GeometryTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
static getBoxBufferData(centre, size, debug) {
|
||||
const a = Vector3.sub(centre, Vector3.mulScalar(size, 0.5));
|
||||
const b = Vector3.add(centre, Vector3.mulScalar(size, 0.5));
|
||||
static getBoxBufferData(centre, debug) {
|
||||
//const a = Vector3.sub(centre, Vector3.mulScalar(size, 0.5));
|
||||
//const b = Vector3.add(centre, Vector3.mulScalar(size, 0.5));
|
||||
const a = Vector3.subScalar(centre, 0.5);
|
||||
const b = Vector3.addScalar(centre, 0.5);
|
||||
|
||||
if (debug) {
|
||||
return {
|
||||
@ -80,9 +82,9 @@ class GeometryTemplates {
|
||||
cube.texcoord.set(default_cube.texcoord);
|
||||
|
||||
for (let i = 0; i < 72; i += 3) {
|
||||
cube.position[i + 0] = (cube.position[i + 0] * size.x) + centre.x;
|
||||
cube.position[i + 1] = (cube.position[i + 1] * size.y) + centre.y;
|
||||
cube.position[i + 2] = (cube.position[i + 2] * size.z) + centre.z;
|
||||
cube.position[i + 0] += centre.x;
|
||||
cube.position[i + 1] += centre.y;
|
||||
cube.position[i + 2] += centre.z;
|
||||
}
|
||||
|
||||
return cube;
|
||||
|
@ -4,9 +4,10 @@ const { Vector3 } = require('./vector.js');
|
||||
const { ArcballCamera } = require('./camera.js');
|
||||
const mouseManager = require('./mouse.js');
|
||||
const shaderManager = require('./shaders.js');
|
||||
const { SegmentedBuffer } = require('./buffer.js');
|
||||
const { SegmentedBuffer, BottomlessBuffer } = require('./buffer.js');
|
||||
const { GeometryTemplates } = require('./geometry.js');
|
||||
|
||||
|
||||
class Renderer {
|
||||
|
||||
constructor(fov, backgroundColour) {
|
||||
@ -41,36 +42,36 @@ class Renderer {
|
||||
this._registerData(data);
|
||||
}
|
||||
|
||||
_registerVoxel(centre, voxelSize, voxelManager) {
|
||||
const sizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
|
||||
|
||||
_registerVoxel(centre, voxelManager) {
|
||||
let occlusions = new Array(6);
|
||||
// For each face
|
||||
for (let f = 0; f < 6; ++f) {
|
||||
// For each vertex
|
||||
occlusions[f] = [0, 0, 0, 0];
|
||||
|
||||
for (let v = 0; v < 4; ++v) {
|
||||
// For each occlusion vertex
|
||||
for (let o = 0; o < 3; ++o) {
|
||||
occlusions[f][v] += voxelManager.isVoxelAt(Vector3.add(centre, this.occlusions[f][v][o]));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert from occlusion denoting the occlusion factor to the
|
||||
// attenuation in light value: 0 -> 1.0, 1 -> 0.8, 2 -> 0.6, 3 -> 0.4
|
||||
occlusions[f] = occlusions[f].map(x => 1.0 - 0.2 * x);
|
||||
}
|
||||
|
||||
let data = GeometryTemplates.getBoxBufferData(centre, false);
|
||||
|
||||
// Each vertex of a face needs the occlusion data for the other 3 vertices
|
||||
// in it's face, not just itself, pack this into a vec4.
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
occlusions[i] = [].concat(...new Array(4).fill(occlusions[i]));
|
||||
}
|
||||
|
||||
let data = GeometryTemplates.getBoxBufferData(centre, sizeVector, false);
|
||||
data.occlusion = [].concat(...occlusions);
|
||||
// in it's face, not just itself. Also flatten occlusion data.
|
||||
data.occlusion = new Array(96);
|
||||
for (let j = 0; j < 6; ++j) {
|
||||
for (let k = 0; k < 16; ++k) {
|
||||
data.occlusion[j * 16 + k] = occlusions[j][k % 4];
|
||||
}
|
||||
}
|
||||
|
||||
// Convert from occlusion denoting the occlusion factor to the
|
||||
// attenuation in light value: 0 -> 1.0, 1 -> 0.8, 2 -> 0.6, 3 -> 0.4
|
||||
data.occlusion = data.occlusion.map(x => 1.0 - 0.2 * x);
|
||||
|
||||
//this._registerData(data);
|
||||
this._registerVoxels.add(data);
|
||||
}
|
||||
|
||||
@ -87,7 +88,9 @@ class Renderer {
|
||||
|
||||
registerVoxelMesh(voxelManager) {
|
||||
const voxelSize = voxelManager._voxelSize;
|
||||
const sizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
|
||||
//const sizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
|
||||
const sizeVector = new Vector3(1.0, 1.0, 1.0);
|
||||
|
||||
if (this._debug) {
|
||||
voxelManager.voxels.forEach((voxel) => {
|
||||
this.registerBox(voxel, sizeVector);
|
||||
@ -96,7 +99,7 @@ class Renderer {
|
||||
this._setupOcclusions(voxelSize); // Setup arrays for calculating voxel ambient occlusion
|
||||
|
||||
voxelManager.voxels.forEach((voxel) => {
|
||||
this._registerVoxel(voxel, voxelSize, voxelManager);
|
||||
this._registerVoxel(voxel, voxelManager);
|
||||
});
|
||||
}
|
||||
|
||||
@ -119,7 +122,7 @@ class Renderer {
|
||||
this._compiled = true;
|
||||
}
|
||||
|
||||
draw() {
|
||||
draw(voxelSize) {
|
||||
if (!this._compiled) {
|
||||
this.compile();
|
||||
return;
|
||||
@ -135,7 +138,8 @@ class Renderer {
|
||||
// Draw voxel register
|
||||
this._drawRegister(this._registerVoxels, this._gl.TRIANGLES, shaderManager.aoProgram, {
|
||||
u_worldViewProjection: this._camera.getWorldViewProjection(),
|
||||
u_texture: this._blockTexture
|
||||
u_texture: this._blockTexture,
|
||||
u_voxelSize: voxelSize
|
||||
});
|
||||
|
||||
// Draw default register
|
||||
@ -152,7 +156,7 @@ class Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
_setupOcclusions(voxelSize) {
|
||||
_setupOcclusions() {
|
||||
this.occlusions = new Array(6).fill(null).map(function() { return new Array(4).fill(0); });
|
||||
|
||||
this.occlusions[0][0] = [new Vector3( 1, 1, 0), new Vector3( 1, 1, -1), new Vector3( 1, 0, -1)];
|
||||
@ -186,6 +190,7 @@ class Renderer {
|
||||
this.occlusions[5][3] = [new Vector3( 0, -1, -1), new Vector3( 1, -1, -1), new Vector3( 1, 0, -1)];
|
||||
|
||||
// Scale each Vector3 by voxelSize
|
||||
/*
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
for (let j = 0; j < 4; ++j) {
|
||||
for (let k = 0; k < 3; ++k) {
|
||||
@ -193,6 +198,7 @@ class Renderer {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
_registerData(data) {
|
||||
@ -248,17 +254,18 @@ class Renderer {
|
||||
}
|
||||
|
||||
_getNewBuffers() {
|
||||
this._registerDebug = new SegmentedBuffer(16384, [
|
||||
const bufferSize = 16384 * 16;
|
||||
this._registerDebug = new SegmentedBuffer(bufferSize, [
|
||||
{name: 'position', numComponents: 3},
|
||||
{name: 'colour', numComponents: 3}
|
||||
]);
|
||||
this._registerVoxels = new SegmentedBuffer(16384, [
|
||||
this._registerVoxels = new SegmentedBuffer(bufferSize, [
|
||||
{name: 'position', numComponents: 3},
|
||||
{name: 'normal', numComponents: 3},
|
||||
{name: 'occlusion', numComponents: 4},
|
||||
{name: 'texcoord', numComponents: 2}
|
||||
]);
|
||||
this._registerDefault = new SegmentedBuffer(16384, [
|
||||
this._registerDefault = new SegmentedBuffer(bufferSize, [
|
||||
{name: 'position', numComponents: 3},
|
||||
{name: 'normal', numComponents: 3}
|
||||
]);
|
||||
|
@ -60,7 +60,7 @@ class VoxelManager {
|
||||
return cubeAABB;
|
||||
}
|
||||
|
||||
_voxelCentreToPosition(vec) {
|
||||
_toGridPosition(vec) {
|
||||
//Vector3.round(Vector3.subScalar(Vector3.divScalar(vec, this._voxelSize), 0.5));
|
||||
return new Vector3(
|
||||
Math.round(vec.x / this._voxelSize),
|
||||
@ -69,7 +69,7 @@ class VoxelManager {
|
||||
);
|
||||
}
|
||||
|
||||
_voxelPositionToCentre(vec) {
|
||||
_toModelPosition(vec) {
|
||||
return new Vector3(
|
||||
vec.x * this._voxelSize,
|
||||
vec.y * this._voxelSize,
|
||||
@ -77,8 +77,7 @@ class VoxelManager {
|
||||
);
|
||||
}
|
||||
|
||||
isVoxelAt(vec) {
|
||||
const pos = this._voxelCentreToPosition(vec);
|
||||
isVoxelAt(pos) {
|
||||
return this.voxelsHash.contains(pos);
|
||||
}
|
||||
|
||||
@ -87,22 +86,20 @@ class VoxelManager {
|
||||
// (0.5, 0.5, 0.5) -> (0, 0, 0);
|
||||
vec = Vector3.subScalar(vec, this._voxelSize / 2);
|
||||
|
||||
const test = Vector3.divScalar(vec, this._voxelSize);
|
||||
|
||||
// [HACK] FIXME: Fix misaligned voxels
|
||||
// Some vec data is not not grid-snapped to voxelSize-spacing
|
||||
const test = Vector3.divScalar(vec, this._voxelSize);
|
||||
if ((test.x % 1 < 0.9 && test.x % 1 > 0.1) || (test.y % 1 < 0.9 && test.y % 1 > 0.1) || (test.z % 1 < 0.9 && test.z % 1 > 0.1)) {
|
||||
console.warn("Misaligned voxel, skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = this._voxelCentreToPosition(vec);
|
||||
// Convert to
|
||||
const pos = this._toGridPosition(vec);
|
||||
if (this.voxelsHash.contains(pos)) {
|
||||
return;
|
||||
}
|
||||
//console.log(pos);
|
||||
this.voxels.push(vec);
|
||||
//console.log("pos:", pos);
|
||||
this.voxels.push(pos);
|
||||
this.voxelsHash.add(pos);
|
||||
|
||||
this.minX = Math.min(this.minX, vec.x);
|
||||
@ -113,6 +110,8 @@ class VoxelManager {
|
||||
this.maxZ = Math.max(this.maxZ, vec.z);
|
||||
}
|
||||
|
||||
// TODO: Fix voxel meshing
|
||||
/*
|
||||
_findXExtent(pos) {
|
||||
let xEnd = pos.x + 1;
|
||||
|
||||
@ -188,8 +187,8 @@ class VoxelManager {
|
||||
this.seen = new HashSet(2048);
|
||||
//console.log(this.voxelsHash);
|
||||
|
||||
const minPos = this._voxelCentreToPosition(new Vector3(this.minX, this.minY, this.minZ));
|
||||
const maxPos = this._voxelCentreToPosition(new Vector3(this.maxX, this.maxY, this.maxZ));
|
||||
const minPos = this._toGridPosition(new Vector3(this.minX, this.minY, this.minZ));
|
||||
const maxPos = this._toGridPosition(new Vector3(this.maxX, this.maxY, this.maxZ));
|
||||
|
||||
for (let y = minPos.y; y <= maxPos.y; ++y) {
|
||||
for (let z = minPos.z; z <= maxPos.z; ++z) {
|
||||
@ -209,8 +208,8 @@ class VoxelManager {
|
||||
let size = new Vector3(xEnd - x + 1, yEnd - y + 1, zEnd - z + 1);
|
||||
|
||||
this.mesh.push({
|
||||
centre: this._voxelPositionToCentre(centre),
|
||||
size: this._voxelPositionToCentre(size)
|
||||
centre: this._toModelPosition(centre),
|
||||
size: this._toModelPosition(size)
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -220,6 +219,7 @@ class VoxelManager {
|
||||
|
||||
return this.mesh;
|
||||
}
|
||||
*/
|
||||
|
||||
splitVoxels() {
|
||||
this._voxelSize /= 2;
|
||||
|
Loading…
Reference in New Issue
Block a user