diff --git a/src/app.ts b/src/app.ts index 6e3672f..b9db21a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -30,10 +30,9 @@ import log from "loglevel"; export class App { //preTasks = [havokModule]; - public static scene: Scene; - public static xr: WebXRDefaultExperience; - public static rig: Rigplatform; - public static gamepad: Gamepad; + private scene: Scene; + private xr: WebXRDefaultExperience; + private rig: Rigplatform; constructor() { log.setLevel('debug'); @@ -49,13 +48,13 @@ export class App { } async initialize(canvas) { - if (App.xr) { - App.xr.dispose(); - App.xr = null; + if (this.xr) { + this.xr.dispose(); + this.xr = null; } - if (App.scene) { - App.scene.dispose(); - App.scene = null; + if (this.scene) { + this.scene.dispose(); + this.scene = null; } if (DiagramManager.onDiagramEventObservable) { DiagramManager.onDiagramEventObservable.clear(); @@ -64,7 +63,7 @@ export class App { const engine = new Engine(canvas, true); const scene = new Scene(engine); - App.scene = scene; + this.scene = scene; const havokInstance = await HavokPhysics(); @@ -82,7 +81,7 @@ export class App { './outdoor_field.jpeg', {}, scene); const ground = this.createGround(); - App.xr = await WebXRDefaultExperience.CreateAsync(scene, { + this.xr = await WebXRDefaultExperience.CreateAsync(scene, { floorMeshes: [ground], disableTeleportation: true, outputCanvasOptions: { @@ -96,9 +95,9 @@ export class App { } }); - App.xr.baseExperience.onStateChangedObservable.add((state) => { + this.xr.baseExperience.onStateChangedObservable.add((state) => { if (state == WebXRState.IN_XR) { - App.xr.baseExperience.camera.position = new Vector3(0, 1.6, 0); + this.xr.baseExperience.camera.position = new Vector3(0, 1.6, 0); window.addEventListener(('pa-button-state-change'), (event: any) => { if (event.detail) { log.debug('App', event.detail); @@ -108,11 +107,11 @@ export class App { } }); - const diagramManager = new DiagramManager(App.scene, App.xr.baseExperience); - App.rig = new Rigplatform(App.scene, App.xr); - const toolbox = new Toolbox(scene, App.xr.baseExperience); + const diagramManager = new DiagramManager(this.scene, this.xr.baseExperience); + this.rig = new Rigplatform(this.scene, this.xr); + const toolbox = new Toolbox(scene, this.xr.baseExperience); - App.scene.gamepadManager.onGamepadConnectedObservable.add((gamepad) => { + this.scene.gamepadManager.onGamepadConnectedObservable.add((gamepad) => { try { const dualshock = (gamepad as DualShockPad); @@ -197,18 +196,18 @@ export class App { } createGround() { - const groundMaterial = new PBRMetallicRoughnessMaterial("groundMaterial", App.scene); - const gText = new Texture("./grass1.jpeg", App.scene); + const groundMaterial = new PBRMetallicRoughnessMaterial("groundMaterial", this.scene); + const gText = new Texture("./grass1.jpeg", this.scene); gText.uScale = 40; gText.vScale = 40; groundMaterial.baseTexture = gText; groundMaterial.metallic = 0; groundMaterial.roughness = 1; - const ground = MeshBuilder.CreateGround("ground", {width: 100, height: 100, subdivisions: 1}, App.scene); + const ground = MeshBuilder.CreateGround("ground", {width: 100, height: 100, subdivisions: 1}, this.scene); ground.material = groundMaterial; - new PhysicsAggregate(ground, PhysicsShapeType.BOX, {mass: 0}, App.scene); + new PhysicsAggregate(ground, PhysicsShapeType.BOX, {mass: 0}, this.scene); return ground; } } diff --git a/src/controllers/controllers.ts b/src/controllers/controllers.ts index 6b62d87..0bf2ced 100644 --- a/src/controllers/controllers.ts +++ b/src/controllers/controllers.ts @@ -1,30 +1,7 @@ import {AbstractMesh, Observable, TransformNode} from "@babylonjs/core"; -export enum ControllerMovementMode { - ROTATE, - TRANSLATE -} -export class MeshHoverEvent { - public readonly mesh: AbstractMesh; - public readonly pointerId: string; - public readonly pointerMeshId: string; - public readonly isHovered: boolean; - constructor(mesh: AbstractMesh, isHovered: boolean, pointerId: string, pointerMeshId: string) { - this.mesh = mesh; - this.isHovered = isHovered; - this.pointerId = pointerId; - this.pointerMeshId = pointerMeshId; - } -} export class Controllers { public static movable: TransformNode | AbstractMesh; public static controllerObserver = new Observable(); - public static movementMode: ControllerMovementMode = ControllerMovementMode.ROTATE; - public static toggleMovementMode() { - if (this.movementMode == ControllerMovementMode.ROTATE) { - this.movementMode = ControllerMovementMode.TRANSLATE; - } else { - this.movementMode = ControllerMovementMode.ROTATE; - } - } + } \ No newline at end of file diff --git a/src/controllers/right.ts b/src/controllers/right.ts index 53d916e..f6bb521 100644 --- a/src/controllers/right.ts +++ b/src/controllers/right.ts @@ -1,13 +1,12 @@ import {Base} from "./base"; import { - Angle, Scene, Vector3, WebXRControllerComponent, WebXRDefaultExperience, WebXRInputSource } from "@babylonjs/core"; -import {ControllerMovementMode, Controllers} from "./controllers"; +import {Controllers} from "./controllers"; import log from "loglevel"; export class Right extends Base { @@ -26,6 +25,7 @@ export class Right extends Base { } + private initBButton(bbutton: WebXRControllerComponent) { if (bbutton) { bbutton.onButtonStateChangedObservable.add((button) => { @@ -62,15 +62,7 @@ export class Right extends Base { if (thumbstick) { thumbstick.onAxisValueChangedObservable.add((value) => { log.trace('Right', `thumbstick moved ${value.x}, ${value.y}`); - if (!Controllers.movable) { - this.moveRig(value); - } else { - if (Controllers.movementMode == ControllerMovementMode.ROTATE) { - this.rotateMovable(value); - } else { - this.moveMovable(value); - } - } + this.moveRig(value); }); thumbstick.onButtonStateChangedObservable.add((value) => { if (value.pressed) { @@ -98,32 +90,4 @@ export class Right extends Base { Controllers.controllerObserver.notifyObservers({type: 'updown', value: 0}); } } - - private rotateMovable(value: { x: number; y: number }) { - if (Math.abs(value.y) > .1) { - Controllers.movable.rotation.x += - Angle.FromDegrees(Math.sign(value.y)).radians(); - Controllers.movable.rotation.x = this.fixRadians(Controllers.movable.rotation.x); - } - if (Math.abs(value.x) > .1) { - Controllers.movable.rotation.z += - Angle.FromDegrees(Math.sign(value.x)).radians(); - Controllers.movable.rotation.z = this.fixRadians(Controllers.movable.rotation.z); - } - } - private fixRadians(value: number) { - if (value > 2 * Math.PI) { - return value - 2 * Math.PI; - } else { - return value; - } - } - private moveMovable(value: { x: number; y: number }) { - if (Math.abs(value.y) > .1) { - Controllers.movable.position.z += Math.sign(value.y) * -.005; - } - if (Math.abs(value.x) > .1) { - Controllers.movable.position.x += Math.sign(value.x) * .005; - } - } } \ No newline at end of file diff --git a/src/controllers/rigplatform.ts b/src/controllers/rigplatform.ts index e1d3051..68f8fbb 100644 --- a/src/controllers/rigplatform.ts +++ b/src/controllers/rigplatform.ts @@ -10,7 +10,7 @@ import { PhysicsShapeType, Quaternion, Scene, - StandardMaterial, TransformNode, + StandardMaterial, Vector3, WebXRDefaultExperience } from "@babylonjs/core"; @@ -26,7 +26,7 @@ export class Rigplatform { private velocityIndex = 2; private readonly velocityArray = [0.01, 0.1, 1, 2, 5]; public bMenu: Bmenu; - private scene: Scene; + private readonly scene: Scene; public static instance: Rigplatform; private static xr: WebXRDefaultExperience; private yRotation: number = 0; diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index ea080fd..b4c67de 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -1,4 +1,4 @@ -import {AbstractMesh, Color3, Material, Observable, Scene, WebXRExperienceHelper} from "@babylonjs/core"; +import {Observable, Scene, WebXRExperienceHelper} from "@babylonjs/core"; import {DiagramEntity, DiagramEvent, DiagramEventType} from "./diagramEntity"; import {IPersistenceManager} from "./persistenceManager"; @@ -6,41 +6,36 @@ import {IndexdbPersistenceManager} from "./indexdbPersistenceManager"; import {MeshConverter} from "./meshConverter"; import log from "loglevel"; - export class DiagramManager { private persistenceManager: IPersistenceManager = new IndexdbPersistenceManager("diagram"); static onDiagramEventObservable: Observable = new Observable(); - private readonly scene: Scene; private xr: WebXRExperienceHelper; - static currentMesh: AbstractMesh; - - private materialMap: Map = new Map(); constructor(scene: Scene, xr: WebXRExperienceHelper) { this.scene = scene; this.xr = xr; this.persistenceManager.updateObserver.add(this.onRemoteEvent, -1, true, this); - log.debug('DiagramManager', "remove event observer added"); + log.getLogger('DiagramManager').debug( "remote event observer added"); this.persistenceManager.initialize(); if (!DiagramManager.onDiagramEventObservable) { - log.debug('DiagramManager', "onDiagramEventObservable missing, recreated"); + log.getLogger('DiagramManager').debug( "onDiagramEventObservable missing, recreated"); DiagramManager.onDiagramEventObservable = new Observable(); } if (DiagramManager.onDiagramEventObservable.hasObservers()) { - log.warn('DiagramManager', "onDiagramEventObservable already has Observers, this shouldn't happen"); + log.getLogger('DiagramManager').debug("onDiagramEventObservable already has Observers, this shouldn't happen"); } else { DiagramManager.onDiagramEventObservable.add(this.onDiagramEvent, -1, true, this); - log.debug('DiagramManager', "onDiagramEventObservable Observer added"); + log.getLogger('DiagramManager').debug( "onDiagramEventObservable Observer added"); } - log.debug('DiagramManager', "DiagramManager constructed"); + log.getLogger('DiagramManager').debug( "DiagramManager constructed"); } private onRemoteEvent(event: DiagramEntity) { //const mesh = Toolbox.instance.newMesh(ToolType[Object.entries(ToolType).find(e => e[1] == event.template)[0]], event.id); - log.debug('DiagramManager', event); + log.getLogger('DiagramManager').debug(event); const mesh = MeshConverter.fromDiagramEntity(event, this.scene); if (event.parent) { mesh.parent = this.scene.getMeshById(event.parent); @@ -48,6 +43,7 @@ export class DiagramManager { } private onDiagramEvent(event: DiagramEvent) { + log.getLogger("DiagramManager").debug(event); const entity = event.entity; let mesh; if (entity) { diff --git a/src/information/inputTextView.ts b/src/information/inputTextView.ts new file mode 100644 index 0000000..0c8074d --- /dev/null +++ b/src/information/inputTextView.ts @@ -0,0 +1,46 @@ +import {AbstractMesh, Angle, MeshBuilder, Scene, WebXRExperienceHelper} from "@babylonjs/core"; +import {AdvancedDynamicTexture, InputText} from "@babylonjs/gui"; + +export class InputTextView { + private mesh: AbstractMesh; + private scene: Scene; + private xr: WebXRExperienceHelper; + private inputPlane: AbstractMesh; + private inputText: InputText; + constructor(scene: Scene, xr: WebXRExperienceHelper, mesh: AbstractMesh ) { + this.scene = scene; + this.xr = xr; + this.mesh = mesh; + } + public async show(text: string) { + this.inputPlane = MeshBuilder.CreatePlane("myPlane", {width: 1, height: .125}, this.scene); + const pos = this.mesh.absolutePosition; + pos.y += .2; + this.inputPlane.position= pos; + this.inputPlane.rotation.y = Angle.FromDegrees(180).radians(); + const textDisplayTexture = AdvancedDynamicTexture.CreateForMesh(this.inputPlane, 1024, 128); + this.inputPlane.material.backFaceCulling = false; + this.inputText = this.createInputText(); + this.inputText.text = text; + textDisplayTexture.addControl(this.inputText); + } + private createInputText(): InputText { + const inputText = new InputText("input"); + inputText.color= "white"; + inputText.background = "black"; + inputText.height= "128px"; + inputText.width= "1024px"; + inputText.maxWidth= "1024px"; + inputText.margin="0px"; + inputText.fontSize= "48px"; + return inputText; + } + public async dispose() { + this.inputPlane.dispose(false, true); + this.inputPlane = null; + this.inputText = null; + } + public async updateText(text: string) { + this.inputText.text = text; + } +} \ No newline at end of file diff --git a/src/integration/ring/cameras.ts b/src/integration/ring/cameras.ts index 598406c..532badc 100644 --- a/src/integration/ring/cameras.ts +++ b/src/integration/ring/cameras.ts @@ -13,10 +13,7 @@ export class Cameras { } public async getCameras() { - - const cameras = await axios.get('https://local.immersiveidea.com/api/cameras'); - this.cameras = cameras; - //console.log(cameras); + this.cameras = await axios.get('https://local.immersiveidea.com/api/cameras'); } public createCameras() { diff --git a/src/menus/bmenu.ts b/src/menus/bmenu.ts index 13724d2..55da78b 100644 --- a/src/menus/bmenu.ts +++ b/src/menus/bmenu.ts @@ -1,34 +1,27 @@ import { - Angle, + AbstractMesh, GizmoManager, - MeshBuilder, PointerEventTypes, Scene, Vector3, WebXRExperienceHelper } from "@babylonjs/core"; -import { - AdvancedDynamicTexture, - Button3D, - ColorPicker, - GUI3DManager, - InputText, - StackPanel3D, - TextBlock -} from "@babylonjs/gui"; +import {Button3D, GUI3DManager, StackPanel3D, TextBlock} from "@babylonjs/gui"; import {DiagramManager} from "../diagram/diagramManager"; import {BmenuState} from "./MenuState"; import {DiagramEvent, DiagramEventType} from "../diagram/diagramEntity"; import {MeshConverter} from "../diagram/meshConverter"; import log from "loglevel"; +import {InputTextView} from "../information/inputTextView"; export class Bmenu { private state: BmenuState = BmenuState.NONE; private manager: GUI3DManager; private readonly scene: Scene; + private textView: InputTextView; + private textInput: HTMLElement; private gizmoManager: GizmoManager; private xr: WebXRExperienceHelper; - private textInput: any; constructor(scene: Scene, xr: WebXRExperienceHelper) { @@ -47,7 +40,21 @@ 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) => { + log.getLogger("bmenu").error(e); + }); + this.textView = null; + } switch (this.state) { + case BmenuState.REMOVING: log.debug("removing " + pointerInfo.pickInfo.pickedMesh.id); const event: DiagramEvent = { @@ -82,40 +89,51 @@ export class Bmenu { case BmenuState.LABELING: const mesh = pointerInfo.pickInfo.pickedMesh; log.debug("labeling " + mesh.id); -/* const myPlane = MeshBuilder.CreatePlane("myPlane", {width: 1, height: .125}, this.scene); - //myPlane.parent=mesh; - const pos = mesh.absolutePosition; - pos.y += .2; - myPlane.position= pos; - myPlane.rotation.y = Angle.FromDegrees(180).radians(); - const advancedTexture2 = AdvancedDynamicTexture.CreateForMesh(myPlane, 1024, 128); - myPlane.material.backFaceCulling = false; - const inputText = new InputText("input"); - inputText.color= "white"; - inputText.background = "black"; - inputText.height= "128px"; - inputText.width= "1024px"; - inputText.maxWidth= "1024px"; - inputText.margin="0px"; - inputText.fontSize= "48px"; - advancedTexture2.addControl(inputText); - */ + const textInput = document.createElement("input"); textInput.type = "text"; document.body.appendChild(textInput); - textInput.value = ""; + if (mesh?.metadata?.text) { + textInput.value = mesh.metadata.text; + } else { + textInput.value = ""; + } textInput.focus(); - textInput.addEventListener('input', (event)=> { - log.debug(event); - }); - textInput.addEventListener('keydown', (event)=> { - log.debug(event); - if (event.key == "Enter") { - textInput.blur(); - textInput.remove(); - } - }); + + 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; } @@ -124,7 +142,17 @@ export class Bmenu { } }); } - + private persist(mesh: AbstractMesh, text: string) { + if (mesh.metadata) { + mesh.metadata.text = text; + } else { + log.getLogger('bmenu').error("mesh has no metadata"); + } + DiagramManager.onDiagramEventObservable.notifyObservers({ + type: DiagramEventType.MODIFY, + entity: MeshConverter.toDiagramEntity(mesh), + }); + } makeButton(name: string, id: string) { const button = new Button3D(name); button.scaling = new Vector3(.1, .1, .1); @@ -133,18 +161,10 @@ export class Bmenu { text.fontSize = "24px"; text.color = "white"; button.content = text; - button.onPointerClickObservable.add(this.#clickhandler, -1, false, this); + button.onPointerClickObservable.add(this.handleClick, -1, false, this); return button; } - public getState() { - return this.state; - } - - public setState(state: BmenuState) { - this.state = state; - } - toggle() { //console.log(mesh.name); if (this.manager) { @@ -167,7 +187,7 @@ export class Bmenu { } } - #clickhandler(_info, state) { + private handleClick(_info, state) { switch (state.currentTarget.name) { case "modify": this.state = BmenuState.MODIFYING; diff --git a/src/menus/keyboard.ts b/src/menus/keyboard.ts index 17aa0ce..d023ee2 100644 --- a/src/menus/keyboard.ts +++ b/src/menus/keyboard.ts @@ -1,16 +1,9 @@ -import {AbstractMesh, MeshBuilder, Scene, Vector3, WebXRExperienceHelper} from "@babylonjs/core"; +import {AbstractMesh, MeshBuilder, Scene, WebXRExperienceHelper} from "@babylonjs/core"; import { - AdvancedDynamicTexture, - Button3D, - GUI3DManager, InputText, PlanePanel, - StackPanel, StackPanel3D, - TextBlock, - TouchHolographicButton + AdvancedDynamicTexture } from "@babylonjs/gui"; -import {MyMenu} from "../util/myMenu"; export class Keyboard { - private manager: GUI3DManager; private readonly scene: Scene; private mesh: AbstractMesh; private panel: AbstractMesh; diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index 7b827f1..93d3e33 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -34,7 +34,7 @@ export class Toolbox { public readonly node : TransformNode; private readonly manager: GUI3DManager; private readonly gridsize = 5; - private addPanel: StackPanel3D; + private readonly addPanel: StackPanel3D; constructor (scene:Scene, xr: WebXRExperienceHelper) { this.scene = scene; this.addPanel = new StackPanel3D(); @@ -145,9 +145,6 @@ export class Toolbox { this.buildColor(Color3.FromHexString(color)); } } - private nextPosition() { - - } public buildTool(tool: ToolType, parent: AbstractMesh) { let newItem: Mesh; diff --git a/src/util/gmap.ts b/src/util/gmap.ts index 02e37aa..db3dce3 100644 --- a/src/util/gmap.ts +++ b/src/util/gmap.ts @@ -1,5 +1,6 @@ import {Angle, Color3, MeshBuilder, Scene, StandardMaterial, Texture} from "@babylonjs/core"; import googleStaticMapsTile from "google-static-maps-tile"; +import log from "loglevel"; export class Gmap { private readonly scene: Scene; @@ -9,6 +10,7 @@ export class Gmap { } public async createMapTiles(lat, lon) { + log.debug('createMapTiles', lat, lon) googleStaticMapsTile({ areaSize: '2560x2560', center: '26.443397,-82.111512', @@ -19,10 +21,7 @@ export class Gmap { maptype: 'satellite' }) .on('progress', function (info) { - //console.log(info.count); - //console.log(info.total); const image = info.image; - image.style.position = 'absolute'; image.style.left = info.data.x + 'px'; image.style.top = info.data.y + 'px'; diff --git a/src/util/myMenu.ts b/src/util/myMenu.ts index 67a3074..ffd43ba 100644 --- a/src/util/myMenu.ts +++ b/src/util/myMenu.ts @@ -1,4 +1,4 @@ -import {PlanePanel, TouchHolographicMenu} from "@babylonjs/gui"; +import {PlanePanel} from "@babylonjs/gui"; export class MyMenu extends PlanePanel { public arrangeChildren: boolean = true;