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`.
* Run `npm install`.
* 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.

View File

@ -1,13 +1,13 @@
{
"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",
"main": "./dist/main.js",
"engines": {
"node": ">=14.0.0"
},
"scripts": {
"lint": "eslint --fix ./src/*.ts",
"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",

View File

@ -13,6 +13,7 @@ import fs from 'fs';
import { ButtonElement } from './ui/elements/button';
import { LabelElement } from './ui/elements/label';
import { OutputElement } from './ui/elements/output';
import { CustomError } from './util';
/* eslint-disable */
export enum ActionReturnType {
@ -93,7 +94,8 @@ export class AppContext {
components: [
{
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', () => { }),
output: new OutputElement('output2'),
@ -103,7 +105,7 @@ export class AppContext {
components: [
{
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'),
@ -182,9 +184,15 @@ export class AppContext {
public do(action: Action) {
const status = this._actionMap.get(action)!();
if (status) {
this._ui[action].output.setMessage(status.message, status.type);
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 {
this._loadedMesh = new Mesh(objPath, this._gl);
this._loadedMesh = new Mesh(objPath, mtlPath);
this._loadedMesh.loadTextures(this._gl);
} catch (err: unknown) {
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 { Vector3 } from './vector';
import { RGB, UV } from './util';
import { RGB, UV, CustomError } from './util';
import { TextureFormat } from './texture';
import { triangleArea } from './math';
@ -113,32 +113,26 @@ class Material {
export class Mesh {
public materials: Array<Material>;
constructor(objPathString: string, gl: WebGLRenderingContext) {
constructor(objPathString: string, mtlPathString: string) {
// 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.');
}
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 materialString = fs.readFileSync(mtlPathString).toString('utf8');
const parsedMTL = this._parseMaterial(materialString, objPath);
this.materials = this._mergeMaterialData(parsedOBJ, parsedMTL);
this._centreMesh();
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) {

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import { BaseUIElement } from "../layout";
import { assert } from "../../util";
import { BaseUIElement } from '../layout';
import { assert } from '../../util';
export interface ComboBoxItem {
id: string;
@ -15,7 +15,7 @@ export class ComboBoxElement extends BaseUIElement {
}
public generateHTML() {
let itemsHTML = "";
let itemsHTML = '';
for (const item of this._items) {
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;
assert(element !== null);
return this._items[element.selectedIndex].id;
}
protected _onEnabledChanged() {
@ -42,4 +41,4 @@ export class ComboBoxElement extends BaseUIElement {
assert(element !== null);
element.disabled = !this._isEnabled;
}
}
}

View File

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

View File

@ -1,4 +1,4 @@
import { assert } from "../../util";
import { assert } from '../../util';
export class LabelElement {
private _id: string;
@ -22,9 +22,9 @@ export class LabelElement {
assert(element !== null);
if (isEnabled) {
element.classList.remove("sub-left-disabled");
element.classList.remove('sub-left-disabled');
} 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 { ActionReturnType } from "../../app_context";
import { assert } from '../../util';
import { ActionReturnType } from '../../app_context';
export class OutputElement {
private _id: string;
@ -21,12 +20,12 @@ export class OutputElement {
assert(element !== null);
element.innerHTML = message;
element.classList.remove("border-warning");
element.classList.remove("border-error");
element.classList.remove('border-warning');
element.classList.remove('border-error');
if (returnType === ActionReturnType.Warning) {
element.classList.add("border-warning");
element.classList.add('border-warning');
} 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 { assert } from "../../util";
import { clamp } from "../../math";
import { BaseUIElement } from '../layout';
import { assert } from '../../util';
import { clamp } from '../../math';
export class SliderElement extends BaseUIElement {
private _min: number;
@ -36,13 +36,13 @@ export class SliderElement extends BaseUIElement {
this._dragging = true;
};
document.addEventListener("mousemove", (e: any) => {
document.addEventListener('mousemove', (e: any) => {
if (this._dragging) {
this._updateValue(e);
}
});
document.addEventListener("mouseup", (e: any) => {
document.addEventListener('mouseup', (e: any) => {
if (this._dragging) {
this._updateValue(e);
}
@ -56,7 +56,7 @@ export class SliderElement extends BaseUIElement {
}
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);
const mouseEvent = e as MouseEvent;
@ -73,17 +73,17 @@ export class SliderElement extends BaseUIElement {
protected _onEnabledChanged() {
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);
if (this._isEnabled) {
//element.classList.add("button");
element.classList.remove("new-slider-disabled");
elementBar.classList.remove("new-slider-bar-disabled");
// element.classList.add("button");
element.classList.remove('new-slider-disabled');
elementBar.classList.remove('new-slider-bar-disabled');
} else {
element.classList.add("new-slider-disabled");
elementBar.classList.add("new-slider-bar-disabled");
//element.classList.remove("button");
element.classList.add('new-slider-disabled');
elementBar.classList.add('new-slider-bar-disabled');
// element.classList.remove("button");
}
}
}
}

View File

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

View File

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