From e0d85a6a3d42452d0a31a0f18c5eb6d9728d832d Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Mon, 17 Jun 2024 11:33:24 -0500 Subject: [PATCH] Added stl person to toolbox. --- public/assets/models/person.stl | Bin 0 -> 62484 bytes server/server.js | 1 - src/diagram/diagramManager.ts | 9 +-- src/diagram/diagramMenuManager.ts | 4 +- .../functions/buildMeshFromDiagramEntity.ts | 1 + src/diagram/types/diagramEntity.ts | 1 + src/integration/pouchdbPersistenceManager.ts | 47 ++++++++++++-- src/integration/presence.ts | 4 +- src/toolbox/functions/buildColor.ts | 4 +- src/toolbox/functions/buildMesh.ts | 12 +++- src/toolbox/functions/buildTool.ts | 4 +- src/toolbox/toolbox.ts | 17 +++-- src/toolbox/types/toolType.ts | 1 + src/vrApp.ts | 61 ++++++++++-------- 14 files changed, 109 insertions(+), 57 deletions(-) create mode 100644 public/assets/models/person.stl diff --git a/public/assets/models/person.stl b/public/assets/models/person.stl new file mode 100644 index 0000000000000000000000000000000000000000..4b2f60982f94714725295fda3020e9dc368d2986 GIT binary patch literal 62484 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%0A&W)hajhbl-WZ}qSNHIfH}20=L1+=e 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 { const message = `{ "count": ${connections.size} }` diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index f4119b8..b352f07 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -27,12 +27,12 @@ export class DiagramManager { private _moving: number = 10; private _i: number = 0; - constructor() { + constructor(readyObservable: Observable) { this._me = getMe(); this._scene = DefaultScene.Scene; this._config = new AppConfig(); this._controllers = new Controllers(); - this._diagramMenuManager = new DiagramMenuManager(this.onDiagramEventObservable, this._controllers, this._config); + this._diagramMenuManager = new DiagramMenuManager(this.onDiagramEventObservable, this._controllers, this._config, readyObservable); this._diagramEntityActionManager = buildEntityActionManager(this._controllers); this.onDiagramEventObservable.add(this.onDiagramEvent, DiagramEventObserverMask.FROM_DB, true, this); @@ -97,6 +97,7 @@ export class DiagramManager { }); this._logger.debug("DiagramManager constructed"); + } public get actionManager(): AbstractActionManager { @@ -140,7 +141,7 @@ export class DiagramManager { private onDiagramEvent(event: DiagramEvent) { - let diagramObject = this._diagramObjects.get(event.entity.id); + let diagramObject = this._diagramObjects.get(event?.entity?.id); switch (event.type) { case DiagramEventType.ADD: if (diagramObject) { @@ -160,7 +161,7 @@ export class DiagramManager { if (diagramObject) { diagramObject.dispose(); } - this._diagramObjects.delete(event.entity.id); + this._diagramObjects.delete(event?.entity?.id); break; case DiagramEventType.MODIFY: this._logger.debug(event); diff --git a/src/diagram/diagramMenuManager.ts b/src/diagram/diagramMenuManager.ts index 2c3de0c..91f2827 100644 --- a/src/diagram/diagramMenuManager.ts +++ b/src/diagram/diagramMenuManager.ts @@ -26,7 +26,7 @@ export class DiagramMenuManager { private _logger = log.getLogger('DiagramMenuManager'); private _connectionPreview: ConnectionPreview; - constructor(notifier: Observable, controllers: Controllers, config: AppConfig) { + constructor(notifier: Observable, controllers: Controllers, config: AppConfig, readyObservable: Observable) { this._scene = DefaultScene.Scene; @@ -38,7 +38,7 @@ export class DiagramMenuManager { const event = {type: DiagramEventType.MODIFY, entity: {id: evt.id, text: evt.text}} this._notifier.notifyObservers(event, DiagramEventObserverMask.FROM_DB); }); - this.toolbox = new Toolbox(); + this.toolbox = new Toolbox(readyObservable); this.scaleMenu = new ScaleMenu2(this._notifier); if (viewOnly()) { this.toolbox.handleMesh.setEnabled(false); diff --git a/src/diagram/functions/buildMeshFromDiagramEntity.ts b/src/diagram/functions/buildMeshFromDiagramEntity.ts index d318c6b..87c0018 100644 --- a/src/diagram/functions/buildMeshFromDiagramEntity.ts +++ b/src/diagram/functions/buildMeshFromDiagramEntity.ts @@ -62,6 +62,7 @@ function createNewInstanceIfNecessary(entity: DiagramEntity, scene: Scene): Abst case DiagramTemplates.CYLINDER: case DiagramTemplates.CONE: case DiagramTemplates.PLANE: + case DiagramTemplates.PERSON: const toolMesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color); if (toolMesh && !oldMesh) { newMesh = new InstancedMesh(entity.id, (toolMesh as Mesh)); diff --git a/src/diagram/types/diagramEntity.ts b/src/diagram/types/diagramEntity.ts index b0ff0ed..d9be6df 100644 --- a/src/diagram/types/diagramEntity.ts +++ b/src/diagram/types/diagramEntity.ts @@ -30,6 +30,7 @@ export enum DiagramTemplates { CONE = "#cone-template", IMAGE = "#image-template", PLANE = "#plane-template", + PERSON = "#person-template" } export type DiagramEvent = { diff --git a/src/integration/pouchdbPersistenceManager.ts b/src/integration/pouchdbPersistenceManager.ts index c6331e1..15d0775 100644 --- a/src/integration/pouchdbPersistenceManager.ts +++ b/src/integration/pouchdbPersistenceManager.ts @@ -28,6 +28,8 @@ export class PouchdbPersistenceManager { private _encryption = new Encryption(); private _encKey = null; private _diagramManager: DiagramManager; + private _salt: string; + private _failCount: number = 0; constructor() { document.addEventListener('passwordset', (evt) => { @@ -37,7 +39,7 @@ export class PouchdbPersistenceManager { this._logger.debug('Initialized'); }); } - console.log(evt); + this._logger.debug(evt); }); } @@ -45,6 +47,14 @@ export class PouchdbPersistenceManager { this._diagramManager = diagramManager; diagramManager.onDiagramEventObservable.add((evt) => { this._logger.debug(evt); + if (!evt?.entity) { + this._logger.warn('no entity'); + return; + } + if (!evt?.entity?.id) { + this._logger.warn('no entity id'); + return; + } switch (evt.type) { case DiagramEventType.REMOVE: this.remove(evt.entity.id); @@ -105,6 +115,9 @@ export class PouchdbPersistenceManager { this._logger.warn('CONFLICTS!', doc._conflicts); } if (this._encKey) { + if (!doc.encrypted) { + this._logger.warn("current local doc is not encrypted, encrypting"); + } await this._encryption.encryptObject(entity); const newDoc = { _id: doc._id, @@ -114,6 +127,9 @@ export class PouchdbPersistenceManager { this.db.put(newDoc) } else { if (doc) { + if (doc.encrypted) { + this._logger.error("current local doc is encrypted, but encryption key is missing... saving in plaintext"); + } const newDoc = {_id: doc._id, _rev: doc._rev, ...entity}; this.db.put(newDoc); } else { @@ -125,6 +141,10 @@ export class PouchdbPersistenceManager { if (err.status == 404) { try { if (this._encKey) { + if (!this._encryption.ready) { + this._logger.error('Encryption not ready, there is a potential problem when this happens, we will generate a new salt which may cause data loss and/or slowness'); + await this._encryption.setPassword(this._encKey); + } await this._encryption.encryptObject(entity); const newDoc = { _id: entity.id, @@ -132,13 +152,16 @@ export class PouchdbPersistenceManager { } this.db.put(newDoc); } else { + this._logger.info('no encryption key, saving in plaintext'); const newEntity = {_id: entity.id, ...entity}; this.db.put(newEntity); } } catch (err2) { + this._logger.error("Unable to save document"); this._logger.error(err2); } } else { + this._logger.error("Unknown error with document get from db"); this._logger.error(err); } } @@ -155,20 +178,27 @@ export class PouchdbPersistenceManager { try { const doc = await this.db.get('metadata'); if (doc.encrypted) { + if (!this._salt && doc.encrypted.salt) { + this._logger.warn('Missing Salt'); + this._salt = doc.encrypted.salt; + } if (!this._encKey) { const promptPassword = new CustomEvent('promptpassword', {detail: 'Please enter password'}); document.dispatchEvent(promptPassword); return false; } if (!this._encryption.ready) { + this._logger.warn("Encryption not ready, setting password"); await this._encryption.setPassword(this._encKey, doc.encrypted.salt); } const decrypted = await this._encryption.decryptToObject(doc.encrypted.encrypted, doc.encrypted.iv); if (decrypted.friendly) { + this._logger.info("Storing Document friendly name in local storage, decrypted"); localStorage.setItem(current, decrypted.friendly); } } else { if (doc && doc.friendly) { + this._logger.info("Storing Document friendly name in local storage"); localStorage.setItem(current, doc.friendly); } if (doc && doc.camera) { @@ -192,7 +222,7 @@ export class PouchdbPersistenceManager { await this.db.put(newDoc); } } else { - this._logger.debug('no friendly name found'); + this._logger.warn('no friendly name found'); } } } @@ -225,12 +255,9 @@ export class PouchdbPersistenceManager { } private async sendLocalDataToScene() { - let salt = null; - const clear = localStorage.getItem('clearLocal'); try { - const all = await this.db.allDocs({include_docs: true}); for (const dbEntity of all.rows) { this._logger.debug(dbEntity.doc); @@ -265,8 +292,14 @@ export class PouchdbPersistenceManager { switch (err.message) { case 'WebCrypto_DecryptionFailure: ': case 'Invalid data type!': - const promptPassword = new CustomEvent('promptpassword', {detail: 'Please enter password'}); - document.dispatchEvent(promptPassword); + this._failCount++; + if (this._failCount < 5) { + const promptPassword = new CustomEvent('promptpassword', {detail: 'Please enter password'}); + document.dispatchEvent(promptPassword); + } else { + this._logger.error('Too many decryption failures, Ignoring... This may compromise your data security'); + window.alert('Too many decryption failures, Ignoring... This may compromise your data security'); + } } this._logger.error(err); } diff --git a/src/integration/presence.ts b/src/integration/presence.ts index 9276555..f1122c1 100644 --- a/src/integration/presence.ts +++ b/src/integration/presence.ts @@ -94,15 +94,15 @@ export class Presence { this._logger.warn('user not found', data.user); const newUser = MeshBuilder.CreateDisc(data.user.id, {radius: 0.3}, scene); const node = new TransformNode(data.netAddr, scene); + const material = new StandardMaterial(data.user.id + 'mat', scene); material.diffuseColor = new Color3(0, 0, 1); material.backFaceCulling = false; + newUser.material = material; newUser.parent = node; newUser.rotation.x = Math.PI / 2; newUser.position.y = 0.01; - node.position = xyztovec(data.user.base.position); - node.rotation = xyztovec(data.user.base.rotation); } } this._logger.debug('user update', data.user); diff --git a/src/toolbox/functions/buildColor.ts b/src/toolbox/functions/buildColor.ts index 4e332da..461063a 100644 --- a/src/toolbox/functions/buildColor.ts +++ b/src/toolbox/functions/buildColor.ts @@ -12,7 +12,7 @@ import {enumKeys} from "../../util/functions/enumKeys"; import {ToolType} from "../types/toolType"; import {buildTool} from "./buildTool"; -export function buildColor(color: Color3, scene: Scene, parent: TransformNode, index: number, toolMap: Map): Node { +export async function buildColor(color: Color3, scene: Scene, parent: TransformNode, index: number, toolMap: Map): Promise { const width = .1; const height = .1; const material = new StandardMaterial("material-" + color.toHexString(), scene); @@ -38,7 +38,7 @@ export function buildColor(color: Color3, scene: Scene, parent: TransformNode, i let i = 0; const tools = []; for (const tool of enumKeys(ToolType)) { - const newItem = buildTool(ToolType[tool], colorBoxMesh, material); + const newItem = await buildTool(ToolType[tool], colorBoxMesh, material); if (newItem) { //buildColorPicker(scene, color, newItem, material, i, colorChangeObservable); newItem.position = new Vector3(calculatePosition(++i), .1, 0); diff --git a/src/toolbox/functions/buildMesh.ts b/src/toolbox/functions/buildMesh.ts index 5a7f72e..9a732b7 100644 --- a/src/toolbox/functions/buildMesh.ts +++ b/src/toolbox/functions/buildMesh.ts @@ -1,11 +1,13 @@ import {ToolType} from "../types/toolType"; -import {Mesh, MeshBuilder, Scene} from "@babylonjs/core"; +import {Mesh, MeshBuilder, Scene, SceneLoader} from "@babylonjs/core"; +import {DefaultScene} from "../../defaultScene"; const detail = { tesselation: 16, subdivisions: 5 } -export function buildMesh(type: ToolType, toolname: string, scene: Scene): Mesh { + +export async function buildMesh(type: ToolType, toolname: string, scene: Scene): Promise { switch (type) { case ToolType.BOX: return MeshBuilder.CreateBox(toolname, {width: 1, height: 1, depth: 1}, scene); @@ -34,7 +36,11 @@ export function buildMesh(type: ToolType, toolname: string, scene: Scene): Mesh diameterBottom: 1, tessellation: detail.tesselation }, scene); - + case ToolType.PERSON: + const result = await SceneLoader.ImportMeshAsync(null, '/assets/models/', 'person.stl', DefaultScene.Scene); + result.meshes[0].id = toolname; + result.meshes[0].name = toolname; + return result.meshes[0] as Mesh; case ToolType.PLANE: return MeshBuilder.CreatePlane(toolname, {width: 1, height: 1}, scene); diff --git a/src/toolbox/functions/buildTool.ts b/src/toolbox/functions/buildTool.ts index f3d5c89..e70cc93 100644 --- a/src/toolbox/functions/buildTool.ts +++ b/src/toolbox/functions/buildTool.ts @@ -4,7 +4,7 @@ import {buildMesh} from "./buildMesh"; const WIDGET_SIZE = .1; -export function buildTool(tool: ToolType, colorParent: AbstractMesh, material: Material) { +export async function buildTool(tool: ToolType, colorParent: AbstractMesh, material: Material) { let id = "ID"; let color = "#000000"; switch (material.getClassName()) { @@ -21,7 +21,7 @@ export function buildTool(tool: ToolType, colorParent: AbstractMesh, material: M } - const newItem = buildMesh(tool, `tool-${id}`, colorParent.getScene()); + const newItem = await buildMesh(tool, `tool-${id}`, colorParent.getScene()); if (!newItem) { return null; } diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index 250b840..0b75288 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -1,4 +1,4 @@ -import {AbstractMesh, Color3, InstancedMesh, Node, Scene, TransformNode, Vector3} from "@babylonjs/core"; +import {AbstractMesh, Color3, InstancedMesh, Node, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core"; import {buildColor} from "./functions/buildColor"; import log from "loglevel"; import {Handle} from "../objects/handle"; @@ -19,13 +19,16 @@ export class Toolbox { private readonly _handle: Handle; private readonly _scene: Scene; - constructor() { + constructor(readyObservable: Observable) { this._scene = DefaultScene.Scene; this._toolboxBaseNode = new TransformNode("toolbox", this._scene); this._handle = new Handle(this._toolboxBaseNode, 'Toolbox'); this._toolboxBaseNode.position.y = .2; this._toolboxBaseNode.scaling = new Vector3(0.6, 0.6, 0.6); - this.buildToolbox(); + this.buildToolbox().then(() => { + readyObservable.notifyObservers(true); + this._logger.info('Toolbox built'); + }); Toolbox._instance = this; } private index = 0; @@ -46,9 +49,9 @@ export class Toolbox { return this._tools.has(mesh.id); } - private buildToolbox() { + private async buildToolbox() { this.setupPointerObservable(); - this.buildColorPicker(); + await this.buildColorPicker(); if (this._toolboxBaseNode.parent) { const platform = this._scene.getMeshById("platform"); if (platform) { @@ -94,10 +97,10 @@ export class Toolbox { node.isEnabled(false) == true }; - private buildColorPicker() { + private async buildColorPicker() { let initial = true; for (const c of colors) { - const cnode = buildColor(Color3.FromHexString(c), this._scene, this._toolboxBaseNode, this.index++, this._tools); + const cnode = await buildColor(Color3.FromHexString(c), this._scene, this._toolboxBaseNode, this.index++, this._tools); if (initial) { initial = false; for (const id of cnode.metadata.tools) { diff --git a/src/toolbox/types/toolType.ts b/src/toolbox/types/toolType.ts index 43062ae..5737994 100644 --- a/src/toolbox/types/toolType.ts +++ b/src/toolbox/types/toolType.ts @@ -5,4 +5,5 @@ export enum ToolType { CONE = "#cone-template", PLANE = "#plane-template", OBJECT = "#object-template", + PERSON = "#person-template" } \ No newline at end of file diff --git a/src/vrApp.ts b/src/vrApp.ts index d22c055..bb6cd3a 100644 --- a/src/vrApp.ts +++ b/src/vrApp.ts @@ -1,4 +1,4 @@ -import {Color3, Engine, FreeCamera, Scene, Vector3, WebGPUEngine} from "@babylonjs/core"; +import {Color3, Engine, FreeCamera, Observable, Scene, Vector3, WebGPUEngine} from "@babylonjs/core"; import '@babylonjs/loaders'; import {DiagramManager} from "./diagram/diagramManager"; import log, {Logger} from "loglevel"; @@ -16,7 +16,7 @@ import {Introduction} from "./tutorial/introduction"; const webGpu = false; -log.setLevel('error', false); +log.setLevel('debug', false); const canvas = (document.querySelector('#gameCanvas') as HTMLCanvasElement); export class VrApp { @@ -30,7 +30,39 @@ export class VrApp { }); } + public async initialize(scene: Scene) { + //const mesh = SceneLoader.ImportMesh(null, '/assets/models/', 'person.stl', DefaultScene.Scene); + setMainCamera(scene); + const spinner = new Spinner(); + spinner.show(); + const diagramReadyObservable = new Observable(); + const diagramManager = new DiagramManager(diagramReadyObservable); + diagramReadyObservable.add((ready) => { + if (ready) { + initDb(diagramManager); + } else { + this.logger.error('DiagramManager not ready'); + } + }); + initEnvironment(diagramManager, spinner); + const gamepadManager = new GamepadManager(scene); + addSceneInspector(); + //const camMenu = new CameraMenu(scene); + const el = document.querySelector('#download'); + if (el) { + el.addEventListener('click', () => { + exportGltf(); + }) + } + if (!localStorage.getItem('tutorialCompleted')) { + this.logger.info('Starting tutorial'); + const intro = new Introduction(); + } + this.logger.info('Render loop started'); + } + private async initializeEngine() { + let engine: WebGPUEngine | Engine = null; if (webGpu) { engine = new WebGPUEngine(canvas); @@ -56,28 +88,6 @@ export class VrApp { }); } - public async initialize(scene: Scene) { - setMainCamera(scene); - const spinner = new Spinner(); - spinner.show(); - const diagramManager = new DiagramManager(); - await initDb(diagramManager); - initEnvironment(diagramManager, spinner); - const gamepadManager = new GamepadManager(scene); - addSceneInspector(); - //const camMenu = new CameraMenu(scene); - const el = document.querySelector('#download'); - if (el) { - el.addEventListener('click', () => { - exportGltf(); - }) - } - if (!localStorage.getItem('tutorialCompleted')) { - this.logger.info('Starting tutorial'); - const intro = new Introduction(); - } - this.logger.info('Render loop started'); - } } @@ -95,10 +105,7 @@ async function initDb(diagramManager: DiagramManager) { const db = new PouchdbPersistenceManager(); //const userManager = new UserManager(db.onUserObservable); db.setDiagramManager(diagramManager); - await db.initialize(); - - } function initEnvironment(diagramManager: DiagramManager, spinner: Spinner) {