From 06b2989f3a9f8838c6e7c70cc400f9e2464667db Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Thu, 27 Jul 2023 11:46:52 -0500 Subject: [PATCH] Fixed up text entry for immersive and xr emulation. --- src/controllers/right.ts | 1 + src/diagram/meshConverter.ts | 58 ++++++++++- src/menus/bmenu.ts | 191 ++++++++++++++++++----------------- 3 files changed, 155 insertions(+), 95 deletions(-) diff --git a/src/controllers/right.ts b/src/controllers/right.ts index f6bb521..8ae8ec4 100644 --- a/src/controllers/right.ts +++ b/src/controllers/right.ts @@ -52,6 +52,7 @@ export class Right extends Base { if (abutton) { abutton.onButtonStateChangedObservable.add((value) => { if (value.pressed) { + log.getLogger("right").debug("a-button pressed"); Controllers.controllerObserver.notifyObservers({type: 'menu'}); } }); diff --git a/src/diagram/meshConverter.ts b/src/diagram/meshConverter.ts index 5dbeb98..c14b3c3 100644 --- a/src/diagram/meshConverter.ts +++ b/src/diagram/meshConverter.ts @@ -1,5 +1,14 @@ import {DiagramEntity} from "./diagramEntity"; -import {AbstractMesh, Color3, InstancedMesh, Mesh, Scene, StandardMaterial} from "@babylonjs/core"; +import { + AbstractMesh, + Color3, + DynamicTexture, + InstancedMesh, + Mesh, + MeshBuilder, + Scene, + StandardMaterial +} from "@babylonjs/core"; import {v4 as uuidv4} from 'uuid'; import {Toolbox} from "../toolbox/toolbox"; import log from "loglevel"; @@ -57,6 +66,7 @@ export class MeshConverter { mesh.metadata = {template: entity.template}; if (entity.text) { mesh.metadata.text = entity.text; + this.updateTextNode(mesh, entity.text); } if (entity.position) { mesh.position = entity.position; @@ -82,4 +92,50 @@ export class MeshConverter { } + public static updateTextNode(mesh: AbstractMesh, text: string) { + let textNode = (mesh.getChildren((node) => { + return node.name == 'text' + })[0] as Mesh); + if (textNode) { + textNode.dispose(false, true); + } + + //Set font + const height = 0.125; + const font_size = 24; + const font = "bold " + font_size + "px Arial"; + //Set height for dynamic texture + const DTHeight = 1.5 * font_size; //or set as wished + //Calc Ratio + const ratio = height / DTHeight; + + //Use a temporary dynamic texture to calculate the length of the text on the dynamic texture canvas + const temp = new DynamicTexture("DynamicTexture", 64, mesh.getScene()); + const tmpctx = temp.getContext(); + tmpctx.font = font; + const DTWidth = tmpctx.measureText(text).width + 8; + + //Calculate width the plane has to be + const planeWidth = DTWidth * ratio; + + //Create dynamic texture and write the text + const dynamicTexture = new DynamicTexture("DynamicTexture", { + width: DTWidth, + height: DTHeight + }, mesh.getScene(), false); + const mat = new StandardMaterial("mat", mesh.getScene()); + mat.diffuseTexture = dynamicTexture; + dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true); + + //Create plane and set dynamic texture as material + 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); + plane.parent = mesh; + log.getLogger('bmenu').debug("max y", mesh.getBoundingInfo().boundingBox.maximum.y); + plane.position.y = .5+ (height / 2); + + return plane; + } } \ No newline at end of file diff --git a/src/menus/bmenu.ts b/src/menus/bmenu.ts index 55da78b..a955b1d 100644 --- a/src/menus/bmenu.ts +++ b/src/menus/bmenu.ts @@ -2,6 +2,7 @@ import { AbstractMesh, GizmoManager, PointerEventTypes, + PointerInfo, Scene, Vector3, WebXRExperienceHelper @@ -40,108 +41,109 @@ export class Bmenu { case PointerEventTypes.POINTERPICK: if (pointerInfo.pickInfo?.pickedMesh?.metadata?.template && pointerInfo.pickInfo?.pickedMesh?.parent?.parent?.id != "toolbox") { - if (this.textInput) { - this.textInput.blur(); - this.textInput.remove(); - this.textInput = null; - } - if (this.textView) { - this.textView.dispose().then(() => { - log.getLogger("bmenu").debug("disposed"); - }).catch((e) => { + this.cleanup() + .then(() => { + log.getLogger("bmenu").debug("cleaned up"); + }) + .catch((e) => { log.getLogger("bmenu").error(e); }); - this.textView = null; - } - switch (this.state) { + this.handleEventStateAction(pointerInfo).then(() => { + log.getLogger("bmenu").debug("handled"); + }).catch((e) => { + log.getLogger("bmenu").error(e); + }); - case BmenuState.REMOVING: - log.debug("removing " + pointerInfo.pickInfo.pickedMesh.id); - const event: DiagramEvent = { - type: DiagramEventType.REMOVE, - entity: - MeshConverter.toDiagramEntity(pointerInfo.pickInfo.pickedMesh) - } - DiagramManager.onDiagramEventObservable.notifyObservers(event); - break; - case BmenuState.MODIFYING: - if (pointerInfo.pickInfo.pickedMesh.metadata?.template && - pointerInfo.pickInfo.pickedMesh.parent?.parent?.id != "toolbox") { - if (this.gizmoManager.gizmos.boundingBoxGizmo.attachedMesh?.id == pointerInfo.pickInfo?.pickedMesh?.id) { - this.gizmoManager.gizmos.boundingBoxGizmo.attachedMesh = null; - } else { - const mesh = pointerInfo.pickInfo.pickedMesh; - this.gizmoManager.attachToMesh(mesh); - - this.gizmoManager.gizmos.boundingBoxGizmo.onScaleBoxDragObservable.add(() => { - DiagramManager.onDiagramEventObservable.notifyObservers({ - type: DiagramEventType.MODIFY, - entity: MeshConverter.toDiagramEntity(mesh), - } - ) - log.debug(mesh.scaling); - }); - } - - } - - break; - case BmenuState.LABELING: - const mesh = pointerInfo.pickInfo.pickedMesh; - log.debug("labeling " + mesh.id); - - - const textInput = document.createElement("input"); - textInput.type = "text"; - document.body.appendChild(textInput); - if (mesh?.metadata?.text) { - textInput.value = mesh.metadata.text; - } else { - textInput.value = ""; - } - textInput.focus(); - - if (navigator.userAgent.indexOf('Macintosh') > -1) { - textInput.addEventListener('input', (event)=> { - log.debug(event); - }); - const textView = new InputTextView(this.scene, this.xr, mesh) - textView.show(textInput.value); - textInput.addEventListener('keydown', (event)=> { - if (event.key == "Enter") { - this.persist(mesh, textInput.value); - textInput.blur(); - textInput.remove(); - this.textView.dispose(); - this.textView = null; - this.textInput = null; - } else { - textView.updateText(textInput.value); - } - }); - this.textView = textView; - } else { - textInput.addEventListener('keydown', (event)=> { - log.debug(event); - if (event.key == "Enter") { - this.persist(mesh, textInput.value); - textInput.blur(); - textInput.remove(); - this.textInput = null; - this.textView = null; - } - }); - } - this.textInput = textInput; - - break; - - } break; } } }); } + + private async handleEventStateAction(pointerInfo: PointerInfo) { + switch (this.state) { + case BmenuState.REMOVING: + log.debug("removing " + pointerInfo.pickInfo.pickedMesh.id); + const event: DiagramEvent = { + type: DiagramEventType.REMOVE, + entity: + MeshConverter.toDiagramEntity(pointerInfo.pickInfo.pickedMesh) + } + DiagramManager.onDiagramEventObservable.notifyObservers(event); + break; + case BmenuState.MODIFYING: + if (pointerInfo.pickInfo.pickedMesh.metadata?.template && + pointerInfo.pickInfo.pickedMesh.parent?.parent?.id != "toolbox") { + if (this.gizmoManager.gizmos.boundingBoxGizmo.attachedMesh?.id == pointerInfo.pickInfo?.pickedMesh?.id) { + this.gizmoManager.gizmos.boundingBoxGizmo.attachedMesh = null; + } else { + const mesh = pointerInfo.pickInfo.pickedMesh; + this.gizmoManager.attachToMesh(mesh); + + this.gizmoManager.gizmos.boundingBoxGizmo.onScaleBoxDragObservable.add(() => { + DiagramManager.onDiagramEventObservable.notifyObservers({ + type: DiagramEventType.MODIFY, + entity: MeshConverter.toDiagramEntity(mesh), + } + ) + log.debug(mesh.scaling); + }); + } + } + break; + case BmenuState.LABELING: + const mesh = pointerInfo.pickInfo.pickedMesh; + log.debug("labeling " + mesh.id); + const textInput = document.createElement("input"); + textInput.type = "text"; + document.body.appendChild(textInput); + if (mesh?.metadata?.text) { + textInput.value = mesh.metadata.text; + } else { + textInput.value = ""; + } + textInput.focus(); + + if (navigator.userAgent.indexOf('Macintosh') > -1) { + textInput.addEventListener('input', (event) => { + log.debug(event); + }); + const textView = new InputTextView(this.scene, this.xr, mesh) + await textView.show(textInput.value); + textInput.addEventListener('keydown', (event) => { + if (event.key == "Enter") { + log.getLogger('bmenu').debug("enter"); + MeshConverter.updateTextNode(mesh, textInput.value); + this.persist(mesh, textInput.value); + this.cleanup(); + } else { + textView.updateText(textInput.value); + MeshConverter.updateTextNode(mesh, textInput.value); + } + }); + this.textView = textView; + } else { + textInput.addEventListener('blur', () => { + log.getLogger('bmenu').debug("blur"); + MeshConverter.updateTextNode(mesh, textInput.value); + this.persist(mesh, textInput.value); + this.cleanup(); + }); + } + this.textInput = textInput; + break; + } + } + private async cleanup() { + if (this.textInput) { + this.textInput.blur(); + this.textInput.remove(); + } + this.textInput = null; + this.textView && await this.textView.dispose(); + this.textView = null; + + } private persist(mesh: AbstractMesh, text: string) { if (mesh.metadata) { mesh.metadata.text = text; @@ -153,6 +155,7 @@ export class Bmenu { entity: MeshConverter.toDiagramEntity(mesh), }); } + makeButton(name: string, id: string) { const button = new Button3D(name); button.scaling = new Vector3(.1, .1, .1);