Last changes for 0.4

This commit is contained in:
Lucas Dower 2022-01-17 19:20:30 +00:00
parent 5f7ad8e21d
commit 37cccb72dd
13 changed files with 93 additions and 86 deletions

View File

@ -11,7 +11,6 @@ You can either download the [latest release](https://github.com/LucasDower/ObjTo
* Navigate to `/ObjToSchematic-main`. * Navigate to `/ObjToSchematic-main`.
* Run `npm install`. * Run `npm install`.
* Run `npm start`. * Run `npm start`.
* Note, for now, all .obj models **must** be triangulated before importing.
Support for choosing the block palette is not yet supported. Instead, you can edit `/tools/default-ignore-list.txt` to include blocks you don't want to be used and then run `npm run-script atlas`. You can also place custom textures in `/tools/blocks/` for more accurate block-colour matching when building with resource packs. Support for choosing the block palette is not yet supported. Instead, you can edit `/tools/default-ignore-list.txt` to include blocks you don't want to be used and then run `npm run-script atlas`. You can also place custom textures in `/tools/blocks/` for more accurate block-colour matching when building with resource packs.

View File

@ -1,13 +1,13 @@
{ {
"name": "objtoschematic", "name": "objtoschematic",
"version": "0.3.0", "version": "0.4.0",
"description": "A tool to convert .obj files into voxels and then into Minecraft Schematic files", "description": "A tool to convert .obj files into voxels and then into Minecraft Schematic files",
"main": "./dist/main.js", "main": "./dist/main.js",
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
}, },
"scripts": { "scripts": {
"lint": "eslint --fix ./src/*.ts", "lint": "eslint --fix ./src/**/*.ts",
"build": "npm run lint && tsc", "build": "npm run lint && tsc",
"start": "npm run build && electron ./dist/main.js --enable-logging", "start": "npm run build && electron ./dist/main.js --enable-logging",
"atlas": "npx ts-node tools/build-atlas.ts", "atlas": "npx ts-node tools/build-atlas.ts",

View File

@ -13,6 +13,7 @@ import fs from 'fs';
import { ButtonElement } from './ui/elements/button'; import { ButtonElement } from './ui/elements/button';
import { LabelElement } from './ui/elements/label'; import { LabelElement } from './ui/elements/label';
import { OutputElement } from './ui/elements/output'; import { OutputElement } from './ui/elements/output';
import { CustomError } from './util';
/* eslint-disable */ /* eslint-disable */
export enum ActionReturnType { export enum ActionReturnType {
@ -93,7 +94,8 @@ export class AppContext {
components: [ components: [
{ {
label: new LabelElement('label3', 'Ratio'), label: new LabelElement('label3', 'Ratio'),
type: new SliderElement('ratio', 0.0, 1.0, 0.01, 0.5) }, type: new SliderElement('ratio', 0.0, 1.0, 0.01, 0.5),
},
], ],
submitButton: new ButtonElement('simplifyMesh', 'Simplify mesh', () => { }), submitButton: new ButtonElement('simplifyMesh', 'Simplify mesh', () => { }),
output: new OutputElement('output2'), output: new OutputElement('output2'),
@ -103,7 +105,7 @@ export class AppContext {
components: [ components: [
{ {
label: new LabelElement('label4', 'Voxel size'), label: new LabelElement('label4', 'Voxel size'),
type: new SliderElement('voxelSize', 0.01, 0.5, 0.01, 0.1), type: new SliderElement('voxelSize', 0.01, 0.5, 0.01, 0.10001),
}, },
{ {
label: new LabelElement('label5', 'Ambient occlusion'), label: new LabelElement('label5', 'Ambient occlusion'),
@ -182,9 +184,15 @@ export class AppContext {
public do(action: Action) { public do(action: Action) {
const status = this._actionMap.get(action)!(); const status = this._actionMap.get(action)!();
if (status) { if (status) {
this._ui[action].output.setMessage(status.message, status.type);
if (status.error) { if (status.error) {
console.error(status.error); console.error('CAUGHT', status.error);
if (status.error instanceof CustomError) {
this._ui[action].output.setMessage(status.error.message, status.type);
} else {
this._ui[action].output.setMessage(status.message, status.type);
}
} else {
this._ui[action].output.setMessage(status.message, status.type);
} }
} }
} }
@ -206,7 +214,7 @@ export class AppContext {
} }
try { try {
this._loadedMesh = new Mesh(objPath, this._gl); this._loadedMesh = new Mesh(objPath, mtlPath);
this._loadedMesh.loadTextures(this._gl); this._loadedMesh.loadTextures(this._gl);
} catch (err: unknown) { } 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 };

View File

@ -4,7 +4,7 @@ import * as path from 'path';
import { Triangle } from './triangle'; import { Triangle } from './triangle';
import { Vector3 } from './vector'; import { Vector3 } from './vector';
import { RGB, UV } from './util'; import { RGB, UV, CustomError } from './util';
import { TextureFormat } from './texture'; import { TextureFormat } from './texture';
import { triangleArea } from './math'; import { triangleArea } from './math';
@ -113,32 +113,26 @@ class Material {
export class Mesh { export class Mesh {
public materials: Array<Material>; public materials: Array<Material>;
constructor(objPathString: string, gl: WebGLRenderingContext) { constructor(objPathString: string, mtlPathString: string) {
// Parse .obj // Parse .obj
const wavefrontString = fs.readFileSync(objPathString).toString('utf8'); const wavefrontString = fs.readFileSync(objPathString).toString('utf8');
const parsedOBJ = this._parseOBJFile(wavefrontString); const parsedOBJ = this._parseOBJFile(wavefrontString);
// TODO: Create blank .mtl when not found
if (!parsedOBJ.mtlPath) {
throw Error('No .mtl file found.');
}
const objPath = path.parse(objPathString); 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 // Parse .mtl
const materialString = fs.readFileSync(parsedOBJ.mtlPath).toString('utf8'); const materialString = fs.readFileSync(mtlPathString).toString('utf8');
const parsedMTL = this._parseMaterial(materialString, objPath); const parsedMTL = this._parseMaterial(materialString, objPath);
this.materials = this._mergeMaterialData(parsedOBJ, parsedMTL); this.materials = this._mergeMaterialData(parsedOBJ, parsedMTL);
this._centreMesh(); this._centreMesh();
this._normaliseMesh(); this._normaliseMesh();
console.log(this.materials); // TODO: Throw at source
for (const material of this.materials) {
if (material.materialData === undefined) {
throw new CustomError('Could not link .obj with .mtl, possible mismatch?');
}
}
} }
private _addMaterial(materialsJSON: Materials, materialName: string, materialDiffuseColour: RGB, materialDiffuseTexturePath: string, materialFormat: TextureFormat) { private _addMaterial(materialsJSON: Materials, materialName: string, materialDiffuseColour: RGB, materialDiffuseTexturePath: string, materialFormat: TextureFormat) {

View File

@ -195,6 +195,8 @@ export class Renderer {
} }
public registerMesh(mesh: Mesh) { public registerMesh(mesh: Mesh) {
this._gl.disable(this._gl.CULL_FACE);
mesh.materials.forEach((material) => { mesh.materials.forEach((material) => {
const materialBuffer = new BottomlessBuffer([ const materialBuffer = new BottomlessBuffer([
{ name: 'position', numComponents: 3 }, { name: 'position', numComponents: 3 },

View File

@ -1,5 +1,5 @@
import { BaseUIElement } from "../layout"; import { BaseUIElement } from '../layout';
import { assert } from "../../util"; import { assert } from '../../util';
export class ButtonElement extends BaseUIElement { export class ButtonElement extends BaseUIElement {
private _label: string; private _label: string;
@ -24,7 +24,7 @@ export class ButtonElement extends BaseUIElement {
const element = document.getElementById(this._id) as HTMLDivElement; const element = document.getElementById(this._id) as HTMLDivElement;
assert(element !== null); assert(element !== null);
element.addEventListener("click", () => { element.addEventListener('click', () => {
if (this._isEnabled) { if (this._isEnabled) {
this._onClick(); this._onClick();
} }
@ -36,11 +36,11 @@ export class ButtonElement extends BaseUIElement {
assert(element !== null); assert(element !== null);
if (this._isEnabled) { if (this._isEnabled) {
//element.classList.add("button"); // element.classList.add("button");
element.classList.remove("button-disabled"); element.classList.remove('button-disabled');
} else { } else {
element.classList.add("button-disabled"); element.classList.add('button-disabled');
//element.classList.remove("button"); // element.classList.remove("button");
} }
} }
} }

View File

@ -1,5 +1,5 @@
import { BaseUIElement } from "../layout"; import { BaseUIElement } from '../layout';
import { assert } from "../../util"; import { assert } from '../../util';
export interface ComboBoxItem { export interface ComboBoxItem {
id: string; id: string;
@ -15,7 +15,7 @@ export class ComboBoxElement extends BaseUIElement {
} }
public generateHTML() { public generateHTML() {
let itemsHTML = ""; let itemsHTML = '';
for (const item of this._items) { for (const item of this._items) {
itemsHTML += `<option value="${item.id}">${item.displayText}</option>`; itemsHTML += `<option value="${item.id}">${item.displayText}</option>`;
} }
@ -34,7 +34,6 @@ export class ComboBoxElement extends BaseUIElement {
const element = document.getElementById(this._id) as HTMLSelectElement; const element = document.getElementById(this._id) as HTMLSelectElement;
assert(element !== null); assert(element !== null);
return this._items[element.selectedIndex].id; return this._items[element.selectedIndex].id;
} }
protected _onEnabledChanged() { protected _onEnabledChanged() {
@ -42,4 +41,4 @@ export class ComboBoxElement extends BaseUIElement {
assert(element !== null); assert(element !== null);
element.disabled = !this._isEnabled; element.disabled = !this._isEnabled;
} }
} }

View File

@ -1,8 +1,8 @@
import { BaseUIElement } from "../layout"; import { BaseUIElement } from '../layout';
import { assert } from "../../util"; import { assert } from '../../util';
import { remote } from "electron"; import { remote } from 'electron';
import * as path from "path"; import * as path from 'path';
export class FileInputElement extends BaseUIElement { export class FileInputElement extends BaseUIElement {
private _fileExtension: string; private _fileExtension: string;
@ -11,7 +11,7 @@ export class FileInputElement extends BaseUIElement {
public constructor(id: string, fileExtension: string) { public constructor(id: string, fileExtension: string) {
super(id); super(id);
this._fileExtension = fileExtension; this._fileExtension = fileExtension;
this._loadedFilePath = ""; this._loadedFilePath = '';
} }
public generateHTML() { public generateHTML() {
@ -32,18 +32,18 @@ export class FileInputElement extends BaseUIElement {
} }
const files = remote.dialog.showOpenDialogSync({ const files = remote.dialog.showOpenDialogSync({
title: "Load file", title: 'Load file',
buttonLabel: "Load", buttonLabel: 'Load',
filters: [{ filters: [{
name: 'Waveform obj file', name: 'Waveform obj file',
extensions: [`${this._fileExtension}`] extensions: [`${this._fileExtension}`],
}] }],
}); });
if (files && files.length === 1) { if (files && files.length === 1) {
const filePath = files[0]; const filePath = files[0];
this._loadedFilePath = filePath; this._loadedFilePath = filePath;
} else { } else {
this._loadedFilePath = ""; this._loadedFilePath = '';
} }
const parsedPath = path.parse(this._loadedFilePath); const parsedPath = path.parse(this._loadedFilePath);
element.innerHTML = parsedPath.name + parsedPath.ext; element.innerHTML = parsedPath.name + parsedPath.ext;
@ -59,11 +59,11 @@ export class FileInputElement extends BaseUIElement {
assert(element !== null); assert(element !== null);
if (this._isEnabled) { if (this._isEnabled) {
//element.classList.add("button"); // element.classList.add("button");
element.classList.remove("input-text-disabled"); element.classList.remove('input-text-disabled');
} else { } else {
element.classList.add("input-text-disabled"); element.classList.add('input-text-disabled');
//element.classList.remove("button"); // element.classList.remove("button");
} }
} }
} }

View File

@ -1,4 +1,4 @@
import { assert } from "../../util"; import { assert } from '../../util';
export class LabelElement { export class LabelElement {
private _id: string; private _id: string;
@ -22,9 +22,9 @@ export class LabelElement {
assert(element !== null); assert(element !== null);
if (isEnabled) { if (isEnabled) {
element.classList.remove("sub-left-disabled"); element.classList.remove('sub-left-disabled');
} else { } else {
element.classList.add("sub-left-disabled"); element.classList.add('sub-left-disabled');
} }
} }
} }

View File

@ -1,6 +1,5 @@
import { BaseUIElement } from "../layout"; import { assert } from '../../util';
import { assert } from "../../util"; import { ActionReturnType } from '../../app_context';
import { ActionReturnType } from "../../app_context";
export class OutputElement { export class OutputElement {
private _id: string; private _id: string;
@ -21,12 +20,12 @@ export class OutputElement {
assert(element !== null); assert(element !== null);
element.innerHTML = message; element.innerHTML = message;
element.classList.remove("border-warning"); element.classList.remove('border-warning');
element.classList.remove("border-error"); element.classList.remove('border-error');
if (returnType === ActionReturnType.Warning) { if (returnType === ActionReturnType.Warning) {
element.classList.add("border-warning"); element.classList.add('border-warning');
} else if (returnType === ActionReturnType.Failure) { } else if (returnType === ActionReturnType.Failure) {
element.classList.add("border-error"); element.classList.add('border-error');
} }
} }
} }

View File

@ -1,6 +1,6 @@
import { BaseUIElement } from "../layout"; import { BaseUIElement } from '../layout';
import { assert } from "../../util"; import { assert } from '../../util';
import { clamp } from "../../math"; import { clamp } from '../../math';
export class SliderElement extends BaseUIElement { export class SliderElement extends BaseUIElement {
private _min: number; private _min: number;
@ -36,13 +36,13 @@ export class SliderElement extends BaseUIElement {
this._dragging = true; this._dragging = true;
}; };
document.addEventListener("mousemove", (e: any) => { document.addEventListener('mousemove', (e: any) => {
if (this._dragging) { if (this._dragging) {
this._updateValue(e); this._updateValue(e);
} }
}); });
document.addEventListener("mouseup", (e: any) => { document.addEventListener('mouseup', (e: any) => {
if (this._dragging) { if (this._dragging) {
this._updateValue(e); this._updateValue(e);
} }
@ -56,7 +56,7 @@ export class SliderElement extends BaseUIElement {
} }
const element = document.getElementById(this._id) as HTMLDivElement; const element = document.getElementById(this._id) as HTMLDivElement;
const elementBar = document.getElementById(this._id + "-bar") as HTMLDivElement; const elementBar = document.getElementById(this._id + '-bar') as HTMLDivElement;
assert(element !== null && elementBar !== null); assert(element !== null && elementBar !== null);
const mouseEvent = e as MouseEvent; const mouseEvent = e as MouseEvent;
@ -73,17 +73,17 @@ export class SliderElement extends BaseUIElement {
protected _onEnabledChanged() { protected _onEnabledChanged() {
const element = document.getElementById(this._id) as HTMLDivElement; const element = document.getElementById(this._id) as HTMLDivElement;
const elementBar = document.getElementById(this._id + "-bar") as HTMLDivElement; const elementBar = document.getElementById(this._id + '-bar') as HTMLDivElement;
assert(element !== null && elementBar !== null); assert(element !== null && elementBar !== null);
if (this._isEnabled) { if (this._isEnabled) {
//element.classList.add("button"); // element.classList.add("button");
element.classList.remove("new-slider-disabled"); element.classList.remove('new-slider-disabled');
elementBar.classList.remove("new-slider-bar-disabled"); elementBar.classList.remove('new-slider-bar-disabled');
} else { } else {
element.classList.add("new-slider-disabled"); element.classList.add('new-slider-disabled');
elementBar.classList.add("new-slider-bar-disabled"); elementBar.classList.add('new-slider-bar-disabled');
//element.classList.remove("button"); // element.classList.remove("button");
} }
} }
} }

View File

@ -1,6 +1,6 @@
import { ButtonElement } from "./elements/button"; import { ButtonElement } from './elements/button';
import { LabelElement } from "./elements/label"; import { LabelElement } from './elements/label';
import { OutputElement } from "./elements/output"; import { OutputElement } from './elements/output';
export interface Group { export interface Group {
label: string; label: string;
@ -47,7 +47,7 @@ function buildSubcomp(subcomp: Component) {
} }
function buildComponent(componentParams: Group) { function buildComponent(componentParams: Group) {
let innerHTML = ""; let innerHTML = '';
for (const subcomp of componentParams.components) { for (const subcomp of componentParams.components) {
innerHTML += buildSubcomp(subcomp); innerHTML += buildSubcomp(subcomp);
} }
@ -77,9 +77,9 @@ export function registerUI(uiGroups: Group[]) {
} }
export function buildUI(myItems: Group[]) { export function buildUI(myItems: Group[]) {
let itemHTML = ""; let itemHTML = '';
for (const item of myItems) { for (const item of myItems) {
itemHTML += ` itemHTML += `
<div class="item item-body"> <div class="item item-body">
<div class="sub-right"> <div class="sub-right">
<div class="h-div"> <div class="h-div">
@ -94,10 +94,10 @@ export function buildUI(myItems: Group[]) {
</div> </div>
</div> </div>
`; `;
itemHTML += buildComponent(item); itemHTML += buildComponent(item);
} }
document.getElementById("properties")!.innerHTML = `<div class="menu"><div class="container"> document.getElementById('properties')!.innerHTML = `<div class="menu"><div class="container">
` + itemHTML + `</div></div>`; ` + itemHTML + `</div></div>`;
}; };
@ -107,4 +107,4 @@ export function setEnabled(group: Group, isEnabled: boolean) {
comp.label.setEnabled(isEnabled); comp.label.setEnabled(isEnabled);
} }
group.submitButton.setEnabled(isEnabled); group.submitButton.setEnabled(isEnabled);
} }

View File

@ -54,3 +54,9 @@ export function assert(condition: boolean, errorMessage = 'Assertion Failed') {
throw Error(errorMessage); throw Error(errorMessage);
} }
} }
export class CustomError extends Error {
constructor(msg: string) {
super(msg);
Object.setPrototypeOf(this, CustomError.prototype);
}
}