mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2024-12-15 02:59:55 +08:00
Last changes for 0.4
This commit is contained in:
parent
5f7ad8e21d
commit
37cccb72dd
@ -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.
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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 };
|
||||
|
24
src/mesh.ts
24
src/mesh.ts
@ -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) {
|
||||
|
@ -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 },
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user