updated so desktop can export diagram created on headset and import to powerpoint.
This commit is contained in:
parent
2cb04a35dc
commit
13a49d63bb
@ -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>
|
||||
|
||||
27
src/app.ts
27
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');
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
27
src/util/diagramExporter.ts
Normal file
27
src/util/diagramExporter.ts
Normal 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();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user