mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2024-12-21 03:09:14 +08:00
Voxeliser rewrite, fixes #5
This commit is contained in:
commit
84043f8de4
49
src/aabb.ts
49
src/aabb.ts
@ -1,49 +0,0 @@
|
||||
import { Vector3 } from "./vector";
|
||||
|
||||
|
||||
export class AABB {
|
||||
|
||||
public readonly centre: Vector3;
|
||||
public readonly size: Vector3;
|
||||
public readonly a: Vector3;
|
||||
public readonly b: Vector3
|
||||
|
||||
constructor(centre: Vector3, size: Vector3) {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class CubeAABB extends AABB {
|
||||
|
||||
readonly width: number;
|
||||
|
||||
constructor(centre: Vector3, width: number) {
|
||||
const sizeVector = new Vector3(width, width, width);
|
||||
super(centre, sizeVector);
|
||||
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public 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),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -110,7 +110,7 @@ export class AppContext {
|
||||
}
|
||||
|
||||
private _voxelise(): ReturnStatus {
|
||||
const newVoxelSize = $("#inputVoxelSize").prop('value');
|
||||
const newVoxelSize: number = parseFloat($("#inputVoxelSize").prop('value'));
|
||||
if (newVoxelSize < 0.001) {
|
||||
return { message: "Voxel size must be at least 0.001", style: ToastStyle.Failure };
|
||||
}
|
||||
@ -118,7 +118,6 @@ export class AppContext {
|
||||
|
||||
try {
|
||||
const voxelManager = VoxelManager.Get;
|
||||
voxelManager.clear();
|
||||
voxelManager.setVoxelSize(this._voxelSize);
|
||||
voxelManager.voxeliseMesh(this._loadedMesh!);
|
||||
|
||||
|
@ -167,7 +167,7 @@ export class SegmentedBuffer {
|
||||
}
|
||||
|
||||
if (this._willOverflow(data)) {
|
||||
console.log("Cycling buffer...");
|
||||
//console.log("Cycling buffer...");
|
||||
this._cycle();
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ export class BottomlessBuffer {
|
||||
}
|
||||
|
||||
if (this._willOverflow(data)) {
|
||||
console.log("Cycling buffer...");
|
||||
//console.log("Cycling buffer...");
|
||||
this._cycle();
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,19 @@ export class HashMap<K extends Hashable, V> {
|
||||
}
|
||||
}
|
||||
|
||||
public has(item: K): boolean {
|
||||
const binIndex = this._getBin(item);
|
||||
|
||||
const list = this._bins[binIndex];
|
||||
for (const {key, value} of list) {
|
||||
if (item.equals(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public add(key: K, value: V) {
|
||||
const binIndex = this._getBin(key);
|
||||
this._bins[binIndex].push({key, value});
|
||||
|
12
src/math.ts
12
src/math.ts
@ -21,6 +21,18 @@ export const clamp = (value: number, min: number, max: number) => {
|
||||
return Math.max(Math.min(max, value), min);
|
||||
}
|
||||
|
||||
export const floorToNearest = (value: number, base: number) => {
|
||||
return Math.floor(value / base) * base;
|
||||
}
|
||||
|
||||
export const ceilToNearest = (value: number, base: number) => {
|
||||
return Math.ceil(value / base) * base;
|
||||
}
|
||||
|
||||
export const roundToNearest = (value: number, base: number) => {
|
||||
return Math.round(value / base) * base;
|
||||
}
|
||||
|
||||
export const triangleArea = (a: number, b: number, c: number) => {
|
||||
const p = (a + b + c) / 2;
|
||||
return Math.sqrt(p * (p - a) * (p - b) * (p - c));
|
||||
|
16
src/mesh.ts
16
src/mesh.ts
@ -357,7 +357,6 @@ export class Mesh {
|
||||
face.v0.position = Vector3.sub(face.v0.position, centre);
|
||||
face.v1.position = Vector3.sub(face.v1.position, centre);
|
||||
face.v2.position = Vector3.sub(face.v2.position, centre);
|
||||
face.updateAABB();
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -373,13 +372,13 @@ export class Mesh {
|
||||
|
||||
this.materials.forEach(material => {
|
||||
material.faces.forEach(face => {
|
||||
const aabb = face.getAABB();
|
||||
a.x = Math.min(a.x, aabb.a.x);
|
||||
a.y = Math.min(a.y, aabb.a.y);
|
||||
a.z = Math.min(a.z, aabb.a.z);
|
||||
b.x = Math.max(b.x, aabb.b.x);
|
||||
b.y = Math.max(b.y, aabb.b.y);
|
||||
b.z = Math.max(b.z, aabb.b.z);
|
||||
const aabb = face.getBounds();
|
||||
a.x = Math.min(a.x, aabb.minX);
|
||||
a.y = Math.min(a.y, aabb.minY);
|
||||
a.z = Math.min(a.z, aabb.minZ);
|
||||
b.x = Math.max(b.x, aabb.maxX);
|
||||
b.y = Math.max(b.y, aabb.maxY);
|
||||
b.z = Math.max(b.z, aabb.maxZ);
|
||||
});
|
||||
});
|
||||
|
||||
@ -395,7 +394,6 @@ export class Mesh {
|
||||
face.v0.position = Vector3.mulScalar(face.v0.position, scaleFactor);
|
||||
face.v1.position = Vector3.mulScalar(face.v1.position, scaleFactor);
|
||||
face.v2.position = Vector3.mulScalar(face.v2.position, scaleFactor);
|
||||
face.updateAABB();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
103
src/ray.ts
Normal file
103
src/ray.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import { Vector3 } from "./vector";
|
||||
import { Triangle } from "./triangle";
|
||||
import { floorToNearest, ceilToNearest, xAxis, yAxis, zAxis } from "./math";
|
||||
import { VoxelManager } from "./voxel_manager";
|
||||
import { Bounds } from "./util";
|
||||
|
||||
const EPSILON = 0.0000001;
|
||||
|
||||
export enum Axes {
|
||||
x, y, z
|
||||
}
|
||||
|
||||
interface Ray {
|
||||
origin: Vector3,
|
||||
direction: Vector3,
|
||||
axis: Axes
|
||||
}
|
||||
|
||||
export function generateRays(v0: Vector3, v1: Vector3, v2: Vector3): Array<Ray> {
|
||||
const bounds: Bounds = {
|
||||
minX: Math.floor(Math.min(v0.x, v1.x, v2.x)),
|
||||
minY: Math.floor(Math.min(v0.y, v1.y, v2.y)),
|
||||
minZ: Math.floor(Math.min(v0.z, v1.z, v2.z)),
|
||||
maxX: Math.ceil(Math.max(v0.x, v1.x, v2.x)),
|
||||
maxY: Math.ceil(Math.max(v0.y, v1.y, v2.y)),
|
||||
maxZ: Math.ceil(Math.max(v0.z, v1.z, v2.z)),
|
||||
}
|
||||
|
||||
let rayList: Array<Ray> = [];
|
||||
traverseX(rayList, bounds);
|
||||
traverseY(rayList, bounds);
|
||||
traverseZ(rayList, bounds);
|
||||
return rayList;
|
||||
}
|
||||
|
||||
function traverseX(rayList: Array<Ray>, bounds: Bounds) {
|
||||
for (let y = bounds.minY; y <= bounds.maxY; ++y) {
|
||||
for (let z = bounds.minZ; z <= bounds.maxZ; ++z) {
|
||||
rayList.push({
|
||||
origin: new Vector3(bounds.minX, y, z),
|
||||
direction: new Vector3(1, 0, 0),
|
||||
axis: Axes.x
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function traverseY(rayList: Array<Ray>, bounds: Bounds) {
|
||||
for (let x = bounds.minX; x <= bounds.maxX; ++x) {
|
||||
for (let z = bounds.minZ; z <= bounds.maxZ; ++z) {
|
||||
rayList.push({
|
||||
origin: new Vector3(x, bounds.minY, z),
|
||||
direction: new Vector3(0, 1, 0),
|
||||
axis: Axes.y
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function traverseZ(rayList: Array<Ray>, bounds: Bounds) {
|
||||
for (let x = bounds.minX; x <= bounds.maxX; ++x) {
|
||||
for (let y = bounds.minY; y <= bounds.maxY; ++y) {
|
||||
rayList.push({
|
||||
origin: new Vector3(x, y, bounds.minZ),
|
||||
direction: new Vector3(0, 0, 1),
|
||||
axis: Axes.z
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function rayIntersectTriangle(ray: Ray, v0: Vector3, v1: Vector3, v2: Vector3): (Vector3 | void) {
|
||||
const edge1 = Vector3.sub(v1, v0);
|
||||
const edge2 = Vector3.sub(v2, v0);
|
||||
|
||||
const h = Vector3.cross(ray.direction, edge2);
|
||||
const a = Vector3.dot(edge1, h);
|
||||
|
||||
if (a > -EPSILON && a < EPSILON) {
|
||||
return; // Ray is parallel to triangle
|
||||
}
|
||||
|
||||
const f = 1.0 / a;
|
||||
const s = Vector3.sub(ray.origin, v0);
|
||||
const u = f * Vector3.dot(s, h);
|
||||
|
||||
if (u < 0.0 || u > 1.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const q = Vector3.cross(s, edge1);
|
||||
const v = f * Vector3.dot(ray.direction, q);
|
||||
|
||||
if (v < 0.0 || u + v > 1.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const t = f * Vector3.dot(edge2, q);
|
||||
|
||||
if (t > EPSILON) {
|
||||
return Vector3.add(ray.origin, Vector3.mulScalar(ray.direction, t));
|
||||
}
|
||||
}
|
184
src/renderer.ts
184
src/renderer.ts
@ -13,6 +13,11 @@ import { Triangle } from "./triangle";
|
||||
import { Mesh, FillMaterial, TextureMaterial, MaterialType } from "./mesh";
|
||||
import { FaceInfo } from "./block_atlas";
|
||||
|
||||
/** Recommended, but slower */
|
||||
const ENABLE_AMBIENT_OCCLUSION = true;
|
||||
|
||||
/** Darkens corner even if corner block does not exist, recommended */
|
||||
const AMBIENT_OCCLUSION_OVERRIDE_CORNER = true;
|
||||
|
||||
export class Renderer {
|
||||
|
||||
@ -21,8 +26,7 @@ export class Renderer {
|
||||
private _backgroundColour: RGB = {r: 0.1, g: 0.1, b: 0.1};
|
||||
private _strokeColour: RGB = {r: 1.0, g: 0.0, b: 0.0};
|
||||
private _atlasTexture: WebGLTexture;
|
||||
private _occlusionNeighbours!: Array<Array<Array<Vector3>>>; // Ew
|
||||
//private _mouseManager: MouseManager
|
||||
private _occlusionNeighboursIndices!: Array<Array<Array<number>>>; // Ew
|
||||
|
||||
private _debug: boolean = false;
|
||||
private _compiled: boolean = false;
|
||||
@ -54,12 +58,8 @@ export class Renderer {
|
||||
src: path.join(__dirname, "../resources/blocks.png"),
|
||||
mag: this._gl.NEAREST
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public set strokeColour(colour: RGB) {
|
||||
this._strokeColour = colour;
|
||||
}
|
||||
@ -73,23 +73,83 @@ export class Renderer {
|
||||
this._registerData(data);
|
||||
}
|
||||
|
||||
private _registerVoxel(centre: Vector3, voxelManager: VoxelManager, blockTexcoord: FaceInfo) {
|
||||
let occlusions = new Array<Array<number>>(6);
|
||||
private static _getNeighbourIndex(neighbour: Vector3) {
|
||||
return 9*(neighbour.x+1) + 3*(neighbour.y+1) + (neighbour.z+1);
|
||||
}
|
||||
|
||||
private static _faceNormal = [
|
||||
new Vector3(1, 0, 0), new Vector3(-1, 0, 0),
|
||||
new Vector3(0, 1, 0), new Vector3(0, -1, 0),
|
||||
new Vector3(0, 0, 1), new Vector3(0, 0, -1),
|
||||
]
|
||||
|
||||
private _calculateOcclusions(centre: Vector3) {
|
||||
const voxelManager = VoxelManager.Get;
|
||||
|
||||
// Cache local neighbours
|
||||
const localNeighbourhoodCache = Array<number>(27);
|
||||
for (let i = -1; i <= 1; ++i) {
|
||||
for (let j = -1; j <= 1; ++j) {
|
||||
for (let k = -1; k <= 1; ++k) {
|
||||
const neighbour = new Vector3(i, j, k);
|
||||
const neighbourIndex = Renderer._getNeighbourIndex(neighbour);
|
||||
localNeighbourhoodCache[neighbourIndex] = voxelManager.isVoxelAt(Vector3.add(centre, neighbour)) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let occlusions = new Array<Array<number>>(6);
|
||||
// For each face
|
||||
for (let f = 0; f < 6; ++f) {
|
||||
// For each vertex
|
||||
occlusions[f] = [0, 0, 0, 0];
|
||||
|
||||
for (let v = 0; v < 4; ++v) {
|
||||
// For each occlusion vertex
|
||||
for (let o = 0; o < 3; ++o) {
|
||||
occlusions[f][v] += voxelManager.isVoxelAt(Vector3.add(centre, this._occlusionNeighbours[f][v][o])) ? 1 : 0;
|
||||
// Only compute ambient occlusion if this face is visible
|
||||
const faceNormal = Renderer._faceNormal[f];
|
||||
const faceNeighbourIndex = Renderer._getNeighbourIndex(faceNormal);
|
||||
const faceVisible = localNeighbourhoodCache[faceNeighbourIndex] === 0;
|
||||
|
||||
if (faceVisible) {
|
||||
for (let v = 0; v < 4; ++v) {
|
||||
let numNeighbours = 0;
|
||||
for (let i = 0; i < 2; ++i) {
|
||||
const neighbourIndex = this._occlusionNeighboursIndices[f][v][i];
|
||||
numNeighbours += localNeighbourhoodCache[neighbourIndex];
|
||||
}
|
||||
// If both edge blocks along this vertex exist,
|
||||
// assume corner exists (even if it doesnt)
|
||||
// (This is a stylistic choice)
|
||||
if (numNeighbours == 2 && AMBIENT_OCCLUSION_OVERRIDE_CORNER) {
|
||||
++numNeighbours;
|
||||
} else {
|
||||
const neighbourIndex = this._occlusionNeighboursIndices[f][v][2];
|
||||
numNeighbours += localNeighbourhoodCache[neighbourIndex];
|
||||
}
|
||||
|
||||
// Convert from occlusion denoting the occlusion factor to the
|
||||
// attenuation in light value: 0 -> 1.0, 1 -> 0.8, 2 -> 0.6, 3 -> 0.4
|
||||
occlusions[f][v] = 1.0 - 0.2 * numNeighbours;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert from occlusion denoting the occlusion factor to the
|
||||
// attenuation in light value: 0 -> 1.0, 1 -> 0.8, 2 -> 0.6, 3 -> 0.4
|
||||
occlusions[f] = occlusions[f].map(x => 1.0 - 0.2 * x);
|
||||
}
|
||||
|
||||
return occlusions;
|
||||
}
|
||||
|
||||
private static _getBlankOcclusions() {
|
||||
let blankOcclusions = new Array<Array<number>>(6);
|
||||
for (let f = 0; f < 6; ++f) {
|
||||
blankOcclusions[f] = [1, 1, 1, 1];
|
||||
}
|
||||
return blankOcclusions;
|
||||
}
|
||||
|
||||
private _registerVoxel(centre: Vector3, voxelManager: VoxelManager, blockTexcoord: FaceInfo) {
|
||||
let occlusions: number[][];
|
||||
if (ENABLE_AMBIENT_OCCLUSION) {
|
||||
occlusions = this._calculateOcclusions(centre);
|
||||
} else {
|
||||
occlusions = Renderer._getBlankOcclusions();
|
||||
}
|
||||
|
||||
let data: VoxelData = GeometryTemplates.getBoxBufferData(centre, false);
|
||||
@ -146,12 +206,9 @@ export class Renderer {
|
||||
}
|
||||
|
||||
registerVoxelMesh() {
|
||||
|
||||
this._gl.enable(this._gl.CULL_FACE);
|
||||
|
||||
const voxelManager = VoxelManager.Get;
|
||||
const voxelSize = voxelManager._voxelSize;
|
||||
const sizeVector = new Vector3(1.0, 1.0, 1.0);
|
||||
|
||||
this._atlasSize = voxelManager.blockAtlas._atlasSize;
|
||||
|
||||
@ -161,26 +218,13 @@ export class Renderer {
|
||||
});
|
||||
} else {
|
||||
// Setup arrays for calculating voxel ambient occlusion
|
||||
|
||||
for (let i = 0; i < voxelManager.voxels.length; ++i) {
|
||||
const voxel = voxelManager.voxels[i];
|
||||
//const colour = voxelManager.voxelColours[i];
|
||||
const texcoord = voxelManager.voxelTexcoords[i];
|
||||
this._registerVoxel(voxel.position, voxelManager, texcoord);
|
||||
}
|
||||
/*
|
||||
voxelManager.voxels.forEach((voxel) => {
|
||||
this._registerVoxel(voxel, voxelManager);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
const mesh = voxelManager.buildMesh();
|
||||
for (const box of mesh) {
|
||||
this.registerBox(box.centre, box.size, false);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
clear() {
|
||||
@ -223,15 +267,6 @@ export class Renderer {
|
||||
u_atlasSize: this._atlasSize
|
||||
});
|
||||
|
||||
/*
|
||||
// Draw default register
|
||||
this._drawRegister(this._registerDefault, this._gl.TRIANGLES, shaderManager.shadedProgram, {
|
||||
u_lightWorldPos: this._camera.getCameraPosition(0.0, 0.0),
|
||||
u_worldViewProjection: this._camera.getWorldViewProjection(),
|
||||
u_worldInverseTranspose: this._camera.getWorldInverseTranspose()
|
||||
});
|
||||
*/
|
||||
|
||||
// Draw material registers
|
||||
const camera = ArcballCamera.Get;
|
||||
this._materialBuffers.forEach((materialBuffer) => {
|
||||
@ -262,49 +297,66 @@ export class Renderer {
|
||||
_setupOcclusions() {
|
||||
// TODO: Find some for-loop to clean this up
|
||||
|
||||
this._occlusionNeighbours = [
|
||||
// [Edge, Edge, Corrner]
|
||||
const occlusionNeighbours = [
|
||||
[
|
||||
[new Vector3(1, 1, 0), new Vector3(1, 1, -1), new Vector3(1, 0, -1)],
|
||||
[new Vector3(1, -1, 0), new Vector3(1, -1, -1), new Vector3(1, 0, -1)],
|
||||
[new Vector3(1, 1, 0), new Vector3(1, 1, 1), new Vector3(1, 0, 1)],
|
||||
[new Vector3(1, -1, 0), new Vector3(1, -1, 1), new Vector3(1, 0, 1)]
|
||||
// +X
|
||||
[new Vector3(1, 1, 0), new Vector3(1, 0, -1), new Vector3(1, 1, -1)],
|
||||
[new Vector3(1, -1, 0), new Vector3(1, 0, -1), new Vector3(1, -1, -1)],
|
||||
[new Vector3(1, 1, 0), new Vector3(1, 0, 1), new Vector3(1, 1, 1)],
|
||||
[new Vector3(1, -1, 0), new Vector3(1, 0, 1), new Vector3(1, -1, 1)]
|
||||
],
|
||||
|
||||
[
|
||||
[new Vector3(-1, 1, 0), new Vector3(-1, 1, 1), new Vector3(-1, 0, 1)],
|
||||
[new Vector3(-1, -1, 0), new Vector3(-1, -1, 1), new Vector3(-1, 0, 1)],
|
||||
[new Vector3(-1, 1, 0), new Vector3(-1, 1, -1), new Vector3(-1, 0, -1)],
|
||||
[new Vector3(-1, -1, 0), new Vector3(-1, -1, -1), new Vector3(-1, 0, -1)]
|
||||
// -X
|
||||
[new Vector3(-1, 1, 0), new Vector3(-1, 0, 1), new Vector3(-1, 1, 1)],
|
||||
[new Vector3(-1, -1, 0), new Vector3(-1, 0, 1), new Vector3(-1, -1, 1)],
|
||||
[new Vector3(-1, 1, 0), new Vector3(-1, 0, -1), new Vector3(-1, 1, -1)],
|
||||
[new Vector3(-1, -1, 0), new Vector3(-1, 0, -1), new Vector3(-1, -1, -1) ]
|
||||
],
|
||||
|
||||
[
|
||||
[new Vector3(-1, 1, 0), new Vector3(-1, 1, 1), new Vector3(0, 1, 1)],
|
||||
[new Vector3(-1, 1, 0), new Vector3(-1, 1, -1), new Vector3(0, 1, -1)],
|
||||
[new Vector3(1, 1, 0), new Vector3(1, 1, 1), new Vector3(0, 1, 1)],
|
||||
[new Vector3(1, 1, 0), new Vector3(1, 1, -1), new Vector3(0, 1, -1)]
|
||||
// +Y
|
||||
[new Vector3(-1, 1, 0), new Vector3(0, 1, 1), new Vector3(-1, 1, 1)],
|
||||
[new Vector3(-1, 1, 0), new Vector3(0, 1, -1), new Vector3(-1, 1, -1)],
|
||||
[new Vector3(1, 1, 0), new Vector3(0, 1, 1), new Vector3(1, 1, 1)],
|
||||
[new Vector3(1, 1, 0), new Vector3(0, 1, -1), new Vector3(1, 1, -1)]
|
||||
],
|
||||
|
||||
[
|
||||
[new Vector3(-1, -1, 0), new Vector3(-1, -1, -1), new Vector3(0, -1, -1)],
|
||||
[new Vector3(-1, -1, 0), new Vector3(-1, -1, 1), new Vector3(0, -1, 1)],
|
||||
[new Vector3(1, -1, 0), new Vector3(1, -1, -1), new Vector3(0, -1, -1)],
|
||||
[new Vector3(1, -1, 0), new Vector3(1, -1, 1), new Vector3(0, -1, 1)]
|
||||
// -Y
|
||||
[new Vector3(-1, -1, 0), new Vector3(0, -1, -1), new Vector3(-1, -1, -1)],
|
||||
[new Vector3(-1, -1, 0), new Vector3(0, -1, 1), new Vector3(-1, -1, 1)],
|
||||
[new Vector3(1, -1, 0), new Vector3(0, -1, -1), new Vector3(1, -1, -1)],
|
||||
[new Vector3(1, -1, 0), new Vector3(0, -1, 1), new Vector3(1, -1, 1)]
|
||||
],
|
||||
|
||||
[
|
||||
[new Vector3(0, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 0, 1)],
|
||||
[new Vector3(0, -1, 1), new Vector3(1, -1, 1), new Vector3(1, 0, 1)],
|
||||
[new Vector3(0, 1, 1), new Vector3(-1, 1, 1), new Vector3(-1, 0, 1)],
|
||||
[new Vector3(0, -1, 1), new Vector3(-1, -1, 1), new Vector3(-1, 0, 1)]
|
||||
// + Z
|
||||
[new Vector3(0, 1, 1), new Vector3(1, 0, 1), new Vector3(1, 1, 1)],
|
||||
[new Vector3(0, -1, 1), new Vector3(1, 0, 1), new Vector3(1, -1, 1)],
|
||||
[new Vector3(0, 1, 1), new Vector3(-1, 0, 1), new Vector3(-1, 1, 1)],
|
||||
[new Vector3(0, -1, 1), new Vector3(-1, 0, 1), new Vector3(-1, -1, 1)]
|
||||
],
|
||||
|
||||
[
|
||||
[new Vector3(0, 1, -1), new Vector3(-1, 1, -1), new Vector3(-1, 0, -1)],
|
||||
[new Vector3(0, -1, -1), new Vector3(-1, -1, -1), new Vector3(-1, 0, -1)],
|
||||
[new Vector3(0, 1, -1), new Vector3(1, 1, -1), new Vector3(1, 0, -1)],
|
||||
[new Vector3(0, -1, -1), new Vector3(1, -1, -1), new Vector3(1, 0, -1)]
|
||||
// -Z
|
||||
[new Vector3(0, 1, -1), new Vector3(-1, 0, -1), new Vector3(-1, 1, -1)],
|
||||
[new Vector3(0, -1, -1), new Vector3(-1, 0, -1), new Vector3(-1, -1, -1)],
|
||||
[new Vector3(0, 1, -1), new Vector3(1, 0, -1), new Vector3(1, 1, -1)],
|
||||
[new Vector3(0, -1, -1), new Vector3(1, 0, -1), new Vector3(1, -1, -1)]
|
||||
]
|
||||
]
|
||||
|
||||
this._occlusionNeighboursIndices = new Array<Array<Array<number>>>();
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
let row = new Array<Array<number>>();
|
||||
for (let j = 0; j < 4; ++j) {
|
||||
row.push(occlusionNeighbours[i][j].map(x => Renderer._getNeighbourIndex(x)));
|
||||
}
|
||||
this._occlusionNeighboursIndices.push(row);
|
||||
}
|
||||
console.log(this._occlusionNeighboursIndices);
|
||||
}
|
||||
|
||||
_registerData(data: VoxelData) {
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { Vector3 } from "./vector";
|
||||
import { AABB } from "./aabb";
|
||||
import { xAxis, yAxis, zAxis, fastCrossXAxis, fastCrossYAxis, fastCrossZAxis } from "./math";
|
||||
import { UV } from "./util";
|
||||
import { Vertex } from "./mesh";
|
||||
import { Bounds } from "./util";
|
||||
|
||||
|
||||
export class Triangle {
|
||||
@ -11,7 +9,6 @@ export class Triangle {
|
||||
public v1: Vertex;
|
||||
public v2: Vertex;
|
||||
public readonly normal: Vector3;
|
||||
private aabb?: AABB;
|
||||
|
||||
constructor(v0: Vertex, v1: Vertex, v2: Vertex) {
|
||||
this.v0 = v0;
|
||||
@ -21,88 +18,21 @@ export class Triangle {
|
||||
const f0 = Vector3.sub(v1.position, v0.position);
|
||||
const f1 = Vector3.sub(v0.position, v2.position);
|
||||
this.normal = Vector3.cross(f0, f1).normalise();
|
||||
|
||||
this.updateAABB();
|
||||
}
|
||||
|
||||
public updateAABB() {
|
||||
const a = new Vector3(
|
||||
Math.min(this.v0.position.x, this.v1.position.x, this.v2.position.x),
|
||||
Math.min(this.v0.position.y, this.v1.position.y, this.v2.position.y),
|
||||
Math.min(this.v0.position.z, this.v1.position.z, this.v2.position.z)
|
||||
);
|
||||
|
||||
const b = new Vector3(
|
||||
Math.max(this.v0.position.x, this.v1.position.x, this.v2.position.x),
|
||||
Math.max(this.v0.position.y, this.v1.position.y, this.v2.position.y),
|
||||
Math.max(this.v0.position.z, this.v1.position.z, this.v2.position.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);
|
||||
}
|
||||
|
||||
public getAABB() {
|
||||
return this.aabb!;
|
||||
}
|
||||
|
||||
public insideAABB(aabb: AABB) {
|
||||
return Vector3.lessThanEqualTo(aabb.a, this.aabb!.a) && Vector3.lessThanEqualTo(this.aabb!.b, aabb.b);
|
||||
}
|
||||
|
||||
public intersectAABB(aabb: AABB) {
|
||||
const extents = Vector3.mulScalar(aabb.size, 0.5);
|
||||
|
||||
const v0 = Vector3.sub(this.v0.position, aabb.centre);
|
||||
const v1 = Vector3.sub(this.v1.position, aabb.centre);
|
||||
const v2 = Vector3.sub(this.v2.position, aabb.centre);
|
||||
|
||||
const f0 = Vector3.sub(v1, v0);
|
||||
const f1 = Vector3.sub(v2, v1);
|
||||
const f2 = Vector3.sub(v0, v2);
|
||||
|
||||
const axis = [
|
||||
Vector3.cross(f0, f2),
|
||||
xAxis,
|
||||
yAxis,
|
||||
zAxis,
|
||||
fastCrossXAxis(f0),
|
||||
fastCrossXAxis(f1),
|
||||
fastCrossXAxis(f2),
|
||||
fastCrossYAxis(f0),
|
||||
fastCrossYAxis(f1),
|
||||
fastCrossYAxis(f2),
|
||||
fastCrossZAxis(f0),
|
||||
fastCrossZAxis(f1),
|
||||
fastCrossZAxis(f2)
|
||||
];
|
||||
|
||||
for (const ax of axis) {
|
||||
if (this._testAxis(v0, v1, v2, ax, extents)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public getCentre(): Vector3 {
|
||||
return Vector3.divScalar(Vector3.add(Vector3.add(this.v0.position, this.v1.position), this.v2.position), 3.0);
|
||||
}
|
||||
|
||||
|
||||
private _testAxis(v0: Vector3, v1: Vector3, v2: Vector3, axis: Vector3, extents: Vector3) {
|
||||
let p0 = Vector3.dot(v0, axis);
|
||||
let p1 = Vector3.dot(v1, axis);
|
||||
let p2 = Vector3.dot(v2, 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);
|
||||
public getBounds(): Bounds {
|
||||
return {
|
||||
minX: Math.min(this.v0.position.x, this.v1.position.x, this.v2.position.x),
|
||||
minY: Math.min(this.v0.position.y, this.v1.position.y, this.v2.position.y),
|
||||
minZ: Math.min(this.v0.position.z, this.v1.position.z, this.v2.position.z),
|
||||
maxX: Math.max(this.v0.position.x, this.v1.position.x, this.v2.position.x),
|
||||
maxY: Math.max(this.v0.position.y, this.v1.position.y, this.v2.position.y),
|
||||
maxZ: Math.max(this.v0.position.z, this.v1.position.z, this.v2.position.z),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -27,4 +27,13 @@ export interface RGBA {
|
||||
g: number,
|
||||
b: number,
|
||||
a: number
|
||||
}
|
||||
|
||||
export interface Bounds {
|
||||
minX: number,
|
||||
minY: number,
|
||||
minZ: number,
|
||||
maxX: number,
|
||||
maxY: number,
|
||||
maxZ: number,
|
||||
}
|
7
src/vendor/bootstrap.bundle.min.js
vendored
7
src/vendor/bootstrap.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
8
src/vendor/bootstrap.css
vendored
8
src/vendor/bootstrap.css
vendored
File diff suppressed because one or more lines are too long
7
src/vendor/bootstrap.min.js
vendored
7
src/vendor/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
5
src/vendor/popper.min.js
vendored
5
src/vendor/popper.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,4 +1,3 @@
|
||||
import { CubeAABB } from "./aabb";
|
||||
import { Vector3 } from "./vector.js";
|
||||
import { HashMap } from "./hash_map";
|
||||
import { Texture } from "./texture";
|
||||
@ -7,6 +6,7 @@ import { RGB } from "./util";
|
||||
import { Triangle } from "./triangle";
|
||||
import { Mesh, MaterialType } from "./mesh";
|
||||
import { triangleArea } from "./math";
|
||||
import { Axes, generateRays, rayIntersectTriangle } from "./ray";
|
||||
|
||||
interface Block {
|
||||
position: Vector3;
|
||||
@ -15,16 +15,10 @@ interface Block {
|
||||
block?: string
|
||||
}
|
||||
|
||||
interface TriangleCubeAABBs {
|
||||
triangle: Triangle;
|
||||
AABBs: Array<CubeAABB>;
|
||||
}
|
||||
|
||||
export class VoxelManager {
|
||||
|
||||
public voxels: Array<Block>;
|
||||
public voxelTexcoords: Array<FaceInfo>;
|
||||
public triangleAABBs: Array<TriangleCubeAABBs>;
|
||||
public _voxelSize: number;
|
||||
|
||||
private voxelsHash: HashMap<Vector3, Block>;
|
||||
@ -47,7 +41,6 @@ export class VoxelManager {
|
||||
this._voxelSize = voxelSize;
|
||||
this.voxels = [];
|
||||
this.voxelTexcoords = [];
|
||||
this.triangleAABBs = [];
|
||||
|
||||
this.voxelsHash = new HashMap(2048);
|
||||
this.blockAtlas = new BlockAtlas();
|
||||
@ -69,45 +62,8 @@ export class VoxelManager {
|
||||
this.voxelsHash = new HashMap(2048);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.triangleAABBs = [];
|
||||
this._clearVoxels();
|
||||
}
|
||||
|
||||
private _snapToVoxelGrid(vec: Vector3) {
|
||||
return vec.copy().divScalar(this._voxelSize).round().mulScalar(this._voxelSize);
|
||||
}
|
||||
|
||||
private _getTriangleCubeAABB(triangle: Triangle) {
|
||||
const triangleAABB = triangle.getAABB();
|
||||
const gridSnappedCentre = this._snapToVoxelGrid(triangleAABB.centre);
|
||||
|
||||
let cubeAABB = new CubeAABB(gridSnappedCentre, this._voxelSize);
|
||||
while (!triangle.insideAABB(cubeAABB)) {
|
||||
cubeAABB = new CubeAABB(cubeAABB.centre, cubeAABB.width * 2.0);
|
||||
}
|
||||
|
||||
return cubeAABB;
|
||||
}
|
||||
|
||||
private _toGridPosition(vec: Vector3) {
|
||||
return new Vector3(
|
||||
Math.round(vec.x / this._voxelSize),
|
||||
Math.round(vec.y / this._voxelSize),
|
||||
Math.round(vec.z / this._voxelSize)
|
||||
);
|
||||
}
|
||||
|
||||
private _toModelPosition(vec: Vector3) {
|
||||
return new Vector3(
|
||||
vec.x * this._voxelSize,
|
||||
vec.y * this._voxelSize,
|
||||
vec.z * this._voxelSize
|
||||
);
|
||||
}
|
||||
|
||||
public isVoxelAt(pos: Vector3) {
|
||||
return this.voxelsHash.get(pos) !== undefined;
|
||||
return this.voxelsHash.has(pos);
|
||||
}
|
||||
|
||||
public assignBlocks() {
|
||||
@ -139,20 +95,7 @@ export class VoxelManager {
|
||||
|
||||
}
|
||||
|
||||
public addVoxel(vec: Vector3, block: BlockInfo) {
|
||||
|
||||
// (0.5, 0.5, 0.5) -> (0, 0, 0);
|
||||
vec = Vector3.subScalar(vec, this._voxelSize / 2);
|
||||
const pos = this._toGridPosition(vec);
|
||||
|
||||
// [HACK] FIXME: Fix misaligned voxels
|
||||
// Some vec data is not not grid-snapped to voxelSize-spacing
|
||||
const test = Vector3.divScalar(vec, this._voxelSize);
|
||||
if ((test.x % 1 < 0.9 && test.x % 1 > 0.1) || (test.y % 1 < 0.9 && test.y % 1 > 0.1) || (test.z % 1 < 0.9 && test.z % 1 > 0.1)) {
|
||||
console.warn("Misaligned voxel, skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
public addVoxel(pos: Vector3, block: BlockInfo) {
|
||||
// Is there already a voxel in this position?
|
||||
let voxel = this.voxelsHash.get(pos);
|
||||
if (voxel !== undefined) {
|
||||
@ -199,39 +142,43 @@ export class VoxelManager {
|
||||
}
|
||||
|
||||
public voxeliseTriangle(triangle: Triangle) {
|
||||
const cubeAABB = this._getTriangleCubeAABB(triangle);
|
||||
|
||||
const triangleAABBs = [];
|
||||
const voxelSize = VoxelManager.Get._voxelSize;
|
||||
const v0Scaled = Vector3.divScalar(triangle.v0.position, voxelSize);
|
||||
const v1Scaled = Vector3.divScalar(triangle.v1.position, voxelSize);
|
||||
const v2Scaled = Vector3.divScalar(triangle.v2.position, voxelSize);
|
||||
|
||||
let queue = [cubeAABB];
|
||||
while (true) {
|
||||
const aabb = queue.shift();
|
||||
if (!aabb) {
|
||||
break;
|
||||
}
|
||||
if (triangle.intersectAABB(aabb)) {
|
||||
if (aabb.width > this._voxelSize) {
|
||||
// Continue to subdivide
|
||||
queue.push(...aabb.subdivide());
|
||||
} else {
|
||||
// We've reached the voxel level, stop
|
||||
const voxelColour = this._getVoxelColour(triangle, aabb.centre);
|
||||
const block = this.blockAtlas.getBlock(voxelColour);
|
||||
const rayList = generateRays(v0Scaled, v1Scaled, v2Scaled);
|
||||
|
||||
this.addVoxel(aabb.centre, block);
|
||||
triangleAABBs.push(aabb);
|
||||
rayList.forEach(ray => {
|
||||
const intersection = rayIntersectTriangle(ray, v0Scaled, v1Scaled, v2Scaled);
|
||||
if (intersection) {
|
||||
let voxelPosition: Vector3;
|
||||
switch (ray.axis) {
|
||||
case Axes.x:
|
||||
voxelPosition = new Vector3(Math.round(intersection.x), intersection.y, intersection.z);
|
||||
break;
|
||||
case Axes.y:
|
||||
voxelPosition = new Vector3(intersection.x, Math.round(intersection.y), intersection.z);
|
||||
break;
|
||||
case Axes.z:
|
||||
voxelPosition = new Vector3(intersection.x, intersection.y, Math.round(intersection.z));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.triangleAABBs.push({triangle: triangle, AABBs: triangleAABBs});
|
||||
const voxelColour = this._getVoxelColour(triangle, Vector3.mulScalar(voxelPosition, voxelSize));
|
||||
const block = this.blockAtlas.getBlock(voxelColour);
|
||||
|
||||
this.addVoxel(voxelPosition, block);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
voxeliseMesh(mesh: Mesh) {
|
||||
this._clearVoxels();
|
||||
|
||||
mesh.materials.forEach(material => {
|
||||
// Setup material
|
||||
console.log("VOXELISING", material.name);
|
||||
|
||||
if (material.materialData?.type === MaterialType.Texture) {
|
||||
this._blockMode = MaterialType.Texture;
|
||||
this._currentTexture = new Texture(material.materialData.texturePath, material.materialData.format);
|
||||
|
Loading…
Reference in New Issue
Block a user