diff --git a/src/app.ts b/src/app.ts index 31e1e0e..41dd6fa 100644 --- a/src/app.ts +++ b/src/app.ts @@ -39,11 +39,7 @@ export class App { constructor() { const config = AppConfig.config; - - log.setLevel('info'); - log.getLogger('AppConfig').setLevel('debug'); - log.getLogger('IndexdbPersistenceManager').setLevel('debug'); - + log.setLevel('debug'); const canvas = document.createElement("canvas"); canvas.style.width = "100%"; canvas.style.height = "100%"; diff --git a/src/controllers/base.ts b/src/controllers/base.ts index ce85155..dff3c27 100644 --- a/src/controllers/base.ts +++ b/src/controllers/base.ts @@ -1,7 +1,5 @@ import { AbstractMesh, - InstancedMesh, - Mesh, Scene, Vector3, WebXRControllerComponent, @@ -13,6 +11,7 @@ import {DiagramManager} from "../diagram/diagramManager"; import {DiagramEvent, DiagramEventType} from "../diagram/diagramEntity"; import log from "loglevel"; import {AppConfig} from "../util/appConfig"; +import {Controllers} from "./controllers"; export class Base { @@ -28,12 +27,14 @@ export class Base { protected readonly xr: WebXRDefaultExperience; protected readonly diagramManager: DiagramManager; - + private logger: log.Logger; constructor(controller: WebXRInputSource, scene: Scene, xr: WebXRDefaultExperience, diagramManager: DiagramManager) { + this.logger = log.getLogger('Base'); this.controller = controller; + this.scene = scene; this.xr = xr; this.diagramManager = diagramManager; @@ -47,6 +48,15 @@ export class Base { } this.initGrip(init.components['xr-standard-squeeze']); }); + Controllers.controllerObserver.add((event) => { + if (event.type == 'pulse') { + this.logger.debug(event); + if (event.gripId == this.controller.grip.id) { + this.controller.motionController.pulse(.25, 30); + } + } + }); + } public disable() { @@ -59,15 +69,6 @@ export class Base { this.controller.pointer.setEnabled(true); } - private createCopy(mesh: AbstractMesh) { - if (!mesh.isAnInstance) { - return new InstancedMesh("new", (mesh as Mesh)); - } else { - return new InstancedMesh("new", (mesh as InstancedMesh).sourceMesh); - } - - } - private initGrip(grip: WebXRControllerComponent) { grip.onButtonStateChangedObservable.add(() => { if (grip.changes.pressed) { @@ -98,7 +99,7 @@ export class Base { this.grabbedMesh = mesh; } else { const config = AppConfig.config; - const newMesh = this.createCopy(mesh); + const newMesh = this.diagramManager.createCopy(mesh); newMesh.position = mesh.absolutePosition.clone(); newMesh.rotation = mesh.absoluteRotationQuaternion.toEulerAngles().clone(); newMesh.scaling = config.createSnapVal; diff --git a/src/controllers/controllers.ts b/src/controllers/controllers.ts index 7af5b08..1e62c02 100644 --- a/src/controllers/controllers.ts +++ b/src/controllers/controllers.ts @@ -2,7 +2,8 @@ import {AbstractMesh, Observable, TransformNode} from "@babylonjs/core"; export type ControllerEventType = { type: string, - value?: number + value?: number, + gripId?: string; } export class Controllers { public static movable: TransformNode | AbstractMesh; diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index 79c89fa..e301b51 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -1,8 +1,21 @@ -import {Observable, Scene, WebXRExperienceHelper} from "@babylonjs/core"; +import { + AbstractMesh, + ActionManager, + Color3, + ExecuteCodeAction, + InstancedMesh, + Mesh, + Observable, + PlaySoundAction, + Scene, + Sound, + WebXRExperienceHelper +} from "@babylonjs/core"; import {DiagramEntity, DiagramEvent, DiagramEventType} from "./diagramEntity"; import {IPersistenceManager} from "./persistenceManager"; import {MeshConverter} from "./meshConverter"; import log from "loglevel"; +import {Controllers} from "../controllers/controllers"; export class DiagramManager { public readonly onDiagramEventObservable: Observable = new Observable(); @@ -11,15 +24,7 @@ export class DiagramManager { private readonly scene: Scene; private xr: WebXRExperienceHelper; - constructor(scene: Scene, xr: WebXRExperienceHelper) { - this.scene = scene; - this.xr = xr; - if (this.onDiagramEventObservable.hasObservers()) { - this.logger.warn("onDiagramEventObservable already has Observers, you should be careful"); - } - this.onDiagramEventObservable.add(this.onDiagramEvent, -1, true, this); - this.logger.debug("DiagramManager constructed"); - } + private readonly tick: Sound; public setPersistenceManager(persistenceManager: IPersistenceManager) { this.persistenceManager = persistenceManager; @@ -33,11 +38,56 @@ export class DiagramManager { } return this.persistenceManager; } + private readonly actionManager: ActionManager; + + constructor(scene: Scene, xr: WebXRExperienceHelper) { + this.scene = scene; + this.xr = xr; + this.tick = new Sound("tick", './tick.mp3', this.scene); + this.tick.setVolume(.3); + this.actionManager = new ActionManager(this.scene); + this.actionManager.registerAction( + new PlaySoundAction(ActionManager.OnPointerOverTrigger, this.tick)); + this.actionManager.registerAction( + new ExecuteCodeAction(ActionManager.OnPointerOverTrigger, (evt) => { + Controllers.controllerObserver.notifyObservers({ + type: 'pulse', + gripId: evt?.additionalData?.pickResult?.gripTransform?.id + }) + this.logger.debug(evt); + }) + ); + if (this.onDiagramEventObservable.hasObservers()) { + this.logger.warn("onDiagramEventObservable already has Observers, you should be careful"); + } + this.onDiagramEventObservable.add(this.onDiagramEvent, -1, true, this); + this.logger.debug("DiagramManager constructed"); + } + + public createCopy(mesh: AbstractMesh): AbstractMesh { + let newMesh; + if (!mesh.isAnInstance) { + newMesh = new InstancedMesh("new", (mesh as Mesh)); + } else { + newMesh = new InstancedMesh("new", (mesh as InstancedMesh).sourceMesh); + } + newMesh.actionManager = this.actionManager; + return newMesh; + + } private onRemoteEvent(event: DiagramEntity) { - //const mesh = Toolbox.instance.newMesh(ToolType[Object.entries(ToolType).find(e => e[1] == event.template)[0]], event.id); this.logger.debug(event); + const toolMesh = this.scene.getMeshById("tool-" + event.template + "-" + event.color); + if (!toolMesh) { + log.debug('no mesh found for ' + event.template + "-" + event.color, 'adding it'); + this.onDiagramEventObservable.notifyObservers({ + type: DiagramEventType.CHANGECOLOR, + entity: event + }); + } const mesh = MeshConverter.fromDiagramEntity(event, this.scene); + mesh.actionManager = this.actionManager; if (event.parent) { mesh.parent = this.scene.getMeshById(event.parent); } @@ -66,7 +116,23 @@ export class DiagramManager { this.getPersistenceManager()?.modify(mesh); break; case DiagramEventType.CHANGECOLOR: - this.getPersistenceManager()?.changeColor(event.oldColor, event.newColor); + if (!event.oldColor) { + if (!event.newColor) { + this.getPersistenceManager()?.changeColor(null, Color3.FromHexString(event.entity.color)); + this.logger.info("Recieved color change event, sending entity color as new color"); + } else { + this.logger.info("Recieved color change event, no old color, sending new color"); + this.getPersistenceManager()?.changeColor(null, event.newColor); + } + } else { + if (event.newColor) { + this.logger.info("changing color from " + event.oldColor + " to " + event.newColor); + this.getPersistenceManager()?.changeColor(event.oldColor, event.newColor); + } else { + this.logger.error("changing color from " + event.oldColor + ", but no new color found"); + } + } + break; case DiagramEventType.REMOVE: if (mesh) { diff --git a/src/diagram/indexdbPersistenceManager.ts b/src/diagram/indexdbPersistenceManager.ts index 8cc2291..beed537 100644 --- a/src/diagram/indexdbPersistenceManager.ts +++ b/src/diagram/indexdbPersistenceManager.ts @@ -4,7 +4,7 @@ import {DiagramEntity} from "./diagramEntity"; import Dexie from "dexie"; import {MeshConverter} from "./meshConverter"; import log from "loglevel"; -import {AppConfigType} from "../util/appConfig"; +import {AppConfigType} from "../util/appConfigType"; export class IndexdbPersistenceManager implements IPersistenceManager { @@ -16,8 +16,8 @@ export class IndexdbPersistenceManager implements IPersistenceManager { constructor(name: string) { this.db = new Dexie(name); const version = 2; - this.db.version(2).stores({config: "id,gridSnap,rotateSnap,createSnap"}); - this.db.version(2).stores({entities: "id,position,rotation,last_seen,template,text,scale,color"}); + this.db.version(version).stores({config: "id,gridSnap,rotateSnap,createSnap"}); + this.db.version(version).stores({entities: "id,position,rotation,last_seen,template,text,scale,color"}); this.logger.debug("IndexdbPersistenceManager constructed"); } @@ -83,8 +83,12 @@ export class IndexdbPersistenceManager implements IPersistenceManager { } public changeColor(oldColor, newColor) { - if (!oldColor || !newColor) { - this.logger.error("changeColor called with null color, early return"); + if (!oldColor) { + if (!newColor) { + this.logger.error("changeColor called with no new color, early return"); + } else { + this.logger.info("changeColor called with no old Color, new color added to diagram, early return"); + } return; } this.logger.debug(`changeColor ${oldColor.toHexString()} to ${newColor.toHexString()}`); diff --git a/src/diagram/meshConverter.ts b/src/diagram/meshConverter.ts index 5344d30..f8ac1b8 100644 --- a/src/diagram/meshConverter.ts +++ b/src/diagram/meshConverter.ts @@ -10,7 +10,6 @@ import { StandardMaterial } from "@babylonjs/core"; import {v4 as uuidv4} from 'uuid'; -import {Toolbox} from "../toolbox/toolbox"; import log from "loglevel"; @@ -61,14 +60,6 @@ export class MeshConverter { } } else { log.debug('no mesh found for ' + entity.template + "-" + entity.color); - Toolbox.instance.updateToolbox(entity.color); - mesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color); - if (!mesh) { - log.debug('no mesh found for ' + entity.template + "-" + entity.color); - } else { - mesh = new InstancedMesh(entity.id, (mesh as Mesh)); - } - //Toolbox.instance.buildTool(Toolbox.getToolTypeFromString(entity.template), entity.color); } } @@ -151,9 +142,14 @@ export class MeshConverter { const plane = MeshBuilder.CreatePlane("text", {width: planeWidth, height: height}, mesh.getScene()); plane.material = mat; plane.billboardMode = Mesh.BILLBOARDMODE_ALL; - //textNode = this.updateTextNode(mesh, entity.text); + + + const yOffset = mesh.getBoundingInfo().boundingSphere.radius; plane.parent = mesh; - plane.position.y = .5 + (.125 / 2); + plane.position.y = yOffset; + plane.scaling.y = 1 / mesh.scaling.y; + plane.scaling.x = 1 / mesh.scaling.x; + plane.scaling.z = 1 / mesh.scaling.z; return plane; } } \ No newline at end of file diff --git a/src/diagram/persistenceManager.ts b/src/diagram/persistenceManager.ts index c12abb2..30baf4b 100644 --- a/src/diagram/persistenceManager.ts +++ b/src/diagram/persistenceManager.ts @@ -1,6 +1,7 @@ import {AbstractMesh, Color3, Observable} from "@babylonjs/core"; import {DiagramEntity} from "./diagramEntity"; -import {AppConfigType} from "../util/appConfig"; +import {AppConfigType} from "../util/appConfigType"; + export interface IPersistenceManager { add(mesh: AbstractMesh); diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index e99a27c..9ac5914 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -40,6 +40,12 @@ export class Toolbox { constructor(scene: Scene, xr: WebXRExperienceHelper, diagramManager: DiagramManager) { this.scene = scene; this.diagramManager = diagramManager; + + this.diagramManager.onDiagramEventObservable.add((evt) => { + if (evt?.entity?.color && evt.type == DiagramEventType.CHANGECOLOR) { + this.updateToolbox(evt.entity.color); + } + }, -1, true, this); this.addPanel = new StackPanel3D(); this.manager = new GUI3DManager(scene); this.manager.addControl(this.addPanel); @@ -62,8 +68,10 @@ export class Toolbox { } else { this.buildToolbox(); } + Toolbox.instance = this; } + private buildToolbox() { this.node.position.y = -.2; this.node.scaling= new Vector3(0.5, 0.5, 0.5); diff --git a/src/util/appConfig.ts b/src/util/appConfig.ts index 33bb92d..c7d5083 100644 --- a/src/util/appConfig.ts +++ b/src/util/appConfig.ts @@ -2,17 +2,13 @@ import {Angle, Vector3} from "@babylonjs/core"; import round from "round"; import log from "loglevel"; import {IPersistenceManager} from "../diagram/persistenceManager"; +import {AppConfigType} from "./appConfigType"; export type SnapValue = { value: number, label: string } -export type AppConfigType = { - id?: number, - gridSnap: number, - rotateSnap: number, - createSnap: number -} + export class AppConfig { private readonly logger = log.getLogger('AppConfig'); diff --git a/src/util/appConfigType.ts b/src/util/appConfigType.ts new file mode 100644 index 0000000..36156e2 --- /dev/null +++ b/src/util/appConfigType.ts @@ -0,0 +1,6 @@ +export type AppConfigType = { + id?: number, + gridSnap: number, + rotateSnap: number, + createSnap: number +} \ No newline at end of file