diff --git a/src/app_context.ts b/src/app_context.ts index 0003d7b..25c3b77 100644 --- a/src/app_context.ts +++ b/src/app_context.ts @@ -3,8 +3,9 @@ import '../styles.css'; import { FallableBehaviour } from './block_mesh'; import { ArcballCamera } from './camera'; import { AppConfig } from './config'; -import { EAppEvent, EventManager } from './event'; +import { EventManager } from './event'; import { MaterialMapManager } from './material-map'; +import { MouseManager } from './mouse'; import { MeshType, Renderer } from './renderer'; import { AppConsole, TMessage } from './ui/console'; import { UI } from './ui/layout'; @@ -17,35 +18,44 @@ import { WorkerController } from './worker_controller'; import { TFromWorkerMessage } from './worker_types'; export class AppContext { + /* Singleton */ + private static _instance: AppContext; + public static get Get() { + return this._instance || (this._instance = new this()); + } + private _workerController: WorkerController; private _lastAction?: EAction; public maxConstraint?: Vector3; private _materialManager: MaterialMapManager; - public constructor() { - AppConsole.info('Initialising...'); + private constructor() { + this._workerController = new WorkerController(); this._materialManager = new MaterialMapManager(new Map()); + } + + public static init() { + AppConsole.info('Initialising...'); Logger.Get.enableLOG(); Logger.Get.enableLOGMAJOR(); Logger.Get.enableLOGWARN(); AppConfig.Get.dumpConfig(); - EventManager.Get.bindToContext(this); + EventManager.Get.bindToContext(this.Get); - const gl = (document.getElementById('canvas')).getContext('webgl'); - if (!gl) { - throw Error('Could not load WebGL context'); - } - - UI.Get.bindToContext(this); + UI.Get.bindToContext(this.Get); UI.Get.build(); UI.Get.registerEvents(); - UI.Get.updateMaterialsAction(this._materialManager); + UI.Get.updateMaterialsAction(this.Get._materialManager); UI.Get.disableAll(); - this._workerController = new WorkerController(); - this._workerController.execute({ action: 'Init', params: {}}).then(() => { + ArcballCamera.Get.init(); + MouseManager.Get.init(); + + window.addEventListener('contextmenu', (e) => e.preventDefault()); + + this.Get._workerController.execute({ action: 'Init', params: {}}).then(() => { UI.Get.enable(EAction.Import); AppConsole.success('Ready'); }); @@ -80,7 +90,7 @@ export class AppContext { AppConsole.success('Imported mesh'); this._addWorkerMessagesToConsole(resultImport.messages); - + this.maxConstraint = Vector3.copy(resultImport.result.dimensions) .mulScalar(AppConfig.Get.CONSTRAINT_MAXIMUM_HEIGHT / 8.0).floor(); this._materialManager = new MaterialMapManager(resultImport.result.materials); @@ -100,7 +110,7 @@ export class AppContext { return false; } ASSERT(resultRender.action === 'RenderMesh'); - + this._addWorkerMessagesToConsole(resultRender.messages); Renderer.Get.useMesh(resultRender.result); } @@ -109,7 +119,7 @@ export class AppContext { return true; } - + private async _materials(): Promise { AppConsole.info('Updating materials...'); { @@ -133,7 +143,7 @@ export class AppContext { Renderer.Get.recreateMaterialBuffer(materialName, material); Renderer.Get.setModelToUse(MeshType.TriangleMesh); }); - + this._addWorkerMessagesToConsole(resultMaterials.messages); } AppConsole.success('Updated materials'); @@ -165,7 +175,7 @@ export class AppContext { return false; } ASSERT(resultVoxelise.action === 'Voxelise'); - + this._addWorkerMessagesToConsole(resultVoxelise.messages); } AppConsole.success('Loaded voxel mesh'); @@ -228,7 +238,7 @@ export class AppContext { return false; } ASSERT(resultAssign.action === 'Assign'); - + this._addWorkerMessagesToConsole(resultAssign.messages); } AppConsole.success('Loaded block mesh'); @@ -281,7 +291,7 @@ export class AppContext { return false; } ASSERT(resultExport.action === 'Export'); - + this._addWorkerMessagesToConsole(resultExport.messages); download(resultExport.result.buffer, 'result.' + resultExport.result.extension); } @@ -311,7 +321,7 @@ export class AppContext { UI.Get.disableAll(); this._lastAction = action; - + const success = await this._executeAction(action); if (success) { if (action === EAction.Import) { @@ -346,9 +356,9 @@ export class AppContext { ASSERT(false); } - public draw() { + public static draw() { Renderer.Get.update(); - UI.Get.tick(this._workerController.isBusy()); + UI.Get.tick(this.Get._workerController.isBusy()); Renderer.Get.draw(); } } diff --git a/src/camera.ts b/src/camera.ts index 34898db..d59fdc7 100644 --- a/src/camera.ts +++ b/src/camera.ts @@ -4,6 +4,7 @@ import { AppConfig } from './config'; import { AppMath, between, clamp, degreesToRadians, roundToNearest, SmoothVariable, SmoothVectorVariable } from './math'; import { MouseManager } from './mouse'; import { Renderer } from './renderer'; +import { ASSERT } from './util/error_util'; import { Vector3 } from './vector'; export class ArcballCamera { @@ -51,8 +52,23 @@ export class ArcballCamera { this._elevation.setClamp(0.001, Math.PI - 0.001); this._distance.setClamp(1.0, 100.0); + } + public init() { this.setCameraMode(this._isPerspective ? 'perspective' : 'orthographic'); + + const canvas = document.getElementById('canvas'); + ASSERT(canvas !== null); + + canvas.addEventListener('mousedown', (e) => { + this.onMouseDown(e); + }); + document.addEventListener('mouseup', (e) => { + this.onMouseUp(e); + }); + canvas.addEventListener('wheel', (e) => { + this.onWheelScroll(e); + }); } public isPerspective() { diff --git a/src/client.ts b/src/client.ts deleted file mode 100644 index 5df95a5..0000000 --- a/src/client.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { AppContext } from './app_context'; -import { ArcballCamera } from './camera'; -import { MouseManager } from './mouse'; - -function addEvent(htmlElementID: string, event: string, delegate: (e: any) => void) { - document.getElementById(htmlElementID)?.addEventListener(event, delegate); -} - -function addDocumentEvent(event: string, delegate: (e: any) => void) { - document.addEventListener(event, delegate); -} - -const camera = ArcballCamera.Get; -addEvent('canvas', 'mousedown', (e) => { - camera.onMouseDown(e); -}); -addDocumentEvent('mouseup', (e) => { - camera.onMouseUp(e); -}); -addEvent('canvas', 'wheel', (e) => { - camera.onWheelScroll(e); -}); - -const mouseManager = MouseManager.Get; -addDocumentEvent('mousemove', (e) => { - mouseManager.onMouseMove(e); -}); - -window.addEventListener('contextmenu', (e) => e.preventDefault()); - - -// Begin draw loop -const context = new AppContext(); -function render() { - context.draw(); - requestAnimationFrame(render); -} -requestAnimationFrame(render); diff --git a/src/main.ts b/src/main.ts index 02e7f04..2b16e17 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,118 +1,10 @@ +import { AppContext } from './app_context'; -// /** -// ,d -// 88 -// ,adPPYba, MM88MMM ,adPPYba, 8b,dPPYba, -// I8[ "" 88 a8" "8a 88P' "8a -// `"Y8ba, 88 8b d8 88 d8 -// aa ]8I 88, "8a, ,a8" 88b, ,a8" -// `"YbbdP"' "Y888 `"YbbdP"' 88`YbbdP"' -// 88 -// 88 +AppContext.init(); -// If you're interested in the code, I recommend starting in /src/AppContext.ts -// The stuff here is boring Electron boilerplate \(•◡•)/ -// */ - -// import { app, BrowserWindow } from 'electron'; -// import url from 'url'; - -// import { AppConfig } from './config'; -// import { AppPaths, PathUtil } from './util/path_util'; - -// app.commandLine.appendSwitch('js-flags', `--max-old-space-size=${AppConfig.Get.OLD_SPACE_SIZE_MB}`); - -// // Keep a global reference of the window object, if you don't, the window will -// // be closed automatically when the JavaScript object is garbage collected. -// let mainWindow: BrowserWindow; - -// function createWindow() { -// // Create the browser window. -// // const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize; -// const width = 1400; -// const height = 800; - -// // Create list of args to pass from main process to render process -// const additionalArgs = []; -// if (process.argv.includes('--OTS-ENABLE-DEBUG')) { -// additionalArgs.push('--OTS-ENABLE-DEBUG'); -// } - -// // const appIcon = new Tray("../resources/icon.png"); -// mainWindow = new BrowserWindow({ -// width: width, -// height: height, -// icon: PathUtil.join(AppPaths.Get.static, process.platform === 'win32' ? './icon.ico' : './icon.png'), -// minWidth: 1280, -// minHeight: 720, -// webPreferences: { -// nodeIntegration: true, -// nodeIntegrationInWorker: true, -// contextIsolation: false, -// enableRemoteModule: true, -// additionalArguments: additionalArgs, -// }, -// }); -// if (AppConfig.Get.RELEASE_MODE) { -// mainWindow.removeMenu(); -// } - -// // Load index.html -// mainWindow.loadURL(url.format({ -// pathname: PathUtil.join(AppPaths.Get.base, './index.html'), -// protocol: 'file:', -// slashes: true, -// })); - -// const baseTitle = 'ObjToSchematic – Convert 3D models into Minecraft builds'; -// if (AppConfig.Get.RELEASE_MODE) { -// mainWindow.setTitle(`${baseTitle} (${AppConfig.Get.RELEASE_VERSION})`); -// } else { -// try { -// const branchName: Buffer = require('child_process') -// .execSync('git rev-parse --abbrev-ref HEAD') -// .toString() -// .replace('\n', ''); - -// const commitHash: (string | Buffer) = require('child_process') -// .execSync('git rev-parse --short HEAD') -// .toString() -// .replace('\n', ''); - -// mainWindow.setTitle(`${baseTitle} (git ${branchName.toString()} ${commitHash.toString().trim()})`); -// } catch (e: any) { -// mainWindow.setTitle(`${baseTitle} (git)`); -// } -// } - - -// // Open the DevTools. -// // mainWindow.webContents.openDevTools(); - -// // Emitted when the window is closed. -// mainWindow.on('closed', function () { -// app.quit(); -// }); -// } - -// // This method will be called when Electron has finished -// // initialization and is ready to create browser windows. -// // Some APIs can only be used after this event occurs. -// app.on('ready', createWindow); - -// // Quit when all windows are closed. -// app.on('window-all-closed', function () { -// // On OS X it is common for applications and their menu bar -// // to stay active until the user quits explicitly with Cmd + Q -// if (process.platform !== 'darwin') { -// app.quit(); -// } -// }); - -// app.on('activate', function () { -// // On OS X it's common to re-create a window in the app when the -// // dock icon is clicked and there are no other windows open. -// if (mainWindow === null) { -// createWindow(); -// } -// }); +// Begin draw loop +function render() { + AppContext.draw(); + requestAnimationFrame(render); +} +requestAnimationFrame(render); diff --git a/src/mouse.ts b/src/mouse.ts index 619e1c0..5d355f8 100644 --- a/src/mouse.ts +++ b/src/mouse.ts @@ -28,8 +28,14 @@ export class MouseManager { this.prevMouse = { x: -1, y: -1, buttons: 0 }; } + public init() { + document.addEventListener('mousemove', (e) => { + this.onMouseMove(e); + }); + } + public onMouseMove(e: MouseEvent) { - this.currMouse = { x: e.clientX, y: e.clientY, buttons: e.buttons }; + this.currMouse = { x: e.clientX, y: e.clientY, buttons: e.buttons }; } public isMouseLeftDown() { diff --git a/webpack.common.js b/webpack.common.js index d27268a..25e102a 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -3,7 +3,7 @@ const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { - entry: './src/client.ts', + entry: './src/main.ts', plugins: [ new NodePolyfillPlugin(), new HtmlWebpackPlugin({ @@ -39,7 +39,7 @@ module.exports = { { test: /\.tsx?$/, use: 'ts-loader', - exclude: /node_modules|main\.ts/, + exclude: /node_modules/, }, ], },