Simplified interactions, changed menu interactions for changing entities.

This commit is contained in:
Michael Mainguy 2024-04-10 16:36:36 -05:00
parent 2d3855621e
commit 727977d5c6
14 changed files with 303 additions and 138 deletions

98
package-lock.json generated
View File

@ -8,14 +8,14 @@
"name": "immersive", "name": "immersive",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@babylonjs/core": "^6.45.1", "@babylonjs/core": "^7.1.0",
"@babylonjs/gui": "^6.45.1", "@babylonjs/gui": "^7.1.0",
"@babylonjs/havok": "1.3.1", "@babylonjs/havok": "1.3.1",
"@babylonjs/inspector": "^6.45.1", "@babylonjs/inspector": "^7.1.0",
"@babylonjs/loaders": "^6.45.1", "@babylonjs/loaders": "^7.1.0",
"@babylonjs/materials": "^6.45.1", "@babylonjs/materials": "^7.1.0",
"@babylonjs/procedural-textures": "^6.45.1", "@babylonjs/procedural-textures": "^7.1.0",
"@babylonjs/serializers": "^6.45.1", "@babylonjs/serializers": "^7.1.0",
"@picovoice/cobra-web": "^2.0.3", "@picovoice/cobra-web": "^2.0.3",
"@picovoice/eagle-web": "^1.0.0", "@picovoice/eagle-web": "^1.0.0",
"@picovoice/web-voice-processor": "^4.0.9", "@picovoice/web-voice-processor": "^4.0.9",
@ -51,26 +51,26 @@
} }
}, },
"node_modules/@babylonjs/core": { "node_modules/@babylonjs/core": {
"version": "6.45.1", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.45.1.tgz", "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-7.1.0.tgz",
"integrity": "sha512-wkORoAqpnZb10bUhrI0vinE9IiW7+gSgH4U4Zp41wO4kSeV0mtJY+Q5Ez6/n9ad9sLykD2FD7650B+Qi5tTMSw==" "integrity": "sha512-nz1CmflajMPnjdnwYrj72s4D/Q8IkOW4DBbwMfUNLFSCondfFXS3sZBgHeCAHpimR5n4e27YwvJ66MkQJsSraQ=="
}, },
"node_modules/@babylonjs/gui": { "node_modules/@babylonjs/gui": {
"version": "6.45.1", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-6.45.1.tgz", "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-7.1.0.tgz",
"integrity": "sha512-lev/3nprv4t8lu3kW1zdlH7VzlWh9dmyZ2PkzybmBI6nB48bswPm7cX2ppaFTkpY8Z904js9TOsrYQotMzsUiw==", "integrity": "sha512-tY6MGtigjZwv+9rqtGTsyBZL9sHSPjgVq+JEWHUsACH0ffNkKsQxtKSDc5bryzNQpo1rzTMHqEeU+DnqNSVe1Q==",
"peerDependencies": { "peerDependencies": {
"@babylonjs/core": "^6.0.0" "@babylonjs/core": "^7.0.0"
} }
}, },
"node_modules/@babylonjs/gui-editor": { "node_modules/@babylonjs/gui-editor": {
"version": "6.8.0", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babylonjs/gui-editor/-/gui-editor-6.8.0.tgz", "resolved": "https://registry.npmjs.org/@babylonjs/gui-editor/-/gui-editor-7.1.0.tgz",
"integrity": "sha512-XSTYEfCdf04Th1xrTuIO/lHBSmfiG4s4fIDzpi0+OERO19bKQffzhFGh95NVzdL0XhvnQT88Fb6bkvsuAxFI/Q==", "integrity": "sha512-YHPR/2HxTT1ILSdGMAKOZ+askVCRV/hnI5CQqLVmAYkoW5FlY7TdRJtHn0JGEiOuK6Z5T3lJ3ru89wM+lGLqeg==",
"peer": true, "peer": true,
"peerDependencies": { "peerDependencies": {
"@babylonjs/core": "^6.0.0", "@babylonjs/core": "^7.0.0",
"@babylonjs/gui": "^6.0.0", "@babylonjs/gui": "^7.0.0",
"@types/react": ">=16.7.3", "@types/react": ">=16.7.3",
"@types/react-dom": ">=16.0.9" "@types/react-dom": ">=16.0.9"
} }
@ -84,57 +84,57 @@
} }
}, },
"node_modules/@babylonjs/inspector": { "node_modules/@babylonjs/inspector": {
"version": "6.45.1", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-6.45.1.tgz", "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-7.1.0.tgz",
"integrity": "sha512-4YhJLD2FrVXUFIU+ttBanhevVaCBVLaBxhPuwrxXxKWkuBulFOQz7GkvRT/CJwA3Ad9/66qwS5+vfRT99GLM/w==", "integrity": "sha512-Hs9pk1LstJMnD4xXpHFlQVqXPtH/hhUcNSPaZlz8iesFrHJEXF4Mlao+ozwbl2WzDbMzmmex5nc1lw2HEBdhww==",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/fontawesome-svg-core": "^6.1.0",
"@fortawesome/free-regular-svg-icons": "^6.0.0", "@fortawesome/free-regular-svg-icons": "^6.0.0",
"@fortawesome/free-solid-svg-icons": "^6.0.0" "@fortawesome/free-solid-svg-icons": "^6.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@babylonjs/core": "^6.0.0", "@babylonjs/core": "^7.0.0",
"@babylonjs/gui": "^6.0.0", "@babylonjs/gui": "^7.0.0",
"@babylonjs/gui-editor": "^6.0.0", "@babylonjs/gui-editor": "^7.0.0",
"@babylonjs/loaders": "^6.0.0", "@babylonjs/loaders": "^7.0.0",
"@babylonjs/materials": "^6.0.0", "@babylonjs/materials": "^7.0.0",
"@babylonjs/serializers": "^6.0.0", "@babylonjs/serializers": "^7.0.0",
"@types/react": ">=16.7.3", "@types/react": ">=16.7.3",
"@types/react-dom": ">=16.0.9" "@types/react-dom": ">=16.0.9"
} }
}, },
"node_modules/@babylonjs/loaders": { "node_modules/@babylonjs/loaders": {
"version": "6.45.1", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-6.45.1.tgz", "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-7.1.0.tgz",
"integrity": "sha512-a75JvRVxT3DROCrl5iigLEpI5/eR7Rh4wdsDzZNn7bv3sXB40Kbw8EL60W3jDBXPdGUqNVzqtrxJF/ec2udg/Q==", "integrity": "sha512-6gLbFiUsiqsQDRWLl4TL5pMKiemSAB/E61SHpp8xkBalxZUiKroSbZt3Irxc7CHIq8SE5CYfPUPYjs6BmpTgqw==",
"peerDependencies": { "peerDependencies": {
"@babylonjs/core": "^6.0.0", "@babylonjs/core": "^7.0.0",
"babylonjs-gltf2interface": "^6.0.0" "babylonjs-gltf2interface": "^7.0.0"
} }
}, },
"node_modules/@babylonjs/materials": { "node_modules/@babylonjs/materials": {
"version": "6.45.1", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-6.45.1.tgz", "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-7.1.0.tgz",
"integrity": "sha512-v1jqgG0bfX+8Qezq4eDT2sSjpQYW+ocNU70dbJF0AYrhuhhgf0SpM28C7DAKi9GUOKjMvZrIBEbFmJ6AijLjcQ==", "integrity": "sha512-tHzpV6xUqpAgF9rPudDXetk1tIPbhggrybfIpvoWwbKlffIPQw4URsA+IBG04U5owWsicdhvTLQWP1WEwfbMQg==",
"peerDependencies": { "peerDependencies": {
"@babylonjs/core": "^6.0.0" "@babylonjs/core": "^7.0.0"
} }
}, },
"node_modules/@babylonjs/procedural-textures": { "node_modules/@babylonjs/procedural-textures": {
"version": "6.45.1", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babylonjs/procedural-textures/-/procedural-textures-6.45.1.tgz", "resolved": "https://registry.npmjs.org/@babylonjs/procedural-textures/-/procedural-textures-7.1.0.tgz",
"integrity": "sha512-QpLuPknYIvylfUscSqorkuXO4QSI49atQnScWN43ZpRzr+ecXWnxEaOyAmt7bbf93htshtIkUQdJhwx8w4fqrg==", "integrity": "sha512-ajjaXEQJpWNQQzWZdUJJzN4NBtI96lxbrhmBUpPdfQwAhX2LRG0tbKWKXd66QHh82f/FG+daIA2BCL7M4f2MMw==",
"peerDependencies": { "peerDependencies": {
"@babylonjs/core": "^6.0.0" "@babylonjs/core": "^7.0.0"
} }
}, },
"node_modules/@babylonjs/serializers": { "node_modules/@babylonjs/serializers": {
"version": "6.45.1", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-6.45.1.tgz", "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-7.1.0.tgz",
"integrity": "sha512-iSPUdjZhQIbSyi21IFiFC/wm6Bj4zcH9NhsN9i+xqLTbO/Uho49cHU0ew39n0Xn+LRsGRMTJWQLTsp+TPvAvrw==", "integrity": "sha512-ToYutpqvqFVic+mhFkza6rLeE31sXXpixhdpu2bE+MRP13FoxNKfPD4np/DGBP7zUWmXIi2yRm6sZ7cGs8jp1g==",
"peerDependencies": { "peerDependencies": {
"@babylonjs/core": "^6.0.0", "@babylonjs/core": "^7.0.0",
"babylonjs-gltf2interface": "^6.0.0" "babylonjs-gltf2interface": "^7.0.0"
} }
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
@ -1083,9 +1083,9 @@
} }
}, },
"node_modules/babylonjs-gltf2interface": { "node_modules/babylonjs-gltf2interface": {
"version": "6.8.0", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-7.1.0.tgz",
"integrity": "sha512-uCehLc8CnMUvWYttqQmJCHnhUjoftb4gK6Q+GXclgNr3CWiNDXHnbiBZQynA8SBfgkgE3bRk1+u+J5PQwkOepQ==", "integrity": "sha512-44UnRGFj/kp6/MxB6SU5ThxYBjm9W0aYGkDbtSoPW2ZuWd4uTCjCKe8oS3fhsqXcZ5+K+rQVsHb785hxC4zRcw==",
"peer": true "peer": true
}, },
"node_modules/base64-js": { "node_modules/base64-js": {

View File

@ -17,14 +17,14 @@
}, },
"dependencies": { "dependencies": {
"axios": "^1.6.8", "axios": "^1.6.8",
"@babylonjs/core": "^6.45.1", "@babylonjs/core": "^7.1.0",
"@babylonjs/gui": "^6.45.1", "@babylonjs/gui": "^7.1.0",
"@babylonjs/havok": "1.3.1", "@babylonjs/havok": "1.3.1",
"@babylonjs/inspector": "^6.45.1", "@babylonjs/inspector": "^7.1.0",
"@babylonjs/loaders": "^6.45.1", "@babylonjs/loaders": "^7.1.0",
"@babylonjs/materials": "^6.45.1", "@babylonjs/materials": "^7.1.0",
"@babylonjs/procedural-textures": "^6.45.1", "@babylonjs/procedural-textures": "^7.1.0",
"@babylonjs/serializers": "^6.45.1", "@babylonjs/serializers": "^7.1.0",
"events": "^3.3.0", "events": "^3.3.0",
"@typed-mxgraph/typed-mxgraph": "^1.0.8", "@typed-mxgraph/typed-mxgraph": "^1.0.8",
"@types/node": "^18.14.0", "@types/node": "^18.14.0",

View File

@ -20,7 +20,9 @@ import {snapGridVal} from "../util/functions/snapGridVal";
import {snapRotateVal} from "../util/functions/snapRotateVal"; import {snapRotateVal} from "../util/functions/snapRotateVal";
import {grabAndClone} from "./functions/grab"; import {grabAndClone} from "./functions/grab";
import {isDiagramEntity} from "../diagram/functions/isDiagramEntity"; import {isDiagramEntity} from "../diagram/functions/isDiagramEntity";
import {ClickMenu} from "../menus/clickMenu";
const CLICK_TIME = 300;
export class Base { export class Base {
static stickVector = Vector3.Zero(); static stickVector = Vector3.Zero();
protected controller: WebXRInputSource; protected controller: WebXRInputSource;
@ -32,7 +34,7 @@ export class Base {
protected previousRotation: Vector3 = null; protected previousRotation: Vector3 = null;
protected previousScaling: Vector3 = null; protected previousScaling: Vector3 = null;
protected previousPosition: Vector3 = null; protected previousPosition: Vector3 = null;
private clickStart: number = 0;
protected readonly xr: WebXRDefaultExperience; protected readonly xr: WebXRDefaultExperience;
protected readonly diagramManager: DiagramManager; protected readonly diagramManager: DiagramManager;
private logger: log.Logger; private logger: log.Logger;
@ -45,6 +47,7 @@ export class Base {
controllers: Controllers, controllers: Controllers,
diagramManager: DiagramManager) { diagramManager: DiagramManager) {
this.logger = log.getLogger('Base'); this.logger = log.getLogger('Base');
this.logger.setLevel(this.logger.levels.DEBUG);
this.controller = controller; this.controller = controller;
this.controllers = controllers; this.controllers = controllers;
this.scene = scene; this.scene = scene;
@ -71,9 +74,14 @@ export class Base {
this.diagramManager = diagramManager; this.diagramManager = diagramManager;
this.controller.onMotionControllerInitObservable.add((init) => { this.controller.onMotionControllerInitObservable.add((init) => {
this.logger.debug(init.components);
if (init.components['xr-standard-squeeze']) { if (init.components['xr-standard-squeeze']) {
this.initGrip(init.components['xr-standard-squeeze']) this.initGrip(init.components['xr-standard-squeeze'])
} }
if (init.components['xr-standard-trigger']) {
this.initClicker(init.components['xr-standard-trigger']);
}
}, -1, false, this); }, -1, false, this);
this.controllers.controllerObserver.add((event) => { this.controllers.controllerObserver.add((event) => {
this.logger.debug(event); this.logger.debug(event);
@ -108,7 +116,33 @@ export class Base {
this.controller.pointer.setEnabled(true) this.controller.pointer.setEnabled(true)
} }
private grab() { protected initClicker(trigger: WebXRControllerComponent) {
this.logger.debug("initTrigger");
trigger.onButtonStateChangedObservable.add(() => {
if (trigger.changes.pressed) {
if (trigger.pressed) {
if (this.clickStart == 0) {
this.clickStart = Date.now();
window.setTimeout(() => {
if (this.clickStart > 0) {
this.logger.debug("grabbing and cloning");
this.grab(true);
}
}, 300, this);
}
} else {
const clickEnd = Date.now();
if (this.clickStart > 0 && (clickEnd - this.clickStart) < CLICK_TIME) {
this.click();
}
this.drop();
this.clickStart = 0;
}
}
}, -1, false, this);
}
private grab(cloneEntity: boolean = false) {
let mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId); let mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId);
if (!mesh) { if (!mesh) {
return; return;
@ -116,7 +150,7 @@ export class Base {
let player = false; let player = false;
if (!isDiagramEntity(mesh)) { if (!isDiagramEntity(mesh)) {
if (mesh?.metadata?.handle == true) { if (this.handleWasGrabbed(mesh)) {
mesh && mesh.setParent(this.controller.motionController.rootMesh); mesh && mesh.setParent(this.controller.motionController.rootMesh);
this.grabbedMesh = mesh; this.grabbedMesh = mesh;
} else { } else {
@ -132,7 +166,6 @@ export class Base {
} }
} else { } else {
if (mesh?.metadata?.template == '#connection-template') { if (mesh?.metadata?.template == '#connection-template') {
return; return;
} }
@ -143,7 +176,7 @@ export class Base {
this.previousScaling = mesh?.scaling.clone(); this.previousScaling = mesh?.scaling.clone();
this.previousPosition = mesh?.position.clone(); this.previousPosition = mesh?.position.clone();
if (!mesh.metadata?.grabClone || player) { if ((!mesh.metadata?.grabClone || player) && !cloneEntity) {
if (mesh.physicsBody) { if (mesh.physicsBody) {
const transformNode = setupTransformNode(mesh, this.controller.motionController.rootMesh); const transformNode = setupTransformNode(mesh, this.controller.motionController.rootMesh);
mesh.physicsBody.setMotionType(PhysicsMotionType.ANIMATED); mesh.physicsBody.setMotionType(PhysicsMotionType.ANIMATED);
@ -151,9 +184,9 @@ export class Base {
} else { } else {
mesh.setParent(this.controller.motionController.rootMesh); mesh.setParent(this.controller.motionController.rootMesh);
} }
this.grabbedMesh = mesh; this.grabbedMesh = mesh;
} else { } else {
this.logger.debug("cloning " + mesh?.id);
const clone = grabAndClone(this.diagramManager, mesh, this.controller.motionController.rootMesh); const clone = grabAndClone(this.diagramManager, mesh, this.controller.motionController.rootMesh);
clone.newMesh.metadata.grabClone = false; clone.newMesh.metadata.grabClone = false;
clone.newMesh.metadata.tool = false; clone.newMesh.metadata.tool = false;
@ -168,29 +201,25 @@ export class Base {
} }
} }
private toolboxHandleWasGrabbed(mesh: AbstractMesh): boolean { private handleWasGrabbed(mesh: AbstractMesh): boolean {
if (isDiagramEntity(mesh)) { if (isDiagramEntity(mesh)) {
this.grabbedMesh = null;
this.previousParentId = null;
mesh.setParent(null);
return false; return false;
} else { } else {
return (mesh?.metadata?.handle == true); return (mesh?.metadata?.handle == true);
} }
} }
private drop() { private drop() {
const mesh = this.grabbedMesh; const mesh = this.grabbedMesh;
if (!mesh) { if (!mesh) {
return; return;
} }
if (this.toolboxHandleWasGrabbed(mesh)) { if (this.handleWasGrabbed(mesh)) {
mesh.setParent(this.scene.getMeshByName("platform")) mesh.setParent(this.scene.getMeshByName("platform"))
return; return;
} }
reparent(mesh, this.previousParentId, this.grabbedMeshParentId); reparent(mesh, this.previousParentId, this.grabbedMeshParentId);
this.grabbedMeshParentId = null; this.grabbedMeshParentId = null;
if (!mesh.physicsBody) { if (!mesh.physicsBody) {
mesh.position = snapGridVal(mesh.position, this.diagramManager._config.current.gridSnap); mesh.position = snapGridVal(mesh.position, this.diagramManager._config.current.gridSnap);
mesh.rotation = snapRotateVal(mesh.rotation, this.diagramManager._config.current.rotateSnap); mesh.rotation = snapRotateVal(mesh.rotation, this.diagramManager._config.current.rotateSnap);
@ -224,15 +253,32 @@ export class Base {
this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1); this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1);
} }
private click() {
let mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId);
if (pointable(mesh)) {
this.logger.debug("click on " + mesh.id);
const menu = new ClickMenu(mesh, this.diagramManager);
} else {
this.logger.debug("click on nothing");
}
}
private initGrip(grip: WebXRControllerComponent) { private initGrip(grip: WebXRControllerComponent) {
grip.onButtonStateChangedObservable.add(() => { grip.onButtonStateChangedObservable.add(() => {
if (grip.changes.pressed) { if (grip.changes.pressed) {
if (grip.pressed) { if (grip.pressed) {
this.grab(); this.grab();
} else { } else {
this.drop(); this.drop();
} }
} }
}); });
} }
}
function pointable(mesh: AbstractMesh): boolean {
return (mesh && mesh.metadata?.template && !mesh.metadata?.tool && !mesh.metadata?.handle && !mesh.metadata?.grabbable && !mesh.metadata?.grabClone);
} }

1
src/env.d.ts vendored
View File

@ -3,6 +3,7 @@
interface ImportMetaEnv { interface ImportMetaEnv {
readonly VITE_SYNCDB_ENDPOINT: string, readonly VITE_SYNCDB_ENDPOINT: string,
readonly VITE_SYNCDB_USER: string readonly VITE_SYNCDB_USER: string
readonly VITE_CREATE_ENDPOINT: string
// more env variables... // more env variables...
} }

View File

@ -37,8 +37,6 @@ export class InputTextView {
} }
public showVirtualKeyboard() { public showVirtualKeyboard() {
const inputMesh = MeshBuilder.CreatePlane("input", {width: 1, height: .5}, this.scene); const inputMesh = MeshBuilder.CreatePlane("input", {width: 1, height: .5}, this.scene);
const handle = new Handle(inputMesh); const handle = new Handle(inputMesh);
setMenuPosition(handle.mesh, this.scene, new Vector3(0, .4, 0)); setMenuPosition(handle.mesh, this.scene, new Vector3(0, .4, 0));

View File

@ -1,20 +0,0 @@
import log from "loglevel";
const logger = log.getLogger('syncDoc');
export function syncDoc(info) {
logger.debug(info);
if (info.direction == 'pull') {
const docs = info.change.docs;
for (const doc of docs) {
logger.debug(doc);
if (doc._deleted) {
logger.debug('Delete', doc);
this.removeObserver.notifyObservers({id: doc._id, template: doc.template}, 1);
} else {
this.updateObserver.notifyObservers(doc, 1);
}
}
}
}

View File

@ -6,7 +6,6 @@ import {v4 as uuidv4} from 'uuid';
import axios from "axios"; import axios from "axios";
import {DiagramManager} from "../diagram/diagramManager"; import {DiagramManager} from "../diagram/diagramManager";
import log, {Logger} from "loglevel"; import log, {Logger} from "loglevel";
import {syncDoc} from "./functions/syncDoc";
const logger: Logger = log.getLogger('PouchdbPersistenceManager'); const logger: Logger = log.getLogger('PouchdbPersistenceManager');
export class PouchdbPersistenceManager { export class PouchdbPersistenceManager {
@ -166,7 +165,7 @@ export class PouchdbPersistenceManager {
createSnap: 0, createSnap: 0,
turnSnap: 0, turnSnap: 0,
flyMode: true, flyMode: true,
currentDiagramId: uuidv4() currentDiagramId: 'public'
} }
try { try {
await this.setConfig(defaultConfig, true); await this.setConfig(defaultConfig, true);
@ -217,21 +216,52 @@ export class PouchdbPersistenceManager {
} }
private async beginSync(remoteDbName: string) { private syncDoc(info) {
logger.debug(info);
if (info.direction == 'pull') {
const docs = info.change.docs;
for (const doc of docs) {
logger.debug(doc);
if (doc._deleted) {
logger.debug('Delete', doc);
this.removeObserver.notifyObservers({id: doc._id, template: doc.template}, 1);
} else {
this.updateObserver.notifyObservers(doc, 1);
}
}
}
}
private async beginSync(localName: string) {
try { try {
//const remoteDbName = "db1"; //const remoteDbName = "db1";
const userHex = remoteDbName.split('-');
if (userHex.length < 2) { const userHex = ascii_to_hex(localName);
return; const remoteDbName = 'userdb-' + userHex;
} const remoteUserName = localName;
const username = hex_to_ascii(userHex[1]); const password = localName;
const remoteUserName = username;
const password = "password";
const dbs = await axios.get(import.meta.env.VITE_SYNCDB_ENDPOINT + 'list'); const dbs = await axios.get(import.meta.env.VITE_SYNCDB_ENDPOINT + 'list');
logger.debug(dbs.data); logger.debug(dbs.data);
if (dbs.data.indexOf(remoteDbName) == -1) { if (dbs.data.indexOf(remoteDbName) == -1) {
logger.warn('sync target missing'); logger.warn('sync target missing attempting to create');
return; const newdb = await axios.post(import.meta.env.VITE_CREATE_ENDPOINT,
{
"_id": "org.couchdb.user:" + localName,
"name": localName,
"password": localName,
"roles": ["readers"],
"type": "user"
}
);
if (newdb.status == 200) {
logger.info('sync target created');
} else {
return;
}
} }
const userEndpoint: string = import.meta.env.VITE_USER_ENDPOINT const userEndpoint: string = import.meta.env.VITE_USER_ENDPOINT
logger.debug(userEndpoint); logger.debug(userEndpoint);
@ -262,9 +292,11 @@ export class PouchdbPersistenceManager {
{auth: {username: remoteUserName, password: password}, skip_setup: true}); {auth: {username: remoteUserName, password: password}, skip_setup: true});
const dbInfo = await this.remote.info(); const dbInfo = await this.remote.info();
logger.debug(dbInfo); logger.debug(dbInfo);
syncDoc.bind(this);
this.db.sync(this.remote, {live: true, retry: true}) this.db.sync(this.remote, {live: true, retry: true})
.on('change', syncDoc) .on('change', (info) => {
this.syncDoc(info)
})
.on('active', function (info) { .on('active', function (info) {
logger.debug('sync active', info) logger.debug('sync active', info)
}) })
@ -272,7 +304,7 @@ export class PouchdbPersistenceManager {
logger.debug('sync paused', info) logger.debug('sync paused', info)
}) })
.on('error', function (err) { .on('error', function (err) {
logger.debug('sync error', err) logger.error('sync error', err)
}); });
} catch (err) { } catch (err) {
logger.error(err); logger.error(err);
@ -283,8 +315,17 @@ export class PouchdbPersistenceManager {
function hex_to_ascii(input) { function hex_to_ascii(input) {
var hex = input.toString(); var hex = input.toString();
let output = ''; let output = '';
for (var n = 0; n < hex.length; n += 2) { for (let n = 0; n < hex.length; n += 2) {
output += String.fromCharCode(parseInt(hex.substr(n, 2), 16)); output += String.fromCharCode(parseInt(hex.substr(n, 2), 16));
} }
return output; return output;
}
function ascii_to_hex(str) {
const arr1 = [];
for (let n = 0, l = str.length; n < l; n++) {
var hex = Number(str.charCodeAt(n)).toString(16);
arr1.push(hex);
}
return arr1.join('');
} }

78
src/menus/clickMenu.ts Normal file
View File

@ -0,0 +1,78 @@
import {Button3D, GUI3DManager, PlanePanel, TextBlock} from "@babylonjs/gui";
import {AbstractMesh, TransformNode} from "@babylonjs/core";
import {DiagramEvent, DiagramEventType} from "../diagram/types/diagramEntity";
import {toDiagramEntity} from "../diagram/functions/toDiagramEntity";
import {DiagramManager} from "../diagram/diagramManager";
export class ClickMenu {
private static readonly sounds;
private readonly entity: AbstractMesh;
private readonly manager: GUI3DManager;
private readonly transform: TransformNode;
private readonly diagramManager: DiagramManager;
constructor(entity: AbstractMesh, diagramManager: DiagramManager) {
this.entity = entity;
this.diagramManager = diagramManager;
const scene = entity.getScene();
const manager = new GUI3DManager(scene);
const transform = new TransformNode("transform", scene);
transform.position = entity.absolutePosition.clone();
transform.position.y += entity.scaling.y;
const panel = new PlanePanel();
panel.orientation = PlanePanel.FACEFORWARDREVERSED_ORIENTATION;
panel.columns = 4;
manager.controlScaling = .1;
manager.addControl(panel);
panel.addControl(this.makeButton("Remove", "remove"));
panel.addControl(this.makeButton("Label", "label"));
panel.addControl(this.makeButton("Connect", "connect"));
panel.addControl(this.makeButton("Close", "close"));
panel.linkToTransformNode(transform);
this.transform = transform;
this.manager = manager;
}
private makeButton(name: string, id: string) {
const button = new Button3D(name);
//button.scaling = new Vector3(.1, .1, .1);
button.name = id;
const text = new TextBlock(name, name);
text.fontSize = "48px";
text.color = "#ffffff";
text.alpha = 1;
button.content = text;
button.onPointerClickObservable.add(() => {
switch (id) {
case "close":
this.dispose();
break;
case "remove":
const event: DiagramEvent = {
type: DiagramEventType.REMOVE,
entity:
toDiagramEntity(this.entity)
}
this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1);
this.dispose();
break;
case "label":
}
}, -1, false, this, true);
return button;
}
private dispose() {
this.manager.dispose();
this.transform.dispose();
}
}

View File

@ -13,6 +13,7 @@ export class Handle {
private buildHandle() { private buildHandle() {
const scene: Scene = this.transformNode.getScene(); const scene: Scene = this.transformNode.getScene();
const handle = getHandleMesh("handle-" + this.transformNode.id + "-mesh", scene); const handle = getHandleMesh("handle-" + this.transformNode.id + "-mesh", scene);
handle.position = Vector3.Zero(); handle.position = Vector3.Zero();
handle.metadata = {handle: true}; handle.metadata = {handle: true};
if (this.transformNode) { if (this.transformNode) {

View File

@ -0,0 +1,6 @@
export type ObjectMetaType = {
id: string;
parent: string;
position: { x: number, y: number, z: number };
rotation: { x: number, y: number, z: number };
}

View File

@ -1,6 +1,5 @@
import {AxesViewer, Color3, Mesh, Node, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core"; import {AbstractMesh, AxesViewer, Color3, Node, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core";
import {GUI3DManager, StackPanel3D,} from "@babylonjs/gui"; import {GUI3DManager, StackPanel3D,} from "@babylonjs/gui";
import {setMenuPosition} from "../util/functions/setMenuPosition";
import {buildColor} from "./functions/buildColor"; import {buildColor} from "./functions/buildColor";
import log from "loglevel"; import log from "loglevel";
import {Handle} from "../objects/handle"; import {Handle} from "../objects/handle";
@ -34,20 +33,14 @@ export class Toolbox {
new Handle(this.toolboxBaseNode); new Handle(this.toolboxBaseNode);
this.toolboxBaseNode.position.y = .2; this.toolboxBaseNode.position.y = .2;
//this.toolboxBaseNode.position.z = .05; //this.toolboxBaseNode.position.z = .05;
/**this.axes = new AxesViewer(this.scene); /*this.axes = new AxesViewer(this.scene);
this.axes.xAxis.parent = this.toolboxBaseNode; this.axes.xAxis.parent = this.toolboxBaseNode;
this.axes.yAxis.parent = this.toolboxBaseNode; this.axes.yAxis.parent = this.toolboxBaseNode;
this.axes.zAxis.parent = this.toolboxBaseNode;*/ this.axes.zAxis.parent = this.toolboxBaseNode; */
this.toolboxBaseNode.scaling = new Vector3(0.6, 0.6, 0.6); this.toolboxBaseNode.scaling = new Vector3(0.6, 0.6, 0.6);
this.buildToolbox(); this.buildToolbox();
} }
public toggle() {
this.toolboxBaseNode.parent.setEnabled(!this.toolboxBaseNode.parent.isEnabled(false));
setMenuPosition(this.toolboxBaseNode.parent as Mesh, this.scene,
Vector3.Zero());
}
public updateToolbox(color: string) { public updateToolbox(color: string) {
if (color) { if (color) {
if (this.scene.getMeshById("toolbox-color-" + color)) { if (this.scene.getMeshById("toolbox-color-" + color)) {
@ -98,9 +91,27 @@ export class Toolbox {
} }
} }
//this.toolboxBaseNode.parent.setEnabled(false); //this.toolboxBaseNode.parent.setEnabled(false);
setMenuPosition(this.toolboxBaseNode.parent as Mesh, this.scene, const offset = new Vector3(0, 1, 1);
Vector3.Zero()); if (this.toolboxBaseNode.parent) {
const platform = this.scene.getNodeById("platform");
if (platform) {
const handle = (this.toolboxBaseNode.parent as TransformNode);
handle.parent = platform;
handle.position = offset;
} else {
this.scene.onNewMeshAddedObservable.add((mesh: AbstractMesh) => {
if (mesh.id == "platform") {
const handle = (this.toolboxBaseNode.parent as TransformNode);
handle.parent = mesh;
handle.position = offset;
}
});
}
}
/*setMenuPosition(this.toolboxBaseNode.parent as Mesh, this.scene,
Vector3.Zero());*/
} }
} }

View File

@ -1,10 +1,11 @@
import {MeshBuilder, Scene, Vector3} from "@babylonjs/core"; import {MeshBuilder, Scene, Vector3} from "@babylonjs/core";
const debug = false; const debug = true;
export function getFrontPosition(distance: number, scene: Scene): Vector3 { export function getFrontPosition(distance: number, scene: Scene): Vector3 {
const offset = new Vector3(0, 0, distance); const offset = new Vector3(0, 0, distance);
offset.applyRotationQuaternionInPlace(scene.activeCamera.absoluteRotation); //offset.applyRotationQuaternionInPlace(scene.activeCamera.absoluteRotation);
const newPos = scene.activeCamera.globalPosition.add(offset); const camPos = scene.activeCamera.globalPosition.clone();
const newPos = camPos.add(offset);
if (debug) { if (debug) {
const mesh = MeshBuilder.CreateIcoSphere("front", {radius: .1}, scene); const mesh = MeshBuilder.CreateIcoSphere("front", {radius: .1}, scene);
mesh.position = newPos; mesh.position = newPos;

View File

@ -1,5 +1,4 @@
import {Scene, TransformNode, Vector3} from "@babylonjs/core"; import {Scene, TransformNode, Vector3, WebXRCamera} from "@babylonjs/core";
import {getFrontPosition} from "./getFrontPosition";
import log from "loglevel"; import log from "loglevel";
const logger = log.getLogger('setMenuPosition'); const logger = log.getLogger('setMenuPosition');
@ -39,34 +38,33 @@ export function setMenuPosition(node: TransformNode, scene: Scene, offset: Vecto
*/ */
if (scene.activeCamera) { if (scene.activeCamera) {
setPosition(node, scene, offset); //setPosition(node, scene, offset);
} else { } else {
scene.onActiveCameraChanged.add((scene: Scene) => { scene.onActiveCameraChanged.add((scene: Scene) => {
setPosition(node, scene, offset); //setPosition(node, scene, offset);
}); });
logger.error("No active camera"); logger.error("No active camera");
} }
} }
const debug = true; const debug = false;
function setPosition(node: TransformNode, scene: Scene, offset: Vector3 = Vector3.Zero()) { function setPosition(node: TransformNode, scene: Scene, offset: Vector3 = Vector3.Zero()) {
const platform = scene.getNodeById("platform"); const platform = scene.getNodeById("platform");
switch (scene.activeCamera.getClassName()) { switch (scene.activeCamera.getClassName()) {
case "WebXRCamera": case "WebXRCamera":
//const oldParent = node.parent; //const oldParent = node.parent;
window.setTimeout(() => {
node.setParent(null); node.setParent(null);
const front = getFrontPosition(1, scene).clone(); const camera = scene.activeCamera as WebXRCamera;
const camPos = scene.activeCamera.globalPosition.clone(); const front = camera.getFrontPosition(.7);
const newPos = new Vector3(front.x + offset.x, 1.2 + offset.y, front.z + offset.z); const camPos = camera.globalPosition.clone();
node.position = newPos; const newPos = new Vector3(front.x + offset.x, front.y + offset.y - .3, front.z + offset.z);
node.lookAt(camPos); node.position = newPos;
// const target = MeshBuilder.CreateIcoSphere("target", {radius: .1}, scene); node.lookAt(camPos);
// target.position = newPos; node.setParent(platform);
// target.setParent(platform); }, 1000);
node.setParent(platform);
break; break;
case "FreeCamera": case "FreeCamera":
case "DeviceOrientationCamera": case "DeviceOrientationCamera":

View File

@ -18,6 +18,10 @@ export default defineConfig({
'^/sync/.*': { '^/sync/.*': {
target: 'https://www.deepdiagram.com/', target: 'https://www.deepdiagram.com/',
changeOrigin: true, changeOrigin: true,
},
'^/create-db': {
target: 'https://www.deepdiagram.com/',
changeOrigin: true,
} }
} }
}, },