Merge pull request #1 from LucasDower/rewrite

Rewrite
This commit is contained in:
Lucas Dower 2021-07-03 17:29:54 +01:00 committed by GitHub
commit d2e275d71a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 920 additions and 387 deletions

View File

@ -12,5 +12,5 @@
</body>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script type="module" src="./src/renderer.js"></script>
<script type="module" src="./src/client.js"></script>
</html>

View File

@ -22,7 +22,9 @@
"expand-vertex-data": "^1.1.2",
"load-wavefront-obj": "^0.8.0",
"obj-file-parser": "^0.5.3",
"tslint": "^6.1.3",
"twgl.js": "^4.19.1",
"typescript": "^4.3.5",
"wavefront-obj-parser": "^2.0.1"
},
"dependencies": {

10
resources/big_tri.mtl Normal file
View File

@ -0,0 +1,10 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl None
Ns 500
Ka 0.8 0.8 0.8
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

14
resources/big_tri.obj Normal file
View File

@ -0,0 +1,14 @@
# Blender v2.93.1 OBJ File: ''
# www.blender.org
mtllib big_tri.mtl
o Plane
v -1200.000000 -1200.000000 1200.000000
v 1200.000000 0.000000 1200.000000
v 1200.000000 400.000000 -1200.000000
vt 0.000000 1.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vn 0.4423 -0.8847 -0.1474
usemtl None
s off
f 2/1/1 1/2/1 3/3/1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 68 KiB

10
resources/sub_tri.mtl Normal file
View File

@ -0,0 +1,10 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl None
Ns 500
Ka 0.8 0.8 0.8
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

23
resources/sub_tri.obj Normal file
View File

@ -0,0 +1,23 @@
# Blender v2.93.1 OBJ File: ''
# www.blender.org
mtllib sub_tri.mtl
o Plane_Plane.002
v -0.392920 -0.796029 0.993253
v 2.863070 2.795673 1.575666
v 0.253923 0.000000 -2.005832
v -0.069498 -0.398015 -0.506290
v 1.235075 0.999822 1.284459
v 1.558496 1.397836 -0.215083
vt 0.000000 0.000000
vt 0.500000 0.000000
vt 0.000000 0.500000
vt 0.500000 0.500000
vt 0.000000 1.000000
vt 1.000000 0.000000
vn -0.7422 0.6699 0.0177
usemtl None
s off
f 1/1/1 5/2/1 4/3/1
f 4/3/1 6/4/1 3/5/1
f 4/3/1 5/2/1 6/4/1
f 5/2/1 2/6/1 6/4/1

10
resources/tri.mtl Normal file
View File

@ -0,0 +1,10 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl None
Ns 500
Ka 0.8 0.8 0.8
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

14
resources/tri.obj Normal file
View File

@ -0,0 +1,14 @@
# Blender v2.93.1 OBJ File: ''
# www.blender.org
mtllib tri.mtl
o Plane_Plane.002
v -0.392920 -0.796029 0.993253
v 2.863070 2.795673 1.575666
v 0.253923 0.000000 -2.005832
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
vn -0.7422 0.6699 0.0177
usemtl None
s off
f 1/1/1 2/2/1 3/3/1

View File

@ -0,0 +1,7 @@
precision mediump float;
varying vec3 v_colour;
void main() {
gl_FragColor = vec4(v_colour, 1.0);
}

11
shaders/debug_vertex.vs Normal file
View File

@ -0,0 +1,11 @@
uniform mat4 u_worldViewProjection;
attribute vec3 position;
attribute vec3 colour;
varying vec3 v_colour;
void main() {
v_colour = colour;
gl_Position = u_worldViewProjection * vec4(position.xyz, 1.0);
}

View File

@ -1,9 +1,6 @@
uniform mat4 u_worldViewProjection;
uniform vec3 u_lightWorldPos;
uniform mat4 u_world;
uniform mat4 u_viewInverse;
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;
uniform vec3 u_translate;
attribute vec4 position;
attribute vec3 normal;
@ -11,16 +8,15 @@ attribute vec3 normal;
varying vec4 v_colour;
void main() {
vec4 a_position = u_worldViewProjection * vec4(position.xyz, 1.0);
//vec4 a_position = u_worldViewProjection * position;
vec4 a_position = u_worldViewProjection * vec4(position.xyz * 1.0, 1.0);
//vec4 a_position = vec4();
vec3 v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
vec3 v_normal = (u_worldInverseTranspose * vec4(normal, 0.0)).xyz;
vec3 v_lightDir = normalize(u_lightWorldPos);
float lighting = dot(v_normal, v_lightDir);
v_colour = vec4((0.25 * normal) + (0.75 * vec3(lighting)), 1.0);
float lighting = abs(dot(v_normal, v_lightDir));
//v_colour = vec4(abs(normal) * lighting, 1.0);
v_colour = vec4(vec3(lighting), 1.0);
gl_Position = a_position;
}

View File

@ -1,9 +0,0 @@
precision mediump float;
//uniform float time;
void main() {
//float alpha = (-cos(time * 0.05) + 1.0) / 4.0;
float alpha = 1.0;
gl_FragColor = vec4(1.0, 1.0, 1.0, 0.5);
}

View File

@ -1,11 +0,0 @@
uniform mat4 u_worldViewProjection;
attribute vec3 position;
attribute vec3 normal;
void main() {
// Extrude the vertices outwards slightly to avoid z-fighting
vec3 translated_position = position + normal * 0.0001;
gl_Position = u_worldViewProjection * vec4(translated_position, 1.0);
}

View File

@ -1,13 +0,0 @@
precision mediump float;
varying vec3 v_colour;
varying float v_distance;
void main() {
float alpha = v_distance / 32.0;
alpha = 1.0 - clamp(alpha, 0.0, 1.0);
gl_FragColor = vec4(v_colour, alpha);
}

View File

@ -1,15 +0,0 @@
uniform mat4 u_worldViewProjection;
uniform vec3 u_scale;
attribute vec3 position;
attribute vec3 colour;
varying vec3 v_colour;
varying float v_distance;
void main() {
v_colour = colour;
v_distance = length(position);
gl_Position = u_worldViewProjection * vec4(u_scale * position * 0.5, 1.0);
}

64
src/aabb.js Normal file
View File

@ -0,0 +1,64 @@
const { Vector3 } = require('./vector.js');
class AABB {
constructor(centre, size) {
this.centre = centre;
this.size = size;
this.a = Vector3.sub(centre, Vector3.mulScalar(size, 0.5));
this.b = Vector3.add(centre, Vector3.mulScalar(size, 0.5));
}
/*
subdivide() {
const newSize = Vector3.divScalar(this.size, 2);
const offset = Vector3.divScalar(this.size, 4);
return [
new AABB(Vector3.add(this.centre, new Vector3(-offset.x, -offset.y, -offset.z)), newSize),
new AABB(Vector3.add(this.centre, new Vector3( offset.x, -offset.y, -offset.z)), newSize),
new AABB(Vector3.add(this.centre, new Vector3(-offset.x, offset.y, -offset.z)), newSize),
new AABB(Vector3.add(this.centre, new Vector3( offset.x, offset.y, -offset.z)), newSize),
new AABB(Vector3.add(this.centre, new Vector3(-offset.x, -offset.y, offset.z)), newSize),
new AABB(Vector3.add(this.centre, new Vector3( offset.x, -offset.y, offset.z)), newSize),
new AABB(Vector3.add(this.centre, new Vector3(-offset.x, offset.y, offset.z)), newSize),
new AABB(Vector3.add(this.centre, new Vector3( offset.x, offset.y, offset.z)), newSize),
];
}
*/
}
class CubeAABB extends AABB {
constructor(centre, width) {
const sizeVector = new Vector3(width, width, width);
super(centre, sizeVector);
this.width = width;
}
subdivide() {
const newWidth = this.width / 2;
const offset = this.width / 4;
return [
new CubeAABB(Vector3.add(this.centre, new Vector3(-offset, -offset, -offset)), newWidth),
new CubeAABB(Vector3.add(this.centre, new Vector3( offset, -offset, -offset)), newWidth),
new CubeAABB(Vector3.add(this.centre, new Vector3(-offset, offset, -offset)), newWidth),
new CubeAABB(Vector3.add(this.centre, new Vector3( offset, offset, -offset)), newWidth),
new CubeAABB(Vector3.add(this.centre, new Vector3(-offset, -offset, offset)), newWidth),
new CubeAABB(Vector3.add(this.centre, new Vector3( offset, -offset, offset)), newWidth),
new CubeAABB(Vector3.add(this.centre, new Vector3(-offset, offset, offset)), newWidth),
new CubeAABB(Vector3.add(this.centre, new Vector3( offset, offset, offset)), newWidth),
];
}
}
module.exports.AABB = AABB;
module.exports.CubeAABB = CubeAABB;

View File

@ -10,7 +10,7 @@ class ArcballCamera {
this.zNear = zNear;
this.zFar = zFar;
this.actualDistance = 8.0;
this.actualDistance = 15.0;
this.actualAzimuth = 0.6;
this.actualElevation = 1.3;
@ -29,7 +29,7 @@ class ArcballCamera {
this.scrollSensitivity = 0.005;
this.zoomDistMin = 2.0;
this.zoomDistMax = 15.0;
this.zoomDistMax = 30.0;
this.isRotating = false;
}

31
src/client.js Normal file
View File

@ -0,0 +1,31 @@
const { Renderer } = require('./src/renderer.js');
const { Vector3 } = require('./src/vector.js');
const { Triangle } = require('./src/triangle.js');
const { Mesh } = require('./src/mesh.js');
const { VoxelManager } = require('./src/voxel_manager.js');
const voxelSize = 0.025;
const renderer = new Renderer(voxelSize);
const voxelManager = new VoxelManager(voxelSize);
const triangle = new Triangle(new Vector3(0, 0, 0), new Vector3(4, 3, 1), new Vector3(2, -3, -2));
const triangle2 = new Triangle(new Vector3(5, 2, -1), new Vector3(-2, 3, -1), new Vector3(0, 3, 2));
const suzanneLeft = new Mesh('./resources/suzanne_left.obj');
voxelManager.voxeliseMesh(suzanneLeft);
renderer.registerVoxels(voxelManager.voxels);
const suzanneRight = new Mesh('./resources/suzanne_right.obj');
renderer.registerMesh(suzanneRight);
renderer.compileRegister();
function render(time) {
renderer.begin();
renderer.end();
requestAnimationFrame(render);
}
requestAnimationFrame(render);

24
src/events.js Normal file
View File

@ -0,0 +1,24 @@
const mouseHandler = require('./mouse.js');
function registerCanvasEvents(gl, camera) {
gl.canvas.addEventListener('mousedown', (e) => {
camera.isRotating = true;
});
gl.canvas.addEventListener('mouseup', (e) => {
camera.isRotating = false;
});
gl.canvas.addEventListener('mousemove', (e) => {
mouseHandler.handleInput(e);
camera.updateCamera();
});
gl.canvas.addEventListener('wheel', (e) => {
camera.handleScroll(e);
});
}
module.exports.registerCanvasEvents = registerCanvasEvents;

View File

@ -1,68 +0,0 @@
const { v3: Vector3 } = require("twgl.js");
const twgl = require('twgl.js');
const fs = require('fs');
function getGridColour(i, j, isXAxis) {
if (i == 0 && isXAxis) {
return [0.25, 0.75, 0.25];
}
if (j == 0 && !isXAxis) {
return [0.75, 0.25, 0.25];
}
return [0.25, 0.25, 0.25];
}
function generateGridMesh() {
vertices = [];
colours = [];
indices = [];
var gridSize = 64;
for (let i = -gridSize / 2; i < gridSize / 2; i++) {
for (let j = -gridSize / 2; j < gridSize / 2; j++) {
let k = indices.length;
let colour = getGridColour(i, j, false);
vertices.push(i, 0, j);
colours.push(colour[0], colour[1], colour[2]); // .push > .concat
indices.push(k);
vertices.push(i + 1, 0, j);
colours.push(colour[0], colour[1], colour[2]);
indices.push(k + 1);
colour = getGridColour(i, j, true);
vertices.push(i, 0, j);
colours.push(colour[0], colour[1], colour[2]);
indices.push(k + 2);
vertices.push(i, 0, j + 1);
colours.push(colour[0], colour[1], colour[2]);
indices.push(k + 3);
}
}
let k = indices.length;
vertices.push(gridSize / 2, 0, -gridSize / 2);
colours.push(0.0, 1.0, 1.0);
indices.push(k);
vertices.push(gridSize / 2, 0, gridSize / 2);
colours.push(0.0, 1.0, 1.0);
indices.push(k + 1);
vertices.push(-gridSize / 2, 0, gridSize / 2);
colours.push(1.0, 0.0, 0.0);
indices.push(k + 2);
vertices.push(gridSize / 2, 0, gridSize / 2);
colours.push(1.0, 0.0, 0.0);
indices.push(k + 3);
return { position: { numComponents: 3, data: vertices }, colour: { numComponents: 3, data: colours }, indices: indices };
}
module.exports.generateGridMesh = generateGridMesh;

60
src/hash_map.js Normal file
View File

@ -0,0 +1,60 @@
const { Vector3 } = require('./vector.js');
class HashMap {
constructor(numBins) {
this.numBins = numBins;
this.bins = new Array(numBins);
}
_getBin(key) {
const hash = key.hash(); // A bit naughty
return Math.abs(hash) % this.numBins;
}
add(key, value) {
const binIndex = this._getBin(key);
console.log(binIndex);
if (!this.bins[binIndex]) {
this.bins[binIndex] = [ {key: key, value: value} ];
} else {
this.bins[binIndex].push({key: key, value: value});
}
}
get(key) {
const binIndex = this._getBin(key);
if (!this.bins[binIndex]) {
return;
}
const list = this.bins[binIndex];
for (const item of list) {
if (item.key.equals(key)) {
return item.value;
}
}
}
}
const hashMap = new HashMap(4);
const v = new Vector3(2.0, 2.0, 3.0);
const v2 = new Vector3(2.0, 2.0, 5.0);
const v3 = new Vector3(-2.0, -2.0, -5.0);
const v4 = new Vector3(-2.0, -5.0, -7.0);
hashMap.add(v, true);
hashMap.add(v2, true);
hashMap.add(v3, true);
console.log(hashMap.bins[0]);
console.log(hashMap.bins[1]);
console.log(hashMap.bins[2]);
console.log(hashMap.bins[3]);
console.log(hashMap.get(v));
console.log(hashMap.get(v2));
console.log(hashMap.get(v3));
console.log(hashMap.get(v4));

View File

@ -1,5 +1,10 @@
// Not apart of rendering, SIMD optimisation not necessary
const { v3: Vector3 } = require('twgl.js');
const { Vector3 } = require('./vector.js');
/*
function roundTo(value, base) {
return Math.round(value / base) * base;
}
function floorTo(value, base) {
return Math.floor(value / base) * base;
@ -9,11 +14,51 @@ function ceilTo(value, base) {
return Math.ceil(value / base) * base;
}
function fastCrossXAxis(vec) {
return Vector3.create(0.0, -vec[2], vec[1]);
}
function fastDotXAxis(vec) {
return vec[0];
}
function fastDotYAxis(vec) {
return vec[1];
}
function fastDotZAxis(vec) {
return vec[2];
}
function fastCrossYAxis(vec) {
return Vector3.create(vec[2], 0.0, -vec[0]);
}
function fastCrossZAxis(vec) {
return Vector3.create(-vec[1], vec[0], 0.0);
}
function roundVector3To(vec, round) {
vec[0] = roundTo(vec[0], round);
vec[1] = roundTo(vec[1], round);
vec[2] = roundTo(vec[2], round);
}
module.exports.floorTo = floorTo;
module.exports.ceilTo = ceilTo;
module.exports.xAxis = Vector3.create(1.0, 0.0, 0.0);
module.exports.yAxis = Vector3.create(0.0, 1.0, 0.0);
module.exports.zAxis = Vector3.create(0.0, 0.0, 1.0);
module.exports.fastCrossXAxis = fastCrossXAxis;
module.exports.fastCrossYAxis = fastCrossYAxis;
module.exports.fastCrossZAxis = fastCrossZAxis;
module.exports.fastDotXAxis = fastDotXAxis;
module.exports.fastDotYAxis = fastDotYAxis;
module.exports.fastDotZAxis = fastDotZAxis;
*/
module.exports.xAxis = new Vector3(1.0, 0.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.roundVector3To = roundVector3To;

View File

@ -1,101 +1,43 @@
const { v3: Vector3 } = require('twgl.js');
const { Triangle } = require('./triangle.js');
const fs = require('fs');
const twgl = require('twgl.js');
const wavefrontObjParser = require('wavefront-obj-parser');
const expandVertexData = require('expand-vertex-data');
const { Triangle } = require('./triangle.js');
const { Vector3 } = require("./vector.js");
class Mesh {
constructor(obj_path, gl) {
constructor(obj_path) {
var wavefrontString = fs.readFileSync(obj_path).toString('utf8');
var parsedJSON = wavefrontObjParser(wavefrontString);
var expanded = expandVertexData(parsedJSON, {facesToTriangles: true});
this.model = {
this._data = {
position: expanded.positions,
normal: expanded.normals,
indices: expanded.positionIndices
};
this.buffers = [twgl.createBufferInfoFromArrays(gl, this.model)];
this._getTriangles();
}
voxelise(voxel_size, gl) {
// Extract triangles from mesh
let triangles = [];
for (let i = 0; i < this.model.indices.length; i += 3) {
let i0 = this.model.indices[i];
let i1 = this.model.indices[i + 1];
let i2 = this.model.indices[i + 2];
let v0 = this.model.position.slice(3 * i0, 3 * i0 + 3);
let v1 = this.model.position.slice(3 * i1, 3 * i1 + 3);
let v2 = this.model.position.slice(3 * i2, 3 * i2 + 3);
triangles.push(new Triangle(v0, v1, v2));
_getTriangles() {
this.triangles = [];
for (let i = 0; i < this._data.indices.length; i += 3) {
let i0 = this._data.indices[i];
let i1 = this._data.indices[i + 1];
let i2 = this._data.indices[i + 2];
let v0 = this._data.position.slice(3 * i0, 3 * i0 + 3);
let v1 = this._data.position.slice(3 * i1, 3 * i1 + 3);
let v2 = this._data.position.slice(3 * i2, 3 * i2 + 3);
const v0_ = new Vector3(v0[0], v0[1], v0[2]);
const v1_ = new Vector3(v1[0], v1[1], v1[2]);
const v2_ = new Vector3(v2[0], v2[1], v2[2]);
this.triangles.push(new Triangle(v0_, v1_, v2_));
}
// Voxelise triangles
let voxel_positions = [];
for (let triangle of triangles) {
let v = triangle.voxelise(voxel_size);
voxel_positions.push(...v);
}
console.log(triangles);
const cube = twgl.primitives.createCubeVertices(voxel_size);
const num_buffers = Math.ceil(voxel_positions.length / 1024);
// Create buffers
let buffers = new Array(num_buffers);
for (let i = 0; i < num_buffers; ++i) {
buffers[i]= {
position: { numComponents: 3, data: []},
normal: { numComponents: 3, data: []},
indices: { numComponents: 3, data: []}
};
}
// Fill buffers with voxels
for (let i = 0; i < voxel_positions.length; ++i) {
let voxel = voxel_positions[i];
let position_ = [];
for (let j = 0; j < 72; j += 3) {
position_.push(
cube.position[j + 0] + voxel[0],
cube.position[j + 1] + voxel[1],
cube.position[j + 2] + voxel[2]
);
}
const buffer_index = Math.floor(i / 1024);
let index_offset = buffers[buffer_index].indices.data.length;
index_offset /= 1.5;
let indices_ = [];
for (let j = 0; j < 36; j += 3) {
indices_.push(
cube.indices[j + 0] + index_offset,
cube.indices[j + 1] + index_offset,
cube.indices[j + 2] + index_offset
);
}
buffers[buffer_index].indices.data.push(...indices_);
buffers[buffer_index].position.data.push(...position_);
buffers[buffer_index].normal.data.push(...cube.normal);
}
let buffer_infos = new Array(num_buffers);
for (let i = 0; i < num_buffers; ++i) {
buffer_infos[i] = twgl.createBufferInfoFromArrays(gl, buffers[i]);
}
this.buffers = buffer_infos;
}
}

View File

View File

@ -1,119 +1,342 @@
const twgl = require('twgl.js');
const fs = require('fs');
const mouseHandler = require('./src/mouse.js');
const cameraHandler = require('./src/camera.js');
const shaderManager = require('./src/shaders.js');
const gridManager = require('./src/grid.js');
const { Vector3 } = require('./vector.js');
const { ArcballCamera } = require('./camera.js');
const mouseManager = require('./mouse.js');
const shaderManager = require('./shaders.js');
const { Triangle } = require('./src/triangle.js');
class Renderer {
const wavefrontObjParser = require('wavefront-obj-parser');
const expandVertexData = require('expand-vertex-data');
constructor(voxelSize) {
this._gl = document.querySelector("#c").getContext("webgl");
const { Mesh } = require('./src/mesh.js');
this._fov = 30;
this._backgroundColour = new Vector3(0.1, 0.1, 0.1);
this._strokeColour = new Vector3(1.0, 1.0, 1.0);
const v3 = twgl.v3;
const gl = document.querySelector("#c").getContext("webgl");
this._camera = new ArcballCamera(this._fov, this._gl.canvas.clientWidth / this._gl.canvas.clientHeight, 0.5, 100.0);
this._registerEvents();
let suzanne_left = new Mesh('./resources/suzanne_left.obj', gl);
let suzanne_right = new Mesh('./resources/suzanne_right.obj', gl);
this._debugRegister = this._getEmptyRegister();
this._register = this._getEmptyRegister();
this._filledDebugRegisters = [];
this._filledRegisters = [];
suzanne_left.voxelise(0.025, gl);
const suzanne_left_buffers = suzanne_left.buffers;
const suzanne_right_buffers = suzanne_right.buffers;
this._registerBuffers = [];
this._debugRegisterBuffers = [];
this._debugMaxIndex = 0;
this._maxIndex = 0;
this._voxelSize = voxelSize;
this._voxelSizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
var camera = new cameraHandler.ArcballCamera(30, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 30.0);
const gridMesh = gridManager.generateGridMesh();
const gridBuffer = twgl.createBufferInfoFromArrays(gl, gridMesh);
gl.canvas.addEventListener('mousedown', (e) => {
camera.isRotating = true;
});
gl.canvas.addEventListener('mouseup', (e) => {
camera.isRotating = false;
});
gl.canvas.addEventListener('mousemove', (e) => {
mouseHandler.handleInput(e);
camera.updateCamera();
});
gl.canvas.addEventListener('wheel', (e) => {
camera.handleScroll(e);
});
function drawModel(model, translation) {
const uniforms = {
u_lightWorldPos: camera.getCameraPosition(0.785398, 0),
u_diffuse: model.textureUnit,
u_viewInverse: camera.getCameraMatrix(),
u_world: camera.getWorldMatrix(),
u_worldInverseTranspose: camera.getWorldInverseTranspose(),
u_worldViewProjection: camera.getWorldViewProjection(),
u_translate: translation
};
drawBufferWithShader(gl.TRIANGLES, model, uniforms, shaderManager.shadedProgram);
}
function drawGrid() {
const uniforms = {
u_worldViewProjection: camera.getWorldViewProjection(),
u_scale: v3.create(2.0/16.0, 2.0, 2.0/16.0)
};
drawBufferWithShader(gl.LINES, gridBuffer, uniforms, shaderManager.unshadedProgram);
}
function drawBufferWithShader(drawMode, buffer, uniforms, shader) {
gl.useProgram(shader.program);
twgl.setBuffersAndAttributes(gl, shader, buffer);
twgl.setUniforms(shader, uniforms);
gl.drawElements(drawMode, buffer.numElements, gl.UNSIGNED_SHORT, 0);
}
function render(time) {
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.enable(gl.BLEND);
gl.clearColor(0.1, 0.1, 0.1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
camera.updateCameraPosition();
drawGrid();
for (const buffer_info of suzanne_right_buffers) {
drawModel(buffer_info, v3.create(0.0, 0.0, 0.0));
this._registersOpen = true;
}
for (const buffer_info of suzanne_left_buffers) {
drawModel(buffer_info, v3.create(0.0, 0.0, 0.0));
_getEmptyDebugRegister() {
return {
position: {numComponents: 3, data: []},
indices: {numComponents: 3, data: []},
colour: {numComponents: 3, data: []}
};
}
//drawModel(buffer_infos[0], v3.create(0.0, 0.0, 0.0));
//for (const buffer_info of buffer_infos) {
// drawModel(buffer_info, v3.create(0.0, 0.0, 0.0));
//}
_getEmptyRegister() {
return {
position: {numComponents: 3, data: []},
normal: {numComponents: 3, data: []},
indices: {numComponents: 3, data: []},
};
}
_registerEvents() {
this._gl.canvas.addEventListener('mousedown', (e) => {
this._camera.isRotating = true;
});
this._gl.canvas.addEventListener('mouseup', (e) => {
this._camera.isRotating = false;
});
this._gl.canvas.addEventListener('mousemove', (e) => {
mouseManager.handleInput(e);
this._camera.updateCamera();
});
this._gl.canvas.addEventListener('wheel', (e) => {
this._camera.handleScroll(e);
});
}
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();
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();
}
_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.5, 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) {
this._gl.useProgram(shader.program);
twgl.setBuffersAndAttributes(this._gl, shader, buffer);
twgl.setUniforms(shader, uniforms);
this._gl.drawElements(drawMode, buffer.numElements, this._gl.UNSIGNED_SHORT, 0);
}
_getBoxData(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 = twgl.primitives.createCubeVertices(this._voxelSize);
for (let i = 0; i < 72; i += 3) {
cube.position[i + 0] += centre.x;
cube.position[i + 1] += centre.y;
cube.position[i + 2] += 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);
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);
}
}
registerTriangle(triangle, debug) {
const data = this._getTriangleData(triangle, debug);
this._addDataToRegister(data, debug);
}
registerVoxel(centre) {
const data = this._getBoxData(centre, this._voxelSizeVector, false);
this._addDataToRegister(data, false);
}
registerVoxels(voxelCentres) {
for (const voxelCentre of voxelCentres) {
this.registerVoxel(voxelCentre);
}
}
_cycleDebugRegister() {
this._filledDebugRegisters.push(this._register);
this._debugRegister = this._getEmptyDebugRegister();
//this._debugMaxIndex = 0;
}
_cycleRegister() {
this._filledRegisters.push(this._register);
this._register = this._getEmptyRegister();
//this._maxIndex = 0;
//console.log("Cycling Registers");
}
_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);
}
*/
requestAnimationFrame(render);
}
requestAnimationFrame(render);
module.exports.Renderer = Renderer;

View File

@ -1,21 +1,15 @@
const twgl = require('twgl.js');
const fs = require('fs');
const gl = document.querySelector("#c").getContext("webgl");
const shaded_vertex_shader = fs.readFileSync('./shaders/shaded_vertex.vs', 'utf8');
const shaded_fragment_shader = fs.readFileSync('./shaders/shaded_fragment.fs', 'utf8');
const unshaded_vertex_shader = fs.readFileSync('./shaders/unshaded_vertex.vs', 'utf8');
const unshaded_fragment_shader = fs.readFileSync('./shaders/unshaded_fragment.fs', 'utf8');
const test_vertex_shader = fs.readFileSync('./shaders/test_vertex.vs', 'utf8');
const test_fragment_shader = fs.readFileSync('./shaders/test_fragment.fs', 'utf8');
const debug_vertex_shader = fs.readFileSync('./shaders/debug_vertex.vs', 'utf8');
const debug_fragment_shader = fs.readFileSync('./shaders/debug_fragment.fs', 'utf8');
const shadedProgram = twgl.createProgramInfo(gl, [shaded_vertex_shader, shaded_fragment_shader]);
const unshadedProgram = twgl.createProgramInfo(gl, [unshaded_vertex_shader, unshaded_fragment_shader]);
const testProgram = twgl.createProgramInfo(gl, [test_vertex_shader, test_fragment_shader]);
const debugProgram = twgl.createProgramInfo(gl, [debug_vertex_shader, debug_fragment_shader]);
module.exports.shadedProgram = shadedProgram;
module.exports.unshadedProgram = unshadedProgram;
module.exports.testProgram = testProgram;
module.exports.debugProgram = debugProgram;

View File

@ -1,5 +1,6 @@
const { v3: Vector3 } = require('twgl.js');
const mathUtil = require('./math.js');
const { Vector3 } = require('./vector.js');
const { AABB } = require('./aabb.js');
const { xAxis, yAxis, zAxis } = require('./math.js');
class Triangle {
@ -7,87 +8,82 @@ class Triangle {
this.v0 = v0;
this.v1 = v1;
this.v2 = v2;
const f0 = Vector3.sub(v1, v0);
const f1 = Vector3.sub(v0, v2);
this.normal = Vector3.cross(f0, f1).normalise();
//this.normal.normalise();
this._calculateBoundingBox();
}
getBoundingBox(voxelSize) {
return {
minX: mathUtil.floorTo(Math.min(this.v0[0], this.v1[0], this.v2[0]), voxelSize),
minY: mathUtil.floorTo(Math.min(this.v0[1], this.v1[1], this.v2[1]), voxelSize),
minZ: mathUtil.floorTo(Math.min(this.v0[2], this.v1[2], this.v2[2]), voxelSize),
_calculateBoundingBox() {
const a = new Vector3(
Math.min(this.v0.x, this.v1.x, this.v2.x),
Math.min(this.v0.y, this.v1.y, this.v2.y),
Math.min(this.v0.z, this.v1.z, this.v2.z)
);
maxX: mathUtil.ceilTo(Math.max(this.v0[0], this.v1[0], this.v2[0]), voxelSize),
maxY: mathUtil.ceilTo(Math.max(this.v0[1], this.v1[1], this.v2[1]), voxelSize),
maxZ: mathUtil.ceilTo(Math.max(this.v0[2], this.v1[2], this.v2[2]), voxelSize),
};
const b = new Vector3(
Math.max(this.v0.x, this.v1.x, this.v2.x),
Math.max(this.v0.y, this.v1.y, this.v2.y),
Math.max(this.v0.z, this.v1.z, this.v2.z)
);
const centre = Vector3.mulScalar(Vector3.add(a, b), 0.5);
const size = Vector3.abs(Vector3.sub(a, b));
this.aabb = new AABB(centre, size);
}
voxelise(voxelSize) {
let voxels = [];
let bb = this.getBoundingBox(voxelSize);
for (let x = bb.minX; x < bb.maxX; x += voxelSize) {
for (let y = bb.minY; y < bb.maxY; y += voxelSize) {
for (let z = bb.minZ; z < bb.maxZ; z += voxelSize) {
if (this.voxelIntersect(x, y, z, voxelSize)) {
voxels.push([x, y, z]);
}
}
}
}
return voxels;
insideAABB(aabb) {
return Vector3.lessThanEqualTo(aabb.a, this.aabb.a) && Vector3.lessThanEqualTo(this.aabb.b, aabb.b);
}
voxelIntersect(x, y, z, voxelSize) {
let c = Vector3.create(x, y, z);
let e = Vector3.create(voxelSize/2, voxelSize/2, voxelSize/2);
intersectAABB(aabb) {
const extents = Vector3.mulScalar(aabb.size, 0.5);
let v0_ = Vector3.subtract(this.v0, c);
let v1_ = Vector3.subtract(this.v1, c);
let v2_ = Vector3.subtract(this.v2, c);
const v0 = Vector3.sub(this.v0, aabb.centre);
const v1 = Vector3.sub(this.v1, aabb.centre);
const v2 = Vector3.sub(this.v2, aabb.centre);
let f0 = Vector3.subtract(v1_, v0_);
let f1 = Vector3.subtract(v2_, v1_);
let f2 = Vector3.subtract(v0_, v2_);
const f0 = Vector3.sub(v1, v0);
const f1 = Vector3.sub(v2, v1);
const f2 = Vector3.sub(v0, v2);
let a0 = Vector3.cross(f0, f2);
if (this.testAxis(v0_, v1_, v2_, a0, e)) {
return false;
}
const axis = [
Vector3.cross(xAxis, f0),
Vector3.cross(xAxis, f1),
Vector3.cross(xAxis, f2),
Vector3.cross(yAxis, f0),
Vector3.cross(yAxis, f1),
Vector3.cross(yAxis, f2),
Vector3.cross(zAxis, f0),
Vector3.cross(zAxis, f1),
Vector3.cross(zAxis, f2),
xAxis,
yAxis,
zAxis,
Vector3.cross(f0, f2)
];
let a1 = [mathUtil.xAxis, mathUtil.yAxis, mathUtil.zAxis];
for (let ax of a1) {
if (this.testAxis(v0_, v1_, v2_, ax, e)) {
for (const ax of axis) {
if (this._testAxis(v0, v1, v2, ax, extents)) {
return false;
}
}
let axis = new Array(9);
axis[0] = Vector3.cross(mathUtil.xAxis, f0);
axis[1] = Vector3.cross(mathUtil.xAxis, f1);
axis[2] = Vector3.cross(mathUtil.xAxis, f2);
axis[3] = Vector3.cross(mathUtil.yAxis, f0);
axis[4] = Vector3.cross(mathUtil.yAxis, f1);
axis[5] = Vector3.cross(mathUtil.yAxis, f2);
axis[6] = Vector3.cross(mathUtil.zAxis, f0);
axis[7] = Vector3.cross(mathUtil.zAxis, f1);
axis[8] = Vector3.cross(mathUtil.zAxis, f2);
for (let ax of axis) {
if (this.testAxis(v0_, v1_, v2_, ax, e)) {
return false;
}
}
return true;
}
testAxis(v0_, v1_, v2_, axis, e) {
let p0 = Vector3.dot(v0_, axis);
let p1 = Vector3.dot(v1_, axis);
let p2 = Vector3.dot(v2_, axis);
_testAxis(v0, v1, v2, axis, extents) {
let p0 = Vector3.dot(v0, axis);
let p1 = Vector3.dot(v1, axis);
let p2 = Vector3.dot(v2, axis);
let r = e[0] * Math.abs(Vector3.dot(mathUtil.xAxis, axis)) +
e[1] * Math.abs(Vector3.dot(mathUtil.yAxis, axis)) +
e[2] * Math.abs(Vector3.dot(mathUtil.zAxis, axis));
let r = extents.x * Math.abs(Vector3.dot(xAxis, axis)) +
extents.y * Math.abs(Vector3.dot(yAxis, axis)) +
extents.z * Math.abs(Vector3.dot(zAxis, axis));
return (Math.min(p0, p1, p2) > r || Math.max(p0, p1, p2) < -r);
}

113
src/vector.js Normal file
View File

@ -0,0 +1,113 @@
const { isVoidExpression, textChangeRangeIsUnchanged } = require("typescript");
class Vector3 {
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
toArray() {
return [this.x, this.y, this.z];
}
static add(vecA, vecB) {
return new Vector3(
vecA.x + vecB.x,
vecA.y + vecB.y,
vecA.z + vecB.z
);
}
static sub(vecA, vecB) {
return new Vector3(
vecA.x - vecB.x,
vecA.y - vecB.y,
vecA.z - vecB.z
);
}
static dot(vecA, vecB) {
return vecA.x * vecB.x + vecA.y * vecB.y + vecA.z * vecB.z;
}
static copy(vec) {
return new Vector3(
vec.x,
vec.y,
vec.z
);
}
static mulScalar(vec, scalar) {
return new Vector3(
scalar * vec.x,
scalar * vec.y,
scalar * vec.z
);
}
static divScalar(vec, scalar) {
return new Vector3(
vec.x / scalar,
vec.y / scalar,
vec.z / scalar
);
}
static lessThanEqualTo(vecA, vecB) {
return vecA.x <= vecB.x && vecA.y <= vecB.y && vecA.z <= vecB.z;
}
static round(vec) {
return new Vector3(
Math.round(vec.x),
Math.round(vec.y),
Math.round(vec.z)
);
}
static abs(vec) {
return new Vector3(
Math.abs(vec.x),
Math.abs(vec.y),
Math.abs(vec.z)
);
}
static cross(vecA, vecB) {
return new Vector3(
vecA.y * vecB.z - vecA.z * vecB.y,
vecA.z * vecB.x - vecA.x * vecB.z,
vecA.x * vecB.y - vecA.y * vecB.x
);
}
hash() {
const p0 = 73856093;
const p1 = 19349663;
const p2 = 83492791;
return (this.x * p0) ^ (this.y * p1) ^ (this.z * p2);
}
equals(vec) {
return this.x == vec.x && this.y == vec.y && this.z == vec.z;
}
_magnitude() {
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2) + Math.pow(this.z, 2));
}
normalise() {
const mag = this._magnitude();
this.x /= mag;
this.y /= mag;
this.z /= mag;
return this;
}
}
module.exports.Vector3 = Vector3;

60
src/voxel_manager.js Normal file
View File

@ -0,0 +1,60 @@
const { AABB, CubeAABB } = require("./aabb.js");
const { Vector3 } = require("./vector.js");
class VoxelManager {
constructor(voxelSize) {
this._voxelSize = voxelSize;
this.voxels = [];
}
_getTriangleCubeAABB(triangle) {
let gridSnappedCentre = Vector3.divScalar(triangle.aabb.centre, this._voxelSize);
gridSnappedCentre = Vector3.round(gridSnappedCentre);
gridSnappedCentre = Vector3.mulScalar(gridSnappedCentre, this._voxelSize);
let width = this._voxelSize;
//let cubeAABB = new AABB(gridSnappedCentre, new Vector3(size, size, size));
let cubeAABB = new CubeAABB(gridSnappedCentre, width);
while (!triangle.insideAABB(cubeAABB)) {
//cubeAABB = new AABB(cubeAABB.centre, Vector3.mulScalar(cubeAABB.size, 2.0));
cubeAABB = new CubeAABB(cubeAABB.centre, cubeAABB.width * 2.0);
}
return cubeAABB;
}
voxeliseTriangle(triangle) {
const cubeAABB = this._getTriangleCubeAABB(triangle);
//renderer.setStroke(new Vector3(1.0, 1.0, 1.0));
//let voxels = [];
let queue = [cubeAABB];
while (queue.length > 0) {
const aabb = queue.shift();
if (triangle.intersectAABB(aabb)) {
if (aabb.width > this._voxelSize) {
// Continue to subdivide
queue.push(...aabb.subdivide());
} else {
// We've reached the voxel level, stop
//renderer.registerBox(aabb.centre, aabb.size);
this.voxels.push(aabb.centre);
}
}
}
//return voxels;
}
voxeliseMesh(mesh) {
for (const triangle of mesh.triangles) {
this.voxeliseTriangle(triangle);
}
}
}
module.exports.VoxelManager = VoxelManager;