This commit is contained in:
Lucas Dower 2021-07-02 23:26:28 +01:00
parent 2d8d8b0ec5
commit 4a0b99c560
21 changed files with 582 additions and 241 deletions

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

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

@ -1,9 +1,8 @@
uniform mat4 u_worldViewProjection;
uniform vec3 u_fillColour;
uniform float u_opacity;
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 +10,14 @@ attribute vec3 normal;
varying vec4 v_colour;
void main() {
//vec4 a_position = u_worldViewProjection * position;
vec4 a_position = u_worldViewProjection * vec4(position.xyz * 1.0, 1.0);
//vec4 a_position = vec4();
vec4 a_position = u_worldViewProjection * vec4(position.xyz * 0.1, 1.0);
vec3 v_normal = (u_worldInverseTranspose * vec4(normal, 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);
v_colour = vec4(u_fillColour * lighting, u_opacity);
gl_Position = a_position;
}

View File

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

View File

@ -1,15 +1,7 @@
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);
gl_Position = u_worldViewProjection * vec4(position.xyz * 0.1, 1.0);
}

122
src/aabb.js Normal file
View File

@ -0,0 +1,122 @@
const { v3: Vector3 } = require('twgl.js');
const twgl = require('twgl.js');
const shaderManager = require('./shaders.js');
const primitivesManager = require('./primitives.js');
const tri = require('./triangle.js');
const mathUtil = require('./math.js');
class AABB {
constructor(argA, argB) {
if (argA instanceof tri.Triangle) {
this._constructFromTriangle(argA, argB);
} else {
this._constructFromCentre(argA, argB);
}
const halfWidthVector = Vector3.create(this.size, this.size, this.size);
this.a = Vector3.subtract(this.centre, halfWidthVector);
this.b = Vector3.add(this.centre, halfWidthVector);
this._bufferInfoReady = false;
}
_constructFromTriangle(tri, voxelSize) {
const a = Vector3.create(
Math.min(tri.v0[0], tri.v1[0], tri.v2[0]),
Math.min(tri.v0[1], tri.v1[1], tri.v2[1]),
Math.min(tri.v0[2], tri.v1[2], tri.v2[2])
);
const b = Vector3.create(
Math.max(tri.v0[0], tri.v1[0], tri.v2[0]),
Math.max(tri.v0[1], tri.v1[1], tri.v2[1]),
Math.max(tri.v0[2], tri.v1[2], tri.v2[2])
);
this.centre = Vector3.mulScalar(Vector3.add(a, b), 0.5);
mathUtil.roundVector3To(this.centre, voxelSize);
const extents = Vector3.create(
Math.abs(a[0] - b[0]) / 2,
Math.abs(a[1] - b[1]) / 2,
Math.abs(a[2] - b[2]) / 2
);
const maxDimension = Math.max(extents[0], extents[1], extents[2]);
const depth = Math.log2(maxDimension);
this.size = Math.pow(2, Math.ceil(depth) + 1);
}
_constructFromCentre(centre, size) {
this.centre = centre;
this.size = size;
}
_createBuffer(gl) {
this._buffer = {
position: [
this.a[0], this.a[1], this.a[2],
this.b[0], this.a[1], this.a[2],
this.b[0], this.b[1], this.a[2],
this.a[0], this.b[1], this.a[2],
this.a[0], this.a[1], this.b[2],
this.b[0], this.a[1], this.b[2],
this.b[0], this.b[1], this.b[2],
this.a[0], this.b[1], this.b[2]
],
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
]
};
this._bufferInfo = twgl.createBufferInfoFromArrays(gl, this._buffer);
this._bufferInfoReady = true;
}
subdivide(voxelSize) {
if (this.size <= voxelSize) {
return;
}
const subSize = this.size / 2;
const subCentre = Vector3.add(Vector3.mulScalar(Vector3.subtract(this.b, this.a), 0.25), this.a);
//const subCentre = Vector3.subtract(this.centre, Vector3.create(subSize, subSize, subSize));
const result = [
new AABB(Vector3.add(subCentre, Vector3.create(0, 0, 0 )), subSize),
new AABB(Vector3.add(subCentre, Vector3.create(this.size, 0, 0 )), subSize),
new AABB(Vector3.add(subCentre, Vector3.create(0, this.size, 0 )), subSize),
new AABB(Vector3.add(subCentre, Vector3.create(this.size, this.size, 0 )), subSize),
new AABB(Vector3.add(subCentre, Vector3.create(0, 0, this.size)), subSize),
new AABB(Vector3.add(subCentre, Vector3.create(this.size, 0, this.size)), subSize),
new AABB(Vector3.add(subCentre, Vector3.create(0, this.size, this.size)), subSize),
new AABB(Vector3.add(subCentre, Vector3.create(this.size, this.size, this.size)), subSize),
];
return result;
}
drawAABB(gl, camera, colour) {
if (!this._bufferInfoReady) {
this._createBuffer(gl);
}
const uniforms = {
u_fillColour: colour,
u_worldViewProjection: camera.getWorldViewProjection()
};
const shader = shaderManager.unshadedProgram;
gl.useProgram(shader.program);
twgl.setBuffersAndAttributes(gl, shader, this._bufferInfo);
twgl.setUniforms(shader, uniforms);
gl.drawElements(gl.LINES, this._bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
}
}
module.exports.AABB = AABB;

View File

@ -10,7 +10,7 @@ class ArcballCamera {
this.zNear = zNear;
this.zFar = zFar;
this.actualDistance = 8.0;
this.actualDistance = 10.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;
}

1
src/constants.js Normal file
View File

@ -0,0 +1 @@
const voxelSize = 1.0;

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,6 +1,10 @@
// Not apart of rendering, SIMD optimisation not necessary
const { v3: Vector3 } = require('twgl.js');
function roundTo(value, base) {
return Math.round(value / base) * base;
}
function floorTo(value, base) {
return Math.floor(value / base) * base;
}
@ -9,11 +13,50 @@ 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.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 = 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.zAxis = Vector3.create(0.0, 0.0, 1.0);
module.exports.roundVector3To = roundVector3To;

View File

@ -7,26 +7,31 @@ const twgl = require('twgl.js');
const wavefrontObjParser = require('wavefront-obj-parser');
const expandVertexData = require('expand-vertex-data');
const shaderManager = require('./shaders.js');
class Mesh {
constructor(obj_path, gl) {
constructor(obj_path, voxelSize, scale) {
var wavefrontString = fs.readFileSync(obj_path).toString('utf8');
var parsedJSON = wavefrontObjParser(wavefrontString);
var expanded = expandVertexData(parsedJSON, {facesToTriangles: true});
this.voxelSize = voxelSize;
this.model = {
position: expanded.positions,
position: expanded.positions.map(x => x * scale),
normal: expanded.normals,
indices: expanded.positionIndices
};
this.buffers = [twgl.createBufferInfoFromArrays(gl, this.model)];
this._triangulate();
this._bufferInfoReady = false;
}
voxelise(voxel_size, gl) {
// Extract triangles from mesh
let triangles = [];
_triangulate() {
this.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];
@ -34,68 +39,49 @@ class Mesh {
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));
this.triangles.push(new Triangle(v0, v1, v2, this.voxelSize));
}
// Voxelise triangles
let voxel_positions = [];
for (let triangle of triangles) {
let v = triangle.voxelise(voxel_size);
voxel_positions.push(...v);
console.log(`Mesh has ${this.triangles.length} triangle(s)`);
}
_updateInfoBuffer(gl) {
this._bufferInfo = twgl.createBufferInfoFromArrays(gl, this.model);
this._bufferInfoReady = true;
}
voxelise() {
let voxels = [];
for (const t of this.triangles) {
voxels.push(...t.voxelise());
}
console.log(triangles);
return voxels;
}
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: []}
};
drawMesh(gl, camera) {
if (!this._bufferInfoReady) {
this._updateInfoBuffer(gl);
}
// Fill buffers with voxels
for (let i = 0; i < voxel_positions.length; ++i) {
let voxel = voxel_positions[i];
const uniforms = {
u_fillColour: Vector3.create(1.0, 1.0, 1.0),
u_opacity: 1.0,
u_lightWorldPos: camera.getCameraPosition(0.785398, 0),
u_worldInverseTranspose: camera.getWorldInverseTranspose(),
u_worldViewProjection: camera.getWorldViewProjection()
};
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;
const shader = shaderManager.shadedProgram;
gl.useProgram(shader.program);
twgl.setBuffersAndAttributes(gl, shader, this._bufferInfo);
twgl.setUniforms(shader, uniforms);
gl.drawElements(gl.TRIANGLES, this._bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
}
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);
drawBounds(gl, camera) {
for (const t of this.triangles) {
t.drawBounds(gl, camera);
}
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

28
src/primitives.js Normal file
View File

@ -0,0 +1,28 @@
const twgl = require('twgl.js');
const { v3: Vector3 } = require('twgl.js');
const { nodeModuleNameResolver } = require('typescript');
function getBoxFromCorners(a, b) {
const centre = Vector3.add(a, b).mulScalar(0.5);
const extents = Vector3.create(
Math.abs(a[0] - b[0]) / 2,
Math.abs(a[1] - b[1]) / 2,
Math.abs(a[2] - b[2]) / 2
);
return getBoxFromCentreExtents(centre, extents);
}
function getBoxFromCentreExtents(centre, extents) {
let cube = twgl.primitives.createCubeVertices(1);
// Translate cube to voxel's position
for (let i = 0; i < 72; i += 3) {
cube.position[i + 0] = (cube.position[i + 0] * extents[0]) + centre[0];
cube.position[i + 1] = (cube.position[i + 1] * extents[1]) + centre[1];
cube.position[i + 2] = (cube.position[i + 2] * extents[2]) + centre[2];
}
return cube;
}
module.exports.getBoxFromCorners = getBoxFromCorners;
module.exports.getBoxFromCentreExtents = getBoxFromCentreExtents;

View File

@ -1,86 +1,39 @@
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 { v3: Vector3 } = require('twgl.js');
const { ArcballCamera } = require('./src/camera.js');
const { VoxelRenderer } = require('./src/voxel_renderer.js');
const { Triangle } = require('./src/triangle.js');
const wavefrontObjParser = require('wavefront-obj-parser');
const expandVertexData = require('expand-vertex-data');
const { Mesh } = require('./src/mesh.js');
const v3 = twgl.v3;
const eventManager = require('./src/events.js');
const { AABB } = require('./src/aabb.js');
const gl = document.querySelector("#c").getContext("webgl");
const camera = new ArcballCamera(30, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 30.0);
const voxelSize = Math.pow(2, 2);
const voxelRenderer = new VoxelRenderer(gl, voxelSize * 2);
let suzanne_left = new Mesh('./resources/suzanne_left.obj', gl);
let suzanne_right = new Mesh('./resources/suzanne_right.obj', gl);
eventManager.registerCanvasEvents(gl, camera);
const v0 = Vector3.create(3.9292, -7.96029, 9.93253);
const v1 = Vector3.create(28.6307, 27.95673, 15.75666);
const v2 = Vector3.create(2.53923, 0, -20.05832);
const triangle = new Triangle(v0, v1, v2, voxelSize);
suzanne_left.voxelise(0.025, gl);
const suzanne_left_buffers = suzanne_left.buffers;
const suzanne_right_buffers = suzanne_right.buffers;
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);
/*
const voxels = triangle.voxelise();
for (const v of voxels) {
voxelRenderer.addVoxel(v[0], v[1], v[2]);
}
*/
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);
}
//console.log(voxels);
//console.log(`There are ${voxels.length} voxels`);
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);
}
const aabb = new AABB(Vector3.create(0, 0, 0), 16.0);
function render(time) {
@ -90,28 +43,17 @@ function render(time) {
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.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));
}
for (const buffer_info of suzanne_left_buffers) {
drawModel(buffer_info, v3.create(0.0, 0.0, 0.0));
}
//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));
//}
//triangle.drawTriangle(gl, camera);
//triangle.drawBounds(gl, camera);
//triangle.drawSubdivisions(gl, camera);
aabb.drawAABB(gl, camera);
requestAnimationFrame(render);
}

View File

@ -1,95 +1,144 @@
const { v3: Vector3 } = require('twgl.js');
const mathUtil = require('./math.js');
const twgl = require('twgl.js');
const shaderManager = require('./shaders.js');
const { AABB } = require('./aabb.js');
const { xAxis, yAxis, zAxis } = require('./math.js');
class Triangle {
constructor(v0, v1, v2) {
constructor(v0, v1, v2, voxelSize) {
this.v0 = v0;
this.v1 = v1;
this.v2 = v2;
const u = Vector3.subtract(v1, v0);
const v = Vector3.subtract(v1, v2);
this.normal = Vector3.normalize(Vector3.cross(u, v));
this.voxelSize = voxelSize;
this._bufferInfoReady = false;
this._aabb = new AABB(this, voxelSize);
}
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),
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),
_createBuffer(gl) {
this._buffer = {
position: [...this.v0, ...this.v1, ...this.v2],
normal: [...this.normal, ...this.normal, ...this.normal],
indices: [1, 0, 2]
};
this._bufferInfo = twgl.createBufferInfoFromArrays(gl, this._buffer);
this._bufferInfoReady = true;
}
voxelise(voxelSize) {
let voxels = [];
_intersectsAABB(aabb) {
const v0 = Vector3.subtract(this.v0, aabb.centre);
const v1 = Vector3.subtract(this.v1, aabb.centre);
const v2 = Vector3.subtract(this.v2, aabb.centre);
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]);
}
}
}
}
const f0 = Vector3.subtract(v1, v0);
const f1 = Vector3.subtract(v2, v1);
const f2 = Vector3.subtract(v0, v2);
return voxels;
}
let 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)
];
voxelIntersect(x, y, z, voxelSize) {
let c = Vector3.create(x, y, z);
let e = Vector3.create(voxelSize/2, voxelSize/2, voxelSize/2);
let v0_ = Vector3.subtract(this.v0, c);
let v1_ = Vector3.subtract(this.v1, c);
let v2_ = Vector3.subtract(this.v2, c);
let f0 = Vector3.subtract(v1_, v0_);
let f1 = Vector3.subtract(v2_, v1_);
let f2 = Vector3.subtract(v0_, v2_);
let a0 = Vector3.cross(f0, f2);
if (this.testAxis(v0_, v1_, v2_, a0, e)) {
return false;
}
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, aabb.size)) {
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, e) {
const p0 = Vector3.dot(v0, axis);
const p1 = Vector3.dot(v1, axis);
const 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));
const r = e * (Math.abs(Vector3.dot(xAxis, axis)) +
Math.abs(Vector3.dot(yAxis, axis)) +
Math.abs(Vector3.dot(zAxis, axis)));
return (Math.min(p0, p1, p2) > r || Math.max(p0, p1, p2) < -r);
return Math.min(p0, p1, 2) > r || Math.max(p0, p1, p2) < -r;
}
drawTriangle(gl, camera) {
if (!this._bufferInfoReady) {
this._createBuffer(gl);
}
const uniforms = {
u_fillColour: Vector3.create(0.0, 1.0, 0.0),
u_opacity: 1.0,
u_lightWorldPos: camera.getCameraPosition(0.785398, 0),
u_worldInverseTranspose: camera.getWorldInverseTranspose(),
u_worldViewProjection: camera.getWorldViewProjection()
};
const shader = shaderManager.unshadedProgram;
gl.useProgram(shader.program);
twgl.setBuffersAndAttributes(gl, shader, this._bufferInfo);
twgl.setUniforms(shader, uniforms);
gl.drawElements(gl.TRIANGLES, this._bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
}
drawBounds(gl, camera) {
this._aabb.drawAABB(gl, camera);
}
drawSubdivisions(gl, camera) {
let queue = [this._aabb];
while (queue.length > 0) {
const aabb = queue.shift();
if (this._intersectsAABB(aabb)) {
const subdivisions = aabb.subdivide(this.voxelSize);
if (subdivisions) {
queue.push(...subdivisions);
} else {
aabb.drawAABB(gl, camera, Vector3.create(0.0, 1.0, 0.0));
}
} else {
aabb.drawAABB(gl, camera, Vector3.create(1.0, 0.0, 0.0));
}
}
}
voxelise() {
let voxels = [];
let queue = [this._aabb];
while (queue.length > 0) {
const aabb = queue.shift();
if (this._intersectsAABB(aabb)) {
const subdivisions = aabb.subdivide(this.voxelSize);
if (subdivisions) {
queue.push(...subdivisions);
} else {
voxels.push(aabb.centre);
}
}
}
return voxels;
}
}

80
src/voxel_renderer.js Normal file
View File

@ -0,0 +1,80 @@
const twgl = require('twgl.js');
const { v3: Vector3 } = require('twgl.js');
const shaderManager = require('./shaders.js');
const primitivesManager = require('./primitives.js');
class VoxelRenderer {
constructor(gl, voxelSize) {
this._gl = gl;
this._voxelSize = voxelSize;
this._voxels = [];
this._buffer = {
position: [],
normal: [],
indices: []
};
this._bufferInfoReady = false;
}
addVoxel(x, y, z) {
const indexOffset = this._voxels.length * 24;
/*
let cube = twgl.primitives.createCubeVertices(this._voxelSize);
// Translate cube to voxel's position
for (let i = 0; i < 72; i += 3) {
cube.position[i + 0] += x;
cube.position[i + 1] += y;
cube.position[i + 2] += z;
}
*/
const centre = Vector3.create(x, y, z);
const extents = Vector3.create(this._voxelSize, this._voxelSize, this._voxelSize);
let cube = primitivesManager.getBoxFromCentreExtents(centre, extents);
// Offset indices
cube.indices = cube.indices.map(x => x + indexOffset);
this._voxels.push([x, y, z]);
this._buffer.position.push(...cube.position);
this._buffer.normal.push(...cube.normal);
this._buffer.indices.push(...cube.indices);
this._bufferInfoReady = false;
}
_updateInfoBuffer() {
this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, this._buffer);
this._bufferInfoReady = true;
}
drawVoxels(camera) {
if (this._voxels.length == 0) {
return;
}
if (!this._bufferInfoReady) {
this._updateInfoBuffer();
}
const uniforms = {
u_fillColour: Vector3.create(1.0, 1.0, 1.0),
u_opacity: 0.1,
u_lightWorldPos: camera.getCameraPosition(0.785398, 0),
u_worldInverseTranspose: camera.getWorldInverseTranspose(),
u_worldViewProjection: camera.getWorldViewProjection()
};
const shader = shaderManager.shadedProgram;
this._gl.useProgram(shader.program);
twgl.setBuffersAndAttributes(this._gl, shader, this._bufferInfo);
twgl.setUniforms(shader, uniforms);
this._gl.drawElements(this._gl.TRIANGLES, this._bufferInfo.numElements, this._gl.UNSIGNED_SHORT, 0);
}
}
module.exports.VoxelRenderer = VoxelRenderer;