Fixed up text entry for immersive and xr emulation.

This commit is contained in:
Michael Mainguy 2023-07-27 11:46:52 -05:00
parent c74dc62654
commit 06b2989f3a
3 changed files with 155 additions and 95 deletions

View File

@ -52,6 +52,7 @@ export class Right extends Base {
if (abutton) { if (abutton) {
abutton.onButtonStateChangedObservable.add((value) => { abutton.onButtonStateChangedObservable.add((value) => {
if (value.pressed) { if (value.pressed) {
log.getLogger("right").debug("a-button pressed");
Controllers.controllerObserver.notifyObservers({type: 'menu'}); Controllers.controllerObserver.notifyObservers({type: 'menu'});
} }
}); });

View File

@ -1,5 +1,14 @@
import {DiagramEntity} from "./diagramEntity"; 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 {v4 as uuidv4} from 'uuid';
import {Toolbox} from "../toolbox/toolbox"; import {Toolbox} from "../toolbox/toolbox";
import log from "loglevel"; import log from "loglevel";
@ -57,6 +66,7 @@ export class MeshConverter {
mesh.metadata = {template: entity.template}; mesh.metadata = {template: entity.template};
if (entity.text) { if (entity.text) {
mesh.metadata.text = entity.text; mesh.metadata.text = entity.text;
this.updateTextNode(mesh, entity.text);
} }
if (entity.position) { if (entity.position) {
mesh.position = 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;
}
} }

View File

@ -2,6 +2,7 @@ import {
AbstractMesh, AbstractMesh,
GizmoManager, GizmoManager,
PointerEventTypes, PointerEventTypes,
PointerInfo,
Scene, Scene,
Vector3, Vector3,
WebXRExperienceHelper WebXRExperienceHelper
@ -40,21 +41,27 @@ export class Bmenu {
case PointerEventTypes.POINTERPICK: case PointerEventTypes.POINTERPICK:
if (pointerInfo.pickInfo?.pickedMesh?.metadata?.template && if (pointerInfo.pickInfo?.pickedMesh?.metadata?.template &&
pointerInfo.pickInfo?.pickedMesh?.parent?.parent?.id != "toolbox") { pointerInfo.pickInfo?.pickedMesh?.parent?.parent?.id != "toolbox") {
if (this.textInput) { this.cleanup()
this.textInput.blur(); .then(() => {
this.textInput.remove(); log.getLogger("bmenu").debug("cleaned up");
this.textInput = null; })
} .catch((e) => {
if (this.textView) { log.getLogger("bmenu").error(e);
this.textView.dispose().then(() => { });
log.getLogger("bmenu").debug("disposed"); this.handleEventStateAction(pointerInfo).then(() => {
log.getLogger("bmenu").debug("handled");
}).catch((e) => { }).catch((e) => {
log.getLogger("bmenu").error(e); log.getLogger("bmenu").error(e);
}); });
this.textView = null;
}
switch (this.state) {
break;
}
}
});
}
private async handleEventStateAction(pointerInfo: PointerInfo) {
switch (this.state) {
case BmenuState.REMOVING: case BmenuState.REMOVING:
log.debug("removing " + pointerInfo.pickInfo.pickedMesh.id); log.debug("removing " + pointerInfo.pickInfo.pickedMesh.id);
const event: DiagramEvent = { const event: DiagramEvent = {
@ -82,15 +89,11 @@ export class Bmenu {
log.debug(mesh.scaling); log.debug(mesh.scaling);
}); });
} }
} }
break; break;
case BmenuState.LABELING: case BmenuState.LABELING:
const mesh = pointerInfo.pickInfo.pickedMesh; const mesh = pointerInfo.pickInfo.pickedMesh;
log.debug("labeling " + mesh.id); log.debug("labeling " + mesh.id);
const textInput = document.createElement("input"); const textInput = document.createElement("input");
textInput.type = "text"; textInput.type = "text";
document.body.appendChild(textInput); document.body.appendChild(textInput);
@ -102,45 +105,44 @@ export class Bmenu {
textInput.focus(); textInput.focus();
if (navigator.userAgent.indexOf('Macintosh') > -1) { if (navigator.userAgent.indexOf('Macintosh') > -1) {
textInput.addEventListener('input', (event)=> { textInput.addEventListener('input', (event) => {
log.debug(event); log.debug(event);
}); });
const textView = new InputTextView(this.scene, this.xr, mesh) const textView = new InputTextView(this.scene, this.xr, mesh)
textView.show(textInput.value); await textView.show(textInput.value);
textInput.addEventListener('keydown', (event)=> { textInput.addEventListener('keydown', (event) => {
if (event.key == "Enter") { if (event.key == "Enter") {
log.getLogger('bmenu').debug("enter");
MeshConverter.updateTextNode(mesh, textInput.value);
this.persist(mesh, textInput.value); this.persist(mesh, textInput.value);
textInput.blur(); this.cleanup();
textInput.remove();
this.textView.dispose();
this.textView = null;
this.textInput = null;
} else { } else {
textView.updateText(textInput.value); textView.updateText(textInput.value);
MeshConverter.updateTextNode(mesh, textInput.value);
} }
}); });
this.textView = textView; this.textView = textView;
} else { } else {
textInput.addEventListener('keydown', (event)=> { textInput.addEventListener('blur', () => {
log.debug(event); log.getLogger('bmenu').debug("blur");
if (event.key == "Enter") { MeshConverter.updateTextNode(mesh, textInput.value);
this.persist(mesh, textInput.value); this.persist(mesh, textInput.value);
textInput.blur(); this.cleanup();
textInput.remove();
this.textInput = null;
this.textView = null;
}
}); });
} }
this.textInput = textInput; this.textInput = textInput;
break;
}
break; 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) { private persist(mesh: AbstractMesh, text: string) {
if (mesh.metadata) { if (mesh.metadata) {
@ -153,6 +155,7 @@ export class Bmenu {
entity: MeshConverter.toDiagramEntity(mesh), entity: MeshConverter.toDiagramEntity(mesh),
}); });
} }
makeButton(name: string, id: string) { makeButton(name: string, id: string) {
const button = new Button3D(name); const button = new Button3D(name);
button.scaling = new Vector3(.1, .1, .1); button.scaling = new Vector3(.1, .1, .1);