Simplified interactions, changed menu interactions for changing entities.
This commit is contained in:
parent
727977d5c6
commit
cabc38ce09
@ -21,6 +21,7 @@ import {snapRotateVal} from "../util/functions/snapRotateVal";
|
||||
import {grabAndClone} from "./functions/grab";
|
||||
import {isDiagramEntity} from "../diagram/functions/isDiagramEntity";
|
||||
import {ClickMenu} from "../menus/clickMenu";
|
||||
import {displayDebug} from "../util/displayDebug";
|
||||
|
||||
const CLICK_TIME = 300;
|
||||
export class Base {
|
||||
@ -144,11 +145,12 @@ export class Base {
|
||||
|
||||
private grab(cloneEntity: boolean = false) {
|
||||
let mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId);
|
||||
|
||||
if (!mesh) {
|
||||
return;
|
||||
}
|
||||
let player = false;
|
||||
|
||||
displayDebug(mesh);
|
||||
if (!isDiagramEntity(mesh)) {
|
||||
if (this.handleWasGrabbed(mesh)) {
|
||||
mesh && mesh.setParent(this.controller.motionController.rootMesh);
|
||||
@ -215,7 +217,12 @@ export class Base {
|
||||
return;
|
||||
}
|
||||
if (this.handleWasGrabbed(mesh)) {
|
||||
mesh.setParent(this.scene.getMeshByName("platform"))
|
||||
mesh.setParent(this.scene.getMeshByName("platform"));
|
||||
const location = {
|
||||
position: {x: mesh.position.x, y: mesh.position.y, z: mesh.position.z},
|
||||
rotation: {x: mesh.rotation.x, y: mesh.rotation.y, z: mesh.rotation.z}
|
||||
}
|
||||
localStorage.setItem(mesh.id, JSON.stringify(location));
|
||||
return;
|
||||
}
|
||||
reparent(mesh, this.previousParentId, this.grabbedMeshParentId);
|
||||
|
||||
@ -14,6 +14,7 @@ import {toDiagramEntity} from "./functions/toDiagramEntity";
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
import {buildEntityActionManager} from "./functions/buildEntityActionManager";
|
||||
import {isDiagramEntity} from "./functions/isDiagramEntity";
|
||||
import {InputTextView} from "../information/inputTextView";
|
||||
|
||||
|
||||
export class DiagramManager {
|
||||
@ -21,6 +22,7 @@ export class DiagramManager {
|
||||
private readonly _controllers: Controllers;
|
||||
private readonly diagramEntityActionManager: ActionManager;
|
||||
private presentationManager: PresentationManager;
|
||||
private readonly inputTextView: InputTextView;
|
||||
|
||||
public readonly onDiagramEventObservable: Observable<DiagramEvent> = new Observable();
|
||||
private readonly logger = log.getLogger('DiagramManager');
|
||||
@ -31,6 +33,21 @@ export class DiagramManager {
|
||||
constructor(scene: Scene) {
|
||||
this._config = new AppConfig();
|
||||
this._controllers = new Controllers();
|
||||
this.inputTextView = new InputTextView(scene, this._controllers);
|
||||
this.inputTextView.onTextObservable.add((evt) => {
|
||||
const mesh = scene.getMeshById(evt.id);
|
||||
if (mesh) {
|
||||
const entity = toDiagramEntity(mesh);
|
||||
entity.text = evt.text;
|
||||
this.onDiagramEventObservable.notifyObservers({
|
||||
type: DiagramEventType.MODIFY,
|
||||
entity: entity
|
||||
}, -1);
|
||||
} else {
|
||||
this.logger.error("mesh not found", evt.id);
|
||||
}
|
||||
});
|
||||
|
||||
this.sounds = new DiaSounds(scene);
|
||||
this.scene = scene;
|
||||
this.toolbox = new Toolbox(scene);
|
||||
@ -67,6 +84,10 @@ export class DiagramManager {
|
||||
});
|
||||
}
|
||||
|
||||
public editText(mesh: AbstractMesh) {
|
||||
this.inputTextView.show(mesh);
|
||||
}
|
||||
|
||||
public get controllers(): Controllers {
|
||||
return this._controllers;
|
||||
}
|
||||
|
||||
@ -1,63 +1,84 @@
|
||||
import {MeshBuilder, Observable, Scene, Vector3, WebXRDefaultExperience} from "@babylonjs/core";
|
||||
import {AbstractMesh, MeshBuilder, Observable, Scene, Vector3} from "@babylonjs/core";
|
||||
import log, {Logger} from "loglevel";
|
||||
import {AdvancedDynamicTexture, Control, InputText, VirtualKeyboard} from "@babylonjs/gui";
|
||||
import {ControllerEventType, Controllers} from "../controllers/controllers";
|
||||
import {setMenuPosition} from "../util/functions/setMenuPosition";
|
||||
import {DiaSounds} from "../util/diaSounds";
|
||||
import {Handle} from "../objects/handle";
|
||||
|
||||
export type TextEvent = {
|
||||
id: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export class InputTextView {
|
||||
public readonly onTextObservable: Observable<TextEvent> = new Observable<TextEvent>();
|
||||
private readonly text: string = "";
|
||||
private readonly scene: Scene;
|
||||
private readonly inputMesh: AbstractMesh;
|
||||
private sounds: DiaSounds;
|
||||
private readonly controllers: Controllers;
|
||||
private readonly xr: WebXRDefaultExperience;
|
||||
private readonly logger: Logger = log.getLogger('InputTextView');
|
||||
private readonly handle: Handle;
|
||||
private inputText: InputText;
|
||||
private diagramMesh: AbstractMesh;
|
||||
|
||||
constructor(text: string, xr: WebXRDefaultExperience, scene: Scene, controllers: Controllers) {
|
||||
this.text = text ? text : "";
|
||||
this.xr = xr;
|
||||
constructor(scene: Scene, controllers: Controllers) {
|
||||
this.controllers = controllers;
|
||||
this.scene = scene;
|
||||
this.sounds = new DiaSounds(scene);
|
||||
this.inputMesh = MeshBuilder.CreatePlane("input", {width: 1, height: .5}, this.scene);
|
||||
this.handle = new Handle(this.inputMesh);
|
||||
this.createKeyboard();
|
||||
}
|
||||
|
||||
public show() {
|
||||
this.showVirtualKeyboard();
|
||||
/*if ((this.xr as WebXRDefaultExperience).baseExperience?.sessionManager?.inXRSession) {
|
||||
this.showXr();
|
||||
public show(mesh: AbstractMesh) {
|
||||
this.inputText.text = mesh.metadata?.label || "";
|
||||
this.handle.mesh.setEnabled(true);
|
||||
this.diagramMesh = mesh;
|
||||
console.log(mesh.metadata);
|
||||
}
|
||||
|
||||
public createKeyboard() {
|
||||
const platform = this.scene.getMeshById('platform');
|
||||
let position = new Vector3(0, 1.66, .5);
|
||||
let rotation = new Vector3(0, .9, 0);
|
||||
const handle = this.handle;
|
||||
if (handle.mesh.position.x != 0 && handle.mesh.position.y != 0 && handle.mesh.position.z != 0) {
|
||||
position = handle.mesh.position;
|
||||
}
|
||||
if (handle.mesh.rotation.x != 0 && handle.mesh.rotation.y != 0 && handle.mesh.rotation.z != 0) {
|
||||
rotation = handle.mesh.rotation;
|
||||
}
|
||||
if (!platform) {
|
||||
this.scene.onNewMeshAddedObservable.add((mesh) => {
|
||||
if (mesh.id == 'platform') {
|
||||
this.logger.debug("platform added");
|
||||
handle.mesh.setParent(platform);
|
||||
|
||||
handle.mesh.position = position;
|
||||
handle.mesh.rotation = rotation;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.showWeb();
|
||||
}*/
|
||||
handle.mesh.parent = platform;
|
||||
handle.mesh.position = position;
|
||||
handle.mesh.rotation = rotation;
|
||||
}
|
||||
|
||||
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));
|
||||
const advancedTexture = AdvancedDynamicTexture.CreateForMesh(inputMesh, 2048, 1024, false);
|
||||
|
||||
//setMenuPosition(handle.mesh, this.scene, new Vector3(0, .4, 0));
|
||||
const advancedTexture = AdvancedDynamicTexture.CreateForMesh(this.inputMesh, 2048, 1024, false);
|
||||
const input = new InputText();
|
||||
|
||||
input.width = 0.5;
|
||||
input.maxWidth = 0.5;
|
||||
input.height = "64px";
|
||||
input.text = this.text;
|
||||
|
||||
input.text = "";
|
||||
input.fontSize = "32px";
|
||||
input.color = "white";
|
||||
input.background = "black";
|
||||
input.thickness = 3;
|
||||
input.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
|
||||
advancedTexture.addControl(input);
|
||||
|
||||
this.inputText = input;
|
||||
advancedTexture.addControl(this.inputText);
|
||||
const keyboard = VirtualKeyboard.CreateDefaultLayout();
|
||||
|
||||
keyboard.scaleY = 2;
|
||||
keyboard.scaleX = 2;
|
||||
keyboard.transformCenterY = 0;
|
||||
@ -84,77 +105,21 @@ export class InputTextView {
|
||||
}, -1, false, this, false);
|
||||
});
|
||||
|
||||
|
||||
keyboard.onPointerDownObservable.add(() => {
|
||||
this.sounds.tick.play();
|
||||
});
|
||||
keyboard.onKeyPressObservable.add((key) => {
|
||||
if (key === '↵') {
|
||||
this.onTextObservable.notifyObservers({text: input.text});
|
||||
input.dispose();
|
||||
keyboard.dispose();
|
||||
advancedTexture.dispose();
|
||||
inputMesh.dispose();
|
||||
this.sounds.exit.play();
|
||||
this.logger.error(this.inputText.text);
|
||||
this.onTextObservable.notifyObservers({id: this.diagramMesh.id, text: this.inputText.text});
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.sounds.enter.play();
|
||||
}, -1, false, this, false);
|
||||
this.handle.mesh.setEnabled(false);
|
||||
}
|
||||
|
||||
public showWeb() {
|
||||
const textInput = new InputText('identity', this.text);
|
||||
textInput.width = 0.2;
|
||||
textInput.height = "40px";
|
||||
textInput.color = "white";
|
||||
textInput.background = "black";
|
||||
textInput.focusedBackground = "black";
|
||||
|
||||
const advancedTexture = AdvancedDynamicTexture.CreateFullscreenUI("myUI");
|
||||
advancedTexture.addControl(textInput);
|
||||
textInput.onKeyboardEventProcessedObservable.add((evt) => {
|
||||
if (evt.key === 'Enter') {
|
||||
this.onTextObservable.notifyObservers({text: textInput.text});
|
||||
textInput.dispose();
|
||||
advancedTexture.dispose();
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private showXr() {
|
||||
const textInput = document.createElement("input");
|
||||
textInput.type = "text";
|
||||
document.body.appendChild(textInput);
|
||||
textInput.value = this.text;
|
||||
this.controllers.controllerObserver.notifyObservers({type: ControllerEventType.HIDE});
|
||||
|
||||
/*if (this.xr?.baseExperience?.sessionManager?.inXRSession) {
|
||||
this.xr.input.controllers.forEach((controller) => {
|
||||
controller.motionController.rootMesh.setEnabled(false);
|
||||
controller.pointer.setEnabled(false);
|
||||
});
|
||||
|
||||
}*/
|
||||
|
||||
textInput.addEventListener('blur', () => {
|
||||
log.getLogger('bmenu').debug("blur");
|
||||
this.onTextObservable.notifyObservers({text: textInput.value});
|
||||
this.controllers.controllerObserver.notifyObservers({type: ControllerEventType.SHOW});
|
||||
|
||||
/*if (this.xr?.baseExperience?.sessionManager?.inXRSession) {
|
||||
this.xr.input.controllers.forEach((controller) => {
|
||||
controller.motionController.rootMesh.setEnabled(true);
|
||||
controller.pointer.setEnabled(true);
|
||||
});
|
||||
}*/
|
||||
textInput.blur();
|
||||
textInput.remove();
|
||||
});
|
||||
|
||||
textInput.focus();
|
||||
|
||||
private hide() {
|
||||
this.handle.mesh.setEnabled(false);
|
||||
this.diagramMesh = null;
|
||||
}
|
||||
}
|
||||
@ -63,6 +63,9 @@ export class ClickMenu {
|
||||
this.dispose();
|
||||
break;
|
||||
case "label":
|
||||
this.diagramManager.editText(this.entity);
|
||||
this.dispose();
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -13,13 +13,23 @@ 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) {
|
||||
this.transformNode.setParent(handle);
|
||||
//this.transformNode.rotation.y = Math.PI;
|
||||
}
|
||||
const stored = localStorage.getItem(handle.id);
|
||||
if (stored) {
|
||||
try {
|
||||
const locationdata = JSON.parse(stored);
|
||||
handle.position = new Vector3(locationdata.position.x, locationdata.position.y, locationdata.position.z);
|
||||
handle.rotation = new Vector3(locationdata.rotation.x, locationdata.rotation.y, locationdata.rotation.z);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
handle.position = Vector3.Zero();
|
||||
}
|
||||
} else {
|
||||
handle.position = Vector3.Zero();
|
||||
}
|
||||
handle.metadata = {handle: true};
|
||||
this.mesh = handle;
|
||||
}
|
||||
}
|
||||
@ -40,6 +50,6 @@ function getHandleMesh(name: string, scene: Scene): InstancedMesh {
|
||||
handle.material = buildStandardMaterial('base-handle-material', scene, "#CCCCDD");
|
||||
handle.id = "base-handle-mesh";
|
||||
const instance = new InstancedMesh(name, (handle as Mesh));
|
||||
instance.setParent(scene.getMeshByName("platform"));
|
||||
instance.setParent(scene.getMeshById("platform"));
|
||||
return instance;
|
||||
}
|
||||
@ -11,6 +11,7 @@ const colors: string[] = [
|
||||
"#1e90ff", "#98fb98", "#ffe4b5", "#ff69b4"
|
||||
]
|
||||
|
||||
|
||||
export class Toolbox {
|
||||
private readonly logger = log.getLogger('Toolbox');
|
||||
private index = 0;
|
||||
@ -18,6 +19,7 @@ export class Toolbox {
|
||||
private readonly scene: Scene;
|
||||
private colorPicker: TransformNode;
|
||||
private changing = false;
|
||||
private readonly handle: Handle;
|
||||
private readonly manager: GUI3DManager;
|
||||
private readonly addPanel: StackPanel3D;
|
||||
public readonly colorChangeObservable: Observable<{ oldColor: string, newColor: string }> =
|
||||
@ -30,7 +32,7 @@ export class Toolbox {
|
||||
this.manager = new GUI3DManager(scene);
|
||||
this.manager.addControl(this.addPanel);
|
||||
this.toolboxBaseNode = new TransformNode("toolbox", this.scene);
|
||||
new Handle(this.toolboxBaseNode);
|
||||
this.handle = new Handle(this.toolboxBaseNode);
|
||||
this.toolboxBaseNode.position.y = .2;
|
||||
//this.toolboxBaseNode.position.z = .05;
|
||||
/*this.axes = new AxesViewer(this.scene);
|
||||
@ -91,19 +93,36 @@ export class Toolbox {
|
||||
}
|
||||
}
|
||||
//this.toolboxBaseNode.parent.setEnabled(false);
|
||||
const offset = new Vector3(0, 1, 1);
|
||||
let offset = new Vector3(-.50, 1.6, .38);
|
||||
let rotation = new Vector3(.5, -.6, .18);
|
||||
|
||||
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;
|
||||
const handle = this.handle;
|
||||
if (handle.mesh.position.x != 0 && handle.mesh.position.y != 0 && handle.mesh.position.z != 0) {
|
||||
offset = handle.mesh.position;
|
||||
}
|
||||
if (handle.mesh.rotation.x != 0 && handle.mesh.rotation.y != 0 && handle.mesh.rotation.z != 0) {
|
||||
rotation = handle.mesh.rotation;
|
||||
}
|
||||
handle.mesh.parent = platform;
|
||||
handle.mesh.position = offset;
|
||||
handle.mesh.rotation = rotation;
|
||||
} else {
|
||||
this.scene.onNewMeshAddedObservable.add((mesh: AbstractMesh) => {
|
||||
if (mesh.id == "platform") {
|
||||
const handle = (this.toolboxBaseNode.parent as TransformNode);
|
||||
handle.parent = mesh;
|
||||
handle.position = offset;
|
||||
const handle = this.handle;
|
||||
if (handle.mesh.position.x != 0 && handle.mesh.position.y != 0 && handle.mesh.position.z != 0) {
|
||||
offset = handle.mesh.position;
|
||||
}
|
||||
if (handle.mesh.rotation.x != 0 && handle.mesh.rotation.y != 0 && handle.mesh.rotation.z != 0) {
|
||||
rotation = handle.mesh.rotation;
|
||||
}
|
||||
handle.mesh.parent = mesh;
|
||||
handle.mesh.position = offset;
|
||||
handle.mesh.rotation = rotation;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
77
src/util/displayDebug.ts
Normal file
77
src/util/displayDebug.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import {
|
||||
AbstractMesh,
|
||||
DynamicTexture,
|
||||
Material,
|
||||
MeshBuilder,
|
||||
StandardMaterial,
|
||||
TransformNode,
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
|
||||
const debug = false;
|
||||
|
||||
export function displayDebug(transform: TransformNode) {
|
||||
if (debug) {
|
||||
const position = `position: (${transform.position.x.toFixed(2)}, ${transform.position.y.toFixed(2)}, ${transform.position.z.toFixed(2)})`;
|
||||
const rotation = `rotation: (${transform.rotation.x.toFixed(2)}, ${transform.rotation.y.toFixed(2)}, ${transform.rotation.z.toFixed(2)})`;
|
||||
buildText(position, transform, new Vector3(0, 1.5, 1));
|
||||
buildText(rotation, transform, new Vector3(0, 1.4, 1));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function buildText(text: string, transform: TransformNode, position) {
|
||||
const height = 0.05;
|
||||
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", 32, transform.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
|
||||
}, transform.getScene(), false);
|
||||
const mat = new StandardMaterial("mat", transform.getScene());
|
||||
mat.diffuseTexture = dynamicTexture;
|
||||
//mat.emissiveColor = Color3.White();
|
||||
dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true);
|
||||
//Create plane and set dynamic texture as material
|
||||
//const plane = MeshBuilder.CreatePlane("text" + text, {width: planeWidth, height: height}, mesh.getScene());
|
||||
const plane1 = createPlane(mat, transform, text, planeWidth, height, position);
|
||||
const plane2 = createPlane(mat, transform, text, planeWidth, height, position);
|
||||
plane2.rotation.y = Math.PI;
|
||||
}
|
||||
|
||||
function createPlane(mat: Material, transform: TransformNode, text: string, planeWidth: number, height: number, position): AbstractMesh {
|
||||
const plane = MeshBuilder.CreatePlane("text" + text, {width: planeWidth, height: height}, transform.getScene());
|
||||
|
||||
plane.material = mat;
|
||||
//plane.billboardMode = Mesh.BILLBOARDMODE_ALL;
|
||||
plane.metadata = {exportable: false, label: false};
|
||||
|
||||
//const yOffset = mesh.getBoundingInfo().boundingSphere.maximum.y;
|
||||
//plane.parent = mesh;
|
||||
//plane.scaling.y = (1 / mesh.scaling.y);
|
||||
//plane.scaling.x = (1 / mesh.scaling.x);
|
||||
//plane.scaling.z = (1 / mesh.scaling.z);
|
||||
//plane.position = transform.position.y = yOffset + (height * plane.scaling.y);
|
||||
plane.position = position;
|
||||
window.setTimeout(() => {
|
||||
plane.dispose();
|
||||
}, 5000);
|
||||
return plane;
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user