updated so desktop can export diagram created on headset and import to powerpoint.

This commit is contained in:
Michael Mainguy 2023-09-05 19:27:06 -05:00
parent 2cb04a35dc
commit 13a49d63bb
10 changed files with 103 additions and 66 deletions

View File

@ -38,7 +38,7 @@
z-index: 100;
}
#hostKey {
#download {
position: fixed;
z-index: 11;
width: 200px;
@ -67,7 +67,7 @@
<div id="questLaunch"><a href="https://www.oculus.com/open_url/?url=https://www.deepdiagram.com/" target="_blank">Launch
On Quest</a>
</div>
<div id="hostKey">keyLaunch On Quest</div>
<div id="download"><a href="#" id="downloadLink">Download Model</a></div>
<script type="module" src="./src/app.ts"></script>
</body>
</html>

View File

@ -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');

View File

@ -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();

View File

@ -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');

View File

@ -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;
}

View File

@ -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;

View File

@ -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<any>;
public readonly connectionObservable: Observable<any> = new Observable<any>;
public readonly dataReplicationObservable: Observable<any> = new Observable<any>;
public readonly diagramEventObservable: Observable<DiagramEvent> = new Observable<DiagramEvent>;
private readonly onDiagramEventObservable: Observable<DiagramEvent>;
constructor(onDiagramEventObservable: Observable<DiagramEvent>) {
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) {

View File

@ -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();
}
}

View File

@ -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();
});
});
}
}

View File

@ -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);