Added linting

This commit is contained in:
Lucas Dower 2022-01-16 23:47:19 +00:00
parent 14d2311820
commit a1065572a8
24 changed files with 676 additions and 676 deletions

28
.eslintrc.json Normal file
View File

@ -0,0 +1,28 @@
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true
},
"extends": [
"google"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"linebreak-style": "off",
"object-curly-spacing": "off",
"max-len": "off",
"require-jsdoc": "off",
"indent": ["error", 4],
"no-multi-spaces": "off",
"no-array-constructor": "off",
"guard-for-in": "off",
"func-call-spacing": "off"
}
}

View File

@ -7,7 +7,8 @@
"node": ">=14.0.0"
},
"scripts": {
"build": "tsc",
"lint": "eslint --fix ./src/*.ts",
"build": "npm run lint && tsc",
"start": "npm run build && electron ./dist/main.js --enable-logging",
"atlas": "npx ts-node tools/build-atlas.ts",
"export": "electron-packager . ObjToSchematic --platform=win32 --arch=x64"
@ -26,10 +27,13 @@
"@types/jquery": "^3.5.6",
"@types/pngjs": "^6.0.1",
"@types/prompt": "^1.1.2",
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"adm-zip": "^0.5.9",
"chalk": "^4.1.2",
"electron": "^13.1.4",
"electron-packager": "^15.2.0",
"eslint": "^8.7.0",
"images": "^3.2.3",
"prompt": "^1.2.1",
"ts-node": "^10.1.0",
@ -37,6 +41,7 @@
},
"dependencies": {
"@popperjs/core": "^2.9.3",
"eslint-config-google": "^0.14.0",
"expand-vertex-data": "^1.1.2",
"jpeg-js": "^0.4.3",
"pngjs": "^6.0.0",

View File

@ -1,30 +1,33 @@
import { buildUI, registerUI, Group, Component, setEnabled } from "./ui/layout";
import { Exporter, Litematic, Schematic } from "./schematic";
import { VoxelManager } from "./voxel_manager";
import { Renderer } from "./renderer";
import { Mesh } from "./mesh";
import { buildUI, registerUI, Group, setEnabled } from './ui/layout';
import { Exporter, Litematic, Schematic } from './schematic';
import { VoxelManager } from './voxel_manager';
import { Renderer } from './renderer';
import { Mesh } from './mesh';
import { SliderElement } from "./ui/elements/slider";
import { ComboBoxElement } from "./ui/elements/combobox";
import { FileInputElement } from "./ui/elements/file_input";
import { SliderElement } from './ui/elements/slider';
import { ComboBoxElement } from './ui/elements/combobox';
import { FileInputElement } from './ui/elements/file_input';
import { remote } from "electron";
import fs from "fs";
import { ButtonElement } from "./ui/elements/button";
import { LabelElement } from "./ui/elements/label";
import { OutputElement } from "./ui/elements/output";
import { remote } from 'electron';
import fs from 'fs';
import { ButtonElement } from './ui/elements/button';
import { LabelElement } from './ui/elements/label';
import { OutputElement } from './ui/elements/output';
/* eslint-disable */
export enum ActionReturnType {
Success,
Warning,
Failure
}
/* eslint-enable */
interface ReturnStatus {
message: string,
type: ActionReturnType,
error?: unknown
}
/* eslint-disable */
export enum Action {
Load = 0,
Simplify = 1,
@ -32,6 +35,7 @@ export enum Action {
Palette = 3,
Export = 4,
}
/* eslint-enable */
export class AppContext {
public ambientOcclusion: boolean;
@ -40,7 +44,7 @@ export class AppContext {
private _gl: WebGLRenderingContext;
private _loadedMesh?: Mesh;
private _ui: Group[];
private _actionMap = new Map<Action, (() => ReturnStatus | void)>();
private _actionMap = new Map<Action, () => (ReturnStatus | void)>();
private static _instance: AppContext;
@ -52,98 +56,114 @@ export class AppContext {
this.ambientOcclusion = true;
this.dithering = true;
this._actionMap.set(Action.Load, () => { return this._load(); });
this._actionMap.set(Action.Load, () => {
return this._load();
});
this._actionMap.set(Action.Simplify, () => {});
this._actionMap.set(Action.Voxelise, () => { return this._voxelise(); });
this._actionMap.set(Action.Palette, () => { return this._palette(); } );
this._actionMap.set(Action.Export, () => { return this._export(); });
this._actionMap.set(Action.Voxelise, () => {
return this._voxelise();
});
this._actionMap.set(Action.Palette, () => {
return this._palette();
} );
this._actionMap.set(Action.Export, () => {
return this._export();
});
this._ui = [
{
label: "Input",
label: 'Input',
components: [
{
label: new LabelElement("label1", "Wavefront .obj file"),
type: new FileInputElement("objFile", "obj")
{
label: new LabelElement('label1', 'Wavefront .obj file'),
type: new FileInputElement('objFile', 'obj'),
},
{
label: new LabelElement("label2", "Material .mtl file"),
type: new FileInputElement("mtlFile", "mtl")
{
label: new LabelElement('label2', 'Material .mtl file'),
type: new FileInputElement('mtlFile', 'mtl'),
},
],
submitButton: new ButtonElement("loadMesh", "Load mesh", () => { this.do(Action.Load); }),
output: new OutputElement("output1")
submitButton: new ButtonElement('loadMesh', 'Load mesh', () => {
this.do(Action.Load);
}),
output: new OutputElement('output1'),
},
{
label: "Simplify",
label: 'Simplify',
components: [
{
label: new LabelElement("label3", "Ratio"),
type: new SliderElement("ratio", 0.0, 1.0, 0.01, 0.5) },
label: new LabelElement('label3', 'Ratio'),
type: new SliderElement('ratio', 0.0, 1.0, 0.01, 0.5) },
],
submitButton: new ButtonElement("simplifyMesh", "Simplify mesh", () => { }),
output: new OutputElement("output2")
submitButton: new ButtonElement('simplifyMesh', 'Simplify mesh', () => { }),
output: new OutputElement('output2'),
},
{
label: "Build",
label: 'Build',
components: [
{
label: new LabelElement("label4", "Voxel size"),
type: new SliderElement("voxelSize", 0.01, 0.5, 0.01, 0.1)
label: new LabelElement('label4', 'Voxel size'),
type: new SliderElement('voxelSize', 0.01, 0.5, 0.01, 0.1),
},
{
label: new LabelElement("label5", "Ambient occlusion"),
type: new ComboBoxElement("ambientOcclusion", [
{ id: "on", displayText: "On (recommended)" },
{ id: "off", displayText: "Off (faster)" },
])
label: new LabelElement('label5', 'Ambient occlusion'),
type: new ComboBoxElement('ambientOcclusion', [
{ id: 'on', displayText: 'On (recommended)' },
{ id: 'off', displayText: 'Off (faster)' },
]),
},
],
submitButton: new ButtonElement("voxeliseMesh", "Voxelise mesh", () => { this.do(Action.Voxelise); }),
output: new OutputElement("output3")
submitButton: new ButtonElement('voxeliseMesh', 'Voxelise mesh', () => {
this.do(Action.Voxelise);
}),
output: new OutputElement('output3'),
},
{
label: "Palette",
label: 'Palette',
components: [
{
label: new LabelElement("label6", "Block palette"),
type: new ComboBoxElement("blockPalette", [
{ id: "default", displayText: "Default" }
])
label: new LabelElement('label6', 'Block palette'),
type: new ComboBoxElement('blockPalette', [
{ id: 'default', displayText: 'Default' },
]),
},
{
label: new LabelElement("label7", "Choice method"),
type: new ComboBoxElement("choiceMethod", [
{ id: "euclidian", displayText: "Euclidian distance" }
])
label: new LabelElement('label7', 'Choice method'),
type: new ComboBoxElement('choiceMethod', [
{ id: 'euclidian', displayText: 'Euclidian distance' },
]),
},
{
label: new LabelElement("label8", "Dithering"),
type: new ComboBoxElement("dithering", [
{ id: "on", displayText: "On (recommended)" },
{ id: "off", displayText: "Off" },
])
label: new LabelElement('label8', 'Dithering'),
type: new ComboBoxElement('dithering', [
{ id: 'on', displayText: 'On (recommended)' },
{ id: 'off', displayText: 'Off' },
]),
},
],
submitButton: new ButtonElement("assignBlocks", "Assign blocks", () => { this.do(Action.Palette); }),
output: new OutputElement("output4")
submitButton: new ButtonElement('assignBlocks', 'Assign blocks', () => {
this.do(Action.Palette);
}),
output: new OutputElement('output4'),
},
{
label: "Export",
label: 'Export',
components: [
{
label: new LabelElement("label9", "File format"),
type: new ComboBoxElement("fileFormat",
[
{ id: "litematic", displayText: "Litematic" },
{ id: "schematic", displayText: "Schematic" },
])
{
label: new LabelElement('label9', 'File format'),
type: new ComboBoxElement('fileFormat',
[
{ id: 'litematic', displayText: 'Litematic' },
{ id: 'schematic', displayText: 'Schematic' },
]),
},
],
submitButton: new ButtonElement("exportStructure", "Export structure", () => { this.do(Action.Export); }),
output: new OutputElement("output5")
}
]
submitButton: new ButtonElement('exportStructure', 'Export structure', () => {
this.do(Action.Export);
}),
output: new OutputElement('output5'),
},
];
buildUI(this._ui);
registerUI(this._ui);
@ -152,9 +172,9 @@ export class AppContext {
setEnabled(this._ui[3], false);
setEnabled(this._ui[4], false);
const gl = (<HTMLCanvasElement>document.getElementById('canvas')).getContext("webgl");
const gl = (<HTMLCanvasElement>document.getElementById('canvas')).getContext('webgl');
if (!gl) {
throw Error("Could not load WebGL context");
throw Error('Could not load WebGL context');
}
this._gl = gl;
}
@ -175,21 +195,21 @@ export class AppContext {
setEnabled(this._ui[3], false);
setEnabled(this._ui[4], false);
const objPath = (<FileInputElement>this._ui[0].components[0].type).getValue();
const objPath = (<FileInputElement> this._ui[0].components[0].type).getValue();
if (!fs.existsSync(objPath)) {
return { message: "Selected .obj cannot be found", type: ActionReturnType.Failure };
return { message: 'Selected .obj cannot be found', type: ActionReturnType.Failure };
}
const mtlPath = (<FileInputElement>this._ui[0].components[1].type).getValue();
const mtlPath = (<FileInputElement> this._ui[0].components[1].type).getValue();
if (!fs.existsSync(mtlPath)) {
return { message: "Selected .mtl cannot be found", type: ActionReturnType.Failure };
return { message: 'Selected .mtl cannot be found', type: ActionReturnType.Failure };
}
try {
this._loadedMesh = new Mesh(objPath, this._gl);
this._loadedMesh.loadTextures(this._gl);
} catch (err: unknown) {
return { error: err, message: "Could not load mesh", type: ActionReturnType.Failure };
return { error: err, message: 'Could not load mesh', type: ActionReturnType.Failure };
}
try {
@ -198,20 +218,20 @@ export class AppContext {
renderer.registerMesh(this._loadedMesh);
renderer.compile();
} catch (err: unknown) {
return { message: "Could not render mesh", type: ActionReturnType.Failure };
return { message: 'Could not render mesh', type: ActionReturnType.Failure };
}
setEnabled(this._ui[2], true);
return { message: "Loaded successfully", type: ActionReturnType.Success };
return { message: 'Loaded successfully', type: ActionReturnType.Success };
}
private _voxelise(): ReturnStatus {
setEnabled(this._ui[3], false);
setEnabled(this._ui[4], false);
const voxelSize = (<SliderElement>this._ui[2].components[0].type).getValue();
const ambientOcclusion = (<ComboBoxElement>this._ui[2].components[1].type).getValue();
this.ambientOcclusion = ambientOcclusion === "on";
const voxelSize = (<SliderElement> this._ui[2].components[0].type).getValue();
const ambientOcclusion = (<ComboBoxElement> this._ui[2].components[1].type).getValue();
this.ambientOcclusion = ambientOcclusion === 'on';
try {
const voxelManager = VoxelManager.Get;
@ -223,18 +243,18 @@ export class AppContext {
renderer.registerVoxelMesh();
renderer.compile();
} catch (err: any) {
return { error: err, message: "Could not register voxel mesh", type: ActionReturnType.Failure };
return { error: err, message: 'Could not register voxel mesh', type: ActionReturnType.Failure };
}
setEnabled(this._ui[3], true);
return { message: "Voxelised successfully", type: ActionReturnType.Success };
return { message: 'Voxelised successfully', type: ActionReturnType.Success };
}
private _palette(): ReturnStatus {
setEnabled(this._ui[4], false);
const dithering = (<ComboBoxElement>this._ui[3].components[2].type).getValue();
this.dithering = dithering === "on";
const dithering = (<ComboBoxElement> this._ui[3].components[2].type).getValue();
this.dithering = dithering === 'on';
try {
const voxelManager = VoxelManager.Get;
@ -245,42 +265,42 @@ export class AppContext {
renderer.registerVoxelMesh();
renderer.compile();
} catch (err: any) {
return { error: err, message: "Could not register voxel mesh", type: ActionReturnType.Failure };
return { error: err, message: 'Could not register voxel mesh', type: ActionReturnType.Failure };
}
setEnabled(this._ui[4], true);
return { message: "Blocks assigned successfully", type: ActionReturnType.Success };
return { message: 'Blocks assigned successfully', type: ActionReturnType.Success };
}
private _export(): ReturnStatus {
const exportFormat = (<ComboBoxElement>this._ui[4].components[0].type).getValue();
const exportFormat = (<ComboBoxElement> this._ui[4].components[0].type).getValue();
let exporter: Exporter;
if (exportFormat === "schematic") {
if (exportFormat === 'schematic') {
exporter = new Schematic();
} else {
exporter = new Litematic();
}
const filePath = remote.dialog.showSaveDialogSync({
title: "Save structure",
buttonLabel: "Save",
filters: [exporter.getFormatFilter()]
title: 'Save structure',
buttonLabel: 'Save',
filters: [exporter.getFormatFilter()],
});
if (filePath === undefined) {
return { message: "Output cancelled", type: ActionReturnType.Warning };
return { message: 'Output cancelled', type: ActionReturnType.Warning };
}
try {
exporter.export(filePath);
} catch (err: unknown) {
return { error: err, message: "Failed to export", type: ActionReturnType.Failure };
return { error: err, message: 'Failed to export', type: ActionReturnType.Failure };
}
return { message: "Successfully exported", type: ActionReturnType.Success };
return { message: 'Successfully exported', type: ActionReturnType.Success };
}
public draw() {
Renderer.Get.draw();
}
}
}

View File

@ -1,6 +1,6 @@
import { BlockAtlas, BlockInfo } from "./block_atlas";
import { assert, RGB } from "./util";
import { Vector3 } from "./vector";
import { BlockAtlas, BlockInfo } from './block_atlas';
import { assert, RGB } from './util';
import { Vector3 } from './vector';
interface BlockAssigner {
assignBlock(voxelColour: RGB, voxelPosition: Vector3): BlockInfo;
@ -13,7 +13,6 @@ export class BasicBlockAssigner implements BlockAssigner {
}
export class OrderedDitheringBlockAssigner implements BlockAssigner {
/** 4x4x4 */
private static _size = 4;
private static _threshold = 256 / 8;
@ -26,12 +25,12 @@ export class OrderedDitheringBlockAssigner implements BlockAssigner {
3, 19, 5, 21, 51, 35, 53, 37,
1, 17, 7, 23, 49, 33, 55, 39,
27, 43, 29, 45, 11, 59, 13, 61,
25, 41, 31, 47, 9, 57, 15, 63
25, 41, 31, 47, 9, 57, 15, 63,
];
private _getThresholdValue(x: number, y: number, z: number) {
const size = OrderedDitheringBlockAssigner._size;
assert(0 <= x && x < size && 0 <= y && y < size && 0 <= z && z < size)
assert(0 <= x && x < size && 0 <= y && y < size && 0 <= z && z < size);
const index = (x + (size * y) + (size * size * z));
assert(0 <= index && index < size * size * size);
return (OrderedDitheringBlockAssigner._mapMatrix[index] / (size * size * size)) - 0.5;
@ -42,16 +41,15 @@ export class OrderedDitheringBlockAssigner implements BlockAssigner {
const map = this._getThresholdValue(
Math.abs(voxelPosition.x % size),
Math.abs(voxelPosition.y % size),
Math.abs(voxelPosition.z % size)
Math.abs(voxelPosition.z % size),
);
const newVoxelColour = {
r: ((255 * voxelColour.r) + map * OrderedDitheringBlockAssigner._threshold) / 255,
g: ((255 * voxelColour.g) + map * OrderedDitheringBlockAssigner._threshold) / 255,
b: ((255 * voxelColour.b) + map * OrderedDitheringBlockAssigner._threshold) / 255
b: ((255 * voxelColour.b) + map * OrderedDitheringBlockAssigner._threshold) / 255,
};
return BlockAtlas.Get.getBlock(newVoxelColour);
}
}
}

View File

@ -1,9 +1,8 @@
import { Vector3 } from "./vector";
import { HashMap } from "./hash_map";
import { UV, RGB } from "./util";
import fs from "fs";
import path from "path";
import { Vector3 } from './vector';
import { HashMap } from './hash_map';
import { UV, RGB } from './util';
import fs from 'fs';
import path from 'path';
export interface TextureInfo {
name: string
@ -27,14 +26,14 @@ export interface BlockInfo {
}
// https://minecraft.fandom.com/wiki/Java_Edition_data_values/Pre-flattening/Block_IDs
/* eslint-disable */
export enum Block {
Stone = 1.0,
Dirt = 3.0,
Cobblestone = 4.0
}
/* eslint-enable */
export class BlockAtlas {
private _cachedBlocks: HashMap<Vector3, number>;
private readonly _blocks: Array<BlockInfo>;
public readonly _atlasSize: number;
@ -48,13 +47,13 @@ export class BlockAtlas {
private constructor() {
this._cachedBlocks = new HashMap(1024);
const _path = path.join(__dirname, "../resources/blocks.json");
const blocksString = fs.readFileSync(_path, "utf-8");
const _path = path.join(__dirname, '../resources/blocks.json');
const blocksString = fs.readFileSync(_path, 'utf-8');
if (!blocksString) {
throw Error("Could not load blocks.json")
throw Error('Could not load blocks.json');
}
const json = JSON.parse(blocksString)
const json = JSON.parse(blocksString);
this._atlasSize = json.atlasSize;
this._blocks = json.blocks;
}
@ -63,7 +62,7 @@ export class BlockAtlas {
public getBlock(voxelColour: RGB): BlockInfo {
const voxelColourVector = new Vector3(voxelColour.r, voxelColour.g, voxelColour.b);
let cachedBlockIndex = this._cachedBlocks.get(voxelColourVector);
const cachedBlockIndex = this._cachedBlocks.get(voxelColourVector);
if (cachedBlockIndex) {
return this._blocks[cachedBlockIndex];
}
@ -77,7 +76,7 @@ export class BlockAtlas {
const blockAvgColourVector = new Vector3(
blockAvgColour.r,
blockAvgColour.g,
blockAvgColour.b
blockAvgColour.b,
);
const distance = Vector3.sub(blockAvgColourVector, voxelColourVector).magnitude();
@ -90,5 +89,4 @@ export class BlockAtlas {
this._cachedBlocks.add(voxelColourVector, blockChoiceIndex);
return this._blocks[blockChoiceIndex];
}
}
}

View File

@ -1,8 +1,8 @@
import * as twgl from "twgl.js";
import * as twgl from 'twgl.js';
/*
WebGL buffers store vertex index data as Uint16 and will lead to frequent overflows.
SegmentedBuffer automatically partitions buffers to avoid overflows and removes the
SegmentedBuffer automatically partitions buffers to avoid overflows and removes the
overhead of .push/.concat when adding data
*/
@ -46,16 +46,15 @@ export interface VoxelData {
}
export class SegmentedBuffer {
public WebGLBuffers: Array<{
buffer: twgl.BufferInfo,
numElements: number
}>
}>;
private _bufferSize: number;
private _completeBuffers: Array<CompleteBuffer>;
private _buffer!: SegmentedBufferData;
private _attributes: {[name: string]: IndexedAttributed}
private _attributes: {[name: string]: IndexedAttributed};
private _indicesInsertIndex: number = 0;
private _maxIndex: number = 0;
private _compiled: boolean = false;
@ -71,7 +70,7 @@ export class SegmentedBuffer {
this._attributes[attr.name] = {
name: attr.name,
numComponents: attr.numComponents,
insertIndex: 0
insertIndex: 0,
};
}
@ -83,12 +82,12 @@ export class SegmentedBuffer {
indices: {
numComponents: 1,
data: new Uint16Array(this._bufferSize),
}
},
};
for (const attr in this._attributes) {
this._buffer[attr] = {
numComponents: this._attributes[attr].numComponents,
data: new Float32Array(this._bufferSize)
data: new Float32Array(this._bufferSize),
};
this._attributes[attr].insertIndex = 0;
}
@ -107,7 +106,7 @@ export class SegmentedBuffer {
private _willOverflow(data: VoxelData): boolean {
// Check for indices Uint16 overflow
//const dataMaxIndex = Math.max(...data.indices);
// const dataMaxIndex = Math.max(...data.indices);
const dataMaxIndex = data.indices.reduce((a, v) => Math.max(a, v));
if ((this._maxIndex + dataMaxIndex) > 65535) {
return true;
@ -132,7 +131,7 @@ export class SegmentedBuffer {
throw `Given data does not have indices data`;
}
*/
//const setsRequired = Math.max(...data.indices) + 1;
// const setsRequired = Math.max(...data.indices) + 1;
const setsRequired = data.indices.reduce((a, v) => Math.max(a, v)) + 1;
for (const attr in this._attributes) {
if (!(attr in data)) {
@ -143,31 +142,31 @@ export class SegmentedBuffer {
}
const numSets = data[attr].length / this._attributes[attr].numComponents;
if (numSets != setsRequired) {
//throw `Number of indices does not match number of ${attr} components given`;
// throw `Number of indices does not match number of ${attr} components given`;
throw Error(`Expected ${setsRequired * this._attributes[attr].numComponents} values for ${attr}, got ${data[attr].length}`);
}
}
}
private _addDataToAttribute(attr: string, attr_data: (Uint16Array | Float32Array | Array<number>)) {
private _addDataToAttribute(attr: string, attrData: (Uint16Array | Float32Array | Array<number>)) {
const indexOffset = this._attributes[attr].insertIndex;
attr_data.forEach((value, i) => {
attrData.forEach((value, i) => {
this._buffer[attr].data[i + indexOffset] = value;
});
this._attributes[attr].insertIndex += attr_data.length;
this._attributes[attr].insertIndex += attrData.length;
}
public add(data: VoxelData) {
if (this._compiled) {
throw Error("Buffer already compiled, cannot add more data");
throw Error('Buffer already compiled, cannot add more data');
}
if (this._sanityCheck) {
this._checkDataMatchesAttributes(data);
}
if (this._willOverflow(data)) {
//console.log("Cycling buffer...");
// console.log("Cycling buffer...");
this._cycle();
}
@ -178,11 +177,10 @@ export class SegmentedBuffer {
this._indicesInsertIndex += data.indices.length;
this._maxIndex += 1 + data.indices.reduce((a, v) => Math.max(a, v));
for (const attr in this._attributes) {
this._addDataToAttribute(attr, data[attr]);
}
}
@ -198,27 +196,24 @@ export class SegmentedBuffer {
this._completeBuffers.forEach((buffer, i) => {
this.WebGLBuffers[i] = {
buffer: twgl.createBufferInfoFromArrays(gl, buffer.buffer),
numElements: buffer.numElements
numElements: buffer.numElements,
};
});
this._compiled = true;
}
}
export class BottomlessBuffer {
public WebGLBuffers: Array<{
buffer: twgl.BufferInfo,
numElements: number
}>
}>;
private _completeBuffers: Array<CompleteBuffer>;
private _buffer!: BottomlessBufferData;
private _attributes: {[name: string]: Attribute}
private _attributes: {[name: string]: Attribute};
private _maxIndex: number = 0;
private _compiled: boolean = false;
private _sanityCheck: boolean = true;
@ -233,7 +228,7 @@ export class BottomlessBuffer {
for (const attr of attributes) {
this._attributes[attr.name] = {
name: attr.name,
numComponents: attr.numComponents
numComponents: attr.numComponents,
};
}
@ -245,12 +240,12 @@ export class BottomlessBuffer {
_getNewBuffer() {
this._buffer = {
indices: {numComponents: 1, data: []}
indices: {numComponents: 1, data: []},
};
for (const attr in this._attributes) {
this._buffer[attr] = {
numComponents: this._attributes[attr].numComponents,
data: []
data: [],
};
}
}
@ -267,14 +262,14 @@ export class BottomlessBuffer {
_willOverflow(data: VoxelData) {
// Check for indices Uint16 overflow
//const dataMaxIndex = Math.max(...data.indices);
// const dataMaxIndex = Math.max(...data.indices);
const dataMaxIndex = data.indices.reduce((a, v) => Math.max(a, v));
return ((this._maxIndex + dataMaxIndex) > 65535);
}
_checkDataMatchesAttributes(data: VoxelData) {
if (!('indices'in data)) {
throw `Given data does not have indices data`;
if (!('indices' in data)) {
throw Error('Given data does not have indices data');
}
const setsRequired = data.indices.reduce((a, v) => Math.max(a, v)) + 1;
for (const attr in this._attributes) {
@ -286,7 +281,7 @@ export class BottomlessBuffer {
}
const numSets = data[attr].length / this._attributes[attr].numComponents;
if (numSets != setsRequired) {
//throw `Number of indices does not match number of ${attr} components given`;
// throw `Number of indices does not match number of ${attr} components given`;
throw Error(`Expected ${setsRequired * this._attributes[attr].numComponents} values for ${attr}, got ${data[attr].length}`);
}
}
@ -295,26 +290,26 @@ export class BottomlessBuffer {
add(data: VoxelData) {
if (this._compiled) {
throw Error("Buffer already compiled, cannot add more data");
throw Error('Buffer already compiled, cannot add more data');
}
if (this._sanityCheck) {
this._checkDataMatchesAttributes(data);
}
if (this._willOverflow(data)) {
//console.log("Cycling buffer...");
// console.log("Cycling buffer...");
this._cycle();
}
// Add the new indices data
data.indices.forEach(index => {
data.indices.forEach((index) => {
this._buffer.indices.data.push(index + this._maxIndex);
});
this._maxIndex += 1 + data.indices.reduce((a, v) => Math.max(a, v));
for (const attr in this._attributes) {
data[attr].forEach(v => {
data[attr].forEach((v) => {
this._buffer[attr].data.push(v);
});
}
@ -332,11 +327,10 @@ export class BottomlessBuffer {
this._completeBuffers.forEach((buffer, i) => {
this.WebGLBuffers[i] = {
buffer: twgl.createBufferInfoFromArrays(gl, buffer.buffer),
numElements: buffer.numElements
numElements: buffer.numElements,
};
});
this._compiled = true;
}
}
}

View File

@ -1,17 +1,16 @@
import { m4, v3 } from "twgl.js";
import { MouseManager } from "./mouse";
import { degreesToRadians, clamp } from "./math";
import { Renderer } from "./renderer";
import { m4, v3 } from 'twgl.js';
import { MouseManager } from './mouse';
import { degreesToRadians, clamp } from './math';
import { Renderer } from './renderer';
export class ArcballCamera {
public isUserRotating = false;
private readonly fov: number;
private readonly zNear: number;
private readonly zFar: number;
private readonly cameraSmoothing = 0.025;
public aspect: number;
private actualDistance = 18.0;
private actualAzimuth = -1.0;
@ -53,7 +52,7 @@ export class ArcballCamera {
public updateCamera() {
this.aspect = this.gl.canvas.width / this.gl.canvas.height;
// Update target location if user is rotating camera
if (this.isUserRotating) {
const mouseDelta = MouseManager.Get.getMouseDelta();
@ -73,7 +72,7 @@ export class ArcballCamera {
this.eye = [
this.actualDistance * Math.cos(this.actualAzimuth) * -Math.sin(this.actualElevation),
this.actualDistance * Math.cos(this.actualElevation),
this.actualDistance * Math.sin(this.actualAzimuth) * -Math.sin(this.actualElevation)
this.actualDistance * Math.sin(this.actualAzimuth) * -Math.sin(this.actualElevation),
];
}
@ -83,7 +82,7 @@ export class ArcballCamera {
return [
this.actualDistance * Math.cos(azimuth ) * -Math.sin(elevation),
this.actualDistance * Math.cos(elevation),
this.actualDistance * Math.sin(azimuth) * -Math.sin(elevation)
this.actualDistance * Math.sin(azimuth) * -Math.sin(elevation),
];
}
@ -138,19 +137,18 @@ export class ArcballCamera {
const inverseProjectionMatrix = this.getInverseWorldViewProjection();
var origin = mathUtil.multiplyMatVec4(inverseProjectionMatrix, [mousePos.x, mousePos.y, -1.0, 1.0]);
var dest = mathUtil.multiplyMatVec4(inverseProjectionMatrix, [mousePos.x, mousePos.y, 1.0, 1.0]);
origin[0] /= origin[3];
origin[1] /= origin[3];
origin[2] /= origin[3];
dest[0] /= dest[3];
dest[1] /= dest[3];
dest[2] /= dest[3];
return {origin: origin, dest: dest};
}
*/
}
module.exports.ArcballCamera = ArcballCamera;
module.exports.ArcballCamera = ArcballCamera;

View File

@ -1,28 +1,26 @@
import { Schematic, Litematic } from "./schematic";
import { AppContext } from "./app_context";
import { ArcballCamera } from "./camera";
import { MouseManager } from "./mouse";
import { AppContext } from './app_context';
import { ArcballCamera } from './camera';
import { MouseManager } from './mouse';
function AddEvent(htmlElementID: string, event: string, delegate: (e: any) => void) {
function addEvent(htmlElementID: string, event: string, delegate: (e: any) => void) {
document.getElementById(htmlElementID)?.addEventListener(event, delegate);
}
// Register Events
/*
AddEvent("buttonChooseFile", "click", () => { context.do(Action.Load); });
AddEvent("inputFile", "click", () => { context.do(Action.Load); });
AddEvent("buttonVoxelise", "click", () => { context.do(Action.Voxelise); });
AddEvent("buttonSchematic", "click", async () => { context.do(Action.ExportSchematic); });
AddEvent("buttonLitematic", "click", async () => { context.do(Action.ExportLitematic); });
*/
const camera = ArcballCamera.Get;
AddEvent("canvas", "mousedown", (e) => { camera.onMouseDown(e); });
AddEvent("canvas", "mouseup", (e) => { camera.onMouseUp(e); });
AddEvent("canvas", "wheel", (e) => { camera.onWheelScroll(e); });
addEvent('canvas', 'mousedown', (e) => {
camera.onMouseDown(e);
});
addEvent('canvas', 'mouseup', (e) => {
camera.onMouseUp(e);
});
addEvent('canvas', 'wheel', (e) => {
camera.onWheelScroll(e);
});
const mouseManager = MouseManager.Get;
AddEvent("canvas", "mousemove", (e) => { mouseManager.onMouseMove(e); });
addEvent('canvas', 'mousemove', (e) => {
mouseManager.onMouseMove(e);
});
// Begin draw loop
@ -31,4 +29,4 @@ function render() {
context.draw();
requestAnimationFrame(render);
}
requestAnimationFrame(render);
requestAnimationFrame(render);

View File

@ -10,5 +10,5 @@ export namespace AppConfig {
/** Recomended, better matches colours */
export const DITHERING_ENABLED = true;
}
}

View File

@ -1,19 +1,18 @@
import * as twgl from "twgl.js";
import { Triangle } from "./triangle";
import { Vector3 } from "./vector";
import { VoxelData } from "./buffer";
import * as twgl from 'twgl.js';
import { Triangle } from './triangle';
import { Vector3 } from './vector';
import { VoxelData } from './buffer';
export class GeometryTemplates {
private static readonly _default_cube = twgl.primitives.createCubeVertices(1.0);
static getTriangleBufferData(triangle: Triangle, debug: boolean): VoxelData {
const a = triangle.v0;
const b = triangle.v1;
const c = triangle.v2;
const n = triangle.normal;
//console.log(triangle);
// console.log(triangle);
if (debug) {
return {
@ -25,8 +24,8 @@ export class GeometryTemplates {
indices: new Uint16Array([
0, 1,
1, 2,
2, 0
])
2, 0,
]),
};
} else {
return {
@ -38,16 +37,16 @@ export class GeometryTemplates {
texcoord: new Float32Array([
a.texcoord.u, a.texcoord.v,
b.texcoord.u, b.texcoord.v,
c.texcoord.u, c.texcoord.v
c.texcoord.u, c.texcoord.v,
]),
normal: new Float32Array([
n.x, n.y, n.z,
n.x, n.y, n.z,
n.x, n.y, n.z
n.x, n.y, n.z,
]),
indices: new Uint16Array([
0, 1, 2
])
0, 1, 2,
]),
};
}
}
@ -55,7 +54,7 @@ export class GeometryTemplates {
static getBoxBufferData(centre: Vector3, debug: boolean): VoxelData {
const a = Vector3.subScalar(centre, 0.5);
const b = Vector3.addScalar(centre, 0.5);
if (debug) {
return {
position: new Float32Array([
@ -66,27 +65,27 @@ export class GeometryTemplates {
a.x, a.y, b.z,
b.x, a.y, b.z,
b.x, b.y, b.z,
a.x, b.y, b.z
a.x, b.y, b.z,
]),
indices: new Uint16Array([
0, 1, 1, 2, 2, 3, 3, 0,
4, 5, 5, 6, 6, 7, 7, 4,
0, 4, 1, 5, 2, 6, 3, 7
])
0, 4, 1, 5, 2, 6, 3, 7,
]),
};
} else {
let cube = {
const cube = {
position: new Float32Array(72),
texcoord: new Float32Array(48),
normal: new Float32Array(72),
indices: new Uint16Array(72),
};
cube.position.set(GeometryTemplates._default_cube.position);
cube.normal.set(GeometryTemplates._default_cube.normal);
cube.indices.set(GeometryTemplates._default_cube.indices);
cube.texcoord.set(GeometryTemplates._default_cube.texcoord);
for (let i = 0; i < 72; i += 3) {
cube.position[i + 0] += centre.x;
cube.position[i + 1] += centre.y;
@ -96,5 +95,4 @@ export class GeometryTemplates {
return cube;
}
}
}
}

View File

@ -5,7 +5,6 @@ export abstract class Hashable {
export class HashSet<T extends Hashable> {
private _numBins: number;
protected _bins: Array<Array<T>>;
@ -39,12 +38,10 @@ export class HashSet<T extends Hashable> {
}
return false;
}
}
export class HashMap<K extends Hashable, V> {
private _numBins: number;
protected _bins: Array<Array<{key: K, value: V}>>;
@ -73,11 +70,12 @@ export class HashMap<K extends Hashable, V> {
}
}
/* eslint-disable */
public has(item: K): boolean {
const binIndex = this._getBin(item);
const list = this._bins[binIndex];
for (const {key, value} of list) {
for (const { key, value } of list) {
if (item.equals(key)) {
return true;
}
@ -85,10 +83,10 @@ export class HashMap<K extends Hashable, V> {
return false;
}
/* eslint-enable */
public add(key: K, value: V) {
const binIndex = this._getBin(key);
this._bins[binIndex].push({key, value});
}
}
}

View File

@ -1,6 +1,6 @@
import { app, BrowserWindow, Tray } from "electron";
import path from "path";
import url from "url";
import { app, BrowserWindow } from 'electron';
import path from 'path';
import url from 'url';
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
@ -8,11 +8,11 @@ let mainWindow: BrowserWindow;
function createWindow() {
// Create the browser window.
//const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize;
// const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize;
const width = 1400;
const height = 800;
//const appIcon = new Tray("../resources/icon.png");
// const appIcon = new Tray("../resources/icon.png");
mainWindow = new BrowserWindow({
width: width,
height: height,
@ -21,8 +21,8 @@ function createWindow() {
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true
}
enableRemoteModule: true,
},
});
mainWindow.removeMenu();
@ -31,14 +31,14 @@ function createWindow() {
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, '../index.html'),
protocol: 'file:',
slashes: true
slashes: true,
}));
// Open the DevTools.
//mainWindow.webContents.openDevTools();
// mainWindow.webContents.openDevTools();
// Emitted when the window is closed.
mainWindow.on('closed', function () {
mainWindow.on('closed', function() {
app.quit();
});
}
@ -49,7 +49,7 @@ function createWindow() {
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', function () {
app.on('window-all-closed', function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
@ -57,10 +57,10 @@ app.on('window-all-closed', function () {
}
});
app.on('activate', function () {
app.on('activate', function() {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow();
}
});
});

View File

@ -1,45 +1,45 @@
import { Vector3 } from "./vector";
import { Vector3 } from './vector';
export const argMax = (array: [number]) => {
return array.map((x, i) => [x, i]).reduce((r, a) => (a[0] > r[0] ? a : r))[1];
}
};
export const fastCrossXAxis = (vec: Vector3) => {
return new Vector3(0.0, -vec.z, vec.y);
}
};
export const fastCrossYAxis = (vec: Vector3) => {
return new Vector3(vec.z, 0.0, -vec.x);
}
};
export const fastCrossZAxis = (vec: Vector3) => {
return new Vector3(-vec.y, vec.x, 0.0);
}
};
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));
}
};
export const xAxis = new Vector3(1.0, 0.0, 0.0);
export const yAxis = new Vector3(0.0, 1.0, 0.0);
export const zAxis = new Vector3(0.0, 0.0, 1.0);
export const degreesToRadians = Math.PI / 180;
export const degreesToRadians = Math.PI / 180;

View File

@ -1,20 +1,18 @@
import * as twgl from "twgl.js";
import * as fs from "fs";
import * as path from "path";
import * as twgl from 'twgl.js';
import * as fs from 'fs';
import * as path from 'path';
import { Triangle } from "./triangle";
import { Vector3 } from "./vector";
import { RGB, UV } from "./util";
import { TextureFormat } from "./texture";
import { triangleArea } from "./math";
import { Triangle } from './triangle';
import { Vector3 } from './vector';
import { RGB, UV } from './util';
import { TextureFormat } from './texture';
import { triangleArea } from './math';
type VertexMap<T> = { [index: number]: T };
export class Vertex {
public position: Vector3;
public texcoord: UV
public texcoord: UV;
public normal: Vector3;
constructor(position: Vector3, texcoord: UV, normal: Vector3) {
@ -38,7 +36,7 @@ export class Vertex {
vertexTexcoordMap: VertexMap<UV>,
vertexNormalMap: VertexMap<Vector3>,
) {
const tokens = vertexToken.split("/");
const tokens = vertexToken.split('/');
const positionIndex = parseFloat(tokens[0]);
const texcoordIndex = parseFloat(tokens[1]);
@ -50,7 +48,6 @@ export class Vertex {
return new Vertex(position, texcoord, normal);
}
}
@ -75,10 +72,12 @@ export interface TextureMaterial {
format: TextureFormat
}
/* eslint-disable */
export enum MaterialType {
Texture,
Fill
}
/* eslint-enable */
export interface MaterialTriangles {
material: (FillMaterial | TextureMaterial);
@ -86,7 +85,6 @@ export interface MaterialTriangles {
}
class Material {
public name: string;
public faces: Array<Triangle>;
public materialData: (FillMaterial | TextureMaterial);
@ -95,7 +93,7 @@ class Material {
this.name = name;
this.faces = [];
let material: FillMaterial = { diffuseColour: { r: 1, g: 1, b: 1 }, type: MaterialType.Fill}
const material: FillMaterial = { diffuseColour: { r: 1, g: 1, b: 1 }, type: MaterialType.Fill};
this.materialData = material;
}
@ -104,35 +102,33 @@ class Material {
}
public isFilled(): boolean {
return this.name !== "" && this.faces.length > 0;
return this.name !== '' && this.faces.length > 0;
}
public attachMTLData(materialData: (FillMaterial | TextureMaterial)) {
this.materialData = materialData;
}
}
export class Mesh {
public materials: Array<Material>
public materials: Array<Material>;
constructor(objPathString: string, gl: WebGLRenderingContext) {
// Parse .obj
const wavefrontString = fs.readFileSync(objPathString).toString('utf8');
const parsedOBJ = this._parseOBJFile(wavefrontString);
// TODO: Create blank .mtl when not found
if (!parsedOBJ.mtlPath) {
throw Error("No .mtl file found.");
throw Error('No .mtl file found.');
}
const objPath = path.parse(objPathString);
if (!path.isAbsolute(parsedOBJ.mtlPath)) {
parsedOBJ.mtlPath = path.join(objPath.dir, parsedOBJ.mtlPath);
}
parsedOBJ.mtlPath = parsedOBJ.mtlPath.trimEnd();
// Parse .mtl
const materialString = fs.readFileSync(parsedOBJ.mtlPath).toString('utf8');
const parsedMTL = this._parseMaterial(materialString, objPath);
@ -146,29 +142,29 @@ export class Mesh {
}
private _addMaterial(materialsJSON: Materials, materialName: string, materialDiffuseColour: RGB, materialDiffuseTexturePath: string, materialFormat: TextureFormat) {
if (materialDiffuseTexturePath !== "") {
if (materialDiffuseTexturePath !== '') {
materialsJSON[materialName] = {
texturePath: materialDiffuseTexturePath,
type: MaterialType.Texture,
format: materialFormat
format: materialFormat,
};
} else if (materialName !== "") {
} else if (materialName !== '') {
materialsJSON[materialName] = {
diffuseColour: materialDiffuseColour,
type: MaterialType.Fill
type: MaterialType.Fill,
};
}
}
// TODO: Rewrite
private _parseMaterial(materialString: string, objPath: path.ParsedPath): Materials {
var materialsJSON: Materials = {};
const materialsJSON: Materials = {};
const lines = materialString.split('\n');
let materialName: string = "";
let materialName: string = '';
let materialDiffuseColour: RGB = { r: 1.0, g: 1.0, b: 1.0 };
let materialDiffuseTexturePath: string = "";
let materialDiffuseTexturePath: string = '';
let materialTextureFormat: TextureFormat = TextureFormat.PNG;
for (let i = 0; i < lines.length; ++i) {
@ -176,50 +172,49 @@ export class Mesh {
const lineTokens = line.trim().split(/\s+/);
switch (lineTokens[0]) {
case "newmtl":
this._addMaterial(materialsJSON, materialName, materialDiffuseColour, materialDiffuseTexturePath, materialTextureFormat);
materialName = lineTokens[1];
materialDiffuseColour = { r: 0, g: 0, b: 0 };
materialDiffuseTexturePath = ""
break;
case 'newmtl':
this._addMaterial(materialsJSON, materialName, materialDiffuseColour, materialDiffuseTexturePath, materialTextureFormat);
materialName = lineTokens[1];
materialDiffuseColour = { r: 0, g: 0, b: 0 };
materialDiffuseTexturePath = '';
break;
case "Kd":
const diffuseColour = lineTokens.slice(1).map(x => parseFloat(x))
if (!diffuseColour || diffuseColour.length != 3) {
throw Error(`Could not parse .mtl file. (Line ${i + 1})`);
}
if (diffuseColour.some(x => Number.isNaN(x))) {
throw Error(`Could not parse .mtl file. (Line ${i + 1})`);
}
materialDiffuseColour = {
r: diffuseColour[0], g: diffuseColour[1], b: diffuseColour[2]
};
break;
case 'Kd':
const diffuseColour = lineTokens.slice(1).map((x) => parseFloat(x));
if (!diffuseColour || diffuseColour.length != 3) {
throw Error(`Could not parse .mtl file. (Line ${i + 1})`);
}
if (diffuseColour.some((x) => Number.isNaN(x))) {
throw Error(`Could not parse .mtl file. (Line ${i + 1})`);
}
materialDiffuseColour = {
r: diffuseColour[0], g: diffuseColour[1], b: diffuseColour[2],
};
break;
case "map_Kd":
if (!lineTokens[1]) {
throw Error(`No valid path to texture in .mtl file. (Line ${i + 1})`);
}
let texturePath = lineTokens[1];
if (!path.isAbsolute(texturePath)) {
texturePath = path.join(objPath.dir, texturePath);
}
if (!fs.existsSync(texturePath)) {
console.error(texturePath);
throw Error(`Cannot load texture ${texturePath}`);
}
const _path = path.parse(texturePath);
if (".png" === _path.ext.toLowerCase()) {
materialTextureFormat = TextureFormat.PNG;
}
else if ([".jpeg", ".jpg"].includes(_path.ext.toLowerCase())) {
materialTextureFormat = TextureFormat.JPEG;
} else {
throw Error(`Can only load PNG and JPEG textures`);
}
case 'map_Kd':
if (!lineTokens[1]) {
throw Error(`No valid path to texture in .mtl file. (Line ${i + 1})`);
}
let texturePath = lineTokens[1];
if (!path.isAbsolute(texturePath)) {
texturePath = path.join(objPath.dir, texturePath);
}
if (!fs.existsSync(texturePath)) {
console.error(texturePath);
throw Error(`Cannot load texture ${texturePath}`);
}
const _path = path.parse(texturePath);
if ('.png' === _path.ext.toLowerCase()) {
materialTextureFormat = TextureFormat.PNG;
} else if (['.jpeg', '.jpg'].includes(_path.ext.toLowerCase())) {
materialTextureFormat = TextureFormat.JPEG;
} else {
throw Error(`Can only load PNG and JPEG textures`);
}
materialDiffuseTexturePath = texturePath;
break;
materialDiffuseTexturePath = texturePath;
break;
}
}
@ -230,7 +225,7 @@ export class Mesh {
private _mergeMaterialData(parsedOBJ: ParsedOBJ, parsedMTL: Materials): Array<Material> {
parsedOBJ.materials.forEach(material => {
parsedOBJ.materials.forEach((material) => {
material.attachMTLData(parsedMTL[material.name]);
});
@ -239,82 +234,82 @@ export class Mesh {
private _parseOBJFile(wavefrontString: string): ParsedOBJ {
const lines = wavefrontString.split("\n");
const lines = wavefrontString.split('\n');
let mtlPath: (string | undefined);
let vertexPositionMap: VertexMap<Vector3> = {};
let vertexTexcoordMap: VertexMap<UV> = {};
let vertexNormalMap: VertexMap<Vector3> = {};
const vertexPositionMap: VertexMap<Vector3> = {};
const vertexTexcoordMap: VertexMap<UV> = {};
const vertexNormalMap: VertexMap<Vector3> = {};
let vertexPositionIndex = 1;
let vertexTexcoordIndex = 1;
let vertexNormalIndex = 1;
let currentMaterial: Material = new Material("");
let currentMaterial: Material = new Material('');
let materialMap: {[name: string]: Material} = {};
//let materials: Array<Material> = [];
const materialMap: {[name: string]: Material} = {};
// let materials: Array<Material> = [];
lines.forEach(line => {
const tokens = line.split(" ");
lines.forEach((line) => {
const tokens = line.split(' ');
switch (tokens[0]) {
case "mtllib":
mtlPath = tokens[1];
break;
case "v":
vertexPositionMap[vertexPositionIndex++] = Vector3.parse(line);
break;
case "vn":
vertexNormalMap[vertexNormalIndex++] = Vector3.parse(line);
break;
case "vt":
vertexTexcoordMap[vertexTexcoordIndex++] = { u: parseFloat(tokens[1]), v: parseFloat(tokens[2]) };
break;
case 'mtllib':
mtlPath = tokens[1];
break;
case 'v':
vertexPositionMap[vertexPositionIndex++] = Vector3.parse(line);
break;
case 'vn':
vertexNormalMap[vertexNormalIndex++] = Vector3.parse(line);
break;
case 'vt':
vertexTexcoordMap[vertexTexcoordIndex++] = { u: parseFloat(tokens[1]), v: parseFloat(tokens[2]) };
break;
}
});
lines.forEach(line => {
lines.forEach((line) => {
line = line.replace(/[\n\r]/g, '').trimEnd();
const tokens = line.split(" ");
const tokens = line.split(' ');
switch (tokens[0]) {
case "usemtl":
if (currentMaterial.isFilled() && !(currentMaterial.name in materialMap)) {
materialMap[currentMaterial.name] = currentMaterial;
}
if (tokens[1] in materialMap) {
console.log("Material already found", tokens[1]);
currentMaterial = materialMap[tokens[1]];
} else {
console.log("New material", tokens[1])
currentMaterial = new Material(tokens[1]);
}
break;
case "f":
if (tokens.length === 5) {
// QUAD
const v1 = Vertex.parseFromOBJ(tokens[1], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v2 = Vertex.parseFromOBJ(tokens[2], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v3 = Vertex.parseFromOBJ(tokens[3], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v4 = Vertex.parseFromOBJ(tokens[4], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
case 'usemtl':
if (currentMaterial.isFilled() && !(currentMaterial.name in materialMap)) {
materialMap[currentMaterial.name] = currentMaterial;
}
if (tokens[1] in materialMap) {
console.log('Material already found', tokens[1]);
currentMaterial = materialMap[tokens[1]];
} else {
console.log('New material', tokens[1]);
currentMaterial = new Material(tokens[1]);
}
break;
case 'f':
if (tokens.length === 5) {
// QUAD
const v1 = Vertex.parseFromOBJ(tokens[1], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v2 = Vertex.parseFromOBJ(tokens[2], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v3 = Vertex.parseFromOBJ(tokens[3], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v4 = Vertex.parseFromOBJ(tokens[4], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const face = new Triangle(v4.copy(), v2.copy(), v1.copy());
const face2 = new Triangle(v4.copy(), v3.copy(), v2.copy());
const face = new Triangle(v4.copy(), v2.copy(), v1.copy());
const face2 = new Triangle(v4.copy(), v3.copy(), v2.copy());
currentMaterial.addFace(face);
currentMaterial.addFace(face2);
} else if (tokens.length === 4) {
// TRI
const v0 = Vertex.parseFromOBJ(tokens[1], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v1 = Vertex.parseFromOBJ(tokens[2], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v2 = Vertex.parseFromOBJ(tokens[3], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
currentMaterial.addFace(face);
currentMaterial.addFace(face2);
} else if (tokens.length === 4) {
// TRI
const v0 = Vertex.parseFromOBJ(tokens[1], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v1 = Vertex.parseFromOBJ(tokens[2], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const v2 = Vertex.parseFromOBJ(tokens[3], vertexPositionMap, vertexTexcoordMap, vertexNormalMap);
const face = new Triangle(v0, v1, v2);
currentMaterial.addFace(face);
} else {
throw Error(`Unexpected number of face vertices, expected 3 or 4, got ${tokens.length - 1}`);
}
break;
const face = new Triangle(v0, v1, v2);
currentMaterial.addFace(face);
} else {
throw Error(`Unexpected number of face vertices, expected 3 or 4, got ${tokens.length - 1}`);
}
break;
}
});
@ -322,7 +317,7 @@ export class Mesh {
materialMap[currentMaterial.name] = currentMaterial;
}
let materials: Array<Material> = [];
const materials: Array<Material> = [];
for (const material in materialMap) {
materials.push(materialMap[material]);
}
@ -331,16 +326,16 @@ export class Mesh {
return {
mtlPath: mtlPath,
materials: materials
}
materials: materials,
};
}
private _centreMesh() {
let centre = new Vector3(0, 0, 0);
const centre = new Vector3(0, 0, 0);
let totalWeight = 0;
this.materials.forEach(material => {
material.faces.forEach(face => {
this.materials.forEach((material) => {
material.faces.forEach((face) => {
const k0 = Vector3.sub(face.v0.position, face.v1.position).magnitude();
const k1 = Vector3.sub(face.v1.position, face.v2.position).magnitude();
const k2 = Vector3.sub(face.v2.position, face.v0.position).magnitude();
@ -352,8 +347,8 @@ export class Mesh {
centre.divScalar(totalWeight);
// Translate each triangle
this.materials.forEach(material => {
material.faces.forEach(face => {
this.materials.forEach((material) => {
material.faces.forEach((face) => {
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);
@ -367,11 +362,11 @@ export class Mesh {
*/
private _normaliseMesh() {
// Find the size
let a = new Vector3(Infinity, Infinity, Infinity);
let b = new Vector3(-Infinity, -Infinity, -Infinity);
const a = new Vector3(Infinity, Infinity, Infinity);
const b = new Vector3(-Infinity, -Infinity, -Infinity);
this.materials.forEach(material => {
material.faces.forEach(face => {
this.materials.forEach((material) => {
material.faces.forEach((face) => {
const aabb = face.getBounds();
a.x = Math.min(a.x, aabb.minX);
a.y = Math.min(a.y, aabb.minY);
@ -383,14 +378,14 @@ export class Mesh {
});
const size = Vector3.sub(b, a);
console.log("size", size);
console.log('size', size);
const targetSize = 8.0;
const scaleFactor = targetSize / Math.max(size.x, size.y, size.z);
console.log("scaleFactor", scaleFactor)
console.log('scaleFactor', scaleFactor);
// Scale each triangle
this.materials.forEach(material => {
material.faces.forEach(face => {
this.materials.forEach((material) => {
material.faces.forEach((face) => {
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);
@ -399,14 +394,13 @@ export class Mesh {
}
public loadTextures(gl: WebGLRenderingContext) {
this.materials.forEach(material => {
this.materials.forEach((material) => {
if (material.materialData?.type === MaterialType.Texture) {
material.materialData.texture = twgl.createTexture(gl, {
src: material.materialData.texturePath,
mag: gl.LINEAR
mag: gl.LINEAR,
});
}
});
}
}
}

View File

@ -1,4 +1,4 @@
import { Renderer } from "./renderer";
import { Renderer } from './renderer';
interface MouseState {
x: number,
@ -7,7 +7,6 @@ interface MouseState {
}
export class MouseManager {
private _gl: WebGLRenderingContext;
private static readonly MOUSE_LEFT = 1;
@ -44,8 +43,8 @@ export class MouseManager {
public getMouseDelta() {
const delta = {
dx: this.currMouse.x - this.prevMouse.x,
dy: -(this.currMouse.y - this.prevMouse.y)
dx: this.currMouse.x - this.prevMouse.x,
dy: -(this.currMouse.y - this.prevMouse.y),
};
this.prevMouse = this.currMouse;
return delta;
@ -56,5 +55,4 @@ export class MouseManager {
const normY = -(2 * (this.currMouse.y / this._gl.canvas.height) - 1);
return { x: normX, y: normY };
}
}
}

View File

@ -1,14 +1,13 @@
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";
import { Vector3 } from './vector';
import { Bounds } from './util';
const EPSILON = 0.0000001;
/* eslint-disable */
export enum Axes {
x, y, z
x, y, z,
}
/* eslint-enable */
interface Ray {
origin: Vector3,
@ -24,9 +23,9 @@ export function generateRays(v0: Vector3, v1: Vector3, v2: Vector3): Array<Ray>
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> = [];
const rayList: Array<Ray> = [];
traverseX(rayList, bounds);
traverseY(rayList, bounds);
traverseZ(rayList, bounds);
@ -39,7 +38,7 @@ function traverseX(rayList: Array<Ray>, bounds: Bounds) {
rayList.push({
origin: new Vector3(bounds.minX, y, z),
direction: new Vector3(1, 0, 0),
axis: Axes.x
axis: Axes.x,
});
}
}
@ -51,7 +50,7 @@ function traverseY(rayList: Array<Ray>, bounds: Bounds) {
rayList.push({
origin: new Vector3(x, bounds.minY, z),
direction: new Vector3(0, 1, 0),
axis: Axes.y
axis: Axes.y,
});
}
}
@ -63,7 +62,7 @@ function traverseZ(rayList: Array<Ray>, bounds: Bounds) {
rayList.push({
origin: new Vector3(x, y, bounds.minZ),
direction: new Vector3(0, 0, 1),
axis: Axes.z
axis: Axes.z,
});
}
}
@ -100,4 +99,4 @@ export function rayIntersectTriangle(ray: Ray, v0: Vector3, v1: Vector3, v2: Vec
if (t > EPSILON) {
return Vector3.add(ray.origin, Vector3.mulScalar(ray.direction, t));
}
}
}

View File

@ -1,22 +1,20 @@
import * as twgl from "twgl.js";
import path from "path";
import * as twgl from 'twgl.js';
import path from 'path';
import { Vector3 } from "./vector";
import { ArcballCamera } from "./camera";
import { MouseManager } from "./mouse";
import { ShaderManager } from "./shaders";
import { BottomlessBuffer, SegmentedBuffer, VoxelData } from "./buffer";
import { GeometryTemplates } from "./geometry";
import { RGB, UV, rgbToArray } from "./util";
import { VoxelManager } from "./voxel_manager";
import { Triangle } from "./triangle";
import { Mesh, FillMaterial, TextureMaterial, MaterialType } from "./mesh";
import { FaceInfo, BlockAtlas } from "./block_atlas";
import { AppConfig } from "./config"
import { AppContext } from "./app_context";
import { Vector3 } from './vector';
import { ArcballCamera } from './camera';
import { ShaderManager } from './shaders';
import { BottomlessBuffer, SegmentedBuffer, VoxelData } from './buffer';
import { GeometryTemplates } from './geometry';
import { RGB, rgbToArray } from './util';
import { VoxelManager } from './voxel_manager';
import { Triangle } from './triangle';
import { Mesh, FillMaterial, TextureMaterial, MaterialType } from './mesh';
import { FaceInfo, BlockAtlas } from './block_atlas';
import { AppConfig } from './config';
import { AppContext } from './app_context';
export class Renderer {
public _gl: WebGLRenderingContext;
private _backgroundColour: RGB = {r: 0.1, g: 0.1, b: 0.1};
@ -43,7 +41,7 @@ export class Renderer {
}
private constructor() {
this._gl = (<HTMLCanvasElement>document.getElementById('canvas')).getContext("webgl")!;
this._gl = (<HTMLCanvasElement>document.getElementById('canvas')).getContext('webgl')!;
this._getNewBuffers();
this._setupOcclusions();
@ -51,8 +49,8 @@ export class Renderer {
this._materialBuffers = [];
this._atlasTexture = twgl.createTexture(this._gl, {
src: path.join(__dirname, "../resources/blocks.png"),
mag: this._gl.NEAREST
src: path.join(__dirname, '../resources/blocks.png'),
mag: this._gl.NEAREST,
});
}
@ -64,7 +62,7 @@ export class Renderer {
this._debug = debug;
}
public registerBox(centre: Vector3) { //, size: Vector3) {
public registerBox(centre: Vector3) { // , size: Vector3) {
const data = GeometryTemplates.getBoxBufferData(centre, this._debug);
this._registerData(data);
}
@ -77,11 +75,11 @@ export class Renderer {
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;
const voxelManager = VoxelManager.Get;
// Cache local neighbours
const localNeighbourhoodCache = Array<number>(27);
for (let i = -1; i <= 1; ++i) {
@ -94,7 +92,7 @@ export class Renderer {
}
}
let occlusions = new Array<Array<number>>(6);
const occlusions = new Array<Array<number>>(6);
// For each face
for (let f = 0; f < 6; ++f) {
occlusions[f] = [1, 1, 1, 1];
@ -117,23 +115,22 @@ export class Renderer {
if (numNeighbours == 2 && AppConfig.AMBIENT_OCCLUSION_OVERRIDE_CORNER) {
++numNeighbours;
} else {
const neighbourIndex = this._occlusionNeighboursIndices[f][v][2];
numNeighbours += localNeighbourhoodCache[neighbourIndex];
const neighbourIndex = this._occlusionNeighboursIndices[f][v][2];
numNeighbours += localNeighbourhoodCache[neighbourIndex];
}
// Convert from occlusion denoting the occlusion factor to the
// 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;
}
}
}
return occlusions;
}
private static _getBlankOcclusions() {
let blankOcclusions = new Array<Array<number>>(6);
const blankOcclusions = new Array<Array<number>>(6);
for (let f = 0; f < 6; ++f) {
blankOcclusions[f] = [1, 1, 1, 1];
}
@ -157,7 +154,7 @@ export class Renderer {
occlusions = Renderer._getBlankOcclusions();
}
let data: VoxelData = GeometryTemplates.getBoxBufferData(centre, false);
const data: VoxelData = GeometryTemplates.getBoxBufferData(centre, false);
// Each vertex of a face needs the occlusion data for the other 3 vertices
// in it's face, not just itself. Also flatten occlusion data.
@ -168,9 +165,9 @@ export class Renderer {
data.occlusion[j * 16 + k] = occlusions[j][k % 4];
}
}
// Assign the textures to each face
const faceOrder = ["north", "south", "up", "down", "east", "west"];
const faceOrder = ['north', 'south', 'up', 'down', 'east', 'west'];
for (const face of faceOrder) {
for (let i = 0; i < 4; ++i) {
const texcoord = blockTexcoord[face].texcoord;
@ -178,8 +175,7 @@ export class Renderer {
}
}
for (let i = 0; i < 6; ++i)
{
for (let i = 0; i < 6; ++i) {
if (!VoxelManager.Get.isVoxelAt(Vector3.add(centre, Renderer._faceNormals[i]))) {
this._registerVoxels.add({
position: data.position.slice(i * 12, (i+1) * 12),
@ -189,7 +185,7 @@ export class Renderer {
texcoord: data.texcoord.slice(i * 8, (i+1) * 8),
blockTexcoord: data.blockTexcoord.slice(i * 8, (i+1) * 8),
});
}
}
}
}
@ -199,21 +195,21 @@ export class Renderer {
}
public registerMesh(mesh: Mesh) {
mesh.materials.forEach(material => {
mesh.materials.forEach((material) => {
const materialBuffer = new BottomlessBuffer([
{ name: 'position', numComponents: 3 },
{ name: 'texcoord', numComponents: 2 },
{ name: 'normal', numComponents: 3 }
{ name: 'normal', numComponents: 3 },
]);
material.faces.forEach(face => {
material.faces.forEach((face) => {
const data = GeometryTemplates.getTriangleBufferData(face, false);
materialBuffer.add(data);
});
this._materialBuffers.push({
buffer: materialBuffer,
material: material.materialData
material: material.materialData,
});
});
}
@ -233,7 +229,7 @@ export class Renderer {
// 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 colour = voxelManager.voxelColours[i];
const texcoord = voxelManager.voxelTexcoords[i];
this._registerVoxel(voxel.position, texcoord);
}
@ -275,8 +271,8 @@ export class Renderer {
this._drawRegister(this._registerVoxels, this._gl.TRIANGLES, ShaderManager.Get.aoProgram, {
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
u_texture: this._atlasTexture,
u_voxelSize: VoxelManager.Get._voxelSize,
u_atlasSize: this._atlasSize
u_voxelSize: VoxelManager.Get.voxelSize,
u_atlasSize: this._atlasSize,
});
// Draw material registers
@ -287,14 +283,14 @@ export class Renderer {
u_lightWorldPos: camera.getCameraPosition(0.0, 0.0),
u_worldViewProjection: camera.getWorldViewProjection(),
u_worldInverseTranspose: camera.getWorldInverseTranspose(),
u_texture: materialBuffer.material.texture
u_texture: materialBuffer.material.texture,
});
} else {
this._drawRegister(materialBuffer.buffer, this._gl.TRIANGLES, ShaderManager.Get.shadedFillProgram, {
u_lightWorldPos: camera.getCameraPosition(0.0, 0.0),
u_worldViewProjection: camera.getWorldViewProjection(),
u_worldInverseTranspose: camera.getWorldInverseTranspose(),
u_fillColour: rgbToArray(materialBuffer.material.diffuseColour)
u_fillColour: rgbToArray(materialBuffer.material.diffuseColour),
});
}
});
@ -307,7 +303,7 @@ export class Renderer {
}
_setupOcclusions() {
// TODO: Find some for-loop to clean this up
// TODO: Find some for-loop to clean this up
// [Edge, Edge, Corrner]
const occlusionNeighbours = [
@ -316,7 +312,7 @@ export class Renderer {
[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, 0, 1), new Vector3(1, -1, 1)],
],
[
@ -324,7 +320,7 @@ export class Renderer {
[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, 0, -1), new Vector3(-1, -1, -1)],
],
[
@ -332,7 +328,7 @@ export class Renderer {
[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(0, 1, -1), new Vector3(1, 1, -1)],
],
[
@ -340,7 +336,7 @@ export class Renderer {
[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(0, -1, 1), new Vector3(1, -1, 1)],
],
[
@ -348,7 +344,7 @@ export class Renderer {
[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, 0, 1), new Vector3(-1, -1, 1)],
],
[
@ -356,15 +352,15 @@ export class Renderer {
[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, 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>>();
const row = new Array<Array<number>>();
for (let j = 0; j < 4; ++j) {
row.push(occlusionNeighbours[i][j].map(x => Renderer._getNeighbourIndex(x)));
row.push(occlusionNeighbours[i][j].map((x) => Renderer._getNeighbourIndex(x)));
}
this._occlusionNeighboursIndices.push(row);
}
@ -382,7 +378,7 @@ export class Renderer {
}
_setupScene() {
twgl.resizeCanvasToDisplaySize(<HTMLCanvasElement>this._gl.canvas);
twgl.resizeCanvasToDisplaySize(<HTMLCanvasElement> this._gl.canvas);
this._gl.viewport(0, 0, this._gl.canvas.width, this._gl.canvas.height);
ArcballCamera.Get.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);
@ -404,7 +400,7 @@ export class Renderer {
const bufferSize = 16384 * 16;
this._registerDebug = new SegmentedBuffer(bufferSize, [
{ name: 'position', numComponents: 3, insertIndex: 0 },
{ name: 'colour', numComponents: 3, insertIndex: 0 }
{ name: 'colour', numComponents: 3, insertIndex: 0 },
]);
this._registerVoxels = new SegmentedBuffer(bufferSize, [
{ name: 'position', numComponents: 3, insertIndex: 0 },
@ -415,11 +411,10 @@ export class Renderer {
]);
this._registerDefault = new SegmentedBuffer(bufferSize, [
{ name: 'position', numComponents: 3, insertIndex: 0 },
//{name: 'colour', numComponents: 3},
{ name: 'normal', numComponents: 3, insertIndex: 0 }
// {name: 'colour', numComponents: 3},
{ name: 'normal', numComponents: 3, insertIndex: 0 },
]);
}
}
module.exports.Renderer = Renderer;
module.exports.Renderer = Renderer;

View File

@ -1,18 +1,17 @@
import * as zlib from "zlib";
import * as fs from "fs";
import { NBT, TagType, writeUncompressed } from "prismarine-nbt";
import { Vector3 } from "./vector";
import { VoxelManager } from "./voxel_manager";
import { Block } from "./block_atlas";
import * as zlib from 'zlib';
import * as fs from 'fs';
import { NBT, TagType, writeUncompressed } from 'prismarine-nbt';
import { Vector3 } from './vector';
import { VoxelManager } from './voxel_manager';
import { Block } from './block_atlas';
export abstract class Exporter {
protected _sizeVector!: Vector3;
protected _sizeVector!: Vector3
abstract convertToNBT(): NBT
public abstract convertToNBT(): NBT
abstract getFormatFilter(): Electron.FileFilter;
abstract getFormatName(): string;
getFormatDisclaimer(): string | undefined {
return;
}
@ -23,7 +22,7 @@ export abstract class Exporter {
const nbt = this.convertToNBT();
const outBuffer = fs.createWriteStream(filePath);
const newBuffer = writeUncompressed(nbt, "big");
const newBuffer = writeUncompressed(nbt, 'big');
zlib.gzip(newBuffer, (err, buffer) => {
if (!err) {
@ -35,17 +34,14 @@ export abstract class Exporter {
return false;
}
}
export class Schematic extends Exporter {
convertToNBT() {
const bufferSize = this._sizeVector.x * this._sizeVector.y * this._sizeVector.z;
let blocksData = Array<number>(bufferSize);
VoxelManager.Get.voxels.forEach(voxel => {
const blocksData = Array<number>(bufferSize);
VoxelManager.Get.voxels.forEach((voxel) => {
const indexVector = Vector3.sub(voxel.position, VoxelManager.Get.min);
const index = this._getBufferIndex(indexVector, this._sizeVector);
blocksData[index] = Block.Stone;
@ -62,8 +58,8 @@ export class Schematic extends Exporter {
Blocks: { type: TagType.ByteArray, value: blocksData },
Data: { type: TagType.ByteArray, value: new Array<number>(bufferSize).fill(0) },
Entities: { type: TagType.List, value: { type: TagType.Int, value: Array(0) } },
TileEntities: { type: TagType.List, value: { type: TagType.Int, value: Array(0) } }
}
TileEntities: { type: TagType.List, value: { type: TagType.Int, value: Array(0) } },
},
};
return nbt;
@ -76,18 +72,17 @@ export class Schematic extends Exporter {
getFormatFilter() {
return {
name: this.getFormatName(),
extensions: ['schematic']
}
extensions: ['schematic'],
};
}
getFormatName() {
return "Schematic";
return 'Schematic';
}
getFormatDisclaimer() {
return "Schematic files only support pre-1.13 blocks. As a result, all blocks will be exported as Stone. To export the blocks, use the .litematic format with the Litematica mod.";
return 'Schematic files only support pre-1.13 blocks. As a result, all blocks will be exported as Stone. To export the blocks, use the .litematic format with the Litematica mod.';
}
}
type BlockID = number;
@ -98,7 +93,6 @@ interface BlockMapping {
}
export class Litematic extends Exporter {
// XZY
_getBufferIndex(vec: Vector3) {
return (this._sizeVector.z * this._sizeVector.x * vec.y) + (this._sizeVector.x * vec.z) + vec.x;
@ -106,8 +100,8 @@ export class Litematic extends Exporter {
_createBlockMapping(): BlockMapping {
const blockPalette = VoxelManager.Get.blockPalette;
let blockMapping: BlockMapping = {"air": 0};
const blockMapping: BlockMapping = { 'air': 0 };
for (let i = 0; i < blockPalette.length; ++i) {
const blockName = blockPalette[i];
blockMapping[blockName] = i + 1; // Ensure 0 maps to air
@ -119,11 +113,11 @@ export class Litematic extends Exporter {
_createBlockBuffer(blockMapping: BlockMapping): Array<BlockID> {
const bufferSize = this._sizeVector.x * this._sizeVector.y * this._sizeVector.z;
let buffer = Array<BlockID>(bufferSize).fill(0);
VoxelManager.Get.voxels.forEach(voxel => {
const buffer = Array<BlockID>(bufferSize).fill(0);
VoxelManager.Get.voxels.forEach((voxel) => {
const indexVector = Vector3.sub(voxel.position, VoxelManager.Get.min);
const index = this._getBufferIndex(indexVector);
buffer[index] = blockMapping[voxel.block || "air"];
buffer[index] = blockMapping[voxel.block || 'air'];
});
return buffer;
@ -132,7 +126,7 @@ export class Litematic extends Exporter {
_createBlockStates(blockMapping: BlockMapping) {
const blockEncoding = this._encodeBlockBuffer(blockMapping);
let blockStates = new Array<long>();
const blockStates = new Array<long>();
for (let i = blockEncoding.length; i > 0; i -= 64) {
let right = parseInt(blockEncoding.substring(i-32, i), 2);
@ -140,55 +134,52 @@ export class Litematic extends Exporter {
// TODO: Cleanup, UINT32 -> INT32
if (right > 2147483647) {
//right = -(-right & 0xFFFFFFFF);
right -= 4294967296;
}
if (left > 2147483647) {
//left = -(-left & 0xFFFFFFFF);
left -= 4294967296;
}
blockStates.push([left, right]);
}
return blockStates;
}
_encodeBlockBuffer(blockMapping: BlockMapping) {
let blockBuffer = this._createBlockBuffer(blockMapping);
const blockBuffer = this._createBlockBuffer(blockMapping);
const paletteSize = Object.keys(blockMapping).length;
let stride = (paletteSize - 1).toString(2).length;
stride = Math.max(2, stride);
let encoding = "";
let encoding = '';
for (let i = blockBuffer.length - 1; i >= 0; --i) {
encoding += blockBuffer[i].toString(2).padStart(stride, "0");
encoding += blockBuffer[i].toString(2).padStart(stride, '0');
}
const requiredLength = Math.ceil(encoding.length / 64) * 64;
encoding = encoding.padStart(requiredLength, "0");
encoding = encoding.padStart(requiredLength, '0');
return encoding;
}
_createBlockStatePalette(blockMapping: BlockMapping) {
let blockStatePalette = Array(Object.keys(blockMapping).length);
const blockStatePalette = Array(Object.keys(blockMapping).length);
for (const block of Object.keys(blockMapping)) {
const index = blockMapping[block];
const blockName = "minecraft:" + block;
const blockName = 'minecraft:' + block;
blockStatePalette[index] = { Name: { type: TagType.String, value: blockName } };
}
blockStatePalette[0] = { Name: { type: TagType.String, value: "minecraft:air" } };
blockStatePalette[0] = { Name: { type: TagType.String, value: 'minecraft:air' } };
return blockStatePalette;
}
convertToNBT() {
const bufferSize = this._sizeVector.x * this._sizeVector.y * this._sizeVector.z;
const blockMapping = this._createBlockMapping();
const blockStates = this._createBlockStates(blockMapping);
const blockStatePalette = this._createBlockStatePalette(blockMapping);
@ -198,16 +189,16 @@ export class Litematic extends Exporter {
value: {
Metadata: {
type: TagType.Compound, value: {
Author: { type: TagType.String, value: "" },
Description: { type: TagType.String, value: "" },
Author: { type: TagType.String, value: '' },
Description: { type: TagType.String, value: '' },
Size: {
type: TagType.Compound, value: {
x: { type: TagType.Int, value: this._sizeVector.x },
y: { type: TagType.Int, value: this._sizeVector.y },
z: { type: TagType.Int, value: this._sizeVector.z },
}
},
},
Name: { type: TagType.String, value: "" },
Name: { type: TagType.String, value: '' },
RegionCount: { type: TagType.Int, value: 1 },
TimeCreated: { type: TagType.Long, value: [0, 0] },
TimeModified: { type: TagType.Long, value: [0, 0] },
@ -226,7 +217,7 @@ export class Litematic extends Exporter {
x: { type: TagType.Int, value: 0 },
y: { type: TagType.Int, value: 0 },
z: { type: TagType.Int, value: 0 },
}
},
},
BlockStatePalette: { type: TagType.List, value: { type: TagType.Compound, value: blockStatePalette } },
Size: {
@ -234,32 +225,31 @@ export class Litematic extends Exporter {
x: { type: TagType.Int, value: this._sizeVector.x },
y: { type: TagType.Int, value: this._sizeVector.y },
z: { type: TagType.Int, value: this._sizeVector.z },
}
},
},
PendingFluidTicks: { type: TagType.List, value: { type: TagType.Int, value: [] } },
TileEntities: { type: TagType.List, value: { type: TagType.Int, value: [] } },
Entities: { type: TagType.List, value: { type: TagType.Int, value: [] } }
}
}
Entities: { type: TagType.List, value: { type: TagType.Int, value: [] } },
},
},
},
},
MinecraftDataVersion: { type: TagType.Int, value: 2730 },
Version: { type: TagType.Int, value: 5 }
}
Version: { type: TagType.Int, value: 5 },
},
};
return nbt;
}
getFormatFilter() {
return {
name: this.getFormatName(),
extensions: ['litematic']
}
extensions: ['litematic'],
};
}
getFormatName() {
return "Litematic";
return 'Litematic';
}
}
}

View File

@ -1,11 +1,9 @@
import * as twgl from "twgl.js";
import * as fs from "fs";
import * as path from "path";
import { Renderer } from "./renderer";
import * as twgl from 'twgl.js';
import * as fs from 'fs';
import * as path from 'path';
import { Renderer } from './renderer';
export class ShaderManager {
public readonly shadedTextureProgram: twgl.ProgramInfo;
public readonly shadedFillProgram: twgl.ProgramInfo;
public readonly debugProgram: twgl.ProgramInfo;
@ -27,7 +25,7 @@ export class ShaderManager {
const shadedVertexFillShader = this._getShader('shaded_vertex_fill.vs');
const shadedFragmentFillShader = this._getShader('shaded_fragment_fill.fs');
this.shadedFillProgram = twgl.createProgramInfo(gl, [shadedVertexFillShader, shadedFragmentFillShader]);
const debugVertexShader = this._getShader('debug_vertex.vs');
const debugFragmentShader = this._getShader('debug_fragment.fs');
this.debugProgram = twgl.createProgramInfo(gl, [debugVertexShader, debugFragmentShader]);
@ -41,5 +39,4 @@ export class ShaderManager {
const absPath = path.join(__dirname, '../shaders/' + filename);
return fs.readFileSync(absPath, 'utf8');
}
}
}

View File

@ -1,51 +1,52 @@
import * as fs from "fs";
import * as jpeg from "jpeg-js";
import { PNG } from "pngjs";
import { UV, RGBA } from "./util";
import * as fs from 'fs';
import * as jpeg from 'jpeg-js';
import { PNG } from 'pngjs';
import { UV, RGBA } from './util';
/* eslint-disable */
export enum TextureFormat {
PNG,
JPEG
PNG,
JPEG
}
/* eslint-enable */
export class Texture {
private _image: {
data: Buffer,
width: number,
height: number
};
private _image: {
data: Buffer,
width: number,
height: number
};
constructor(filename: string, format: TextureFormat) {
try {
const data = fs.readFileSync(filename);
if (format === TextureFormat.PNG) {
this._image = PNG.sync.read(data);
} else {
this._image = jpeg.decode(data);
}
if (this._image.width * this._image.height * 4 !== this._image.data.length) {
throw Error();
}
} catch (err) {
throw Error(`Could not parse ${filename}`);
}
}
constructor(filename: string, format: TextureFormat) {
try {
const data = fs.readFileSync(filename);
if (format === TextureFormat.PNG) {
this._image = PNG.sync.read(data);
} else {
this._image = jpeg.decode(data);
}
if (this._image.width * this._image.height * 4 !== this._image.data.length) {
throw Error();
}
} catch (err) {
throw Error(`Could not parse ${filename}`);
}
}
getRGBA(uv: UV): RGBA {
uv.v = 1 - uv.v;
getRGBA(uv: UV): RGBA {
uv.v = 1 - uv.v;
const x = Math.floor(uv.u * this._image.width);
const y = Math.floor(uv.v * this._image.height);
const x = Math.floor(uv.u * this._image.width);
const y = Math.floor(uv.v * this._image.height);
const index = 4 * (this._image.width * y + x);
const rgba = this._image.data.slice(index, index + 4);
const index = 4 * (this._image.width * y + x);
const rgba = this._image.data.slice(index, index + 4)
return {
r: rgba[0]/255,
g: rgba[1]/255,
b: rgba[2]/255,
a: 1.0
};
}
}
return {
r: rgba[0] / 255,
g: rgba[1] / 255,
b: rgba[2] / 255,
a: 1.0,
};
}
}

View File

@ -1,10 +1,7 @@
import { Vector3 } from "./vector";
import { Vertex } from "./mesh";
import { Bounds } from "./util";
import { Vector3 } from './vector';
import { Vertex } from './mesh';
import { Bounds } from './util';
export class Triangle {
public v0: Vertex;
public v1: Vertex;
public v2: Vertex;
@ -32,7 +29,6 @@ export class Triangle {
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),
}
};
}
}
}

View File

@ -16,8 +16,10 @@ export interface RGB {
}
export function getAverageColour(colours: Array<RGB>) {
let averageColour = colours.reduce((a, c) => { return { r: a.r + c.r, g: a.g + c.g, b: a.b + c.b } });
let n = colours.length;
const averageColour = colours.reduce((a, c) => {
return { r: a.r + c.r, g: a.g + c.g, b: a.b + c.b };
});
const n = colours.length;
averageColour.r /= n;
averageColour.g /= n;
averageColour.b /= n;
@ -47,8 +49,8 @@ export interface Bounds {
maxZ: number,
}
export function assert(condition: boolean, errorMessage: string = "Assertion Failed") {
export function assert(condition: boolean, errorMessage = 'Assertion Failed') {
if (!condition) {
throw Error(errorMessage);
}
}
}

View File

@ -1,7 +1,6 @@
import { Hashable } from "./hash_map";
import { Hashable } from './hash_map';
export class Vector3 extends Hashable {
public x: number;
public y: number;
public z: number;
@ -21,17 +20,19 @@ export class Vector3 extends Hashable {
return new Vector3(
vecA.x + vecB.x,
vecA.y + vecB.y,
vecA.z + vecB.z
vecA.z + vecB.z,
);
}
static parse(line: string) {
var regex = /[+-]?\d+(\.\d+)?/g;
var floats = line.match(regex)!.map(function(v) { return parseFloat(v); });
const regex = /[+-]?\d+(\.\d+)?/g;
const floats = line.match(regex)!.map(function(v) {
return parseFloat(v);
});
return new Vector3(
floats[0], floats[1], floats[2]
)
floats[0], floats[1], floats[2],
);
}
add(vec: Vector3) {
@ -45,7 +46,7 @@ export class Vector3 extends Hashable {
return new Vector3(
vec.x + scalar,
vec.y + scalar,
vec.z + scalar
vec.z + scalar,
);
}
@ -60,7 +61,7 @@ export class Vector3 extends Hashable {
return new Vector3(
vecA.x - vecB.x,
vecA.y - vecB.y,
vecA.z - vecB.z
vecA.z - vecB.z,
);
}
@ -75,7 +76,7 @@ export class Vector3 extends Hashable {
return new Vector3(
vec.x - scalar,
vec.y - scalar,
vec.z - scalar
vec.z - scalar,
);
}
@ -87,7 +88,7 @@ export class Vector3 extends Hashable {
return new Vector3(
vec.x,
vec.y,
vec.z
vec.z,
);
}
@ -95,7 +96,7 @@ export class Vector3 extends Hashable {
return new Vector3(
this.x,
this.y,
this.z
this.z,
);
}
@ -103,7 +104,7 @@ export class Vector3 extends Hashable {
return new Vector3(
scalar * vec.x,
scalar * vec.y,
scalar * vec.z
scalar * vec.z,
);
}
@ -118,7 +119,7 @@ export class Vector3 extends Hashable {
return new Vector3(
vec.x / scalar,
vec.y / scalar,
vec.z / scalar
vec.z / scalar,
);
}
@ -137,7 +138,7 @@ export class Vector3 extends Hashable {
return new Vector3(
Math.round(vec.x),
Math.round(vec.y),
Math.round(vec.z)
Math.round(vec.z),
);
}
@ -152,7 +153,7 @@ export class Vector3 extends Hashable {
return new Vector3(
Math.abs(vec.x),
Math.abs(vec.y),
Math.abs(vec.z)
Math.abs(vec.z),
);
}
@ -160,25 +161,23 @@ export class Vector3 extends Hashable {
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
vecA.x * vecB.y - vecA.y * vecB.x,
);
}
/** Performs element-wise min */
static min(vecA: Vector3, vecB: Vector3) {
return new Vector3(
Math.min(vecA.x, vecB.x),
Math.min(vecA.y, vecB.y),
Math.min(vecA.z, vecB.z)
Math.min(vecA.z, vecB.z),
);
}
/** Performs element-wise max */
static max(vecA: Vector3, vecB: Vector3) {
return new Vector3(
Math.max(vecA.x, vecB.x),
Math.max(vecA.y, vecB.y),
Math.max(vecA.z, vecB.z)
Math.max(vecA.z, vecB.z),
);
}
@ -186,7 +185,7 @@ export class Vector3 extends Hashable {
const p0 = 73856093;
const p1 = 19349663;
const p2 = 83492791;
return (this.x * p0) ^ (this.y * p1) ^ (this.z * p2);
return (this.x * p0) ^ (this.y * p1) ^ (this.z * p2);
}
equals(vec: Vector3) {
@ -205,5 +204,4 @@ export class Vector3 extends Hashable {
return this;
}
}
}

View File

@ -1,14 +1,14 @@
import { Vector3 } from "./vector.js";
import { HashMap } from "./hash_map";
import { Texture } from "./texture";
import { BlockAtlas, BlockInfo, FaceInfo } from "./block_atlas";
import { RGB, getAverageColour } from "./util";
import { Triangle } from "./triangle";
import { Mesh, MaterialType } from "./mesh";
import { triangleArea } from "./math";
import { Axes, generateRays, rayIntersectTriangle } from "./ray";
import { BasicBlockAssigner, OrderedDitheringBlockAssigner } from "./block_assigner.js";
import { AppContext } from "./app_context.js";
import { Vector3 } from './vector.js';
import { HashMap } from './hash_map';
import { Texture } from './texture';
import { BlockAtlas, BlockInfo, FaceInfo } from './block_atlas';
import { RGB, getAverageColour } from './util';
import { Triangle } from './triangle';
import { Mesh, MaterialType } from './mesh';
import { triangleArea } from './math';
import { Axes, generateRays, rayIntersectTriangle } from './ray';
import { BasicBlockAssigner, OrderedDitheringBlockAssigner } from './block_assigner.js';
import { AppContext } from './app_context.js';
interface Block {
position: Vector3;
@ -18,19 +18,17 @@ interface Block {
}
export class VoxelManager {
public voxels: Array<Block>;
public voxelTexcoords: Array<FaceInfo>;
public _voxelSize: number;
public voxelSize: number;
public blockPalette: Array<string>;
public min = new Vector3( Infinity, Infinity, Infinity);
public max = new Vector3(-Infinity, -Infinity, -Infinity);
private voxelsHash: HashMap<Vector3, Block>;
private _voxelsHash: HashMap<Vector3, Block>;
private _blockMode!: MaterialType;
private _currentTexture!: Texture;
private _currentColour!: RGB;
public blockPalette: Array<string>;
public min = new Vector3( Infinity, Infinity, Infinity);
public max = new Vector3(-Infinity, -Infinity, -Infinity);
private static _instance: VoxelManager;
@ -39,32 +37,32 @@ export class VoxelManager {
}
private constructor(voxelSize: number) {
this._voxelSize = voxelSize;
this.voxelSize = voxelSize;
this.voxels = [];
this.voxelTexcoords = [];
this.voxelsHash = new HashMap(2048);
this._voxelsHash = new HashMap(2048);
this.blockPalette = [];
}
public setVoxelSize(voxelSize: number) {
this._voxelSize = voxelSize;
this.voxelSize = voxelSize;
}
private _clearVoxels() {
this.voxels = [];
this.voxelTexcoords = [];
this.blockPalette = [];
this.min = new Vector3( Infinity, Infinity, Infinity);
this.max = new Vector3(-Infinity, -Infinity, -Infinity);
this.voxelsHash = new HashMap(2048);
this._voxelsHash = new HashMap(2048);
}
public isVoxelAt(pos: Vector3) {
return this.voxelsHash.has(pos);
}
return this._voxelsHash.has(pos);
}
private _assignBlock(voxelIndex: number, block: BlockInfo) {
this.voxels[voxelIndex].block = block.name;
@ -82,7 +80,7 @@ export class VoxelManager {
for (let i = 0; i < this.voxels.length; ++i) {
const voxel = this.voxels[i];
const averageColour = getAverageColour(voxel.colours!);
const ditheringEnabled = AppContext.Get.dithering;
@ -96,7 +94,7 @@ export class VoxelManager {
}
meanSquaredError /= this.voxels.length;
console.log("Mean Squared Error:", meanSquaredError);
console.log('Mean Squared Error:', meanSquaredError);
}
public assignBlankBlocks() {
@ -112,13 +110,13 @@ export class VoxelManager {
public addVoxel(pos: Vector3, block: BlockInfo) {
// Is there already a voxel in this position?
let voxel = this.voxelsHash.get(pos);
if (voxel !== undefined) {
let voxel = this._voxelsHash.get(pos);
if (voxel !== undefined) {
voxel.colours!.push(block.colour);
} else {
voxel = {position: pos, colours: [block.colour]};
this.voxels.push(voxel);
this.voxelsHash.add(pos, voxel);
this._voxelsHash.add(pos, voxel);
}
this.min = Vector3.min(this.min, pos);
@ -129,7 +127,7 @@ export class VoxelManager {
if (this._blockMode === MaterialType.Fill) {
return this._currentColour;
}
// TODO: Could cache dist values
const dist01 = Vector3.sub(triangle.v0.position, triangle.v1.position).magnitude();
const dist12 = Vector3.sub(triangle.v1.position, triangle.v2.position).magnitude();
@ -151,34 +149,33 @@ export class VoxelManager {
const uv = {
u: triangle.v0.texcoord.u * w0 + triangle.v1.texcoord.u * w1 + triangle.v2.texcoord.u * w2,
v: triangle.v0.texcoord.v * w0 + triangle.v1.texcoord.v * w1 + triangle.v2.texcoord.v * w2,
}
};
return this._currentTexture.getRGBA(uv);
}
public voxeliseTriangle(triangle: Triangle) {
const voxelSize = VoxelManager.Get._voxelSize;
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);
const rayList = generateRays(v0Scaled, v1Scaled, v2Scaled);
rayList.forEach(ray => {
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;
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;
}
const voxelColour = this._getVoxelColour(triangle, Vector3.mulScalar(voxelPosition, voxelSize));
@ -196,7 +193,7 @@ export class VoxelManager {
voxeliseMesh(mesh: Mesh) {
this._clearVoxels();
mesh.materials.forEach(material => {
mesh.materials.forEach((material) => {
// Setup material
if (material.materialData?.type === MaterialType.Texture) {
this._blockMode = MaterialType.Texture;
@ -206,13 +203,11 @@ export class VoxelManager {
this._blockMode = MaterialType.Fill;
}
// Handle triangles
material.faces.forEach(face => {
material.faces.forEach((face) => {
this.voxeliseTriangle(face);
});
});
this.assignBlankBlocks();
//this.assignBlocks();
}
}
}