forked from mirror/ObjToSchematic
commit
3f60531fcd
153
src/buffer.js
Normal file
153
src/buffer.js
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
const twgl = require('twgl.js');
|
||||||
|
|
||||||
|
/*
|
||||||
|
WebGL buffers store vertex index data as Uint16 and will lead to frequent overflows.
|
||||||
|
SegmentedBuffer automatically partitions buffers to avoid overflows and removes the
|
||||||
|
overhead of .push/.concat when adding data
|
||||||
|
*/
|
||||||
|
class SegmentedBuffer {
|
||||||
|
|
||||||
|
constructor(bufferSize, attributes) {
|
||||||
|
this._bufferSize = bufferSize;
|
||||||
|
this._completeBuffers = [];
|
||||||
|
|
||||||
|
this._compiled = false;
|
||||||
|
this.WebGLBuffers = [];
|
||||||
|
|
||||||
|
this._attributes = {};
|
||||||
|
for (const attr of attributes) {
|
||||||
|
this._attributes[attr.name] = {
|
||||||
|
numComponents: attr.numComponents,
|
||||||
|
insertIndex: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this._indicesInsertIndex = 0;
|
||||||
|
this._maxIndex = 0;
|
||||||
|
|
||||||
|
this._getNewBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
_getNewBuffer() {
|
||||||
|
this._buffer = {
|
||||||
|
indices: {
|
||||||
|
numComponents: 1,
|
||||||
|
data: new Uint16Array(this._bufferSize),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const attr in this._attributes) {
|
||||||
|
this._buffer[attr] = {
|
||||||
|
numComponents: this._attributes[attr].numComponents,
|
||||||
|
data: new Float32Array(this._bufferSize)
|
||||||
|
};
|
||||||
|
this._attributes[attr].insertIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_cycle() {
|
||||||
|
this._completeBuffers.push({
|
||||||
|
buffer: this._buffer,
|
||||||
|
numElements: this._indicesInsertIndex,
|
||||||
|
});
|
||||||
|
this._getNewBuffer();
|
||||||
|
|
||||||
|
this._indicesInsertIndex = 0;
|
||||||
|
this._maxIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_willOverflow(data) {
|
||||||
|
// Check for indices Uint16 overflow
|
||||||
|
const dataMaxIndex = Math.max(...data.indices);
|
||||||
|
if ((this._maxIndex + dataMaxIndex) > 65535) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attr in this._attributes) {
|
||||||
|
if (data[attr].length > this._bufferSize) {
|
||||||
|
// TODO: Automatically partition into smaller segments
|
||||||
|
throw Error(`Data for ${attr} does not fit within the buffer.`);
|
||||||
|
}
|
||||||
|
if (data[attr].length + this._attributes[attr].insertIndex > this._bufferSize) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_addDataToAttribute(attr, attr_data) {
|
||||||
|
const indexOffset = this._attributes[attr].insertIndex;
|
||||||
|
attr_data.forEach((value, i) => {
|
||||||
|
this._buffer[attr].data[i + indexOffset] = value;
|
||||||
|
});
|
||||||
|
this._attributes[attr].insertIndex += attr_data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(data) {
|
||||||
|
if (this._compiled) {
|
||||||
|
throw Error("Buffer already compiled, cannot add more data");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._checkDataMatchesAttributes(data);
|
||||||
|
|
||||||
|
if (this._willOverflow(data)) {
|
||||||
|
this._cycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the new indices data
|
||||||
|
data.indices.forEach((indexData, i) => {
|
||||||
|
this._buffer.indices.data[i + this._indicesInsertIndex] = indexData + this._maxIndex;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._indicesInsertIndex += data.indices.length;
|
||||||
|
this._maxIndex += 1 + Math.max(...data.indices);
|
||||||
|
|
||||||
|
for (const attr in this._attributes) {
|
||||||
|
this._addDataToAttribute(attr, 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;
|
@ -7,9 +7,9 @@ const { Schematic } = require('./src/schematic.js');
|
|||||||
const dialog = require('electron').remote.dialog;
|
const dialog = require('electron').remote.dialog;
|
||||||
|
|
||||||
const voxelSize = document.querySelector("#voxelInput").value;
|
const voxelSize = document.querySelector("#voxelInput").value;
|
||||||
let renderer = new Renderer(voxelSize);
|
let renderer = new Renderer(30, new Vector3(0.1, 0.1, 0.1));
|
||||||
const voxelManager = new VoxelManager(voxelSize);
|
|
||||||
|
|
||||||
|
const voxelManager = new VoxelManager(voxelSize);
|
||||||
const canvas = document.querySelector("#c");
|
const canvas = document.querySelector("#c");
|
||||||
|
|
||||||
let loadedMesh = null;
|
let loadedMesh = null;
|
||||||
@ -49,7 +49,7 @@ $("#loadBtn").on("click", () => {
|
|||||||
|
|
||||||
renderer.clear();
|
renderer.clear();
|
||||||
renderer.registerMesh(loadedMesh);
|
renderer.registerMesh(loadedMesh);
|
||||||
renderer.compileRegister();
|
renderer.compile();
|
||||||
|
|
||||||
$('#voxelInput').prop('disabled', false);
|
$('#voxelInput').prop('disabled', false);
|
||||||
$('#voxelBtn').prop('disabled', false);
|
$('#voxelBtn').prop('disabled', false);
|
||||||
@ -69,12 +69,8 @@ $("#voxelBtn").on("click", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.clear();
|
|
||||||
voxelManager.clear();
|
voxelManager.clear();
|
||||||
|
|
||||||
renderer.setVoxelSize(voxelSize);
|
|
||||||
voxelManager.setVoxelSize(voxelSize);
|
voxelManager.setVoxelSize(voxelSize);
|
||||||
|
|
||||||
voxelManager.voxeliseMesh(loadedMesh);
|
voxelManager.voxeliseMesh(loadedMesh);
|
||||||
|
|
||||||
renderer.clear();
|
renderer.clear();
|
||||||
@ -115,13 +111,7 @@ $("#voxelBtn").on("click", () => {
|
|||||||
showToastWithText("Model successfully voxelised", 'success');
|
showToastWithText("Model successfully voxelised", 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.compileRegister();
|
renderer.compile();
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
|
||||||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
|
||||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -132,9 +122,8 @@ $("#splitBtn").on("click", () => {
|
|||||||
voxelManager.splitVoxels();
|
voxelManager.splitVoxels();
|
||||||
|
|
||||||
renderer.clear();
|
renderer.clear();
|
||||||
renderer.setVoxelSize(voxelSize / 2);
|
|
||||||
renderer.registerVoxelMesh(voxelManager, true);
|
renderer.registerVoxelMesh(voxelManager, true);
|
||||||
renderer.compileRegister();
|
renderer.compile();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -159,10 +148,10 @@ $("#exportBtn").on("click", async () => {
|
|||||||
const schematic = new Schematic(voxelManager);
|
const schematic = new Schematic(voxelManager);
|
||||||
schematic.exportSchematic(filePath);
|
schematic.exportSchematic(filePath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToastWithText("Failed to export schematic", false);
|
showToastWithText("Failed to export schematic", 'danger');
|
||||||
}
|
}
|
||||||
|
|
||||||
showToastWithText("Successfully saved schematic", true);
|
showToastWithText("Successfully saved schematic", 'success');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -172,8 +161,7 @@ $(document).resize(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function render(time) {
|
function render(time) {
|
||||||
renderer.begin();
|
renderer.draw();
|
||||||
renderer.end();
|
|
||||||
|
|
||||||
requestAnimationFrame(render);
|
requestAnimationFrame(render);
|
||||||
}
|
}
|
||||||
|
92
src/geometry.js
Normal file
92
src/geometry.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
const twgl = require('twgl.js');
|
||||||
|
const { Vector3 } = require('./vector.js');
|
||||||
|
|
||||||
|
const default_cube = twgl.primitives.createCubeVertices(1.0);
|
||||||
|
|
||||||
|
class GeometryTemplates {
|
||||||
|
|
||||||
|
static getTriangleBufferData(triangle, debug) {
|
||||||
|
const a = triangle.v0;
|
||||||
|
const b = triangle.v1;
|
||||||
|
const c = triangle.v2;
|
||||||
|
const n = triangle.normal;
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
return {
|
||||||
|
position: [
|
||||||
|
a.x, a.y, a.z,
|
||||||
|
b.x, b.y, b.z,
|
||||||
|
c.x, c.y, c.z,
|
||||||
|
],
|
||||||
|
indices: [
|
||||||
|
0, 1,
|
||||||
|
1, 2,
|
||||||
|
2, 0
|
||||||
|
]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
position: [
|
||||||
|
a.x, a.y, a.z,
|
||||||
|
b.x, b.y, b.z,
|
||||||
|
c.x, c.y, c.z,
|
||||||
|
],
|
||||||
|
normal: [
|
||||||
|
n.x, n.y, n.z,
|
||||||
|
n.x, n.y, n.z,
|
||||||
|
n.x, n.y, n.z
|
||||||
|
],
|
||||||
|
indices: [
|
||||||
|
0, 1, 2
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
return {
|
||||||
|
position: [
|
||||||
|
a.x, a.y, a.z,
|
||||||
|
b.x, a.y, a.z,
|
||||||
|
b.x, b.y, a.z,
|
||||||
|
a.x, b.y, a.z,
|
||||||
|
a.x, a.y, b.z,
|
||||||
|
b.x, a.y, b.z,
|
||||||
|
b.x, b.y, b.z,
|
||||||
|
a.x, b.y, b.z
|
||||||
|
],
|
||||||
|
indices: [
|
||||||
|
0, 1, 1, 2, 2, 3, 3, 0,
|
||||||
|
4, 5, 5, 6, 6, 7, 7, 4,
|
||||||
|
0, 4, 1, 5, 2, 6, 3, 7
|
||||||
|
]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
|
||||||
|
let cube = {
|
||||||
|
position: new Float32Array(72),
|
||||||
|
normal: new Float32Array(72),
|
||||||
|
indices: new Float32Array(72)
|
||||||
|
};
|
||||||
|
|
||||||
|
cube.position.set(default_cube.position);
|
||||||
|
cube.normal.set(default_cube.normal);
|
||||||
|
cube.indices.set(default_cube.indices);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cube;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.GeometryTemplates = GeometryTemplates;
|
34
src/math.js
34
src/math.js
@ -1,32 +1,16 @@
|
|||||||
// Not apart of rendering, SIMD optimisation not necessary
|
// Not apart of rendering, SIMD optimisation not necessary
|
||||||
const { Vector3 } = require('./vector.js');
|
const { Vector3 } = require('./vector.js');
|
||||||
|
|
||||||
/*
|
/**
|
||||||
function roundTo(value, base) {
|
* Retrieve the array key corresponding to the largest element in the array.
|
||||||
return Math.round(value / base) * base;
|
*
|
||||||
|
* @param {Array.<number>} array Input array
|
||||||
|
* @return {number} Index of array element with largest value
|
||||||
|
*/
|
||||||
|
function argMax(array) {
|
||||||
|
return array.map((x, i) => [x, i]).reduce((r, a) => (a[0] > r[0] ? a : r))[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
function floorTo(value, base) {
|
|
||||||
return Math.floor(value / base) * base;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ceilTo(value, base) {
|
|
||||||
return Math.ceil(value / base) * base;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fastDotXAxis(vec) {
|
|
||||||
return vec[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function fastDotYAxis(vec) {
|
|
||||||
return vec[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
function fastDotZAxis(vec) {
|
|
||||||
return vec[2];
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function fastCrossXAxis(vec) {
|
function fastCrossXAxis(vec) {
|
||||||
return new Vector3(0.0, -vec.z, vec.y);
|
return new Vector3(0.0, -vec.z, vec.y);
|
||||||
}
|
}
|
||||||
@ -65,4 +49,6 @@ module.exports.xAxis = new Vector3(1.0, 0.0, 0.0);
|
|||||||
module.exports.yAxis = new Vector3(0.0, 1.0, 0.0);
|
module.exports.yAxis = new Vector3(0.0, 1.0, 0.0);
|
||||||
module.exports.zAxis = new Vector3(0.0, 0.0, 1.0);
|
module.exports.zAxis = new Vector3(0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
module.exports.argMax = argMax;
|
||||||
|
|
||||||
//module.exports.roundVector3To = roundVector3To;
|
//module.exports.roundVector3To = roundVector3To;
|
467
src/renderer.js
467
src/renderer.js
@ -4,53 +4,129 @@ const { Vector3 } = require('./vector.js');
|
|||||||
const { ArcballCamera } = require('./camera.js');
|
const { ArcballCamera } = require('./camera.js');
|
||||||
const mouseManager = require('./mouse.js');
|
const mouseManager = require('./mouse.js');
|
||||||
const shaderManager = require('./shaders.js');
|
const shaderManager = require('./shaders.js');
|
||||||
|
const { SegmentedBuffer } = require('./buffer.js');
|
||||||
|
const { GeometryTemplates } = require('./geometry.js');
|
||||||
|
|
||||||
class Renderer {
|
class Renderer {
|
||||||
|
|
||||||
constructor(voxelSize) {
|
constructor(fov, backgroundColour) {
|
||||||
this._gl = document.querySelector("#c").getContext("webgl");
|
this._backgroundColour = backgroundColour;
|
||||||
|
|
||||||
this._fov = 30;
|
|
||||||
this._backgroundColour = new Vector3(0.1, 0.1, 0.1);
|
|
||||||
this._strokeColour = new Vector3(1.0, 1.0, 1.0);
|
this._strokeColour = new Vector3(1.0, 1.0, 1.0);
|
||||||
|
|
||||||
this._camera = new ArcballCamera(this._fov, this._gl.canvas.clientWidth / this._gl.canvas.clientHeight, 0.5, 100.0);
|
this._gl = document.querySelector("#c").getContext("webgl");
|
||||||
|
this._camera = new ArcballCamera(fov, this._gl.canvas.clientWidth / this._gl.canvas.clientHeight, 0.5, 100.0);
|
||||||
|
|
||||||
this._registerEvents();
|
this._registerEvents();
|
||||||
|
this._getNewBuffers();
|
||||||
|
|
||||||
this._debugRegister = this._getEmptyDebugRegister();
|
this._debug = false;
|
||||||
this._register = this._getEmptyRegister();
|
this._compiled = false;
|
||||||
|
|
||||||
this._filledDebugRegisters = [];
|
|
||||||
this._filledRegisters = [];
|
|
||||||
|
|
||||||
this._registerBuffers = [];
|
|
||||||
this._debugRegisterBuffers = [];
|
|
||||||
|
|
||||||
this._debugMaxIndex = 0;
|
|
||||||
this._maxIndex = 0;
|
|
||||||
|
|
||||||
this._voxelSize = voxelSize;
|
|
||||||
this._voxelSizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
|
|
||||||
this._cube = twgl.primitives.createCubeVertices(1.0);
|
|
||||||
|
|
||||||
this._registersOpen = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getEmptyDebugRegister() {
|
|
||||||
return {
|
|
||||||
position: {numComponents: 3, data: []},
|
|
||||||
colour: {numComponents: 3, data: []},
|
setStroke(colour) {
|
||||||
indices: {numComponents: 3, data: []}
|
this._strokeColour = colour;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getEmptyRegister() {
|
setDebug(debug) {
|
||||||
return {
|
this._debug = debug;
|
||||||
position: {numComponents: 3, data: []},
|
}
|
||||||
normal: {numComponents: 3, data: []},
|
|
||||||
indices: {numComponents: 3, data: []},
|
registerBox(centre, size) {
|
||||||
|
const data = GeometryTemplates.getBoxBufferData(centre, size, this._debug);
|
||||||
|
this._registerData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerTriangle(triangle) {
|
||||||
|
const data = GeometryTemplates.getTriangleBufferData(triangle, this._debug);
|
||||||
|
this._registerData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerMesh(mesh) {
|
||||||
|
for (const triangle of mesh.triangles) {
|
||||||
|
this.registerTriangle(triangle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerVoxelMesh(voxelManager) {
|
||||||
|
const mesh = voxelManager.buildMesh();
|
||||||
|
for (const box of mesh) {
|
||||||
|
this.registerBox(box.centre, box.size, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this._getNewBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
compile() {
|
||||||
|
this._registerDebug.compile(this._gl);
|
||||||
|
this._register.compile(this._gl);
|
||||||
|
this._compiled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
if (!this._compiled) {
|
||||||
|
this.compile();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._setupScene();
|
||||||
|
|
||||||
|
this._drawDebugRegisters();
|
||||||
|
this._drawRegisters();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_drawDebugRegisters() {
|
||||||
|
const debugUniforms = {
|
||||||
|
u_worldViewProjection: this._camera.getWorldViewProjection(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (const buffer of this._registerDebug.WebGLBuffers) {
|
||||||
|
this._drawBuffer(this._gl.LINES, buffer, shaderManager.debugProgram, debugUniforms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawRegisters() {
|
||||||
|
const uniforms = {
|
||||||
|
u_lightWorldPos: this._camera.getCameraPosition(0.0, 0.0),
|
||||||
|
u_worldViewProjection: this._camera.getWorldViewProjection(),
|
||||||
|
u_worldInverseTranspose: this._camera.getWorldInverseTranspose()
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const buffer of this._register.WebGLBuffers) {
|
||||||
|
this._drawBuffer(this._gl.TRIANGLES, buffer, shaderManager.shadedProgram, uniforms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_registerData(data) {
|
||||||
|
if (this._debug) {
|
||||||
|
const numVertices = data.position.length / 3;
|
||||||
|
data.colour = [].concat(...new Array(numVertices).fill(this._strokeColour.toArray()));
|
||||||
|
this._registerDebug.add(data);
|
||||||
|
} else {
|
||||||
|
this._register.add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupScene() {
|
||||||
|
twgl.resizeCanvasToDisplaySize(this._gl.canvas);
|
||||||
|
this._gl.viewport(0, 0, this._gl.canvas.width, this._gl.canvas.height);
|
||||||
|
this._camera.aspect = this._gl.canvas.width / this._gl.canvas.height;
|
||||||
|
//this._gl.blendFuncSeparate(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
this._gl.enable(this._gl.DEPTH_TEST);
|
||||||
|
this._gl.enable(this._gl.CULL_FACE);
|
||||||
|
//this._gl.enable(this._gl.BLEND);
|
||||||
|
this._gl.clearColor(this._backgroundColour.x, this._backgroundColour.y, this._backgroundColour.z, 1);
|
||||||
|
this._gl.clear(this._gl.COLOR_BUFFER_BIT | this._gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
this._camera.updateCameraPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerEvents() {
|
_registerEvents() {
|
||||||
@ -72,331 +148,18 @@ class Renderer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
begin() {
|
|
||||||
twgl.resizeCanvasToDisplaySize(this._gl.canvas);
|
|
||||||
this._gl.viewport(0, 0, this._gl.canvas.width, this._gl.canvas.height);
|
|
||||||
this._camera.aspect = this._gl.canvas.width / this._gl.canvas.height;
|
|
||||||
this._gl.blendFuncSeparate(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA);
|
|
||||||
|
|
||||||
this._gl.enable(this._gl.DEPTH_TEST);
|
|
||||||
this._gl.enable(this._gl.CULL_FACE);
|
|
||||||
//this._gl.enable(this._gl.BLEND);
|
|
||||||
this._gl.clearColor(this._backgroundColour.x, this._backgroundColour.y, this._backgroundColour.z, 1);
|
|
||||||
this._gl.clear(this._gl.COLOR_BUFFER_BIT | this._gl.DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
this._camera.updateCameraPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
compileRegister() {
|
|
||||||
this._cycleRegister();
|
|
||||||
this._cycleDebugRegister();
|
|
||||||
|
|
||||||
//console.log(this._debug);
|
|
||||||
|
|
||||||
for (const register of this._filledRegisters) {
|
|
||||||
this._registerBuffers.push(twgl.createBufferInfoFromArrays(this._gl, register));
|
|
||||||
}
|
|
||||||
for (const debugRegister of this._filledDebugRegisters) {
|
|
||||||
this._debugRegisterBuffers.push(twgl.createBufferInfoFromArrays(this._gl, debugRegister));
|
|
||||||
}
|
|
||||||
//this._debugRegisterBuffer = twgl.createBufferInfoFromArrays(this._gl, this._debugRegister);
|
|
||||||
//this._registerBuffer = twgl.createBufferInfoFromArrays(this._gl, this._register);
|
|
||||||
this._registersOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
setStroke(colour) {
|
|
||||||
this._strokeColour = colour;
|
|
||||||
}
|
|
||||||
|
|
||||||
end() {
|
|
||||||
if (this._registersOpen) {
|
|
||||||
//console.error("Trying to draw register objects before register is closed. Call compileRegister() first.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._drawDebugRegisters();
|
|
||||||
this._drawRegisters();
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
//console.log("clearing");
|
|
||||||
|
|
||||||
this._debugRegister = this._getEmptyDebugRegister();
|
|
||||||
this._register = this._getEmptyRegister();
|
|
||||||
|
|
||||||
this._filledDebugRegisters = [];
|
|
||||||
this._filledRegisters = [];
|
|
||||||
|
|
||||||
this._registerBuffers = [];
|
|
||||||
this._debugRegisterBuffers = [];
|
|
||||||
|
|
||||||
this._debugMaxIndex = 0;
|
|
||||||
this._maxIndex = 0;
|
|
||||||
|
|
||||||
this._registersOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
setVoxelSize(voxelSize) {
|
|
||||||
this._voxelSize = voxelSize;
|
|
||||||
this._voxelSizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
_drawDebugRegisters() {
|
|
||||||
const uniforms = {
|
|
||||||
u_worldViewProjection: this._camera.getWorldViewProjection()
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const debugBuffer of this._debugRegisterBuffers) {
|
|
||||||
this._drawBuffer(this._gl.LINES, debugBuffer, shaderManager.debugProgram, uniforms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_drawRegisters() {
|
|
||||||
const uniforms = {
|
|
||||||
u_lightWorldPos: this._camera.getCameraPosition(0.0, 0.0),
|
|
||||||
//u_lightWorldPos: new Vector3(4, 2, 1).normalise().toArray(),
|
|
||||||
u_worldViewProjection: this._camera.getWorldViewProjection(),
|
|
||||||
u_worldInverseTranspose: this._camera.getWorldInverseTranspose()
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const buffer of this._registerBuffers) {
|
|
||||||
this._drawBuffer(this._gl.TRIANGLES, buffer, shaderManager.shadedProgram, uniforms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_drawBuffer(drawMode, buffer, shader, uniforms) {
|
_drawBuffer(drawMode, buffer, shader, uniforms) {
|
||||||
this._gl.useProgram(shader.program);
|
this._gl.useProgram(shader.program);
|
||||||
twgl.setBuffersAndAttributes(this._gl, shader, buffer);
|
twgl.setBuffersAndAttributes(this._gl, shader, buffer.buffer);
|
||||||
twgl.setUniforms(shader, uniforms);
|
twgl.setUniforms(shader, uniforms);
|
||||||
this._gl.drawElements(drawMode, buffer.numElements, this._gl.UNSIGNED_SHORT, 0);
|
this._gl.drawElements(drawMode, buffer.numElements, this._gl.UNSIGNED_SHORT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getBoxData(centre, size, debug) {
|
_getNewBuffers() {
|
||||||
const a = Vector3.sub(centre, Vector3.mulScalar(size, 0.5));
|
this._registerDebug = new SegmentedBuffer(16384, [{name: 'position', numComponents: 3}, {name: 'colour', numComponents: 3}]);
|
||||||
const b = Vector3.add(centre, Vector3.mulScalar(size, 0.5));
|
this._register = new SegmentedBuffer(16384, [{name: 'position', numComponents: 3}, {name: 'normal', numComponents: 3}]);
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
return {
|
|
||||||
position: [
|
|
||||||
a.x, a.y, a.z,
|
|
||||||
b.x, a.y, a.z,
|
|
||||||
b.x, b.y, a.z,
|
|
||||||
a.x, b.y, a.z,
|
|
||||||
a.x, a.y, b.z,
|
|
||||||
b.x, a.y, b.z,
|
|
||||||
b.x, b.y, b.z,
|
|
||||||
a.x, b.y, b.z
|
|
||||||
],
|
|
||||||
indices: [
|
|
||||||
0, 1, 1, 2, 2, 3, 3, 0,
|
|
||||||
4, 5, 5, 6, 6, 7, 7, 4,
|
|
||||||
0, 4, 1, 5, 2, 6, 3, 7
|
|
||||||
]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
|
|
||||||
let cube = {
|
|
||||||
position: new Float32Array(72),
|
|
||||||
normal: new Float32Array(72),
|
|
||||||
indices: new Float32Array(72)
|
|
||||||
};
|
|
||||||
|
|
||||||
cube.position.set(this._cube.position);
|
|
||||||
cube.normal.set(this._cube.normal);
|
|
||||||
cube.indices.set(this._cube.indices);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cube;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getTriangleData(triangle, debug) {
|
|
||||||
|
|
||||||
const a = triangle.v0;
|
|
||||||
const b = triangle.v1;
|
|
||||||
const c = triangle.v2;
|
|
||||||
const n = triangle.normal;
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
return {
|
|
||||||
position: [
|
|
||||||
a.x, a.y, a.z,
|
|
||||||
b.x, b.y, b.z,
|
|
||||||
c.x, c.y, c.z,
|
|
||||||
],
|
|
||||||
indices: [
|
|
||||||
0, 1,
|
|
||||||
1, 2,
|
|
||||||
2, 0
|
|
||||||
]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
position: [
|
|
||||||
a.x, a.y, a.z,
|
|
||||||
b.x, b.y, b.z,
|
|
||||||
c.x, c.y, c.z,
|
|
||||||
],
|
|
||||||
normal: [
|
|
||||||
n.x, n.y, n.z,
|
|
||||||
n.x, n.y, n.z,
|
|
||||||
n.x, n.y, n.z
|
|
||||||
],
|
|
||||||
indices: [
|
|
||||||
0, 1, 2
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Use when immediate drawing
|
|
||||||
drawBox(centre, size) {
|
|
||||||
const data = this._getBoxData(centre, size);
|
|
||||||
this._drawData(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use when immediate drawing
|
|
||||||
drawTriangle(a, b, c) {
|
|
||||||
const data = this._getTriangleData(a, b, c);
|
|
||||||
this._drawData(data);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Use when drawing the same thing each frame
|
|
||||||
registerBox(centre, size, debug) {
|
|
||||||
const data = this._getBoxData(centre, size, debug);
|
|
||||||
//console.log(data);
|
|
||||||
this._addDataToRegister(data, debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use when drawing the same triangle each frame
|
|
||||||
/*
|
|
||||||
registerTriangle(a, b, c) {
|
|
||||||
const data = this._getTriangleData(a, b, c);
|
|
||||||
this._addDataToRegister(data);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
registerMesh(mesh) {
|
|
||||||
for (const triangle of mesh.triangles) {
|
|
||||||
this.registerTriangle(triangle, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerVoxelMesh(voxelManager, useMeshing) {
|
|
||||||
if (useMeshing) {
|
|
||||||
const mesh = voxelManager.buildMesh();
|
|
||||||
for (const box of mesh) {
|
|
||||||
this.registerBox(box.centre, box.size, false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const voxel of voxelManager.voxels) {
|
|
||||||
this.registerVoxel(voxel, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerTriangle(triangle, debug) {
|
|
||||||
const data = this._getTriangleData(triangle, debug);
|
|
||||||
this._addDataToRegister(data, debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerVoxel(centre, debug) {
|
|
||||||
const data = this._getBoxData(centre, this._voxelSizeVector, debug);
|
|
||||||
this._addDataToRegister(data, debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerVoxels(voxelCentres, debug) {
|
|
||||||
/*
|
|
||||||
for (let i = 0; i < voxelCentres.length; ++i) {
|
|
||||||
console.log(i / voxelCentres.length);
|
|
||||||
this.registerVoxel(voxelCentres[i], debug);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
for (const voxelCentre of voxelCentres) {
|
|
||||||
this.registerVoxel(voxelCentre, debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_cycleDebugRegister() {
|
|
||||||
this._filledDebugRegisters.push(this._debugRegister);
|
|
||||||
this._debugRegister = this._getEmptyDebugRegister();
|
|
||||||
//this._debugMaxIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cycleRegister() {
|
|
||||||
this._filledRegisters.push(this._register);
|
|
||||||
this._register = this._getEmptyRegister();
|
|
||||||
//this._maxIndex = 0;
|
|
||||||
//console.log("Cycling Registers");
|
|
||||||
}
|
|
||||||
|
|
||||||
_willDataOverflowBuffer(data) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_addDataToRegister(data, debug) {
|
|
||||||
if (!this._registersOpen) {
|
|
||||||
console.error("Trying to register object when register is closed. Register before calling compileRegister()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
let newMaxIndex = this._debugMaxIndex + 1 + Math.max(...data.indices);
|
|
||||||
if (newMaxIndex >= 65535) {
|
|
||||||
this._cycleDebugRegister();
|
|
||||||
newMaxIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._debugRegister.position.data.push(...data.position);
|
|
||||||
this._debugRegister.indices.data.push(...data.indices.map(x => x + this._debugMaxIndex));
|
|
||||||
|
|
||||||
const numVertices = data.position.length / 3;
|
|
||||||
const vertexColours = [].concat(...new Array(numVertices).fill(this._strokeColour.toArray()));
|
|
||||||
this._debugRegister.colour.data.push(...vertexColours);
|
|
||||||
|
|
||||||
this._debugMaxIndex = newMaxIndex;
|
|
||||||
} else {
|
|
||||||
let newMaxIndex = this._maxIndex + 1 + Math.max(...data.indices);
|
|
||||||
if (newMaxIndex >= 65535) {
|
|
||||||
this._cycleRegister();
|
|
||||||
newMaxIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._register.position.data.push(...data.position);
|
|
||||||
this._register.normal.data.push(...data.normal);
|
|
||||||
this._register.indices.data.push(...data.indices.map(x => x + this._maxIndex));
|
|
||||||
|
|
||||||
this._maxIndex = newMaxIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
_drawData(data) {
|
|
||||||
const buffer = twgl.createBufferInfoFromArrays(this._gl, data);
|
|
||||||
|
|
||||||
const uniforms = {
|
|
||||||
u_fillColour: this._strokeColour.toArray(),
|
|
||||||
u_worldViewProjection: this._camera.getWorldViewProjection()
|
|
||||||
};
|
|
||||||
|
|
||||||
const shader = shaderManager.unshadedProgram;
|
|
||||||
this._gl.useProgram(shader.program);
|
|
||||||
twgl.setBuffersAndAttributes(this._gl, shader, buffer);
|
|
||||||
twgl.setUniforms(shader, uniforms);
|
|
||||||
this._gl.drawElements(this._gl.LINES, buffer.numElements, this._gl.UNSIGNED_SHORT, 0);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.Renderer = Renderer;
|
module.exports.Renderer = Renderer;
|
@ -14,7 +14,8 @@ canvas {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 58px;
|
height: 58px;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
background-color: #212529;
|
background-color: rgba(0, 0, 0, .25);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas-toast {
|
.canvas-toast {
|
||||||
|
Loading…
Reference in New Issue
Block a user