forked from mirror/ObjToSchematic
commit
d2e275d71a
@ -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>
|
@ -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
10
resources/big_tri.mtl
Normal 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
14
resources/big_tri.obj
Normal 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
10
resources/sub_tri.mtl
Normal 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
23
resources/sub_tri.obj
Normal 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
10
resources/tri.mtl
Normal 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
14
resources/tri.obj
Normal 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
|
7
shaders/debug_fragment.fs
Normal file
7
shaders/debug_fragment.fs
Normal 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
11
shaders/debug_vertex.vs
Normal 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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
64
src/aabb.js
Normal 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;
|
@ -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
31
src/client.js
Normal 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
24
src/events.js
Normal 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;
|
68
src/grid.js
68
src/grid.js
@ -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
60
src/hash_map.js
Normal 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));
|
53
src/math.js
53
src/math.js
@ -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;
|
102
src/mesh.js
102
src/mesh.js
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
421
src/renderer.js
421
src/renderer.js
@ -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;
|
@ -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;
|
||||
|
124
src/triangle.js
124
src/triangle.js
@ -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
113
src/vector.js
Normal 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
60
src/voxel_manager.js
Normal 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;
|
Loading…
Reference in New Issue
Block a user