From 13a49d63bb053297159c8c2100f1dc817558bd70 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Tue, 5 Sep 2023 19:27:06 -0500 Subject: [PATCH] updated so desktop can export diagram created on headset and import to powerpoint. --- index.html | 4 +- src/app.ts | 27 +++++++++-- src/diagram/diagramConnection.ts | 1 + src/diagram/diagramEventHandler.ts | 5 +- src/diagram/functions/fromDiagramEntity.ts | 54 +++++++++++----------- src/diagram/textLabel.ts | 2 +- src/integration/peerjsNetworkConnection.ts | 20 ++++---- src/menus/editMenu.ts | 25 +++------- src/util/diagramExporter.ts | 27 +++++++++++ src/worker.ts | 4 +- 10 files changed, 103 insertions(+), 66 deletions(-) create mode 100644 src/util/diagramExporter.ts diff --git a/index.html b/index.html index 04561b4..34b88fe 100644 --- a/index.html +++ b/index.html @@ -38,7 +38,7 @@ z-index: 100; } - #hostKey { + #download { position: fixed; z-index: 11; width: 200px; @@ -67,7 +67,7 @@
Launch On Quest
-
keyLaunch On Quest
+
Download Model
diff --git a/src/app.ts b/src/app.ts index e36c2fd..bc5f904 100644 --- a/src/app.ts +++ b/src/app.ts @@ -18,6 +18,7 @@ import {Controllers} from "./controllers/controllers"; import workerUrl from "./worker?worker&url"; import {DiagramEventType} from "./diagram/diagramEntity"; import {PeerjsNetworkConnection} from "./integration/peerjsNetworkConnection"; +import {DiagramExporter} from "./util/diagramExporter"; export class App { @@ -51,17 +52,24 @@ export class App { const engine = new Engine(canvas, true); const scene = new Scene(engine); const config = new AppConfig(); + const peerjsNetworkConnection = new PeerjsNetworkConnection(); + //const persistenceManager = new IndexdbPersistenceManager("diagram"); const worker = new Worker(workerUrl, {type: 'module'}); - + peerjsNetworkConnection.connectionObservable.add((peerId) => { + worker.postMessage({type: 'sync'}); + }); const controllers = new Controllers(); const toolbox = new Toolbox(scene, controllers); - const diagramManager = new DiagramManager(scene, controllers, toolbox, config); - + peerjsNetworkConnection.diagramEventObservable.add((evt) => { + this.logger.debug('App', 'peerjs network event', evt); + diagramManager.onDiagramEventObservable.notifyObservers(evt, 1); + }); diagramManager.onDiagramEventObservable.add((evt) => { this.logger.debug('App', 'diagram event', evt); + peerjsNetworkConnection.dataReplicationObservable.notifyObservers(evt); worker.postMessage({entity: evt}); }, 2); config.onConfigChangedObservable.add((config) => { @@ -73,6 +81,10 @@ export class App { if (evt.data.entity) { this.logger.debug('App', 'worker message', evt.data.entity); + peerjsNetworkConnection.dataReplicationObservable.notifyObservers({ + type: DiagramEventType.ADD, + entity: evt.data.entity + }); diagramManager.onDiagramEventObservable.notifyObservers({ type: DiagramEventType.ADD, entity: evt.data.entity @@ -80,7 +92,6 @@ export class App { } if (evt.data.config) { - config.onConfigChangedObservable.notifyObservers(evt.data.config, 1); } } @@ -191,6 +202,14 @@ export class App { }); } }); + const exportLink = document.querySelector('#downloadLink'); + if (exportLink) { + exportLink.addEventListener('click', (ev) => { + ev.preventDefault(); + const exporter = new DiagramExporter(scene); + exporter.export(); + }); + } this.logger.info('keydown event listener added, use Ctrl+Shift+Alt+I to toggle debug layer'); diff --git a/src/diagram/diagramConnection.ts b/src/diagram/diagramConnection.ts index 8fb0a4b..14f0f8d 100644 --- a/src/diagram/diagramConnection.ts +++ b/src/diagram/diagramConnection.ts @@ -70,6 +70,7 @@ export class DiagramConnection { this.toAnchor.dispose(); this.toAnchor = toAnchor; this._mesh.metadata.to = this.to; + this._mesh.metadata.exportable = true; this._mesh.id = this.id; this.recalculate(); this.setPoints(); diff --git a/src/diagram/diagramEventHandler.ts b/src/diagram/diagramEventHandler.ts index 2548cac..d29900c 100644 --- a/src/diagram/diagramEventHandler.ts +++ b/src/diagram/diagramEventHandler.ts @@ -17,10 +17,7 @@ export function diagramEventHandler(event: DiagramEvent, sounds: DiaSounds) { const entity = event.entity; let mesh; - if (entity) { - mesh = scene.getMeshById(entity.id); - } - if (!mesh && event?.entity?.template) { + if (event?.entity?.template) { const toolMesh = scene.getMeshById("tool-" + event.entity.template + "-" + event.entity.color); if (!toolMesh && event.type != DiagramEventType.CHANGECOLOR) { log.debug('no mesh found for ' + event.entity.template + "-" + event.entity.color, 'adding it'); diff --git a/src/diagram/functions/fromDiagramEntity.ts b/src/diagram/functions/fromDiagramEntity.ts index a14142c..6330306 100644 --- a/src/diagram/functions/fromDiagramEntity.ts +++ b/src/diagram/functions/fromDiagramEntity.ts @@ -15,65 +15,63 @@ export function fromDiagramEntity(entity: DiagramEntity, scene: Scene): Abstract if (!entity.id) { entity.id = "id" + uuidv4(); } - let mesh: AbstractMesh = scene.getMeshById(entity.id); - if (mesh) { - logger.debug(`mesh ${mesh.id} already exists`); + const oldMesh: AbstractMesh = scene.getMeshById(entity.id); + let newMesh: AbstractMesh; + if (oldMesh) { + logger.debug(`mesh ${oldMesh.id} already exists`); + newMesh = oldMesh; } else { if (entity.template == "#connection-template") { const connection: DiagramConnection = new DiagramConnection(entity.from, entity.to, scene); logger.debug(`connection.mesh = ${connection.mesh.id}`); - mesh = connection.mesh; + newMesh = connection.mesh; } else { - mesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color); - if (mesh) { - if (mesh.isAnInstance) { - logger.error(`mesh ${mesh.id} is an instance`); - } else { - mesh = new InstancedMesh(entity.id, (mesh as Mesh)); - } + const toolMesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color); + if (toolMesh && !oldMesh) { + newMesh = new InstancedMesh(entity.id, (toolMesh as Mesh)); + newMesh.metadata = {template: entity.template, exportable: true}; } else { - logger.warn('no mesh found for ' + entity.template + "-" + entity.color); + logger.warn('no tool mesh found for ' + entity.template + "-" + entity.color); } } - } - if (mesh) { - mesh.metadata = {template: entity.template}; + + if (newMesh) { if (entity.position) { - mesh.position = xyztovec(entity.position); + newMesh.position = xyztovec(entity.position); } if (entity.rotation) { - if (mesh.rotationQuaternion) { - mesh.rotationQuaternion = Quaternion.FromEulerAngles(entity.rotation.x, entity.rotation.y, entity.rotation.z); + if (newMesh.rotationQuaternion) { + newMesh.rotationQuaternion = Quaternion.FromEulerAngles(entity.rotation.x, entity.rotation.y, entity.rotation.z); } else { - mesh.rotation = xyztovec(entity.rotation); + newMesh.rotation = xyztovec(entity.rotation); } } if (entity.parent) { - mesh.parent = scene.getNodeById(entity.parent); + newMesh.parent = scene.getNodeById(entity.parent); } if (entity.scale) { - mesh.scaling = xyztovec(entity.scale); + newMesh.scaling = xyztovec(entity.scale); } - if (!mesh.material) { + if (!newMesh.material) { const material = new StandardMaterial("material-" + entity.id, scene); material.diffuseColor = Color3.FromHexString(entity.color); - mesh.material = material; + newMesh.material = material; } if (entity.text) { - mesh.metadata.text = entity.text; - TextLabel.updateTextNode(mesh, entity.text); + newMesh.metadata.text = entity.text; + TextLabel.updateTextNode(newMesh, entity.text); } if (entity.from) { - mesh.metadata.from = entity.from; + newMesh.metadata.from = entity.from; } if (entity.to) { - mesh.metadata.to = entity.to; + newMesh.metadata.to = entity.to; } } else { logger.error("fromDiagramEntity: mesh is null after it should have been created"); } - return mesh; + return newMesh; } diff --git a/src/diagram/textLabel.ts b/src/diagram/textLabel.ts index 1ea7ab6..cab5596 100644 --- a/src/diagram/textLabel.ts +++ b/src/diagram/textLabel.ts @@ -51,7 +51,7 @@ export class TextLabel { const plane = MeshBuilder.CreatePlane("text" + text, {width: planeWidth, height: height}, mesh.getScene()); plane.material = mat; plane.billboardMode = Mesh.BILLBOARDMODE_ALL; - + plane.metadata = {exportable: true}; const yOffset = mesh.getBoundingInfo().boundingSphere.radius; plane.parent = mesh; diff --git a/src/integration/peerjsNetworkConnection.ts b/src/integration/peerjsNetworkConnection.ts index 26a0623..1e41182 100644 --- a/src/integration/peerjsNetworkConnection.ts +++ b/src/integration/peerjsNetworkConnection.ts @@ -1,15 +1,17 @@ import P2PDataChannel from 'p2p-data-channel'; import log from "loglevel"; import {Observable} from "@babylonjs/core"; -import {DiagramEvent, DiagramEventMask} from "../diagram/diagramEntity"; +import {DiagramEvent} from "../diagram/diagramEntity"; + export class PeerjsNetworkConnection { private logger: log.Logger = log.getLogger('PeerjsNetworkConnection'); private dataChannel: P2PDataChannel; + public readonly connectionObservable: Observable = new Observable; + public readonly dataReplicationObservable: Observable = new Observable; + public readonly diagramEventObservable: Observable = new Observable; - private readonly onDiagramEventObservable: Observable; - - constructor(onDiagramEventObservable: Observable) { + constructor() { const config = { debug: false, dataChannel: 'deepSharedDiagram', @@ -22,11 +24,10 @@ export class PeerjsNetworkConnection { // @ts-ignore const passphrase = window.niceware.generatePassphrase(6).join('-'); this.logger.debug('Local Passphrase: ', passphrase); - this.onDiagramEventObservable = onDiagramEventObservable; this.dataChannel = new P2PDataChannel(passphrase, config); - this.dataChannel.onConnected((peerId) => { this.logger.debug('Connected to ', peerId); + this.connectionObservable.notifyObservers(peerId); }); this.dataChannel.onMessage((message) => { this.logger.debug(message); @@ -38,8 +39,7 @@ export class PeerjsNetworkConnection { } if (message.payload.diagramEvent) { this.logger.debug('Received diagram event from ', message.sender, message.payload.diagramEvent); - const event = message.payload.diagramEvent; - this.onDiagramEventObservable.notifyObservers(event, DiagramEventMask.REMOTE); + this.diagramEventObservable.notifyObservers(message.payload.diagramEvent); } } }); @@ -52,6 +52,10 @@ export class PeerjsNetworkConnection { const linkEl = document.querySelector('#questLaunch a'); linkEl.setAttribute('href', link + passphrase); } + this.dataReplicationObservable.add((evt) => { + this.logger.debug(evt); + this.dataChannel.broadcast({diagramEvent: evt}); + }); } public connectToRemote(host: string) { diff --git a/src/menus/editMenu.ts b/src/menus/editMenu.ts index f9923cd..84bd30a 100644 --- a/src/menus/editMenu.ts +++ b/src/menus/editMenu.ts @@ -25,6 +25,7 @@ import {toDiagramEntity} from "../diagram/functions/toDiagramEntity"; import {AbstractMenu} from "./abstractMenu"; import {Controllers} from "../controllers/controllers"; import {setMenuPosition} from "../util/functions/setMenuPosition"; +import {DiagramExporter} from "../util/diagramExporter"; export class EditMenu extends AbstractMenu { private state: EditMenuState = EditMenuState.NONE; @@ -310,24 +311,7 @@ export class EditMenu extends AbstractMenu { this.showNewRelic(); break; case "export": - import("@babylonjs/serializers").then((serializers) => { - - serializers.GLTF2Export.GLBAsync(this.scene, 'diagram.glb', { - shouldExportNode: function (node) { - if (node?.metadata?.template) { - return true; - } else { - return false; - } - - } - }).then((gltf) => { - gltf.downloadFiles(); - }); - - }); - - + this.download(); break; default: this.logger.error("Unknown button"); @@ -337,4 +321,9 @@ export class EditMenu extends AbstractMenu { this.isVisible = false; } + + private download() { + const exporter = new DiagramExporter(this.scene); + exporter.export(); + } } \ No newline at end of file diff --git a/src/util/diagramExporter.ts b/src/util/diagramExporter.ts new file mode 100644 index 0000000..993f0bf --- /dev/null +++ b/src/util/diagramExporter.ts @@ -0,0 +1,27 @@ +import {Scene} from "@babylonjs/core"; + +export class DiagramExporter { + private scene: Scene; + + constructor(scene: Scene) { + this.scene = scene; + } + + public export() { + import("@babylonjs/serializers").then((serializers) => { + serializers.GLTF2Export.GLBAsync(this.scene, 'diagram.glb', { + shouldExportNode: function (node) { + if (node?.metadata?.exportable) { + return true; + } else { + return false; + } + + } + }).then((gltf) => { + gltf.downloadFiles(); + }); + + }); + } +} \ No newline at end of file diff --git a/src/worker.ts b/src/worker.ts index aff17fd..45b6e98 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -8,7 +8,9 @@ const ctx: Worker = self as any; ctx.onmessage = (event) => { console.log(event); - + if (event.data.type == 'sync') { + persistenceManager.sync(); + } if (event.data.type == 'init') { persistenceManager.updateObserver.add((event) => { console.log(event);