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;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
#hostKey {
|
#download {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
width: 200px;
|
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
|
<div id="questLaunch"><a href="https://www.oculus.com/open_url/?url=https://www.deepdiagram.com/" target="_blank">Launch
|
||||||
On Quest</a>
|
On Quest</a>
|
||||||
</div>
|
</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>
|
<script type="module" src="./src/app.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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 workerUrl from "./worker?worker&url";
|
||||||
import {DiagramEventType} from "./diagram/diagramEntity";
|
import {DiagramEventType} from "./diagram/diagramEntity";
|
||||||
import {PeerjsNetworkConnection} from "./integration/peerjsNetworkConnection";
|
import {PeerjsNetworkConnection} from "./integration/peerjsNetworkConnection";
|
||||||
|
import {DiagramExporter} from "./util/diagramExporter";
|
||||||
|
|
||||||
|
|
||||||
export class App {
|
export class App {
|
||||||
@ -51,17 +52,24 @@ export class App {
|
|||||||
const engine = new Engine(canvas, true);
|
const engine = new Engine(canvas, true);
|
||||||
const scene = new Scene(engine);
|
const scene = new Scene(engine);
|
||||||
const config = new AppConfig();
|
const config = new AppConfig();
|
||||||
|
const peerjsNetworkConnection = new PeerjsNetworkConnection();
|
||||||
|
|
||||||
//const persistenceManager = new IndexdbPersistenceManager("diagram");
|
//const persistenceManager = new IndexdbPersistenceManager("diagram");
|
||||||
const worker = new Worker(workerUrl, {type: 'module'});
|
const worker = new Worker(workerUrl, {type: 'module'});
|
||||||
|
peerjsNetworkConnection.connectionObservable.add((peerId) => {
|
||||||
|
worker.postMessage({type: 'sync'});
|
||||||
|
});
|
||||||
const controllers = new Controllers();
|
const controllers = new Controllers();
|
||||||
const toolbox = new Toolbox(scene, controllers);
|
const toolbox = new Toolbox(scene, controllers);
|
||||||
|
|
||||||
|
|
||||||
const diagramManager = new DiagramManager(scene, controllers, toolbox, config);
|
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) => {
|
diagramManager.onDiagramEventObservable.add((evt) => {
|
||||||
this.logger.debug('App', 'diagram event', evt);
|
this.logger.debug('App', 'diagram event', evt);
|
||||||
|
peerjsNetworkConnection.dataReplicationObservable.notifyObservers(evt);
|
||||||
worker.postMessage({entity: evt});
|
worker.postMessage({entity: evt});
|
||||||
}, 2);
|
}, 2);
|
||||||
config.onConfigChangedObservable.add((config) => {
|
config.onConfigChangedObservable.add((config) => {
|
||||||
@ -73,6 +81,10 @@ export class App {
|
|||||||
|
|
||||||
if (evt.data.entity) {
|
if (evt.data.entity) {
|
||||||
this.logger.debug('App', 'worker message', 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({
|
diagramManager.onDiagramEventObservable.notifyObservers({
|
||||||
type: DiagramEventType.ADD,
|
type: DiagramEventType.ADD,
|
||||||
entity: evt.data.entity
|
entity: evt.data.entity
|
||||||
@ -80,7 +92,6 @@ export class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (evt.data.config) {
|
if (evt.data.config) {
|
||||||
|
|
||||||
config.onConfigChangedObservable.notifyObservers(evt.data.config, 1);
|
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');
|
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.dispose();
|
||||||
this.toAnchor = toAnchor;
|
this.toAnchor = toAnchor;
|
||||||
this._mesh.metadata.to = this.to;
|
this._mesh.metadata.to = this.to;
|
||||||
|
this._mesh.metadata.exportable = true;
|
||||||
this._mesh.id = this.id;
|
this._mesh.id = this.id;
|
||||||
this.recalculate();
|
this.recalculate();
|
||||||
this.setPoints();
|
this.setPoints();
|
||||||
|
|||||||
@ -17,10 +17,7 @@ export function diagramEventHandler(event: DiagramEvent,
|
|||||||
sounds: DiaSounds) {
|
sounds: DiaSounds) {
|
||||||
const entity = event.entity;
|
const entity = event.entity;
|
||||||
let mesh;
|
let mesh;
|
||||||
if (entity) {
|
if (event?.entity?.template) {
|
||||||
mesh = scene.getMeshById(entity.id);
|
|
||||||
}
|
|
||||||
if (!mesh && event?.entity?.template) {
|
|
||||||
const toolMesh = scene.getMeshById("tool-" + event.entity.template + "-" + event.entity.color);
|
const toolMesh = scene.getMeshById("tool-" + event.entity.template + "-" + event.entity.color);
|
||||||
if (!toolMesh && event.type != DiagramEventType.CHANGECOLOR) {
|
if (!toolMesh && event.type != DiagramEventType.CHANGECOLOR) {
|
||||||
log.debug('no mesh found for ' + event.entity.template + "-" + event.entity.color, 'adding it');
|
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) {
|
if (!entity.id) {
|
||||||
entity.id = "id" + uuidv4();
|
entity.id = "id" + uuidv4();
|
||||||
}
|
}
|
||||||
let mesh: AbstractMesh = scene.getMeshById(entity.id);
|
const oldMesh: AbstractMesh = scene.getMeshById(entity.id);
|
||||||
if (mesh) {
|
let newMesh: AbstractMesh;
|
||||||
logger.debug(`mesh ${mesh.id} already exists`);
|
if (oldMesh) {
|
||||||
|
logger.debug(`mesh ${oldMesh.id} already exists`);
|
||||||
|
newMesh = oldMesh;
|
||||||
} else {
|
} else {
|
||||||
if (entity.template == "#connection-template") {
|
if (entity.template == "#connection-template") {
|
||||||
const connection: DiagramConnection = new DiagramConnection(entity.from, entity.to, scene);
|
const connection: DiagramConnection = new DiagramConnection(entity.from, entity.to, scene);
|
||||||
logger.debug(`connection.mesh = ${connection.mesh.id}`);
|
logger.debug(`connection.mesh = ${connection.mesh.id}`);
|
||||||
mesh = connection.mesh;
|
newMesh = connection.mesh;
|
||||||
} else {
|
} else {
|
||||||
mesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color);
|
const toolMesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color);
|
||||||
if (mesh) {
|
if (toolMesh && !oldMesh) {
|
||||||
if (mesh.isAnInstance) {
|
newMesh = new InstancedMesh(entity.id, (toolMesh as Mesh));
|
||||||
logger.error(`mesh ${mesh.id} is an instance`);
|
newMesh.metadata = {template: entity.template, exportable: true};
|
||||||
} else {
|
|
||||||
mesh = new InstancedMesh(entity.id, (mesh as Mesh));
|
|
||||||
}
|
|
||||||
} else {
|
} 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) {
|
if (entity.position) {
|
||||||
mesh.position = xyztovec(entity.position);
|
newMesh.position = xyztovec(entity.position);
|
||||||
}
|
}
|
||||||
if (entity.rotation) {
|
if (entity.rotation) {
|
||||||
if (mesh.rotationQuaternion) {
|
if (newMesh.rotationQuaternion) {
|
||||||
mesh.rotationQuaternion = Quaternion.FromEulerAngles(entity.rotation.x, entity.rotation.y, entity.rotation.z);
|
newMesh.rotationQuaternion = Quaternion.FromEulerAngles(entity.rotation.x, entity.rotation.y, entity.rotation.z);
|
||||||
} else {
|
} else {
|
||||||
mesh.rotation = xyztovec(entity.rotation);
|
newMesh.rotation = xyztovec(entity.rotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (entity.parent) {
|
if (entity.parent) {
|
||||||
mesh.parent = scene.getNodeById(entity.parent);
|
newMesh.parent = scene.getNodeById(entity.parent);
|
||||||
}
|
}
|
||||||
if (entity.scale) {
|
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);
|
const material = new StandardMaterial("material-" + entity.id, scene);
|
||||||
material.diffuseColor = Color3.FromHexString(entity.color);
|
material.diffuseColor = Color3.FromHexString(entity.color);
|
||||||
mesh.material = material;
|
newMesh.material = material;
|
||||||
}
|
}
|
||||||
if (entity.text) {
|
if (entity.text) {
|
||||||
mesh.metadata.text = entity.text;
|
newMesh.metadata.text = entity.text;
|
||||||
TextLabel.updateTextNode(mesh, entity.text);
|
TextLabel.updateTextNode(newMesh, entity.text);
|
||||||
}
|
}
|
||||||
if (entity.from) {
|
if (entity.from) {
|
||||||
mesh.metadata.from = entity.from;
|
newMesh.metadata.from = entity.from;
|
||||||
}
|
}
|
||||||
if (entity.to) {
|
if (entity.to) {
|
||||||
mesh.metadata.to = entity.to;
|
newMesh.metadata.to = entity.to;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error("fromDiagramEntity: mesh is null after it should have been created");
|
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());
|
const plane = MeshBuilder.CreatePlane("text" + text, {width: planeWidth, height: height}, mesh.getScene());
|
||||||
plane.material = mat;
|
plane.material = mat;
|
||||||
plane.billboardMode = Mesh.BILLBOARDMODE_ALL;
|
plane.billboardMode = Mesh.BILLBOARDMODE_ALL;
|
||||||
|
plane.metadata = {exportable: true};
|
||||||
|
|
||||||
const yOffset = mesh.getBoundingInfo().boundingSphere.radius;
|
const yOffset = mesh.getBoundingInfo().boundingSphere.radius;
|
||||||
plane.parent = mesh;
|
plane.parent = mesh;
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
import P2PDataChannel from 'p2p-data-channel';
|
import P2PDataChannel from 'p2p-data-channel';
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import {Observable} from "@babylonjs/core";
|
import {Observable} from "@babylonjs/core";
|
||||||
import {DiagramEvent, DiagramEventMask} from "../diagram/diagramEntity";
|
import {DiagramEvent} from "../diagram/diagramEntity";
|
||||||
|
|
||||||
|
|
||||||
export class PeerjsNetworkConnection {
|
export class PeerjsNetworkConnection {
|
||||||
private logger: log.Logger = log.getLogger('PeerjsNetworkConnection');
|
private logger: log.Logger = log.getLogger('PeerjsNetworkConnection');
|
||||||
private dataChannel: P2PDataChannel<any>;
|
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() {
|
||||||
|
|
||||||
constructor(onDiagramEventObservable: Observable<DiagramEvent>) {
|
|
||||||
const config = {
|
const config = {
|
||||||
debug: false,
|
debug: false,
|
||||||
dataChannel: 'deepSharedDiagram',
|
dataChannel: 'deepSharedDiagram',
|
||||||
@ -22,11 +24,10 @@ export class PeerjsNetworkConnection {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const passphrase = window.niceware.generatePassphrase(6).join('-');
|
const passphrase = window.niceware.generatePassphrase(6).join('-');
|
||||||
this.logger.debug('Local Passphrase: ', passphrase);
|
this.logger.debug('Local Passphrase: ', passphrase);
|
||||||
this.onDiagramEventObservable = onDiagramEventObservable;
|
|
||||||
this.dataChannel = new P2PDataChannel(passphrase, config);
|
this.dataChannel = new P2PDataChannel(passphrase, config);
|
||||||
|
|
||||||
this.dataChannel.onConnected((peerId) => {
|
this.dataChannel.onConnected((peerId) => {
|
||||||
this.logger.debug('Connected to ', peerId);
|
this.logger.debug('Connected to ', peerId);
|
||||||
|
this.connectionObservable.notifyObservers(peerId);
|
||||||
});
|
});
|
||||||
this.dataChannel.onMessage((message) => {
|
this.dataChannel.onMessage((message) => {
|
||||||
this.logger.debug(message);
|
this.logger.debug(message);
|
||||||
@ -38,8 +39,7 @@ export class PeerjsNetworkConnection {
|
|||||||
}
|
}
|
||||||
if (message.payload.diagramEvent) {
|
if (message.payload.diagramEvent) {
|
||||||
this.logger.debug('Received diagram event from ', message.sender, message.payload.diagramEvent);
|
this.logger.debug('Received diagram event from ', message.sender, message.payload.diagramEvent);
|
||||||
const event = message.payload.diagramEvent;
|
this.diagramEventObservable.notifyObservers(message.payload.diagramEvent);
|
||||||
this.onDiagramEventObservable.notifyObservers(event, DiagramEventMask.REMOTE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -52,6 +52,10 @@ export class PeerjsNetworkConnection {
|
|||||||
const linkEl = document.querySelector('#questLaunch a');
|
const linkEl = document.querySelector('#questLaunch a');
|
||||||
linkEl.setAttribute('href', link + passphrase);
|
linkEl.setAttribute('href', link + passphrase);
|
||||||
}
|
}
|
||||||
|
this.dataReplicationObservable.add((evt) => {
|
||||||
|
this.logger.debug(evt);
|
||||||
|
this.dataChannel.broadcast({diagramEvent: evt});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public connectToRemote(host: string) {
|
public connectToRemote(host: string) {
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import {toDiagramEntity} from "../diagram/functions/toDiagramEntity";
|
|||||||
import {AbstractMenu} from "./abstractMenu";
|
import {AbstractMenu} from "./abstractMenu";
|
||||||
import {Controllers} from "../controllers/controllers";
|
import {Controllers} from "../controllers/controllers";
|
||||||
import {setMenuPosition} from "../util/functions/setMenuPosition";
|
import {setMenuPosition} from "../util/functions/setMenuPosition";
|
||||||
|
import {DiagramExporter} from "../util/diagramExporter";
|
||||||
|
|
||||||
export class EditMenu extends AbstractMenu {
|
export class EditMenu extends AbstractMenu {
|
||||||
private state: EditMenuState = EditMenuState.NONE;
|
private state: EditMenuState = EditMenuState.NONE;
|
||||||
@ -310,24 +311,7 @@ export class EditMenu extends AbstractMenu {
|
|||||||
this.showNewRelic();
|
this.showNewRelic();
|
||||||
break;
|
break;
|
||||||
case "export":
|
case "export":
|
||||||
import("@babylonjs/serializers").then((serializers) => {
|
this.download();
|
||||||
|
|
||||||
serializers.GLTF2Export.GLBAsync(this.scene, 'diagram.glb', {
|
|
||||||
shouldExportNode: function (node) {
|
|
||||||
if (node?.metadata?.template) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}).then((gltf) => {
|
|
||||||
gltf.downloadFiles();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.logger.error("Unknown button");
|
this.logger.error("Unknown button");
|
||||||
@ -337,4 +321,9 @@ export class EditMenu extends AbstractMenu {
|
|||||||
|
|
||||||
this.isVisible = false;
|
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) => {
|
ctx.onmessage = (event) => {
|
||||||
console.log(event);
|
console.log(event);
|
||||||
|
if (event.data.type == 'sync') {
|
||||||
|
persistenceManager.sync();
|
||||||
|
}
|
||||||
if (event.data.type == 'init') {
|
if (event.data.type == 'init') {
|
||||||
persistenceManager.updateObserver.add((event) => {
|
persistenceManager.updateObserver.add((event) => {
|
||||||
console.log(event);
|
console.log(event);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user