forked from mirror/ObjToSchematic
Fixed headless, improved usability and logging
This commit is contained in:
parent
88e754325d
commit
80a8454fae
@ -31,6 +31,8 @@
|
||||
"no-unused-vars": "warn",
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"block-spacing": [2, "always"],
|
||||
"semi": "error"
|
||||
"semi": "error",
|
||||
"spaced-comment": "off",
|
||||
"keyword-spacing": "off"
|
||||
}
|
||||
}
|
||||
|
@ -7,15 +7,13 @@
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --fix ./src/**/*.ts && eslint --fix ./tools/**/*.ts",
|
||||
"debug": "tsc && electron ./dist/src/main.js --enable-logging",
|
||||
"build": "npm run lint && tsc",
|
||||
"fast-build": "tsc",
|
||||
"lint": "eslint --fix ./**/*.ts",
|
||||
"build": "tsc",
|
||||
"test": "jest --config jestconfig.json",
|
||||
"start": "npm run build && electron ./dist/src/main.js --enable-logging",
|
||||
"atlas": "node ./dist/tools/build-atlas.js",
|
||||
"palette": "node ./dist/tools/build-palette.js",
|
||||
"headless": "node ./dist/tools/headless.js",
|
||||
"headless": "tsc && node ./dist/tools/headless.js",
|
||||
"package:win": "electron-packager . ObjToSchematic --overwrite --platform=win32 --arch=x64 --app-version=0.5.1 --prune=true",
|
||||
"package:linux": "electron-packager . ObjToSchematic --overwrite --platform=linux --arch=x64 --icon=res/static/icon.png --app-version=0.5.1 --prune=true",
|
||||
"package:macos": "electron-packager . ObjToSchematic --overwrite --platform=darwin --arch=x64 --icon=res/static/icon.icns --app-version=0.5.1 --prune=true"
|
||||
|
@ -7,7 +7,7 @@ import { ArcballCamera } from './camera';
|
||||
import path from 'path';
|
||||
import { TWorkerJob, WorkerController } from './worker_controller';
|
||||
import { TFromWorkerMessage, TToWorkerMessage } from './worker_types';
|
||||
import { LOG } from './util/log_util';
|
||||
import { Logger } from './util/log_util';
|
||||
import { ASSERT } from './util/error_util';
|
||||
import { ColourSpace, EAction } from './util';
|
||||
import { AppConfig } from './config';
|
||||
@ -22,6 +22,9 @@ export class AppContext {
|
||||
private _ui: UI;
|
||||
private _workerController: WorkerController;
|
||||
public constructor() {
|
||||
Logger.Get.enableLOG();
|
||||
Logger.Get.enableLOGMAJOR();
|
||||
|
||||
const gl = (<HTMLCanvasElement>document.getElementById('canvas')).getContext('webgl');
|
||||
if (!gl) {
|
||||
throw Error('Could not load WebGL context');
|
||||
@ -48,7 +51,7 @@ export class AppContext {
|
||||
this._ui.enable(action);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const uiOutput = this._ui.getActionOutput(action);
|
||||
|
||||
const jobCallback = (payload: TFromWorkerMessage) => {
|
||||
@ -59,14 +62,14 @@ export class AppContext {
|
||||
uiOutput.setTaskComplete(
|
||||
'action',
|
||||
StatusHandler.Get.getDefaultFailureMessage(action),
|
||||
[ payload.action === 'KnownError' ? payload.error.message : 'Something unexpectedly went wrong' ],
|
||||
'error'
|
||||
[payload.action === 'KnownError' ? payload.error.message : 'Something unexpectedly went wrong'],
|
||||
'error',
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this._ui.enable(action + 1);
|
||||
|
||||
|
||||
const { builder, style } = this._getActionMessageBuilder(action, payload.statusMessages);
|
||||
uiOutput.setMessage(builder, style as OutputStyle);
|
||||
|
||||
@ -75,7 +78,7 @@ export class AppContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._workerController.addJob({
|
||||
id: workerJob.id,
|
||||
@ -86,13 +89,13 @@ export class AppContext {
|
||||
|
||||
private _getActionMessageBuilder(action: EAction, statusMessages: StatusMessage[]) {
|
||||
const infoStatuses = statusMessages
|
||||
.filter(x => x.status === 'info')
|
||||
.map(x => x.message);
|
||||
.filter((x) => x.status === 'info')
|
||||
.map((x) => x.message);
|
||||
const hasInfos = infoStatuses.length > 0;
|
||||
|
||||
const warningStatuses = statusMessages
|
||||
.filter(x => x.status === 'warning')
|
||||
.map(x => x.message);
|
||||
.filter((x) => x.status === 'warning')
|
||||
.map((x) => x.message);
|
||||
const hasWarnings = warningStatuses.length > 0;
|
||||
|
||||
const builder = new UIMessageBuilder();
|
||||
@ -123,15 +126,15 @@ export class AppContext {
|
||||
|
||||
private _import(): TWorkerJob {
|
||||
const uiElements = this._ui.layout.import.elements;
|
||||
|
||||
|
||||
this._ui.getActionOutput(EAction.Import)
|
||||
.setTaskInProgress('action', '[Importer]: Loading...');
|
||||
|
||||
const payload: TToWorkerMessage = {
|
||||
action: 'Import',
|
||||
params: {
|
||||
filepath: uiElements.input.getCachedValue()
|
||||
}
|
||||
filepath: uiElements.input.getCachedValue(),
|
||||
},
|
||||
};
|
||||
|
||||
const callback = (payload: TFromWorkerMessage) => {
|
||||
@ -142,10 +145,10 @@ export class AppContext {
|
||||
|
||||
if (payload.result.triangleCount < AppConfig.RENDER_TRIANGLE_THRESHOLD) {
|
||||
this._workerController.addJob(this._renderMesh());
|
||||
outputElement.setTaskInProgress('render', '[Renderer]: Processing...')
|
||||
outputElement.setTaskInProgress('render', '[Renderer]: Processing...');
|
||||
} else {
|
||||
const message = `Will not render mesh as its over ${AppConfig.RENDER_TRIANGLE_THRESHOLD} triangles.`;
|
||||
outputElement.setTaskComplete('render', '[Renderer]: Stopped.', [ message ], 'warning')
|
||||
outputElement.setTaskComplete('render', '[Renderer]: Stopped.', [message], 'warning');
|
||||
}
|
||||
};
|
||||
|
||||
@ -155,7 +158,7 @@ export class AppContext {
|
||||
private _renderMesh(): TWorkerJob {
|
||||
const payload: TToWorkerMessage = {
|
||||
action: 'RenderMesh',
|
||||
params: {}
|
||||
params: {},
|
||||
};
|
||||
|
||||
const callback = (payload: TFromWorkerMessage) => {
|
||||
@ -167,8 +170,8 @@ export class AppContext {
|
||||
this._ui.getActionOutput(EAction.Import).setTaskComplete(
|
||||
'render',
|
||||
'[Renderer]: Failed',
|
||||
[ payload.action === 'KnownError' ? payload.error.message : 'Something unexpectedly went wrong' ],
|
||||
'error'
|
||||
[payload.action === 'KnownError' ? payload.error.message : 'Something unexpectedly went wrong'],
|
||||
'error',
|
||||
);
|
||||
break;
|
||||
}
|
||||
@ -180,8 +183,8 @@ export class AppContext {
|
||||
'render',
|
||||
'[Renderer]: Succeeded',
|
||||
[],
|
||||
'success'
|
||||
)
|
||||
'success',
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -215,7 +218,7 @@ export class AppContext {
|
||||
const outputElement = this._ui.getActionOutput(EAction.Voxelise);
|
||||
|
||||
this._workerController.addJob(this._renderVoxelMesh());
|
||||
outputElement.setTaskInProgress('render', '[Renderer]: Processing...')
|
||||
outputElement.setTaskInProgress('render', '[Renderer]: Processing...');
|
||||
};
|
||||
|
||||
return { id: 'Voxelise', payload: payload, callback: callback };
|
||||
@ -241,21 +244,21 @@ export class AppContext {
|
||||
this._ui.getActionOutput(EAction.Voxelise).setTaskComplete(
|
||||
'render',
|
||||
'[Renderer]: Failed',
|
||||
[ payload.action === 'KnownError' ? payload.error.message : 'Something unexpectedly went wrong' ],
|
||||
'error'
|
||||
[payload.action === 'KnownError' ? payload.error.message : 'Something unexpectedly went wrong'],
|
||||
'error',
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ASSERT(payload.action === 'RenderVoxelMesh');
|
||||
Renderer.Get.useVoxelMesh(payload.result)
|
||||
Renderer.Get.useVoxelMesh(payload.result);
|
||||
|
||||
this._ui.getActionOutput(EAction.Voxelise).setTaskComplete(
|
||||
'render',
|
||||
'[Renderer]: Succeeded',
|
||||
[],
|
||||
'success'
|
||||
)
|
||||
'success',
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -287,7 +290,7 @@ export class AppContext {
|
||||
const outputElement = this._ui.getActionOutput(EAction.Assign);
|
||||
|
||||
this._workerController.addJob(this._renderBlockMesh());
|
||||
outputElement.setTaskInProgress('render', '[Renderer]: Processing...')
|
||||
outputElement.setTaskInProgress('render', '[Renderer]: Processing...');
|
||||
};
|
||||
|
||||
return { id: 'Assign', payload: payload, callback: callback };
|
||||
@ -312,21 +315,21 @@ export class AppContext {
|
||||
this._ui.getActionOutput(EAction.Assign).setTaskComplete(
|
||||
'render',
|
||||
'[Renderer]: Failed',
|
||||
[ payload.action === 'KnownError' ? payload.error.message : 'Something unexpectedly went wrong' ],
|
||||
'error'
|
||||
[payload.action === 'KnownError' ? payload.error.message : 'Something unexpectedly went wrong'],
|
||||
'error',
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ASSERT(payload.action === 'RenderBlockMesh');
|
||||
Renderer.Get.useBlockMesh(payload.result)
|
||||
Renderer.Get.useBlockMesh(payload.result);
|
||||
|
||||
this._ui.getActionOutput(EAction.Assign).setTaskComplete(
|
||||
'render',
|
||||
'[Renderer]: Succeeded',
|
||||
[],
|
||||
'success'
|
||||
)
|
||||
'success',
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -338,7 +341,7 @@ export class AppContext {
|
||||
const exporterID: TExporters = this._ui.layout.export.elements.export.getCachedValue();
|
||||
const exporter: IExporter = ExporterFactory.GetExporter(exporterID);
|
||||
|
||||
let filepath = remote.dialog.showSaveDialogSync({
|
||||
const filepath = remote.dialog.showSaveDialogSync({
|
||||
title: 'Save structure',
|
||||
buttonLabel: 'Save',
|
||||
filters: [exporter.getFormatFilter()],
|
||||
@ -356,7 +359,7 @@ export class AppContext {
|
||||
params: {
|
||||
filepath: filepath,
|
||||
exporter: exporterID,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const callback = (payload: TFromWorkerMessage) => {
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { Voxel, VoxelMesh } from './voxel_mesh';
|
||||
import { BlockInfo } from './block_atlas';
|
||||
import { ColourSpace, RESOURCES_DIR } from './util';
|
||||
import { Renderer } from './renderer';
|
||||
import { AppConstants } from './constants';
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
@ -14,6 +12,7 @@ import { BlockAssignerFactory, TBlockAssigners } from './assigners/assigners';
|
||||
import { AtlasPalette } from './block_assigner';
|
||||
import { AppError, ASSERT } from './util/error_util';
|
||||
import { AssignParams } from './worker_types';
|
||||
import { BufferGenerator, TBlockMeshBufferDescription } from './buffer';
|
||||
|
||||
interface Block {
|
||||
voxel: Voxel;
|
||||
@ -48,6 +47,7 @@ export class BlockMesh {
|
||||
this._blocks = [];
|
||||
this._voxelMesh = voxelMesh;
|
||||
this._atlas = Atlas.getVanillaAtlas()!;
|
||||
//this._recreateBuffer = true;
|
||||
|
||||
const fallableBlocksString = fs.readFileSync(path.join(RESOURCES_DIR, 'fallable_blocks.json'), 'utf-8');
|
||||
this._fallableBlocks = JSON.parse(fallableBlocksString).fallable_blocks;
|
||||
@ -64,7 +64,7 @@ export class BlockMesh {
|
||||
const atlasPalette = new AtlasPalette(atlas, palette);
|
||||
|
||||
const blockAssigner = BlockAssignerFactory.GetAssigner(blockMeshParams.blockAssigner);
|
||||
|
||||
|
||||
let countFalling = 0;
|
||||
const voxels = this._voxelMesh.getVoxels();
|
||||
for (let voxelIndex = 0; voxelIndex < voxels.length; ++voxelIndex) {
|
||||
@ -73,7 +73,7 @@ export class BlockMesh {
|
||||
|
||||
const isFallable = this._fallableBlocks.includes(block.name);
|
||||
const isSupported = this._voxelMesh.isVoxelAt(Vector3.add(voxel.position, new Vector3(0, -1, 0)));
|
||||
|
||||
|
||||
if (isFallable && !isSupported) {
|
||||
++countFalling;
|
||||
}
|
||||
@ -116,63 +116,26 @@ export class BlockMesh {
|
||||
return this._voxelMesh;
|
||||
}
|
||||
|
||||
/*
|
||||
public createBuffer() {
|
||||
ASSERT(this._blocks.length === this._voxelMesh.getVoxelCount());
|
||||
|
||||
// FIXME: Too hacky
|
||||
const voxelBufferRaw = (typeof window === 'undefined') ? this._voxelMesh.createBuffer(false) : Renderer.Get._voxelBufferRaw!;
|
||||
|
||||
const numBlocks = this._blocks.length;
|
||||
const newBuffer = {
|
||||
position: {
|
||||
numComponents: AppConstants.ComponentSize.POSITION,
|
||||
data: voxelBufferRaw.position.data,
|
||||
},
|
||||
colour: {
|
||||
numComponents: AppConstants.ComponentSize.COLOUR,
|
||||
data: voxelBufferRaw.colour.data,
|
||||
},
|
||||
occlusion: {
|
||||
numComponents: AppConstants.ComponentSize.OCCLUSION,
|
||||
data: voxelBufferRaw.occlusion.data,
|
||||
},
|
||||
texcoord: {
|
||||
numComponents: AppConstants.ComponentSize.TEXCOORD,
|
||||
data: voxelBufferRaw.texcoord.data,
|
||||
},
|
||||
normal: {
|
||||
numComponents: AppConstants.ComponentSize.NORMAL,
|
||||
data: voxelBufferRaw.normal.data,
|
||||
},
|
||||
indices: {
|
||||
numComponents: AppConstants.ComponentSize.INDICES,
|
||||
data: voxelBufferRaw.indices.data,
|
||||
},
|
||||
blockTexcoord: {
|
||||
numComponents: AppConstants.ComponentSize.TEXCOORD,
|
||||
data: new Float32Array(numBlocks * AppConstants.VoxelMeshBufferComponentOffsets.TEXCOORD),
|
||||
},
|
||||
};
|
||||
|
||||
const faceOrder = ['north', 'south', 'up', 'down', 'east', 'west'];
|
||||
let insertIndex = 0;
|
||||
for (let i = 0; i < numBlocks; ++i) {
|
||||
for (let f = 0; f < AppConstants.FACES_PER_VOXEL; ++f) {
|
||||
const faceName = faceOrder[f];
|
||||
const texcoord = this._blocks[i].blockInfo.faces[faceName].texcoord;
|
||||
for (let v = 0; v < AppConstants.VERTICES_PER_FACE; ++v) {
|
||||
newBuffer.blockTexcoord.data[insertIndex++] = texcoord.u;
|
||||
newBuffer.blockTexcoord.data[insertIndex++] = texcoord.v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newBuffer;
|
||||
}
|
||||
*/
|
||||
|
||||
public getAtlas() {
|
||||
return this._atlas;
|
||||
}
|
||||
|
||||
/*
|
||||
private _renderParams?: RenderBlockMeshParams.Input;
|
||||
private _recreateBuffer: boolean;
|
||||
public setRenderParams(params: RenderBlockMeshParams.Input) {
|
||||
this._renderParams = params;
|
||||
this._recreateBuffer = true;
|
||||
}
|
||||
*/
|
||||
|
||||
private _buffer?: TBlockMeshBufferDescription;
|
||||
public getBuffer(): TBlockMeshBufferDescription {
|
||||
//ASSERT(this._renderParams, 'Called BlockMesh.getBuffer() without setting render params');
|
||||
if (this._buffer === undefined) {
|
||||
this._buffer = BufferGenerator.fromBlockMesh(this);
|
||||
//this._recreateBuffer = false;
|
||||
}
|
||||
return this._buffer;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Vector3 } from "./vector";
|
||||
import { Vector3 } from './vector';
|
||||
|
||||
/**
|
||||
* A 3D cuboid volume defined by two opposing corners
|
||||
*/
|
||||
export class Bounds {
|
||||
export class Bounds {
|
||||
private _min: Vector3;
|
||||
private _max: Vector3;
|
||||
|
||||
@ -45,4 +45,4 @@ import { Vector3 } from "./vector";
|
||||
public getDimensions() {
|
||||
return Vector3.sub(this._max, this._min);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { GeometryTemplates } from "./geometry";
|
||||
import { Mesh, SolidMaterial, TexturedMaterial } from "./mesh";
|
||||
import { AttributeData } from "./render_buffer";
|
||||
import { Vector3 } from "./vector";
|
||||
import { VoxelMesh } from "./voxel_mesh";
|
||||
import { AppConstants } from "./constants";
|
||||
import { RenderVoxelMeshParams } from "./worker_types";
|
||||
import { OcclusionManager } from "./occlusion";
|
||||
import { BlockMesh } from "./block_mesh";
|
||||
import { GeometryTemplates } from './geometry';
|
||||
import { Mesh, SolidMaterial, TexturedMaterial } from './mesh';
|
||||
import { AttributeData } from './render_buffer';
|
||||
import { Vector3 } from './vector';
|
||||
import { VoxelMesh } from './voxel_mesh';
|
||||
import { AppConstants } from './constants';
|
||||
import { RenderVoxelMeshParams } from './worker_types';
|
||||
import { OcclusionManager } from './occlusion';
|
||||
import { BlockMesh } from './block_mesh';
|
||||
|
||||
export type TMeshBuffer = {
|
||||
position: { numComponents: 3, data: Float32Array },
|
||||
@ -53,7 +53,6 @@ export type TBlockMeshBufferDescription = {
|
||||
type TMaterialID = string;
|
||||
|
||||
export class BufferGenerator {
|
||||
|
||||
public static fromMesh(mesh: Mesh): TMeshBufferDescription[] {
|
||||
// Count the number of triangles that use each material
|
||||
const materialTriangleCount = new Map<TMaterialID, number>();
|
||||
@ -168,15 +167,11 @@ export class BufferGenerator {
|
||||
};
|
||||
}
|
||||
|
||||
public static fromBlockMesh(blockMesh: BlockMesh, voxelMeshBuffer: TVoxelMeshBuffer): TBlockMeshBufferDescription {
|
||||
//ASSERT(this._blocks.length === this._voxelMesh.getVoxelCount());
|
||||
|
||||
//const voxelBufferRaw = (typeof window === 'undefined') ? this._voxelMesh.createBuffer(false) : Renderer.Get._voxelBufferRaw!;
|
||||
|
||||
public static fromBlockMesh(blockMesh: BlockMesh): TBlockMeshBufferDescription {
|
||||
const blocks = blockMesh.getBlocks();
|
||||
const numBlocks = blocks.length;
|
||||
|
||||
const newBuffer = this.createBlockMeshBuffer(numBlocks, voxelMeshBuffer);
|
||||
const newBuffer = this.createBlockMeshBuffer(numBlocks, blockMesh.getVoxelMesh().getBuffer().buffer);
|
||||
|
||||
const faceOrder = ['north', 'south', 'up', 'down', 'east', 'west'];
|
||||
let insertIndex = 0;
|
||||
@ -279,5 +274,4 @@ export class BufferGenerator {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ASSERT } from "./util/error_util";
|
||||
import { LOG } from "./util/log_util";
|
||||
import { ASSERT } from './util/error_util';
|
||||
import { LOG } from './util/log_util';
|
||||
|
||||
/* eslint-disable */
|
||||
export enum EAppEvent {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Vector3 } from '../vector';
|
||||
import { BlockMesh } from '../block_mesh';
|
||||
import { TOptional } from '../util';
|
||||
import { TBlockMeshBuffer } from '../buffer';
|
||||
|
||||
export abstract class IExporter {
|
||||
protected _sizeVector!: Vector3;
|
||||
@ -19,7 +18,7 @@ export abstract class IExporter {
|
||||
return;
|
||||
}
|
||||
|
||||
public abstract export(blockMesh: BlockMesh, filePath: string, blockMeshBuffer: TBlockMeshBuffer): boolean;
|
||||
public abstract export(blockMesh: BlockMesh, filePath: string): boolean;
|
||||
|
||||
public getFormatFilter() {
|
||||
return {
|
||||
|
@ -4,7 +4,6 @@ import { ASSERT } from '../util/error_util';
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { TBlockMeshBuffer } from '../buffer';
|
||||
|
||||
export class ObjExporter extends IExporter {
|
||||
public override getFormatFilter(): Electron.FileFilter {
|
||||
@ -22,32 +21,34 @@ export class ObjExporter extends IExporter {
|
||||
return 'Wavefront OBJ';
|
||||
}
|
||||
|
||||
public override export(blockMesh: BlockMesh, filepath: string, blockMeshBuffer: TBlockMeshBuffer) {
|
||||
public override export(blockMesh: BlockMesh, filepath: string) {
|
||||
ASSERT(path.isAbsolute(filepath));
|
||||
const parsedPath = path.parse(filepath);
|
||||
|
||||
|
||||
const filepathOBJ = filepath;
|
||||
const filepathMTL = path.join(parsedPath.dir, parsedPath.name + '.mtl');
|
||||
const filepathTexture = path.join(parsedPath.dir, parsedPath.name + '.png');
|
||||
|
||||
this._exportOBJ(filepathOBJ, blockMesh, blockMeshBuffer, parsedPath.name + '.mtl');
|
||||
this._exportOBJ(filepathOBJ, blockMesh, parsedPath.name + '.mtl');
|
||||
this._exportMTL(filepathMTL, filepathTexture, blockMesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private _exportOBJ(filepath: string, blockMesh: BlockMesh, buffer: TBlockMeshBuffer, mtlRelativePath: string) {
|
||||
private _exportOBJ(filepath: string, blockMesh: BlockMesh, mtlRelativePath: string) {
|
||||
const buffer = blockMesh.getBuffer().buffer;
|
||||
|
||||
const positionData = buffer.position.data as Float32Array;
|
||||
const normalData = buffer.normal.data as Float32Array;
|
||||
const texcoordData = buffer.texcoord.data as Float32Array;
|
||||
const blockTexcoordData = buffer.blockTexcoord.data as Float32Array;
|
||||
const indexData = buffer.indices.data as Uint32Array;
|
||||
|
||||
|
||||
const writeStream = fs.createWriteStream(filepath);
|
||||
|
||||
writeStream.write('# Created with ObjToSchematic\n');
|
||||
writeStream.write('# https://github.com/LucasDower/ObjToSchematic/\n\n');
|
||||
|
||||
|
||||
if (positionData && normalData && texcoordData && indexData && blockTexcoordData) {
|
||||
const numTris = indexData.length / 3;
|
||||
// Add vertex data
|
||||
@ -90,7 +91,7 @@ export class ObjExporter extends IExporter {
|
||||
const mtlData: string[] = [];
|
||||
mtlData.push('# Created with ObjToSchematic');
|
||||
mtlData.push('# https://github.com/LucasDower/ObjToSchematic/');
|
||||
|
||||
|
||||
mtlData.push('newmtl Default');
|
||||
mtlData.push('Kd 1.000000 1.000000 1.000000');
|
||||
mtlData.push(`map_Kd ${filepathTexture}`);
|
||||
|
@ -34,7 +34,7 @@ export interface TexturedMaterial {
|
||||
alphaPath?: string;
|
||||
alphaFactor: number;
|
||||
}
|
||||
export type MaterialMap = {[key: string]: (SolidMaterial | TexturedMaterial)};
|
||||
export type MaterialMap = { [key: string]: (SolidMaterial | TexturedMaterial) };
|
||||
|
||||
export class Mesh {
|
||||
public readonly id: string;
|
||||
@ -43,7 +43,7 @@ export class Mesh {
|
||||
public _normals!: Vector3[];
|
||||
public _uvs!: UV[];
|
||||
public _tris!: Tri[];
|
||||
|
||||
|
||||
private _materials!: MaterialMap;
|
||||
private _loadedTextures: { [materialName: string]: Texture };
|
||||
public static desiredHeight = 8.0;
|
||||
@ -170,7 +170,7 @@ export class Mesh {
|
||||
colour: RGBAColours.WHITE,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Check texture paths are absolute and exist
|
||||
for (const materialName in this._materials) {
|
||||
const material = this._materials[materialName];
|
||||
@ -208,7 +208,7 @@ export class Mesh {
|
||||
centre.divScalar(totalWeight);
|
||||
*/
|
||||
const centre = this.getBounds().getCentre();
|
||||
|
||||
|
||||
if (!centre.isNumber()) {
|
||||
throw new AppError('Could not find centre of mesh');
|
||||
}
|
||||
|
@ -3,15 +3,11 @@ import { ArcballCamera } from './camera';
|
||||
import { ShaderManager } from './shaders';
|
||||
import { RenderBuffer } from './render_buffer';
|
||||
import { DebugGeometryTemplates } from './geometry';
|
||||
import { Mesh, SolidMaterial, TexturedMaterial, MaterialType } from './mesh';
|
||||
import { VoxelMesh } from './voxel_mesh';
|
||||
import { BlockMesh } from './block_mesh';
|
||||
import { SolidMaterial, TexturedMaterial, MaterialType } from './mesh';
|
||||
|
||||
import * as twgl from 'twgl.js';
|
||||
import { RGBA, RGBAUtil } from './colour';
|
||||
import { Texture } from './texture';
|
||||
import { LOG } from './util/log_util';
|
||||
import { TMeshBufferDescription } from './buffer';
|
||||
import { RenderBlockMeshParams, RenderMeshParams, RenderVoxelMeshParams } from './worker_types';
|
||||
|
||||
/* eslint-disable */
|
||||
@ -55,7 +51,7 @@ export class Renderer {
|
||||
numElements: number,
|
||||
}>;
|
||||
public _voxelBuffer?: twgl.BufferInfo;
|
||||
public _voxelBufferRaw?: {[attribute: string]: { numComponents: number, data: Float32Array | Uint32Array }};
|
||||
public _voxelBufferRaw?: { [attribute: string]: { numComponents: number, data: Float32Array | Uint32Array } };
|
||||
private _blockBuffer?: twgl.BufferInfo;
|
||||
private _debugBuffers: { [meshType: string]: { [bufferComponent: string]: RenderBuffer } };
|
||||
private _axisBuffer: RenderBuffer;
|
||||
@ -64,9 +60,9 @@ export class Renderer {
|
||||
private _axesEnabled: boolean;
|
||||
|
||||
private _gridBuffers: {
|
||||
x: { [meshType: string]: RenderBuffer};
|
||||
y: { [meshType: string]: RenderBuffer};
|
||||
z: { [meshType: string]: RenderBuffer};
|
||||
x: { [meshType: string]: RenderBuffer };
|
||||
y: { [meshType: string]: RenderBuffer };
|
||||
z: { [meshType: string]: RenderBuffer };
|
||||
};
|
||||
private _gridEnabled: boolean;
|
||||
|
||||
@ -167,7 +163,7 @@ export class Renderer {
|
||||
this.setModelToUse(MeshType.None);
|
||||
}
|
||||
|
||||
public useMesh(params: RenderMeshParams.Output) {
|
||||
public useMesh(params: RenderMeshParams.Output) {
|
||||
this._materialBuffers = [];
|
||||
|
||||
for (const { material, buffer, numElements } of params.buffers) {
|
||||
@ -226,23 +222,23 @@ export class Renderer {
|
||||
this._gridBuffers.x[MeshType.VoxelMesh] = DebugGeometryTemplates.gridX(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
||||
this._gridBuffers.y[MeshType.VoxelMesh] = DebugGeometryTemplates.gridY(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
||||
this._gridBuffers.z[MeshType.VoxelMesh] = DebugGeometryTemplates.gridZ(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
||||
|
||||
|
||||
this._modelsAvailable = 2;
|
||||
this.setModelToUse(MeshType.VoxelMesh);
|
||||
}
|
||||
|
||||
|
||||
public useBlockMesh(params: RenderBlockMeshParams.Output) {
|
||||
this._blockBuffer = twgl.createBufferInfoFromArrays(this._gl, params.buffer.buffer);
|
||||
|
||||
|
||||
this._atlasTexture = twgl.createTexture(this._gl, {
|
||||
src: params.atlasTexturePath,
|
||||
mag: this._gl.NEAREST,
|
||||
});
|
||||
|
||||
this._atlasSize = params.atlasSize,
|
||||
|
||||
this._atlasSize = params.atlasSize;
|
||||
|
||||
this._gridBuffers.y[MeshType.BlockMesh] = this._gridBuffers.y[MeshType.VoxelMesh];
|
||||
|
||||
|
||||
this._modelsAvailable = 3;
|
||||
this.setModelToUse(MeshType.BlockMesh);
|
||||
}
|
||||
@ -382,7 +378,7 @@ export class Renderer {
|
||||
}
|
||||
|
||||
private _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);
|
||||
@ -403,7 +399,7 @@ export class Renderer {
|
||||
public getModelsAvailable() {
|
||||
return this._modelsAvailable;
|
||||
}
|
||||
|
||||
|
||||
public getActiveMeshType() {
|
||||
return this._meshToUse;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { EAction } from './util';
|
||||
import { ASSERT } from './util/error_util';
|
||||
import { LOG, LOG_WARN } from './util/log_util';
|
||||
import { LOG, LOG_MAJOR, LOG_WARN } from './util/log_util';
|
||||
|
||||
export type StatusType = 'warning' | 'info';
|
||||
|
||||
@ -15,7 +15,7 @@ export class StatusHandler {
|
||||
public static get Get() {
|
||||
return this._instance || (this._instance = new this());
|
||||
}
|
||||
|
||||
|
||||
private _statusMessages: StatusMessage[];
|
||||
|
||||
private constructor() {
|
||||
@ -36,7 +36,7 @@ export class StatusHandler {
|
||||
}
|
||||
|
||||
public getStatusMessages(statusType: StatusType): string[] {
|
||||
const messagesToReturn = (statusType !== undefined) ? this._statusMessages.filter((m) => m.status === statusType ): this._statusMessages;
|
||||
const messagesToReturn = (statusType !== undefined) ? this._statusMessages.filter((m) => m.status === statusType) : this._statusMessages;
|
||||
return messagesToReturn.map((m) => m.message);
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ export class StatusHandler {
|
||||
case EAction.Export:
|
||||
return '[Exporter]: Saved';
|
||||
default:
|
||||
ASSERT(false)
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,4 +73,15 @@ export class StatusHandler {
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
public dump() {
|
||||
for (const { message, status } of this._statusMessages) {
|
||||
if (status === 'warning') {
|
||||
LOG_WARN(message);
|
||||
} else {
|
||||
LOG_MAJOR(' - ' + message);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -66,4 +66,4 @@ export const TESTS_DATA_DIR = PathUtil.join(BASE_DIR, './tests/data/');
|
||||
|
||||
export function getRandomID(): string {
|
||||
return (Math.random() + 1).toString(36).substring(7);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,63 @@
|
||||
/* eslint-disable */
|
||||
export const LOG = console.log;
|
||||
|
||||
/**
|
||||
* Performs console.log if logging LOG is enabled
|
||||
*/
|
||||
export const LOG = (...data: any[]) => {
|
||||
if (Logger.Get.isLOGEnabled()) {
|
||||
console.log(...data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs console.log if logging LOG_MAJOR is enabled
|
||||
*/
|
||||
export const LOG_MAJOR = (...data: any[]) => {
|
||||
if (Logger.Get.isLOGMAJOREnabled()) {
|
||||
console.log(...data);
|
||||
}
|
||||
}
|
||||
|
||||
export const LOG_WARN = console.warn;
|
||||
export const LOG_ERROR = console.error;
|
||||
export const TIME_START = console.time;
|
||||
export const TIME_END = console.timeEnd;
|
||||
/* eslint-enable */
|
||||
|
||||
export class Logger {
|
||||
/* Singleton */
|
||||
private static _instance: Logger;
|
||||
public static get Get() {
|
||||
return this._instance || (this._instance = new this());
|
||||
}
|
||||
|
||||
private _enabledLOG = false;
|
||||
private _enabledLOGMAJOR = false;
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
public enableLOG() {
|
||||
this._enabledLOG = true;
|
||||
}
|
||||
|
||||
public disableLOG() {
|
||||
this._enabledLOG = false;
|
||||
}
|
||||
|
||||
public enableLOGMAJOR() {
|
||||
this._enabledLOGMAJOR = true;
|
||||
}
|
||||
|
||||
public disableLOGMAJOR() {
|
||||
this._enabledLOGMAJOR = false;
|
||||
}
|
||||
|
||||
public isLOGEnabled() {
|
||||
return this._enabledLOG;
|
||||
}
|
||||
|
||||
public isLOGMAJOREnabled() {
|
||||
return this._enabledLOGMAJOR;
|
||||
}
|
||||
}
|
||||
|
@ -263,4 +263,4 @@ export const fastCrossYAxis = (vec: Vector3) => {
|
||||
|
||||
export const fastCrossZAxis = (vec: Vector3) => {
|
||||
return new Vector3(-vec.y, vec.x, 0.0);
|
||||
};
|
||||
};
|
||||
|
@ -1,14 +1,12 @@
|
||||
import { Bounds } from './bounds';
|
||||
import { AttributeData } from './render_buffer';
|
||||
import { RGBA } from './colour';
|
||||
import { AppConstants } from './constants';
|
||||
import { GeometryTemplates } from './geometry';
|
||||
import { HashMap } from './hash_map';
|
||||
import { OcclusionManager } from './occlusion';
|
||||
import { TOptional } from './util';
|
||||
import { ASSERT } from './util/error_util';
|
||||
import { Vector3 } from './vector';
|
||||
import { VoxeliseParams } from './worker_types';
|
||||
import { RenderVoxelMeshParams, VoxeliseParams } from './worker_types';
|
||||
import { BufferGenerator, TVoxelMeshBufferDescription } from './buffer';
|
||||
|
||||
export interface Voxel {
|
||||
position: Vector3;
|
||||
@ -18,7 +16,7 @@ export interface Voxel {
|
||||
|
||||
export type TVoxelOverlapRule = 'first' | 'average';
|
||||
|
||||
export type TVoxelMeshParams = Pick<VoxeliseParams.Input, "voxelOverlapRule" | "calculateNeighbours">;
|
||||
export type TVoxelMeshParams = Pick<VoxeliseParams.Input, 'voxelOverlapRule' | 'calculateNeighbours'>;
|
||||
|
||||
export class VoxelMesh {
|
||||
private _voxels: (Voxel & { collisions: number })[];
|
||||
@ -33,6 +31,7 @@ export class VoxelMesh {
|
||||
this._neighbourMap = new Map();
|
||||
this._bounds = Bounds.getInfiniteBounds();
|
||||
this._voxelMeshParams = voxelMeshParams;
|
||||
this._recreateBuffer = true;
|
||||
}
|
||||
|
||||
public getVoxels() {
|
||||
@ -149,4 +148,21 @@ export class VoxelMesh {
|
||||
public hasNeighbour(pos: Vector3, offset: Vector3): boolean {
|
||||
return (this.getNeighbours(pos).value & (1 << OcclusionManager.getNeighbourIndex(offset))) > 0;
|
||||
}
|
||||
|
||||
private _renderParams?: RenderVoxelMeshParams.Input;
|
||||
private _recreateBuffer: boolean;
|
||||
public setRenderParams(params: RenderVoxelMeshParams.Input) {
|
||||
this._renderParams = params;
|
||||
this._recreateBuffer = true;
|
||||
}
|
||||
|
||||
private _buffer?: TVoxelMeshBufferDescription;
|
||||
public getBuffer(): TVoxelMeshBufferDescription {
|
||||
ASSERT(this._renderParams, 'Called VoxelMesh.getBuffer() without setting render params');
|
||||
if (this._buffer === undefined || this._recreateBuffer) {
|
||||
this._buffer = BufferGenerator.fromVoxelMesh(this, this._renderParams);
|
||||
this._recreateBuffer = false;
|
||||
}
|
||||
return this._buffer;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { clamp } from './math';
|
||||
import { WorkerClient } from './worker_client';
|
||||
import { TToWorkerMessage, TFromWorkerMessage } from './worker_types';
|
||||
import { StatusHandler } from './status';
|
||||
@ -20,7 +19,7 @@ export function doWork(message: TToWorkerMessage): TFromWorkerMessage {
|
||||
result: WorkerClient.Get.renderMesh(message.params),
|
||||
statusMessages: StatusHandler.Get.getAllStatusMessages(),
|
||||
};
|
||||
case 'Voxelise':
|
||||
case 'Voxelise':
|
||||
return {
|
||||
action: 'Voxelise',
|
||||
result: WorkerClient.Get.voxelise(message.params),
|
||||
@ -32,7 +31,7 @@ export function doWork(message: TToWorkerMessage): TFromWorkerMessage {
|
||||
result: WorkerClient.Get.renderVoxelMesh(message.params),
|
||||
statusMessages: StatusHandler.Get.getAllStatusMessages(),
|
||||
};
|
||||
case 'Assign':
|
||||
case 'Assign':
|
||||
return {
|
||||
action: 'Assign',
|
||||
result: WorkerClient.Get.assign(message.params),
|
||||
@ -44,7 +43,7 @@ export function doWork(message: TToWorkerMessage): TFromWorkerMessage {
|
||||
result: WorkerClient.Get.renderBlockMesh(message.params),
|
||||
statusMessages: StatusHandler.Get.getAllStatusMessages(),
|
||||
};
|
||||
case 'Export':
|
||||
case 'Export':
|
||||
return {
|
||||
action: 'Export',
|
||||
result: WorkerClient.Get.export(message.params),
|
||||
@ -55,7 +54,6 @@ export function doWork(message: TToWorkerMessage): TFromWorkerMessage {
|
||||
return { action: e instanceof AppError ? 'KnownError' : 'UnknownError', error: e as Error };
|
||||
}
|
||||
|
||||
return { action: 'KnownError', error: new AppError('Worker could not handle message') };
|
||||
|
||||
return { action: 'KnownError', error: new AppError('Worker could not handle message') };
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,15 @@
|
||||
import { RenderBuffer } from "./render_buffer";
|
||||
import { GeometryTemplates } from "./geometry";
|
||||
import { ObjImporter } from "./importers/obj_importer";
|
||||
import { MaterialType, Mesh, SolidMaterial, TexturedMaterial } from "./mesh";
|
||||
import { ASSERT } from "./util/error_util";
|
||||
import { AssignParams, ExportParams, ImportParams, RenderBlockMeshParams, RenderMeshParams, RenderVoxelMeshParams, VoxeliseParams } from "./worker_types";
|
||||
import { BufferGenerator, TBlockMeshBuffer, TVoxelMeshBuffer } from "./buffer";
|
||||
import { TVoxelisers, VoxeliserFactory } from "./voxelisers/voxelisers";
|
||||
import { param } from "jquery";
|
||||
import { IVoxeliser } from "./voxelisers/base-voxeliser";
|
||||
import { TIME_END, TIME_START } from "./util/log_util";
|
||||
import { VoxelMesh } from "./voxel_mesh";
|
||||
import { BlockMesh } from "./block_mesh";
|
||||
import { Atlas } from "./atlas";
|
||||
import { ExporterFactory } from "./exporters/exporters";
|
||||
import { IExporter } from "./exporters/base_exporter";
|
||||
import { ObjImporter } from './importers/obj_importer';
|
||||
import { Mesh } from './mesh';
|
||||
import { ASSERT } from './util/error_util';
|
||||
import { AssignParams, ExportParams, ImportParams, RenderBlockMeshParams, RenderMeshParams, RenderVoxelMeshParams, VoxeliseParams } from './worker_types';
|
||||
import { BufferGenerator } from './buffer';
|
||||
import { VoxeliserFactory } from './voxelisers/voxelisers';
|
||||
import { IVoxeliser } from './voxelisers/base-voxeliser';
|
||||
import { VoxelMesh } from './voxel_mesh';
|
||||
import { BlockMesh } from './block_mesh';
|
||||
import { Atlas } from './atlas';
|
||||
import { ExporterFactory } from './exporters/exporters';
|
||||
import { IExporter } from './exporters/base_exporter';
|
||||
|
||||
export class WorkerClient {
|
||||
private static _instance: WorkerClient;
|
||||
@ -25,9 +21,6 @@ export class WorkerClient {
|
||||
private _loadedVoxelMesh?: VoxelMesh;
|
||||
private _loadedBlockMesh?: BlockMesh;
|
||||
|
||||
private _voxelMeshBuffer?: TVoxelMeshBuffer;
|
||||
private _blockMeshBuffer?: TBlockMeshBuffer;
|
||||
|
||||
public import(params: ImportParams.Input): ImportParams.Output {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseFile(params.filepath);
|
||||
@ -50,22 +43,21 @@ export class WorkerClient {
|
||||
|
||||
public voxelise(params: VoxeliseParams.Input): VoxeliseParams.Output {
|
||||
ASSERT(this._loadedMesh !== undefined);
|
||||
|
||||
|
||||
const voxeliser: IVoxeliser = VoxeliserFactory.GetVoxeliser(params.voxeliser);
|
||||
this._loadedVoxelMesh = voxeliser.voxelise(this._loadedMesh, params);
|
||||
|
||||
return {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public renderVoxelMesh(params: RenderVoxelMeshParams.Input): RenderVoxelMeshParams.Output {
|
||||
ASSERT(this._loadedVoxelMesh !== undefined);
|
||||
|
||||
const buffer = BufferGenerator.fromVoxelMesh(this._loadedVoxelMesh, params);
|
||||
this._voxelMeshBuffer = buffer.buffer;
|
||||
this._loadedVoxelMesh.setRenderParams(params);
|
||||
|
||||
return {
|
||||
buffer: buffer,
|
||||
buffer: this._loadedVoxelMesh.getBuffer(),
|
||||
dimensions: this._loadedVoxelMesh.getBounds().getDimensions(),
|
||||
voxelSize: 8.0 / params.desiredHeight,
|
||||
};
|
||||
@ -73,25 +65,21 @@ export class WorkerClient {
|
||||
|
||||
public assign(params: AssignParams.Input): AssignParams.Output {
|
||||
ASSERT(this._loadedVoxelMesh !== undefined);
|
||||
|
||||
|
||||
this._loadedBlockMesh = BlockMesh.createFromVoxelMesh(this._loadedVoxelMesh, params);
|
||||
|
||||
return {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public renderBlockMesh(params: RenderBlockMeshParams.Input): RenderBlockMeshParams.Output {
|
||||
ASSERT(this._loadedBlockMesh !== undefined);
|
||||
ASSERT(this._voxelMeshBuffer !== undefined);
|
||||
|
||||
const atlas = Atlas.load(params.textureAtlas);
|
||||
ASSERT(atlas !== undefined);
|
||||
|
||||
const buffer = BufferGenerator.fromBlockMesh(this._loadedBlockMesh, this._voxelMeshBuffer);
|
||||
this._blockMeshBuffer = buffer.buffer;
|
||||
|
||||
return {
|
||||
buffer: buffer,
|
||||
buffer: this._loadedBlockMesh.getBuffer(),
|
||||
dimensions: this._loadedBlockMesh.getVoxelMesh().getBounds().getDimensions(),
|
||||
atlasTexturePath: atlas.getAtlasTexturePath(),
|
||||
atlasSize: atlas.getAtlasSize(),
|
||||
@ -100,16 +88,15 @@ export class WorkerClient {
|
||||
|
||||
public export(params: ExportParams.Input): ExportParams.Output {
|
||||
ASSERT(this._loadedBlockMesh !== undefined);
|
||||
ASSERT(this._blockMeshBuffer !== undefined);
|
||||
|
||||
const exporter: IExporter = ExporterFactory.GetExporter(params.exporter);
|
||||
const fileExtension = '.' + exporter.getFileExtension();
|
||||
if (!params.filepath.endsWith(fileExtension)) {
|
||||
params.filepath += fileExtension;
|
||||
}
|
||||
exporter.export(this._loadedBlockMesh, params.filepath, this._blockMeshBuffer);
|
||||
exporter.export(this._loadedBlockMesh, params.filepath);
|
||||
|
||||
return {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { TBlockAssigners } from "./assigners/assigners"
|
||||
import { FallableBehaviour } from "./block_mesh"
|
||||
import { TBlockMeshBufferDescription, TMeshBufferDescription, TVoxelMeshBuffer, TVoxelMeshBufferDescription } from "./buffer"
|
||||
import { TExporters } from "./exporters/exporters"
|
||||
import { StatusMessage } from "./status"
|
||||
import { TextureFiltering } from "./texture"
|
||||
import { ColourSpace } from "./util"
|
||||
import { AppError } from "./util/error_util"
|
||||
import { Vector3 } from "./vector"
|
||||
import { TVoxelisers } from "./voxelisers/voxelisers"
|
||||
import { TVoxelOverlapRule } from "./voxel_mesh"
|
||||
import { TBlockAssigners } from './assigners/assigners';
|
||||
import { FallableBehaviour } from './block_mesh';
|
||||
import { TBlockMeshBufferDescription, TMeshBufferDescription, TVoxelMeshBufferDescription } from './buffer';
|
||||
import { TExporters } from './exporters/exporters';
|
||||
import { StatusMessage } from './status';
|
||||
import { TextureFiltering } from './texture';
|
||||
import { ColourSpace } from './util';
|
||||
import { AppError } from './util/error_util';
|
||||
import { Vector3 } from './vector';
|
||||
import { TVoxelisers } from './voxelisers/voxelisers';
|
||||
import { TVoxelOverlapRule } from './voxel_mesh';
|
||||
|
||||
export namespace ImportParams {
|
||||
export type Input = {
|
||||
@ -32,7 +32,7 @@ export namespace RenderMeshParams {
|
||||
}
|
||||
|
||||
export namespace VoxeliseParams {
|
||||
export type Input = {
|
||||
export type Input = {
|
||||
voxeliser: TVoxelisers,
|
||||
desiredHeight: number,
|
||||
useMultisampleColouring: boolean,
|
||||
@ -106,22 +106,22 @@ export type TStatus = {
|
||||
}
|
||||
|
||||
export type TToWorkerMessage =
|
||||
| { action: 'Import', params: ImportParams.Input }
|
||||
| { action: 'RenderMesh', params: RenderMeshParams.Input }
|
||||
| { action: 'Voxelise', params: VoxeliseParams.Input }
|
||||
| { action: 'RenderVoxelMesh', params: RenderVoxelMeshParams.Input }
|
||||
| { action: 'Assign', params: AssignParams.Input }
|
||||
| { action: 'RenderBlockMesh', params: RenderBlockMeshParams.Input }
|
||||
| { action: 'Export', params: ExportParams.Input }
|
||||
| { action: 'Import', params: ImportParams.Input }
|
||||
| { action: 'RenderMesh', params: RenderMeshParams.Input }
|
||||
| { action: 'Voxelise', params: VoxeliseParams.Input }
|
||||
| { action: 'RenderVoxelMesh', params: RenderVoxelMeshParams.Input }
|
||||
| { action: 'Assign', params: AssignParams.Input }
|
||||
| { action: 'RenderBlockMesh', params: RenderBlockMeshParams.Input }
|
||||
| { action: 'Export', params: ExportParams.Input }
|
||||
|
||||
export type TFromWorkerMessage =
|
||||
export type TFromWorkerMessage =
|
||||
| { action: 'KnownError', error: AppError }
|
||||
| { action: 'UnknownError', error: Error }
|
||||
| (TStatus & (
|
||||
| { action: 'Import', result: ImportParams.Output }
|
||||
| { action: 'RenderMesh', result: RenderMeshParams.Output }
|
||||
| { action: 'Voxelise', result: VoxeliseParams.Output }
|
||||
| { action: 'RenderVoxelMesh', result: RenderVoxelMeshParams.Output }
|
||||
| { action: 'Assign', result: AssignParams.Output }
|
||||
| { action: 'RenderBlockMesh', result: RenderBlockMeshParams.Output }
|
||||
| { action: 'Export', result: ExportParams.Output } ));
|
||||
| (TStatus & (
|
||||
| { action: 'Import', result: ImportParams.Output }
|
||||
| { action: 'RenderMesh', result: RenderMeshParams.Output }
|
||||
| { action: 'Voxelise', result: VoxeliseParams.Output }
|
||||
| { action: 'RenderVoxelMesh', result: RenderVoxelMeshParams.Output }
|
||||
| { action: 'Assign', result: AssignParams.Output }
|
||||
| { action: 'RenderBlockMesh', result: RenderBlockMeshParams.Output }
|
||||
| { action: 'Export', result: ExportParams.Output }));
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { AttributeData, MergeAttributeData } from "../src/render_buffer";
|
||||
import { AttributeData, MergeAttributeData } from '../src/render_buffer';
|
||||
|
||||
test('MergeAttributeData #1', () => {
|
||||
const a: AttributeData = {
|
||||
|
@ -1,34 +1,32 @@
|
||||
/*
|
||||
import { THeadlessConfig } from './headless';
|
||||
import { TextureFiltering } from '../src/texture';
|
||||
import { ColourSpace } from '../src/util';
|
||||
|
||||
export const headlessConfig: THeadlessConfig = {
|
||||
import: {
|
||||
absoluteFilePathLoad: 'C:/Users/<username>/Desktop/my_model.obj', // Must be an absolute path to the file (can be anywhere)
|
||||
filepath: '/Users/lucasdower/ObjToSchematic/res/samples/skull.obj', // Must be an absolute path
|
||||
},
|
||||
voxelise: {
|
||||
voxeliser: 'bvh-ray',
|
||||
voxelMeshParams: {
|
||||
desiredHeight: 80, // 5-320 inclusive
|
||||
useMultisampleColouring: false,
|
||||
textureFiltering: TextureFiltering.Linear,
|
||||
voxelOverlapRule: 'average',
|
||||
},
|
||||
desiredHeight: 80,
|
||||
useMultisampleColouring: false,
|
||||
textureFiltering: TextureFiltering.Linear,
|
||||
voxelOverlapRule: 'average',
|
||||
enableAmbientOcclusion: false, // Only want true if exporting to .obj
|
||||
calculateNeighbours: false, // Only want true if exporting to .obj
|
||||
},
|
||||
palette: {
|
||||
blockMeshParams: {
|
||||
textureAtlas: 'vanilla', // Must be an atlas name that exists in /resources/atlases
|
||||
blockPalette: 'all-snapshot', // Must be a palette name that exists in /resources/palettes
|
||||
blockAssigner: 'ordered-dithering',
|
||||
colourSpace: ColourSpace.RGB,
|
||||
fallable: 'replace-falling',
|
||||
},
|
||||
assign: {
|
||||
textureAtlas: 'vanilla', // Must be an atlas name that exists in /resources/atlases
|
||||
blockPalette: 'all-snapshot', // Must be a palette name that exists in /resources/palettes
|
||||
blockAssigner: 'ordered-dithering',
|
||||
colourSpace: ColourSpace.RGB,
|
||||
fallable: 'replace-falling',
|
||||
},
|
||||
export: {
|
||||
absoluteFilePathSave: 'C:/Users/Lucas/Desktop/my_structure.schematic', // Must be an absolute path to the file (can be anywhere)
|
||||
exporter: 'schematic', // 'schematic' / 'litematic',
|
||||
filepath: '/Users/lucasdower/Documents/out.obj', // Must be an absolute path to the file (can be anywhere)
|
||||
exporter: 'obj', // 'schematic' / 'litematic',
|
||||
},
|
||||
debug: {
|
||||
logging: true,
|
||||
},
|
||||
};
|
||||
|
||||
*/
|
||||
|
@ -1,118 +1,55 @@
|
||||
/*
|
||||
import { Mesh } from '../src/mesh';
|
||||
import { ObjImporter } from '../src/importers/obj_importer';
|
||||
import { IVoxeliser } from '../src/voxelisers/base-voxeliser';
|
||||
import { TVoxelOverlapRule, VoxelMesh } from '../src/voxel_mesh';
|
||||
import { BlockMesh, BlockMeshParams, FallableBehaviour } from '../src/block_mesh';
|
||||
import { IExporter} from '../src/exporters/base_exporter';
|
||||
import { TextureFiltering } from '../src/texture';
|
||||
import { ColourSpace } from '../src/util';
|
||||
import { log, LogStyle } from './logging';
|
||||
import { headlessConfig } from './headless-config';
|
||||
import { TVoxelisers, VoxeliserFactory } from '../src/voxelisers/voxelisers';
|
||||
import { ExporterFactory, TExporters } from '../src/exporters/exporters';
|
||||
import { TBlockAssigners } from '../src/assigners/assigners';
|
||||
import { Atlas } from '../src/atlas';
|
||||
import { Palette } from '../src/palette';
|
||||
import { VoxeliseParams } from '../src/worker_types';
|
||||
import { AssignParams, ExportParams, ImportParams, VoxeliseParams } from '../src/worker_types';
|
||||
import { WorkerClient } from '../src/worker_client';
|
||||
import { StatusHandler } from '../src/status';
|
||||
import { Logger, LOG_MAJOR } from '../src/util/log_util';
|
||||
|
||||
export type THeadlessConfig = {
|
||||
import: {
|
||||
absoluteFilePathLoad: string,
|
||||
},
|
||||
voxelise: {
|
||||
voxeliser: TVoxelisers,
|
||||
voxelMeshParams: {
|
||||
desiredHeight: number
|
||||
useMultisampleColouring: boolean,
|
||||
textureFiltering: TextureFiltering,
|
||||
voxelOverlapRule: TVoxelOverlapRule,
|
||||
},
|
||||
},
|
||||
palette: {
|
||||
blockMeshParams: {
|
||||
textureAtlas: string,
|
||||
blockPalette: string,
|
||||
blockAssigner: TBlockAssigners,
|
||||
colourSpace: ColourSpace,
|
||||
fallable: FallableBehaviour,
|
||||
},
|
||||
},
|
||||
export: {
|
||||
absoluteFilePathSave: string,
|
||||
exporter: TExporters,
|
||||
},
|
||||
import: ImportParams.Input,
|
||||
voxelise: VoxeliseParams.Input,
|
||||
assign: AssignParams.Input,
|
||||
export: ExportParams.Input,
|
||||
debug: {
|
||||
logging: boolean,
|
||||
}
|
||||
}
|
||||
|
||||
void async function main() {
|
||||
const mesh = _import({
|
||||
absoluteFilePathLoad: headlessConfig.import.absoluteFilePathLoad,
|
||||
});
|
||||
const voxelMesh = _voxelise(mesh, {
|
||||
voxeliser: VoxeliserFactory.GetVoxeliser(headlessConfig.voxelise.voxeliser)
|
||||
desiredHeight: headlessConfig.voxelise.voxelMeshParams.desiredHeight,
|
||||
useMultisampleColouring: headlessConfig.voxelise.voxelMeshParams.useMultisampleColouring,
|
||||
textureFiltering: headlessConfig.voxelise.voxelMeshParams.textureFiltering,
|
||||
enableAmbientOcclusion: false,
|
||||
voxelOverlapRule: headlessConfig.voxelise.voxelMeshParams.voxelOverlapRule,
|
||||
calculateNeighbours: false,
|
||||
});
|
||||
|
||||
const atlasId = headlessConfig.palette.blockMeshParams.textureAtlas;
|
||||
const atlas = Atlas.load(atlasId);
|
||||
if (atlas === undefined) {
|
||||
return 'Could not load atlas';
|
||||
if (headlessConfig.debug.logging) {
|
||||
Logger.Get.enableLOGMAJOR();
|
||||
}
|
||||
|
||||
const paletteId = headlessConfig.palette.blockMeshParams.blockPalette;
|
||||
const palette = Palette.load(paletteId);
|
||||
if (palette === undefined) {
|
||||
return 'Could not load palette';
|
||||
const worker = WorkerClient.Get;
|
||||
{
|
||||
LOG_MAJOR('Importing...');
|
||||
worker.import(headlessConfig.import);
|
||||
StatusHandler.Get.dump().clear();
|
||||
}
|
||||
|
||||
const blockMesh = _palette(voxelMesh, {
|
||||
blockMeshParams: {
|
||||
textureAtlas: atlas,
|
||||
blockPalette: palette,
|
||||
blockAssigner: headlessConfig.palette.blockMeshParams.blockAssigner as TBlockAssigners,
|
||||
colourSpace: headlessConfig.palette.blockMeshParams.colourSpace,
|
||||
fallable: headlessConfig.palette.blockMeshParams.fallable as FallableBehaviour,
|
||||
},
|
||||
});
|
||||
_export(blockMesh, {
|
||||
absoluteFilePathSave: headlessConfig.export.absoluteFilePathSave,
|
||||
exporter: ExporterFactory.GetExporter(headlessConfig.export.exporter),
|
||||
});
|
||||
log(LogStyle.Success, 'Finished!');
|
||||
{
|
||||
LOG_MAJOR('Voxelising...');
|
||||
worker.voxelise(headlessConfig.voxelise);
|
||||
StatusHandler.Get.dump().clear();
|
||||
}
|
||||
{
|
||||
LOG_MAJOR('Assigning...');
|
||||
worker.assign(headlessConfig.assign);
|
||||
StatusHandler.Get.dump().clear();
|
||||
}
|
||||
{
|
||||
LOG_MAJOR('Exporting...');
|
||||
/**
|
||||
* The OBJExporter is unique in that it uses the actual render buffer used by WebGL
|
||||
* to create its data, in headless mode this render buffer is not created so we must
|
||||
* generate it manually
|
||||
*/
|
||||
if (headlessConfig.export.exporter === 'obj') {
|
||||
worker.renderVoxelMesh({
|
||||
enableAmbientOcclusion: headlessConfig.voxelise.enableAmbientOcclusion,
|
||||
desiredHeight: headlessConfig.voxelise.desiredHeight,
|
||||
});
|
||||
}
|
||||
worker.export(headlessConfig.export);
|
||||
StatusHandler.Get.dump().clear();
|
||||
}
|
||||
LOG_MAJOR('Finished!');
|
||||
}();
|
||||
|
||||
// TODO: Log status messages
|
||||
function _import(params: ImportParams): Mesh {
|
||||
log(LogStyle.Info, 'Importing...');
|
||||
const importer = new ObjImporter();
|
||||
importer.parseFile(params.absoluteFilePathLoad);
|
||||
const mesh = importer.toMesh();
|
||||
mesh.processMesh();
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// TODO: Log status messages
|
||||
function _voxelise(mesh: Mesh, params: ActionVoxeliseParams): VoxelMesh {
|
||||
log(LogStyle.Info, 'Voxelising...');
|
||||
const voxeliser: IVoxeliser = params.voxeliser;
|
||||
return voxeliser.voxelise(mesh, params.voxeliseParams);
|
||||
}
|
||||
|
||||
// TODO: Log status messages
|
||||
function _palette(voxelMesh: VoxelMesh, params: PaletteParams): BlockMesh {
|
||||
log(LogStyle.Info, 'Assigning blocks...');
|
||||
return BlockMesh.createFromVoxelMesh(voxelMesh, params.blockMeshParams);
|
||||
}
|
||||
|
||||
// TODO: Log status messages
|
||||
function _export(blockMesh: BlockMesh, params: ExportParams) {
|
||||
log(LogStyle.Info, 'Exporting...');
|
||||
params.exporter.export(blockMesh, params.absoluteFilePathSave);
|
||||
}
|
||||
|
||||
*/
|
||||
|
@ -3,7 +3,7 @@
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"incremental": true, /* Enable incremental compilation */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
//"resolveJsonModule": true,
|
||||
|
Loading…
Reference in New Issue
Block a user