forked from mirror/ObjToSchematic
Migrated exporting to worker thread
This commit is contained in:
parent
73f0c03c92
commit
3e559b3298
@ -14,6 +14,9 @@ import { AppConfig } from './config';
|
||||
import { OutputStyle } from './ui/elements/output';
|
||||
import { TextureFiltering } from './texture';
|
||||
import { FallableBehaviour } from './block_mesh';
|
||||
import { remote } from 'electron';
|
||||
import { ExporterFactory, TExporters } from './exporters/exporters';
|
||||
import { IExporter } from './exporters/base_exporter';
|
||||
|
||||
export class AppContext {
|
||||
private _ui: UI;
|
||||
@ -37,10 +40,15 @@ export class AppContext {
|
||||
}
|
||||
|
||||
public do(action: EAction) {
|
||||
this._ui.disable(action);
|
||||
this._ui.cacheValues(action);
|
||||
this._ui.disable(action);
|
||||
|
||||
const workerJob = this._getWorkerJob(action);
|
||||
if (workerJob === undefined) {
|
||||
this._ui.enable(action);
|
||||
return;
|
||||
}
|
||||
|
||||
const uiOutput = this._ui.getActionOutput(action);
|
||||
|
||||
const jobCallback = (payload: TFromWorkerMessage) => {
|
||||
@ -92,14 +100,14 @@ export class AppContext {
|
||||
builder.addItem('action', infoStatuses, 'success');
|
||||
|
||||
if (hasWarnings) {
|
||||
builder.addHeading('action', 'There were some warnings:', 'warning');
|
||||
builder.addHeading('action', 'There were some warnings', 'warning');
|
||||
builder.addItem('action', warningStatuses, 'warning');
|
||||
}
|
||||
|
||||
return { builder: builder, style: hasWarnings ? 'warning' : 'success' };
|
||||
}
|
||||
|
||||
private _getWorkerJob(action: EAction): TWorkerJob {
|
||||
private _getWorkerJob(action: EAction): (TWorkerJob | undefined) {
|
||||
switch (action) {
|
||||
case EAction.Import:
|
||||
return this._import();
|
||||
@ -107,6 +115,8 @@ export class AppContext {
|
||||
return this._voxelise();
|
||||
case EAction.Assign:
|
||||
return this._assign();
|
||||
case EAction.Export:
|
||||
return this._export();
|
||||
}
|
||||
ASSERT(false);
|
||||
}
|
||||
@ -115,7 +125,7 @@ export class AppContext {
|
||||
const uiElements = this._ui.layout.import.elements;
|
||||
|
||||
this._ui.getActionOutput(EAction.Import)
|
||||
.setTaskInProgress('action', '[Voxel Mesh]: Loading...');
|
||||
.setTaskInProgress('action', '[Importer]: Loading...');
|
||||
|
||||
const payload: TToWorkerMessage = {
|
||||
action: 'Import',
|
||||
@ -135,7 +145,7 @@ export class AppContext {
|
||||
outputElement.setTaskInProgress('render', '[Renderer]: Processing...')
|
||||
} else {
|
||||
const message = `Will not render mesh as its over ${AppConfig.RENDER_TRIANGLE_THRESHOLD} triangles.`;
|
||||
outputElement.setTaskComplete('render', '[Renderer]', [ message ], 'warning')
|
||||
outputElement.setTaskComplete('render', '[Renderer]: Stopped.', [ message ], 'warning')
|
||||
}
|
||||
};
|
||||
|
||||
@ -324,52 +334,38 @@ export class AppContext {
|
||||
return { id: 'RenderBlockMesh', payload: payload, callback: callback };
|
||||
}
|
||||
|
||||
/*
|
||||
private _assign() {
|
||||
ASSERT(this._loadedVoxelMesh);
|
||||
|
||||
const uiElements = this._ui.layout.assign.elements;
|
||||
|
||||
const atlasId = uiElements.textureAtlas.getCachedValue();
|
||||
const atlas = Atlas.load(atlasId);
|
||||
ASSERT(atlas, 'Could not load atlas');
|
||||
|
||||
const paletteId = uiElements.blockPalette.getCachedValue();
|
||||
const palette = Palette.load(paletteId);
|
||||
ASSERT(palette);
|
||||
|
||||
const blockMeshParams: BlockMeshParams = {
|
||||
textureAtlas: atlas,
|
||||
blockPalette: palette,
|
||||
blockAssigner: uiElements.dithering.getCachedValue(),
|
||||
colourSpace: ColourSpace.RGB,
|
||||
fallable: uiElements.fallable.getCachedValue() as FallableBehaviour,
|
||||
};
|
||||
|
||||
this._loadedBlockMesh = BlockMesh.createFromVoxelMesh(this._loadedVoxelMesh, blockMeshParams);
|
||||
Renderer.Get.useBlockMesh(this._loadedBlockMesh);
|
||||
}
|
||||
|
||||
private _export() {
|
||||
private _export(): (TWorkerJob | undefined) {
|
||||
const exporterID: TExporters = this._ui.layout.export.elements.export.getCachedValue();
|
||||
const exporter: IExporter = ExporterFactory.GetExporter(exporterID);
|
||||
|
||||
let filePath = remote.dialog.showSaveDialogSync({
|
||||
let filepath = remote.dialog.showSaveDialogSync({
|
||||
title: 'Save structure',
|
||||
buttonLabel: 'Save',
|
||||
filters: [exporter.getFormatFilter()],
|
||||
});
|
||||
|
||||
ASSERT(this._loadedBlockMesh);
|
||||
if (filePath) {
|
||||
const fileExtension = '.' + exporter.getFileExtension();
|
||||
if (!filePath.endsWith(fileExtension)) {
|
||||
filePath += fileExtension;
|
||||
}
|
||||
exporter.export(this._loadedBlockMesh, filePath);
|
||||
if (filepath === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this._ui.getActionOutput(EAction.Export)
|
||||
.setTaskInProgress('action', '[Exporter]: Saving...');
|
||||
|
||||
const payload: TToWorkerMessage = {
|
||||
action: 'Export',
|
||||
params: {
|
||||
filepath: filepath,
|
||||
exporter: exporterID,
|
||||
}
|
||||
};
|
||||
|
||||
const callback = (payload: TFromWorkerMessage) => {
|
||||
// This callback is managed through `AppContext::do`, therefore
|
||||
// this callback is only called if the job is successful.
|
||||
};
|
||||
|
||||
return { id: 'Export', payload: payload, callback: callback };
|
||||
}
|
||||
*/
|
||||
|
||||
public draw() {
|
||||
Renderer.Get.update();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Vector3 } from '../vector';
|
||||
import { BlockMesh } from '../block_mesh';
|
||||
import { TOptional } from '../util';
|
||||
import { TBlockMeshBuffer } from '../buffer';
|
||||
|
||||
export abstract class IExporter {
|
||||
protected _sizeVector!: Vector3;
|
||||
@ -18,7 +19,7 @@ export abstract class IExporter {
|
||||
return;
|
||||
}
|
||||
|
||||
public abstract export(blockMesh: BlockMesh, filePath: string): boolean;
|
||||
public abstract export(blockMesh: BlockMesh, filePath: string, blockMeshBuffer: TBlockMeshBuffer): boolean;
|
||||
|
||||
public getFormatFilter() {
|
||||
return {
|
||||
|
@ -4,6 +4,7 @@ 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 {
|
||||
@ -21,7 +22,7 @@ export class ObjExporter extends IExporter {
|
||||
return 'Wavefront OBJ';
|
||||
}
|
||||
|
||||
public override export(blockMesh: BlockMesh, filepath: string) {
|
||||
public override export(blockMesh: BlockMesh, filepath: string, blockMeshBuffer: TBlockMeshBuffer) {
|
||||
ASSERT(path.isAbsolute(filepath));
|
||||
const parsedPath = path.parse(filepath);
|
||||
|
||||
@ -29,15 +30,13 @@ export class ObjExporter extends IExporter {
|
||||
const filepathMTL = path.join(parsedPath.dir, parsedPath.name + '.mtl');
|
||||
const filepathTexture = path.join(parsedPath.dir, parsedPath.name + '.png');
|
||||
|
||||
this._exportOBJ(filepathOBJ, blockMesh, parsedPath.name + '.mtl');
|
||||
this._exportOBJ(filepathOBJ, blockMesh, blockMeshBuffer, parsedPath.name + '.mtl');
|
||||
this._exportMTL(filepathMTL, filepathTexture, blockMesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private _exportOBJ(filepath: string, blockMesh: BlockMesh, mtlRelativePath: string) {
|
||||
/*
|
||||
const buffer = blockMesh.createBuffer();
|
||||
private _exportOBJ(filepath: string, blockMesh: BlockMesh, buffer: TBlockMeshBuffer, mtlRelativePath: string) {
|
||||
const positionData = buffer.position.data as Float32Array;
|
||||
const normalData = buffer.normal.data as Float32Array;
|
||||
const texcoordData = buffer.texcoord.data as Float32Array;
|
||||
@ -82,7 +81,6 @@ export class ObjExporter extends IExporter {
|
||||
}
|
||||
|
||||
writeStream.end();
|
||||
*/
|
||||
}
|
||||
|
||||
private _exportMTL(filepathMTL: string, filepathTexture: string, blockMesh: BlockMesh) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { EAction } from './util';
|
||||
import { ASSERT } from './util/error_util';
|
||||
import { LOG, LOG_WARN } from './util/log_util';
|
||||
|
||||
export type StatusType = 'warning' | 'info';
|
||||
@ -46,30 +47,30 @@ export class StatusHandler {
|
||||
public getDefaultSuccessMessage(action: EAction): string {
|
||||
switch (action) {
|
||||
case EAction.Import:
|
||||
return '[Mesh]: Loaded';
|
||||
return '[Importer]: Loaded';
|
||||
case EAction.Voxelise:
|
||||
return 'Voxelised mesh';
|
||||
return '[Voxeliser]: Succeeded';
|
||||
case EAction.Assign:
|
||||
return 'Assigned blocks';
|
||||
return '[Assigner]: Succeeded';
|
||||
case EAction.Export:
|
||||
return 'Exported mesh';
|
||||
return '[Exporter]: Saved';
|
||||
default:
|
||||
return 'Performed action';
|
||||
ASSERT(false)
|
||||
}
|
||||
}
|
||||
|
||||
public getDefaultFailureMessage(action: EAction): string {
|
||||
switch (action) {
|
||||
case EAction.Import:
|
||||
return 'Mesh: Failed';
|
||||
return '[Importer]: Failed';
|
||||
case EAction.Voxelise:
|
||||
return 'Failed to voxelise mesh';
|
||||
return '[Voxeliser]: Failed';
|
||||
case EAction.Assign:
|
||||
return 'Failed to assign blocks';
|
||||
return '[Assigner]: Failed';
|
||||
case EAction.Export:
|
||||
return 'Failed to export mesh';
|
||||
return '[Exporter]: Failed';
|
||||
default:
|
||||
return 'Failed to perform action';
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,9 +53,6 @@ export class FileInputElement extends LabelledElement<string> {
|
||||
const filePath = files[0];
|
||||
this._loadedFilePath = filePath;
|
||||
this._value = filePath;
|
||||
} else {
|
||||
this._loadedFilePath = '';
|
||||
this._value = '';
|
||||
}
|
||||
const parsedPath = path.parse(this._loadedFilePath);
|
||||
element.innerHTML = parsedPath.name + parsedPath.ext;
|
||||
|
@ -67,7 +67,7 @@ export class OutputElement {
|
||||
const builder = this.getMessage().clear(taskId);
|
||||
|
||||
if (taskItems.length > 0) {
|
||||
builder.addHeading(taskId, taskId + taskHeading, style);
|
||||
builder.addHeading(taskId, taskHeading, style);
|
||||
} else {
|
||||
builder.addBold(taskId, [ taskHeading ], style);
|
||||
}
|
||||
|
@ -44,6 +44,12 @@ export function doWork(message: TToWorkerMessage): TFromWorkerMessage {
|
||||
result: WorkerClient.Get.renderBlockMesh(message.params),
|
||||
statusMessages: StatusHandler.Get.getAllStatusMessages(),
|
||||
};
|
||||
case 'Export':
|
||||
return {
|
||||
action: 'Export',
|
||||
result: WorkerClient.Get.export(message.params),
|
||||
statusMessages: StatusHandler.Get.getAllStatusMessages(),
|
||||
};
|
||||
}
|
||||
} catch (e: any) {
|
||||
return { action: e instanceof AppError ? 'KnownError' : 'UnknownError', error: e as Error };
|
||||
|
@ -3,8 +3,8 @@ 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, ImportParams, RenderBlockMeshParams, RenderMeshParams, RenderVoxelMeshParams, VoxeliseParams } from "./worker_types";
|
||||
import { BufferGenerator, TVoxelMeshBuffer } from "./buffer";
|
||||
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";
|
||||
@ -12,6 +12,8 @@ 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";
|
||||
|
||||
export class WorkerClient {
|
||||
private static _instance: WorkerClient;
|
||||
@ -24,6 +26,7 @@ export class WorkerClient {
|
||||
private _loadedBlockMesh?: BlockMesh;
|
||||
|
||||
private _voxelMeshBuffer?: TVoxelMeshBuffer;
|
||||
private _blockMeshBuffer?: TBlockMeshBuffer;
|
||||
|
||||
public import(params: ImportParams.Input): ImportParams.Output {
|
||||
const importer = new ObjImporter();
|
||||
@ -84,11 +87,29 @@ export class WorkerClient {
|
||||
const atlas = Atlas.load(params.textureAtlas);
|
||||
ASSERT(atlas !== undefined);
|
||||
|
||||
const buffer = BufferGenerator.fromBlockMesh(this._loadedBlockMesh, this._voxelMeshBuffer);
|
||||
this._blockMeshBuffer = buffer.buffer;
|
||||
|
||||
return {
|
||||
buffer: BufferGenerator.fromBlockMesh(this._loadedBlockMesh, this._voxelMeshBuffer),
|
||||
buffer: buffer,
|
||||
dimensions: this._loadedBlockMesh.getVoxelMesh().getBounds().getDimensions(),
|
||||
atlasTexturePath: atlas.getAtlasTexturePath(),
|
||||
atlasSize: atlas.getAtlasSize(),
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
const workerInstance = require('./worker');
|
||||
|
||||
addEventListener('message', (e) => {
|
||||
postMessage(workerInstance.doWork(e.data));
|
||||
setTimeout(() => {
|
||||
postMessage(workerInstance.doWork(e.data));
|
||||
}, 1000);
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
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"
|
||||
@ -91,7 +92,8 @@ export namespace RenderBlockMeshParams {
|
||||
|
||||
export namespace ExportParams {
|
||||
export type Input = {
|
||||
|
||||
filepath: string,
|
||||
exporter: TExporters,
|
||||
}
|
||||
|
||||
export type Output = {
|
||||
|
Loading…
Reference in New Issue
Block a user