From b2c5c85d7ea9470e5c87560a1ed615d07a7cbf47 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Sat, 20 Apr 2024 06:56:42 -0500 Subject: [PATCH] Updated click menu to use Html Button. Added Scale feature. --- package-lock.json | 14 ++- package.json | 2 +- src/diagram/diagramManager.ts | 14 ++- src/menus/clickMenu.ts | 129 ++++++++++---------- src/menus/scaleMenu.ts | 214 +++++++++++++++++++--------------- src/vrApp.ts | 1 - 6 files changed, 206 insertions(+), 168 deletions(-) diff --git a/package-lock.json b/package-lock.json index 609666c..c81f936 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@types/react": "^18.2.72", "@types/react-dom": "^18.2.22", "axios": "^1.6.8", - "babylon-html": "0.0.1", + "babylon-html": "^0.0.2", "dom-to-image-more": "^3.3.0", "earcut": "^2.2.4", "events": "^3.3.0", @@ -55,7 +55,8 @@ } }, "../babylon-html": { - "version": "0.0.1", + "version": "0.0.2", + "extraneous": true, "license": "MIT", "dependencies": { "@babylonjs/core": "^7.1.0", @@ -1107,8 +1108,13 @@ } }, "node_modules/babylon-html": { - "resolved": "../babylon-html", - "link": true + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/babylon-html/-/babylon-html-0.0.2.tgz", + "integrity": "sha512-bcdLgMmnjvLrysZq5VFzc71TkDT0b6coCv3ZoFzTxJDhXtZF96FFaRMba9rxMHp1TM3rX9u6yW8N/sJodA/qVg==", + "dependencies": { + "@babylonjs/core": "^7.1.0", + "dom-to-image-more": "^3.3.0" + } }, "node_modules/babylonjs-gltf2interface": { "version": "7.1.0", diff --git a/package.json b/package.json index f8c1193..50339a4 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@types/react": "^18.2.72", "@types/react-dom": "^18.2.22", "axios": "^1.6.8", - "babylon-html": "0.0.1", + "babylon-html": "^0.0.2", "dom-to-image-more": "^3.3.0", "earcut": "^2.2.4", "events": "^3.3.0", diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index cc2dada..98a1a1b 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -14,6 +14,7 @@ import {buildEntityActionManager} from "./functions/buildEntityActionManager"; import {isDiagramEntity} from "./functions/isDiagramEntity"; import {InputTextView} from "../information/inputTextView"; import {DefaultScene} from "../defaultScene"; +import {ScaleMenu} from "../menus/scaleMenu"; export class DiagramManager { @@ -21,7 +22,7 @@ export class DiagramManager { private readonly _controllers: Controllers; private readonly diagramEntityActionManager: ActionManager; private readonly inputTextView: InputTextView; - + public readonly scaleMenu: ScaleMenu; public readonly onDiagramEventObservable: Observable = new Observable(); private readonly logger = log.getLogger('DiagramManager'); private readonly toolbox: Toolbox; @@ -48,6 +49,17 @@ export class DiagramManager { }); this.toolbox = new Toolbox(); + this.scaleMenu = new ScaleMenu(); + this.scaleMenu.onScaleChangeObservable.add((mesh: AbstractMesh) => { + this.onDiagramEventObservable.notifyObservers({ + type: DiagramEventType.MODIFY, + entity: toDiagramEntity(mesh), + }, -1); + + const position = mesh.absolutePosition.clone(); + position.y = mesh.getBoundingInfo().boundingBox.maximumWorld.y + .1; + this.scaleMenu.changePosition(position); + }); //this.presentationManager = new PresentationManager(this._scene); this.diagramEntityActionManager = buildEntityActionManager(this._controllers); diff --git a/src/menus/clickMenu.ts b/src/menus/clickMenu.ts index 8389413..aac6c23 100644 --- a/src/menus/clickMenu.ts +++ b/src/menus/clickMenu.ts @@ -1,55 +1,76 @@ -import {GUI3DManager, PlanePanel} from "@babylonjs/gui"; -import {AbstractMesh, Tools, TransformNode, Vector3} from "@babylonjs/core"; +import {AbstractMesh, Scene, TransformNode, Vector3} from "@babylonjs/core"; import {DiagramEvent, DiagramEventType} from "../diagram/types/diagramEntity"; import {toDiagramEntity} from "../diagram/functions/toDiagramEntity"; import {DiagramManager} from "../diagram/diagramManager"; import {DiagramConnection} from "../diagram/diagramConnection"; import {isDiagramEntity} from "../diagram/functions/isDiagramEntity"; -import {makeButton} from "./functions/makeButton"; +import {HtmlButton} from "babylon-html"; export class ClickMenu { - private static readonly sounds; - private readonly entity: AbstractMesh; - private readonly manager: GUI3DManager; + private readonly _mesh: AbstractMesh; private readonly transform: TransformNode; private readonly diagramManager: DiagramManager; - private utilityPosition: Vector3; - private connection: DiagramConnection = null; - constructor(entity: AbstractMesh, diagramManager: DiagramManager, grip: TransformNode) { - this.entity = entity; + constructor(mesh: AbstractMesh, diagramManager: DiagramManager, grip: TransformNode) { + this._mesh = mesh; this.diagramManager = diagramManager; - const scene = entity.getScene(); - const manager = new GUI3DManager(scene); - manager.onPickingObservable.add((mesh) => { - if (mesh) { - this.utilityPosition = mesh.getAbsolutePosition(); + const scene = mesh.getScene(); + this.transform = new TransformNode("transform", scene); + let x = -.54 / 2; + + const removeButton: HtmlButton = this.makeNewButton("Remove", "remove", scene, x += .11); + removeButton.onPointerObservable.add((eventData) => { + if (eventData.sourceEvent.type == "pointerup") { + const event: DiagramEvent = { + type: DiagramEventType.REMOVE, + entity: + toDiagramEntity(this._mesh) + } + this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1); + this.dispose(); } - }); - const transform = new TransformNode("transform", scene); - const panel = new PlanePanel(); + }, -1, false, this, false); - panel.orientation = PlanePanel.FACEFORWARD_ORIENTATION; - panel.columns = 4; - panel.margin = .1; - manager.addControl(panel); - panel.linkToTransformNode(transform); + const labelButton: HtmlButton = this.makeNewButton("Label", "label", scene, x += .11); + labelButton.onPointerObservable.add((eventData) => { + if (eventData.sourceEvent.type == "pointerup") { + this.diagramManager.editText(this._mesh); + this.dispose(); + } + }, -1, false, this, false); - panel.addControl(this.makeButton("Remove", "remove", grip)); - panel.addControl(this.makeButton("Label", "label", grip)); - panel.addControl(this.makeButton("Connect", "connect", grip)); - panel.addControl(this.makeButton("Close", "close", grip)); + const connectButton: HtmlButton = this.makeNewButton("Connect", "connect", scene, x += .11); + connectButton.onPointerObservable.add((eventData) => { + if (eventData.sourceEvent.type == "pointerup") { + this.createMeshConnection(this._mesh, grip, eventData.additionalData.pickedPoint.clone()); + } + }, -1, false, this, false); - manager.controlScaling = .1; - panel.updateLayout(); - this.transform = transform; - this.manager = manager; - Tools.SetImmediate(() => { - transform.position = entity.absolutePosition.clone(); - transform.position.y = entity.getBoundingInfo().boundingBox.maximumWorld.y + .1; - transform.billboardMode = TransformNode.BILLBOARDMODE_Y; - }); + const closeButton: HtmlButton = this.makeNewButton("Close", "close", scene, x += .11); + closeButton.onPointerObservable.add((eventData) => { + eventData.sourceEvent.type == "pointerup" && this.dispose(); + }, -1, false, this, false); + + const sizeButton: HtmlButton = this.makeNewButton("Size", "size", scene, x += .11); + sizeButton.onPointerObservable.add((eventData) => { + if (eventData.sourceEvent.type == "pointerup") { + this.diagramManager.scaleMenu.show(this._mesh); + } + }, -1, false, this, false); + + + this.transform.position = mesh.absolutePosition.clone(); + this.transform.position.y = mesh.getBoundingInfo().boundingBox.maximumWorld.y + .1; + this.transform.billboardMode = TransformNode.BILLBOARDMODE_Y; + } + + private makeNewButton(name: string, id: string, scene: Scene, x: number): HtmlButton { + const button = new HtmlButton(name, id, scene, null, {html: null, image: {width: 268, height: 268}}); + button.transform.parent = this.transform; + button.transform.rotation.y = Math.PI; + button.transform.position.x = x; + return button; } public get isConnecting() { @@ -74,40 +95,12 @@ export class ClickMenu { } } - private makeButton(name: string, id: string, grip: TransformNode) { - const button = makeButton(id, name); - 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": - this.diagramManager.editText(this.entity); - this.dispose(); - break; - case "connect": - this.createMeshConnection(this.entity, grip); - } - }, -1, false, this, true); - return button; - } - - private createMeshConnection(mesh: AbstractMesh, grip: TransformNode) { - this.connection = new DiagramConnection(mesh.id, null, null, this.transform.getScene(), grip, this.utilityPosition); + private createMeshConnection(mesh: AbstractMesh, grip: TransformNode, utilityPosition: Vector3) { + this.connection = new DiagramConnection(mesh.id, null, null, this.transform.getScene(), grip, utilityPosition); } private dispose() { - this.manager.onPickingObservable.clear(); - this.manager.dispose(); - this.transform.dispose(); + this.diagramManager.scaleMenu.hide(); + this.transform.dispose(false, true); } } \ No newline at end of file diff --git a/src/menus/scaleMenu.ts b/src/menus/scaleMenu.ts index c89bb4a..4e3a636 100644 --- a/src/menus/scaleMenu.ts +++ b/src/menus/scaleMenu.ts @@ -1,103 +1,131 @@ -import {AbstractMesh, TransformNode, Vector3, WebXRDefaultExperience} from "@babylonjs/core"; +import {DefaultScene} from "../defaultScene"; +import {HtmlButton, HtmlMeshBuilder} from "babylon-html"; +import {AbstractMesh, Observable, TransformNode, Vector3} from "@babylonjs/core"; -import {Controllers} from "../controllers/controllers"; -import {DiaSounds} from "../util/diaSounds"; -import {AbstractMenu} from "./abstractMenu"; -import {GUI3DManager, Slider3D} from "@babylonjs/gui"; - -export class ScaleMenu extends AbstractMenu { - private sounds: DiaSounds; - private mesh: AbstractMesh; - private xSlider: Slider3D; - private ySlider: Slider3D; - private zSlider: Slider3D; - private transformNode: TransformNode; - private xTransformNode: TransformNode; - private yTransformNode: TransformNode; - private zTransformNode: TransformNode; - - constructor(xr: WebXRDefaultExperience, controllers: Controllers) { - super(xr, controllers); - this.transformNode = new TransformNode("scaleMenu", this.scene); - this.xTransformNode = new TransformNode("xTransformNode", this.scene); - this.xTransformNode.parent = this.transformNode; - this.yTransformNode = new TransformNode("yTransformNode", this.scene); - this.yTransformNode.parent = this.transformNode; - this.zTransformNode = new TransformNode("zTransformNode", this.scene); - this.zTransformNode.parent = this.transformNode; - //super.createHandle(this.transformNode); - this.transformNode.position.y = 0; - this.transformNode.position.z = 0; - this.transformNode.position.x = 0; - - this.buildMenu(); - - //this.transformNode.position.y = 2; +export class ScaleMenu { + private static Sizes = [ + .025, .05, .1, .25, .5, 1.0, 2.0, 3.0, 4.0, 5.0 + ] + public readonly onScaleChangeObservable: Observable = new Observable(); + private readonly transform; + private _mesh: AbstractMesh; + constructor() { + this.transform = new TransformNode("scaleMenu", DefaultScene.Scene); + this.transform.scaling = new Vector3(.5, .5, .5); + this.build(); } - public changeMesh(mesh: AbstractMesh) { - this.mesh = mesh; - this.xSlider.value = mesh.scaling.x; - this.ySlider.value = mesh.scaling.y; - this.zSlider.value = mesh.scaling.z; - - const two = new Vector3(2, 2, 2); - this.transformNode.position = this.mesh.absolutePosition.clone(); - this.transformNode.rotation = this.mesh.absoluteRotationQuaternion.toEulerAngles(); - + public changePosition(position: Vector3) { + this.transform.position = position.clone(); } - private buildMenu() { - const manager = new GUI3DManager(this.scene); - //manager.rootContainer.position.y = 2; - //manager.rootContainer.node.position.y = 2; - this.xSlider = new Slider3D("xslider"); - this.ySlider = new Slider3D("yslider"); - this.zSlider = new Slider3D("zslider"); - - manager.addControl(this.xSlider); - manager.addControl(this.ySlider); - manager.addControl(this.zSlider); - this.xSlider.linkToTransformNode(this.xTransformNode); - this.ySlider.linkToTransformNode(this.yTransformNode); - this.zSlider.linkToTransformNode(this.zTransformNode); - - this.xTransformNode.position = new Vector3(0, 0, .6); - this.xTransformNode.rotation.y = Math.PI; - this.yTransformNode.position = new Vector3(.6, .6, .6); - this.yTransformNode.rotation.z = Math.PI / 2; - this.zTransformNode.position = new Vector3(.6, .6, 0); - this.zTransformNode.rotation.y = Math.PI / 2; - setValues(this.xSlider); - setValues(this.ySlider); - setValues(this.zSlider); - this.xSlider.onValueChangedObservable.add((value) => { - if (this.mesh) { - this.mesh.scaling.x = value; - } - }); - this.ySlider.onValueChangedObservable.add((value) => { - if (this.mesh) { - this.mesh.scaling.y = value; - } - }); - this.zSlider.onValueChangedObservable.add((value) => { - if (this.mesh) { - this.mesh.scaling.z = value; - } - }); - this.transformNode.scaling.x = .5; - this.transformNode.scaling.y = .5; - this.transformNode.scaling.z = .5; + public show(mesh: AbstractMesh) { + this.transform.position = mesh.absolutePosition.clone(); + this.transform.position.y = mesh.getBoundingInfo().boundingBox.maximumWorld.y + .1; + //this.transform.billboardMode = TransformNode.BILLBOARDMODE_Y; + this.transform.setEnabled(true); + this._mesh = mesh; } + public hide() { + this.transform.setEnabled(false); + this._mesh = null; + } + + private async build() { + let x = .12; + const xParent = new TransformNode("xParent", DefaultScene.Scene); + xParent.parent = this.transform; + const yParent = new TransformNode("yParent", DefaultScene.Scene); + yParent.parent = this.transform; + const zParent = new TransformNode("zParent", DefaultScene.Scene); + zParent.parent = this.transform; + xParent.rotation.x = Math.PI / 2; + yParent.rotation.z = Math.PI / 2; + yParent.billboardMode = TransformNode.BILLBOARDMODE_Y; + zParent.rotation.y = Math.PI / 2; + zParent.rotation.x = Math.PI / 2; + for (const size of ScaleMenu.Sizes) { + const xbutton = this.makeButton(size.toString(), x, 0, xParent); + xbutton.onPointerObservable.add((eventData) => { + if (eventData.sourceEvent.type == "pointerup") { + this.scaleX(size) + } + }, -1, false, this, false); + + const ybutton = this.makeButton(size.toString(), x, Math.PI / 2, yParent); + ybutton.onPointerObservable.add((eventData) => { + if (eventData.sourceEvent.type == "pointerup") { + this.scaleY(size) + } + }, -1, false, this, false); + + const zbutton = this.makeButton(size.toString(), x, -Math.PI / 2, zParent); + zbutton.onPointerObservable.add((eventData) => { + if (eventData.sourceEvent.type == "pointerup") { + this.scaleZ(size) + } + }, -1, false, this, false); + x += .11; + } +// const labelX = await this.createLabel('X Size', .3); + // const labelY = await this.createLabel('Y Size', .2); + // const labelZ = await this.createLabel('Z Size', .1); + this.transform.position.y = 1; + this.transform.rotation.y = Math.PI; + this.transform.setEnabled(false); + } + + private makeButton(name: string, x: number, y: number, parent: TransformNode = null) { + const button = new HtmlButton(name, name, DefaultScene.Scene); + button.transform.parent = parent; + button.transform.position.x = x; + //button.transform.position.y = y; + button.transform.rotation.z = y; + button.transform.rotation.y = Math.PI; + return button; + } + + private scaleX(size: number) { + if (this._mesh) { + this._mesh.scaling.x = size; + this.scaleChanged(); + } + } + + private scaleY(size: number) { + if (this._mesh) { + this._mesh.scaling.y = size; + this.scaleChanged(); + } + } + + private scaleZ(size: number) { + if (this._mesh) { + this._mesh.scaling.z = size; + this.scaleChanged(); + } + } + + private scaleChanged() { + if (this._mesh) { + this.onScaleChangeObservable.notifyObservers(this._mesh); + } + } + + private async createLabel(name: string, y: number) { + const label = await HtmlMeshBuilder.CreatePlane(`${name}-label`, + { + html: `
${name}
`, + height: .1, image: {width: 128, height: 128} + }, + DefaultScene.Scene); + + label.parent = this.transform; + label.position.y = y; + label.position.x = -.42; + return label; + } } - -function setValues(slider: Slider3D) { - slider.minimum = .1; - slider.maximum = 1; - slider.step = .1; - slider.value = .1; -} \ No newline at end of file diff --git a/src/vrApp.ts b/src/vrApp.ts index d990b9b..9fcac42 100644 --- a/src/vrApp.ts +++ b/src/vrApp.ts @@ -52,7 +52,6 @@ export class VrApp { */ addSceneInspector(); - //const mainMenu = new MainMenu(scene); const el = document.querySelector('#download'); if (el) { el.addEventListener('click', () => {