mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2025-03-07 14:06:41 +08:00
Rewrite
This commit is contained in:
parent
2d8d8b0ec5
commit
4a0b99c560
@ -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
|
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
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
122
src/aabb.js
Normal 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;
|
@ -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
1
src/constants.js
Normal file
@ -0,0 +1 @@
|
||||
const voxelSize = 1.0;
|
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;
|
45
src/math.js
45
src/math.js
@ -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;
|
104
src/mesh.js
104
src/mesh.js
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
28
src/primitives.js
Normal file
28
src/primitives.js
Normal 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;
|
112
src/renderer.js
112
src/renderer.js
@ -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);
|
||||
}
|
||||
|
185
src/triangle.js
185
src/triangle.js
@ -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
80
src/voxel_renderer.js
Normal 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;
|
Loading…
Reference in New Issue
Block a user