From 727977d5c61aad479c2058ffd151f9935b8d75b1 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Wed, 10 Apr 2024 16:36:36 -0500 Subject: [PATCH] Simplified interactions, changed menu interactions for changing entities. --- package-lock.json | 98 ++++++++++---------- package.json | 14 +-- src/controllers/base.ts | 72 +++++++++++--- src/env.d.ts | 1 + src/information/inputTextView.ts | 2 - src/integration/functions/syncDoc.ts | 20 ---- src/integration/pouchdbPersistenceManager.ts | 73 +++++++++++---- src/menus/clickMenu.ts | 78 ++++++++++++++++ src/objects/handle.ts | 1 + src/objects/objectMetaType.ts | 6 ++ src/toolbox/toolbox.ts | 35 ++++--- src/util/functions/getFrontPosition.ts | 7 +- src/util/functions/setMenuPosition.ts | 30 +++--- vite.config.ts | 4 + 14 files changed, 303 insertions(+), 138 deletions(-) delete mode 100644 src/integration/functions/syncDoc.ts create mode 100644 src/menus/clickMenu.ts create mode 100644 src/objects/objectMetaType.ts diff --git a/package-lock.json b/package-lock.json index 44fc23e..a91945d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,14 +8,14 @@ "name": "immersive", "version": "0.0.1", "dependencies": { - "@babylonjs/core": "^6.45.1", - "@babylonjs/gui": "^6.45.1", + "@babylonjs/core": "^7.1.0", + "@babylonjs/gui": "^7.1.0", "@babylonjs/havok": "1.3.1", - "@babylonjs/inspector": "^6.45.1", - "@babylonjs/loaders": "^6.45.1", - "@babylonjs/materials": "^6.45.1", - "@babylonjs/procedural-textures": "^6.45.1", - "@babylonjs/serializers": "^6.45.1", + "@babylonjs/inspector": "^7.1.0", + "@babylonjs/loaders": "^7.1.0", + "@babylonjs/materials": "^7.1.0", + "@babylonjs/procedural-textures": "^7.1.0", + "@babylonjs/serializers": "^7.1.0", "@picovoice/cobra-web": "^2.0.3", "@picovoice/eagle-web": "^1.0.0", "@picovoice/web-voice-processor": "^4.0.9", @@ -51,26 +51,26 @@ } }, "node_modules/@babylonjs/core": { - "version": "6.45.1", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.45.1.tgz", - "integrity": "sha512-wkORoAqpnZb10bUhrI0vinE9IiW7+gSgH4U4Zp41wO4kSeV0mtJY+Q5Ez6/n9ad9sLykD2FD7650B+Qi5tTMSw==" + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-7.1.0.tgz", + "integrity": "sha512-nz1CmflajMPnjdnwYrj72s4D/Q8IkOW4DBbwMfUNLFSCondfFXS3sZBgHeCAHpimR5n4e27YwvJ66MkQJsSraQ==" }, "node_modules/@babylonjs/gui": { - "version": "6.45.1", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-6.45.1.tgz", - "integrity": "sha512-lev/3nprv4t8lu3kW1zdlH7VzlWh9dmyZ2PkzybmBI6nB48bswPm7cX2ppaFTkpY8Z904js9TOsrYQotMzsUiw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-7.1.0.tgz", + "integrity": "sha512-tY6MGtigjZwv+9rqtGTsyBZL9sHSPjgVq+JEWHUsACH0ffNkKsQxtKSDc5bryzNQpo1rzTMHqEeU+DnqNSVe1Q==", "peerDependencies": { - "@babylonjs/core": "^6.0.0" + "@babylonjs/core": "^7.0.0" } }, "node_modules/@babylonjs/gui-editor": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/@babylonjs/gui-editor/-/gui-editor-6.8.0.tgz", - "integrity": "sha512-XSTYEfCdf04Th1xrTuIO/lHBSmfiG4s4fIDzpi0+OERO19bKQffzhFGh95NVzdL0XhvnQT88Fb6bkvsuAxFI/Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui-editor/-/gui-editor-7.1.0.tgz", + "integrity": "sha512-YHPR/2HxTT1ILSdGMAKOZ+askVCRV/hnI5CQqLVmAYkoW5FlY7TdRJtHn0JGEiOuK6Z5T3lJ3ru89wM+lGLqeg==", "peer": true, "peerDependencies": { - "@babylonjs/core": "^6.0.0", - "@babylonjs/gui": "^6.0.0", + "@babylonjs/core": "^7.0.0", + "@babylonjs/gui": "^7.0.0", "@types/react": ">=16.7.3", "@types/react-dom": ">=16.0.9" } @@ -84,57 +84,57 @@ } }, "node_modules/@babylonjs/inspector": { - "version": "6.45.1", - "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-6.45.1.tgz", - "integrity": "sha512-4YhJLD2FrVXUFIU+ttBanhevVaCBVLaBxhPuwrxXxKWkuBulFOQz7GkvRT/CJwA3Ad9/66qwS5+vfRT99GLM/w==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-7.1.0.tgz", + "integrity": "sha512-Hs9pk1LstJMnD4xXpHFlQVqXPtH/hhUcNSPaZlz8iesFrHJEXF4Mlao+ozwbl2WzDbMzmmex5nc1lw2HEBdhww==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/free-regular-svg-icons": "^6.0.0", "@fortawesome/free-solid-svg-icons": "^6.0.0" }, "peerDependencies": { - "@babylonjs/core": "^6.0.0", - "@babylonjs/gui": "^6.0.0", - "@babylonjs/gui-editor": "^6.0.0", - "@babylonjs/loaders": "^6.0.0", - "@babylonjs/materials": "^6.0.0", - "@babylonjs/serializers": "^6.0.0", + "@babylonjs/core": "^7.0.0", + "@babylonjs/gui": "^7.0.0", + "@babylonjs/gui-editor": "^7.0.0", + "@babylonjs/loaders": "^7.0.0", + "@babylonjs/materials": "^7.0.0", + "@babylonjs/serializers": "^7.0.0", "@types/react": ">=16.7.3", "@types/react-dom": ">=16.0.9" } }, "node_modules/@babylonjs/loaders": { - "version": "6.45.1", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-6.45.1.tgz", - "integrity": "sha512-a75JvRVxT3DROCrl5iigLEpI5/eR7Rh4wdsDzZNn7bv3sXB40Kbw8EL60W3jDBXPdGUqNVzqtrxJF/ec2udg/Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-7.1.0.tgz", + "integrity": "sha512-6gLbFiUsiqsQDRWLl4TL5pMKiemSAB/E61SHpp8xkBalxZUiKroSbZt3Irxc7CHIq8SE5CYfPUPYjs6BmpTgqw==", "peerDependencies": { - "@babylonjs/core": "^6.0.0", - "babylonjs-gltf2interface": "^6.0.0" + "@babylonjs/core": "^7.0.0", + "babylonjs-gltf2interface": "^7.0.0" } }, "node_modules/@babylonjs/materials": { - "version": "6.45.1", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-6.45.1.tgz", - "integrity": "sha512-v1jqgG0bfX+8Qezq4eDT2sSjpQYW+ocNU70dbJF0AYrhuhhgf0SpM28C7DAKi9GUOKjMvZrIBEbFmJ6AijLjcQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-7.1.0.tgz", + "integrity": "sha512-tHzpV6xUqpAgF9rPudDXetk1tIPbhggrybfIpvoWwbKlffIPQw4URsA+IBG04U5owWsicdhvTLQWP1WEwfbMQg==", "peerDependencies": { - "@babylonjs/core": "^6.0.0" + "@babylonjs/core": "^7.0.0" } }, "node_modules/@babylonjs/procedural-textures": { - "version": "6.45.1", - "resolved": "https://registry.npmjs.org/@babylonjs/procedural-textures/-/procedural-textures-6.45.1.tgz", - "integrity": "sha512-QpLuPknYIvylfUscSqorkuXO4QSI49atQnScWN43ZpRzr+ecXWnxEaOyAmt7bbf93htshtIkUQdJhwx8w4fqrg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babylonjs/procedural-textures/-/procedural-textures-7.1.0.tgz", + "integrity": "sha512-ajjaXEQJpWNQQzWZdUJJzN4NBtI96lxbrhmBUpPdfQwAhX2LRG0tbKWKXd66QHh82f/FG+daIA2BCL7M4f2MMw==", "peerDependencies": { - "@babylonjs/core": "^6.0.0" + "@babylonjs/core": "^7.0.0" } }, "node_modules/@babylonjs/serializers": { - "version": "6.45.1", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-6.45.1.tgz", - "integrity": "sha512-iSPUdjZhQIbSyi21IFiFC/wm6Bj4zcH9NhsN9i+xqLTbO/Uho49cHU0ew39n0Xn+LRsGRMTJWQLTsp+TPvAvrw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-7.1.0.tgz", + "integrity": "sha512-ToYutpqvqFVic+mhFkza6rLeE31sXXpixhdpu2bE+MRP13FoxNKfPD4np/DGBP7zUWmXIi2yRm6sZ7cGs8jp1g==", "peerDependencies": { - "@babylonjs/core": "^6.0.0", - "babylonjs-gltf2interface": "^6.0.0" + "@babylonjs/core": "^7.0.0", + "babylonjs-gltf2interface": "^7.0.0" } }, "node_modules/@esbuild/aix-ppc64": { @@ -1083,9 +1083,9 @@ } }, "node_modules/babylonjs-gltf2interface": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-6.8.0.tgz", - "integrity": "sha512-uCehLc8CnMUvWYttqQmJCHnhUjoftb4gK6Q+GXclgNr3CWiNDXHnbiBZQynA8SBfgkgE3bRk1+u+J5PQwkOepQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-7.1.0.tgz", + "integrity": "sha512-44UnRGFj/kp6/MxB6SU5ThxYBjm9W0aYGkDbtSoPW2ZuWd4uTCjCKe8oS3fhsqXcZ5+K+rQVsHb785hxC4zRcw==", "peer": true }, "node_modules/base64-js": { diff --git a/package.json b/package.json index 5cc6348..604faef 100644 --- a/package.json +++ b/package.json @@ -17,14 +17,14 @@ }, "dependencies": { "axios": "^1.6.8", - "@babylonjs/core": "^6.45.1", - "@babylonjs/gui": "^6.45.1", + "@babylonjs/core": "^7.1.0", + "@babylonjs/gui": "^7.1.0", "@babylonjs/havok": "1.3.1", - "@babylonjs/inspector": "^6.45.1", - "@babylonjs/loaders": "^6.45.1", - "@babylonjs/materials": "^6.45.1", - "@babylonjs/procedural-textures": "^6.45.1", - "@babylonjs/serializers": "^6.45.1", + "@babylonjs/inspector": "^7.1.0", + "@babylonjs/loaders": "^7.1.0", + "@babylonjs/materials": "^7.1.0", + "@babylonjs/procedural-textures": "^7.1.0", + "@babylonjs/serializers": "^7.1.0", "events": "^3.3.0", "@typed-mxgraph/typed-mxgraph": "^1.0.8", "@types/node": "^18.14.0", diff --git a/src/controllers/base.ts b/src/controllers/base.ts index d48c149..97f3b99 100644 --- a/src/controllers/base.ts +++ b/src/controllers/base.ts @@ -20,7 +20,9 @@ import {snapGridVal} from "../util/functions/snapGridVal"; import {snapRotateVal} from "../util/functions/snapRotateVal"; import {grabAndClone} from "./functions/grab"; import {isDiagramEntity} from "../diagram/functions/isDiagramEntity"; +import {ClickMenu} from "../menus/clickMenu"; +const CLICK_TIME = 300; export class Base { static stickVector = Vector3.Zero(); protected controller: WebXRInputSource; @@ -32,7 +34,7 @@ export class Base { protected previousRotation: Vector3 = null; protected previousScaling: Vector3 = null; protected previousPosition: Vector3 = null; - + private clickStart: number = 0; protected readonly xr: WebXRDefaultExperience; protected readonly diagramManager: DiagramManager; private logger: log.Logger; @@ -45,6 +47,7 @@ export class Base { controllers: Controllers, diagramManager: DiagramManager) { this.logger = log.getLogger('Base'); + this.logger.setLevel(this.logger.levels.DEBUG); this.controller = controller; this.controllers = controllers; this.scene = scene; @@ -71,9 +74,14 @@ export class Base { this.diagramManager = diagramManager; this.controller.onMotionControllerInitObservable.add((init) => { + this.logger.debug(init.components); if (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); this.controllers.controllerObserver.add((event) => { this.logger.debug(event); @@ -108,7 +116,33 @@ export class Base { 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); if (!mesh) { return; @@ -116,7 +150,7 @@ export class Base { let player = false; if (!isDiagramEntity(mesh)) { - if (mesh?.metadata?.handle == true) { + if (this.handleWasGrabbed(mesh)) { mesh && mesh.setParent(this.controller.motionController.rootMesh); this.grabbedMesh = mesh; } else { @@ -132,7 +166,6 @@ export class Base { } } else { - if (mesh?.metadata?.template == '#connection-template') { return; } @@ -143,7 +176,7 @@ export class Base { this.previousScaling = mesh?.scaling.clone(); this.previousPosition = mesh?.position.clone(); - if (!mesh.metadata?.grabClone || player) { + if ((!mesh.metadata?.grabClone || player) && !cloneEntity) { if (mesh.physicsBody) { const transformNode = setupTransformNode(mesh, this.controller.motionController.rootMesh); mesh.physicsBody.setMotionType(PhysicsMotionType.ANIMATED); @@ -151,9 +184,9 @@ export class Base { } else { mesh.setParent(this.controller.motionController.rootMesh); } - this.grabbedMesh = mesh; } else { + this.logger.debug("cloning " + mesh?.id); const clone = grabAndClone(this.diagramManager, mesh, this.controller.motionController.rootMesh); clone.newMesh.metadata.grabClone = 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)) { - this.grabbedMesh = null; - this.previousParentId = null; - mesh.setParent(null); return false; } else { return (mesh?.metadata?.handle == true); } } + private drop() { const mesh = this.grabbedMesh; if (!mesh) { return; } - if (this.toolboxHandleWasGrabbed(mesh)) { + if (this.handleWasGrabbed(mesh)) { mesh.setParent(this.scene.getMeshByName("platform")) return; } - reparent(mesh, this.previousParentId, this.grabbedMeshParentId); this.grabbedMeshParentId = null; - if (!mesh.physicsBody) { mesh.position = snapGridVal(mesh.position, this.diagramManager._config.current.gridSnap); mesh.rotation = snapRotateVal(mesh.rotation, this.diagramManager._config.current.rotateSnap); @@ -224,15 +253,32 @@ export class Base { 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) { grip.onButtonStateChangedObservable.add(() => { if (grip.changes.pressed) { if (grip.pressed) { this.grab(); } else { + this.drop(); } } }); } +} + +function pointable(mesh: AbstractMesh): boolean { + return (mesh && mesh.metadata?.template && !mesh.metadata?.tool && !mesh.metadata?.handle && !mesh.metadata?.grabbable && !mesh.metadata?.grabClone); } \ No newline at end of file diff --git a/src/env.d.ts b/src/env.d.ts index f936976..df22b43 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -3,6 +3,7 @@ interface ImportMetaEnv { readonly VITE_SYNCDB_ENDPOINT: string, readonly VITE_SYNCDB_USER: string + readonly VITE_CREATE_ENDPOINT: string // more env variables... } diff --git a/src/information/inputTextView.ts b/src/information/inputTextView.ts index 7992a09..3820f97 100644 --- a/src/information/inputTextView.ts +++ b/src/information/inputTextView.ts @@ -37,8 +37,6 @@ export class InputTextView { } public showVirtualKeyboard() { - - const inputMesh = MeshBuilder.CreatePlane("input", {width: 1, height: .5}, this.scene); const handle = new Handle(inputMesh); setMenuPosition(handle.mesh, this.scene, new Vector3(0, .4, 0)); diff --git a/src/integration/functions/syncDoc.ts b/src/integration/functions/syncDoc.ts deleted file mode 100644 index 3445d8e..0000000 --- a/src/integration/functions/syncDoc.ts +++ /dev/null @@ -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); - } - - } - } - -} \ No newline at end of file diff --git a/src/integration/pouchdbPersistenceManager.ts b/src/integration/pouchdbPersistenceManager.ts index f51375c..2addf7c 100644 --- a/src/integration/pouchdbPersistenceManager.ts +++ b/src/integration/pouchdbPersistenceManager.ts @@ -6,7 +6,6 @@ import {v4 as uuidv4} from 'uuid'; import axios from "axios"; import {DiagramManager} from "../diagram/diagramManager"; import log, {Logger} from "loglevel"; -import {syncDoc} from "./functions/syncDoc"; const logger: Logger = log.getLogger('PouchdbPersistenceManager'); export class PouchdbPersistenceManager { @@ -166,7 +165,7 @@ export class PouchdbPersistenceManager { createSnap: 0, turnSnap: 0, flyMode: true, - currentDiagramId: uuidv4() + currentDiagramId: 'public' } try { 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 { //const remoteDbName = "db1"; - const userHex = remoteDbName.split('-'); - if (userHex.length < 2) { - return; - } - const username = hex_to_ascii(userHex[1]); - const remoteUserName = username; - const password = "password"; + + const userHex = ascii_to_hex(localName); + const remoteDbName = 'userdb-' + userHex; + const remoteUserName = localName; + const password = localName; const dbs = await axios.get(import.meta.env.VITE_SYNCDB_ENDPOINT + 'list'); logger.debug(dbs.data); if (dbs.data.indexOf(remoteDbName) == -1) { - logger.warn('sync target missing'); - return; + logger.warn('sync target missing attempting to create'); + 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 logger.debug(userEndpoint); @@ -262,9 +292,11 @@ export class PouchdbPersistenceManager { {auth: {username: remoteUserName, password: password}, skip_setup: true}); const dbInfo = await this.remote.info(); logger.debug(dbInfo); - syncDoc.bind(this); + this.db.sync(this.remote, {live: true, retry: true}) - .on('change', syncDoc) + .on('change', (info) => { + this.syncDoc(info) + }) .on('active', function (info) { logger.debug('sync active', info) }) @@ -272,7 +304,7 @@ export class PouchdbPersistenceManager { logger.debug('sync paused', info) }) .on('error', function (err) { - logger.debug('sync error', err) + logger.error('sync error', err) }); } catch (err) { logger.error(err); @@ -283,8 +315,17 @@ export class PouchdbPersistenceManager { function hex_to_ascii(input) { var hex = input.toString(); 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)); } 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(''); } \ No newline at end of file diff --git a/src/menus/clickMenu.ts b/src/menus/clickMenu.ts new file mode 100644 index 0000000..dfdeec8 --- /dev/null +++ b/src/menus/clickMenu.ts @@ -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(); + } +} \ No newline at end of file diff --git a/src/objects/handle.ts b/src/objects/handle.ts index fd08e85..892e8cc 100644 --- a/src/objects/handle.ts +++ b/src/objects/handle.ts @@ -13,6 +13,7 @@ export class Handle { private buildHandle() { const scene: Scene = this.transformNode.getScene(); const handle = getHandleMesh("handle-" + this.transformNode.id + "-mesh", scene); + handle.position = Vector3.Zero(); handle.metadata = {handle: true}; if (this.transformNode) { diff --git a/src/objects/objectMetaType.ts b/src/objects/objectMetaType.ts new file mode 100644 index 0000000..ce58e3c --- /dev/null +++ b/src/objects/objectMetaType.ts @@ -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 }; +} \ No newline at end of file diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index 334d930..abb6c97 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -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 {setMenuPosition} from "../util/functions/setMenuPosition"; import {buildColor} from "./functions/buildColor"; import log from "loglevel"; import {Handle} from "../objects/handle"; @@ -34,20 +33,14 @@ export class Toolbox { new Handle(this.toolboxBaseNode); this.toolboxBaseNode.position.y = .2; //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.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.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) { if (color) { if (this.scene.getMeshById("toolbox-color-" + color)) { @@ -98,9 +91,27 @@ export class Toolbox { } } //this.toolboxBaseNode.parent.setEnabled(false); - setMenuPosition(this.toolboxBaseNode.parent as Mesh, this.scene, - Vector3.Zero()); + const offset = new Vector3(0, 1, 1); + 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());*/ } } diff --git a/src/util/functions/getFrontPosition.ts b/src/util/functions/getFrontPosition.ts index 709ecef..06ce276 100644 --- a/src/util/functions/getFrontPosition.ts +++ b/src/util/functions/getFrontPosition.ts @@ -1,10 +1,11 @@ import {MeshBuilder, Scene, Vector3} from "@babylonjs/core"; -const debug = false; +const debug = true; export function getFrontPosition(distance: number, scene: Scene): Vector3 { const offset = new Vector3(0, 0, distance); - offset.applyRotationQuaternionInPlace(scene.activeCamera.absoluteRotation); - const newPos = scene.activeCamera.globalPosition.add(offset); + //offset.applyRotationQuaternionInPlace(scene.activeCamera.absoluteRotation); + const camPos = scene.activeCamera.globalPosition.clone(); + const newPos = camPos.add(offset); if (debug) { const mesh = MeshBuilder.CreateIcoSphere("front", {radius: .1}, scene); mesh.position = newPos; diff --git a/src/util/functions/setMenuPosition.ts b/src/util/functions/setMenuPosition.ts index d5cf7ad..adb838d 100644 --- a/src/util/functions/setMenuPosition.ts +++ b/src/util/functions/setMenuPosition.ts @@ -1,5 +1,4 @@ -import {Scene, TransformNode, Vector3} from "@babylonjs/core"; -import {getFrontPosition} from "./getFrontPosition"; +import {Scene, TransformNode, Vector3, WebXRCamera} from "@babylonjs/core"; import log from "loglevel"; const logger = log.getLogger('setMenuPosition'); @@ -39,34 +38,33 @@ export function setMenuPosition(node: TransformNode, scene: Scene, offset: Vecto */ if (scene.activeCamera) { - setPosition(node, scene, offset); + //setPosition(node, scene, offset); } else { scene.onActiveCameraChanged.add((scene: Scene) => { - setPosition(node, scene, offset); + //setPosition(node, scene, offset); }); logger.error("No active camera"); } } -const debug = true; +const debug = false; function setPosition(node: TransformNode, scene: Scene, offset: Vector3 = Vector3.Zero()) { const platform = scene.getNodeById("platform"); switch (scene.activeCamera.getClassName()) { case "WebXRCamera": //const oldParent = node.parent; - - node.setParent(null); - const front = getFrontPosition(1, scene).clone(); - const camPos = scene.activeCamera.globalPosition.clone(); - const newPos = new Vector3(front.x + offset.x, 1.2 + offset.y, front.z + offset.z); - node.position = newPos; - node.lookAt(camPos); - // const target = MeshBuilder.CreateIcoSphere("target", {radius: .1}, scene); - // target.position = newPos; - // target.setParent(platform); - node.setParent(platform); + window.setTimeout(() => { + node.setParent(null); + const camera = scene.activeCamera as WebXRCamera; + const front = camera.getFrontPosition(.7); + const camPos = camera.globalPosition.clone(); + const newPos = new Vector3(front.x + offset.x, front.y + offset.y - .3, front.z + offset.z); + node.position = newPos; + node.lookAt(camPos); + node.setParent(platform); + }, 1000); break; case "FreeCamera": case "DeviceOrientationCamera": diff --git a/vite.config.ts b/vite.config.ts index cae1323..f2ed5b6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -18,6 +18,10 @@ export default defineConfig({ '^/sync/.*': { target: 'https://www.deepdiagram.com/', changeOrigin: true, + }, + '^/create-db': { + target: 'https://www.deepdiagram.com/', + changeOrigin: true, } } },