diff --git a/src/controllers/base.ts b/src/controllers/base.ts index 8f52b93..5d2417e 100644 --- a/src/controllers/base.ts +++ b/src/controllers/base.ts @@ -61,7 +61,6 @@ export class Base { this._meshUnderPointer = null; } } - }); this.diagramManager = diagramManager; @@ -116,6 +115,7 @@ export class Base { this.grabbedMesh = clone.mesh; this.grabbedMeshType = getMeshType(clone.mesh, this.diagramManager); this._meshUnderPointer = clone.mesh; + clone.grabbed = true; } }, 300, this); } @@ -149,6 +149,7 @@ export class Base { const diagramObject = this.diagramManager.getDiagramObject(mesh.id); if (diagramObject.isGrabbable) { diagramObject.baseTransform.setParent(this.xrInputSource.motionController.rootMesh); + diagramObject.grabbed = true; this.grabbedObject = diagramObject; } break; @@ -159,6 +160,7 @@ export class Base { const clone = grabAndClone(this.diagramManager, mesh, this.xrInputSource.motionController.rootMesh); this.grabbedObject = clone; this.grabbedMesh = clone.mesh; + clone.grabbed = true; } } @@ -182,6 +184,7 @@ export class Base { } this.diagramManager.onDiagramEventObservable.notifyObservers(event, DiagramEventObserverMask.ALL); diagramObject.mesh.computeWorldMatrix(false); + diagramObject.grabbed = false; } this.grabbedObject = null; @@ -199,6 +202,7 @@ export class Base { } this.diagramManager.onDiagramEventObservable.notifyObservers(event, DiagramEventObserverMask.ALL); diagramObject.mesh.computeWorldMatrix(false); + this.grabbedObject.grabbed = false; this.grabbedObject = null; this.grabbedMesh = null; this.grabbedMeshType = null; diff --git a/src/information/inputTextView.ts b/src/information/inputTextView.ts index 8473a84..f94a67d 100644 --- a/src/information/inputTextView.ts +++ b/src/information/inputTextView.ts @@ -27,7 +27,7 @@ export class InputTextView { this.controllers = controllers; this.scene = DefaultScene.Scene; this.inputMesh = MeshBuilder.CreatePlane("input", {width: 1, height: .5}, this.scene); - this.handle = new Handle(this.inputMesh); + this.handle = new Handle(this.inputMesh, 'Input'); this.inputMesh.position.y = .06; this.inputMesh.position.z = .02; this.createKeyboard(); diff --git a/src/menus/abstractMenu.ts b/src/menus/abstractMenu.ts index becbd9a..2dc48c5 100644 --- a/src/menus/abstractMenu.ts +++ b/src/menus/abstractMenu.ts @@ -1,4 +1,4 @@ -import {Scene, TransformNode, Vector3, WebXRDefaultExperience} from "@babylonjs/core"; +import {Scene, WebXRDefaultExperience} from "@babylonjs/core"; import {Controllers} from "../controllers/controllers"; import {Handle} from "../objects/handle"; import {DefaultScene} from "../defaultScene"; @@ -15,9 +15,6 @@ export abstract class AbstractMenu { this.controllers = controllers; } - protected createHandle(mesh: TransformNode, offset: Vector3, rotation: Vector3) { - this.handle = new Handle(mesh, offset, rotation); - } public toggle() { throw new Error("AbstractMenu.toggle() not implemented"); } diff --git a/src/menus/configMenu.ts b/src/menus/configMenu.ts index 947136e..ef07868 100644 --- a/src/menus/configMenu.ts +++ b/src/menus/configMenu.ts @@ -30,7 +30,7 @@ export class ConfigMenu { this._scene = DefaultScene.Scene; this.baseTransform = new TransformNode("configMenuBase", this._scene); - this._handle = new Handle(this.baseTransform); + this._handle = new Handle(this.baseTransform, 'Configuration'); this.config = config; this.buildMenu(); } diff --git a/src/objects/diagramObject.ts b/src/objects/diagramObject.ts index dcf93f2..72927b4 100644 --- a/src/objects/diagramObject.ts +++ b/src/objects/diagramObject.ts @@ -30,9 +30,10 @@ export class DiagramObject { private _observingStart: number; private _sceneObserver: Observer; private _eventObservable: Observable; - private _mesh: AbstractMesh; private _label: AbstractMesh; + + public grabbed: boolean = false; public get mesh(): AbstractMesh { return this._mesh; } diff --git a/src/objects/handle.ts b/src/objects/handle.ts index e0572db..9ff68a9 100644 --- a/src/objects/handle.ts +++ b/src/objects/handle.ts @@ -1,105 +1,59 @@ -import {AbstractMesh, InstancedMesh, Mesh, Scene, TransformNode, Vector3} from "@babylonjs/core"; +import {AbstractMesh, Scene, TransformNode, Vector3} from "@babylonjs/core"; import {HtmlMeshBuilder} from "babylon-html"; +import log, {Logger} from "loglevel"; export class Handle { public mesh: AbstractMesh; - private readonly menuItem: TransformNode; + private readonly _menuItem: TransformNode; private _isStored: boolean = false; - private offset: Vector3; - private rotation: Vector3; + private _offset: Vector3; + private _rotation: Vector3; + private readonly _label: string; + private readonly _logger: Logger = log.getLogger('Handle'); - constructor(mesh: TransformNode, offset: Vector3 = Vector3.Zero(), rotation: Vector3 = Vector3.Zero()) { - this.menuItem = mesh; - this.offset = offset; - this.rotation = rotation; + constructor(mesh: TransformNode, label: string = 'Handle', offset: Vector3 = Vector3.Zero(), rotation: Vector3 = Vector3.Zero()) { + this._menuItem = mesh; + this._offset = offset; + this._rotation = rotation; + this._label = label; + this._logger.debug('Handle created with label ' + label); this.buildHandle(); } public get idStored() { return this._isStored; } + private buildHandle() { - const scene: Scene = this.menuItem.getScene(); - const handle = getHandleMesh("handle-" + this.menuItem.id + "-mesh", scene); - if (this.menuItem) { - this.menuItem.setParent(handle); + const scene: Scene = this._menuItem.getScene(); + const handle = HtmlMeshBuilder.CreatePlaneSync('handle-' + this._menuItem.id, { + html: + `
${this._label}
+ `, width: .5, height: .1, image: {width: 256, height: 51} + }, scene); + handle.id = 'handle-' + this._menuItem.id; + if (this._menuItem) { + this._menuItem.setParent(handle); } const stored = localStorage.getItem(handle.id); if (stored) { + this._logger.debug('Stored location found for ' + handle.id); try { const locationdata = JSON.parse(stored); + this._logger.debug('Stored location data found ', locationdata); 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); + handle.rotation = new Vector3(locationdata._rotation.x, locationdata._rotation.y, locationdata._rotation.z); this._isStored = true; } catch (e) { - console.error(e); + this._logger.error(e); handle.position = Vector3.Zero(); } } else { - handle.position = this.offset; - handle.rotation = this.rotation; - `` + handle.position = this._offset; + handle.rotation = this._rotation; } handle.metadata = {handle: true}; this.mesh = handle; } - private setPlatformParent() { - const platform = this.menuItem.getScene().getNodeById("platform"); - - if (platform) { - - this.mesh.parent = platform; - /*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; - if (!this._isStored) { - this.mesh.position = this.offset; - this.mesh.rotation = this.rotation; - } - - } else { - this.menuItem.getScene().onNewMeshAddedObservable.add((mesh: AbstractMesh) => { - if (mesh && mesh.id == "platform") { - //const handle = this.handle; - this.menuItem.parent = mesh; - if (!this._isStored) { - this.mesh.position = this.offset; - this.mesh.rotation = this.rotation; - } - } - }, -1, false, this, false); - } - - } -} - - -function getHandleMesh(name: string, scene: Scene): InstancedMesh { - const existingBase = scene.getMeshById("base-handle-mesh"); - if (existingBase) { - const instance = new InstancedMesh(name, (existingBase as Mesh)); - instance.setParent(scene.getMeshByName("platform")); - return instance; - } - /*const handle = MeshBuilder.CreateCapsule("base-handle-mesh", { - radius: .04, - orientation: Vector3.Right(), - height: .3 - }, scene);*/ - const handle = HtmlMeshBuilder.CreatePlaneSync("base-handle-mesh", { - html: - `
Handle
- `, width: .5, height: .1, image: {width: 256, height: 51} - }, scene); - - //handle.material = buildStandardMaterial('base-handle-material', scene, "#CCCCDD"); - handle.id = "base-handle-mesh"; - const instance = new InstancedMesh(name, (handle as Mesh)); - instance.setParent(scene.getMeshById("platform")); - return instance; } \ No newline at end of file diff --git a/src/soccer/soccerMenu.ts b/src/soccer/soccerMenu.ts index 894425d..326a13a 100644 --- a/src/soccer/soccerMenu.ts +++ b/src/soccer/soccerMenu.ts @@ -1,4 +1,4 @@ -import {Color3, MeshBuilder, Scene, Vector3, WebXRDefaultExperience} from "@babylonjs/core"; +import {Color3, MeshBuilder, Vector3, WebXRDefaultExperience} from "@babylonjs/core"; import {AbstractMenu} from "../menus/abstractMenu"; import {ControllerEvent, ControllerEventType, Controllers} from "../controllers/controllers"; import {Control3D, GUI3DManager, PlanePanel, Slider3D} from "@babylonjs/gui"; @@ -19,12 +19,12 @@ export class SoccerMenu extends AbstractMenu { private manager: GUI3DManager; private state: SoccerMenuState = SoccerMenuState.NONE; private readonly field: Field; - private logger: Logger = log.getLogger('SoccerMenu') + private readonly logger: Logger = log.getLogger('SoccerMenu') private startTime: number; private startPosition: Vector3; - constructor(scene: Scene, xr: WebXRDefaultExperience, controllers: Controllers) { - super(scene, xr, controllers); + constructor(xr: WebXRDefaultExperience, controllers: Controllers) { + super(xr, controllers); this.field = new Field(this.scene); this.manager = new GUI3DManager(this.scene); @@ -180,7 +180,7 @@ export class SoccerMenu extends AbstractMenu { this.manager.controlScaling = .2; //panel.updateLayout(); //slider.position.x = 1; - this.createHandle(this.manager.rootContainer.children[0].node); + //this.createHandle(this.manager.rootContainer.children[0].node); //this.handle.mesh.position = getFrontPosition(3, this.scene).add(new Vector3(0, .5, 0)); } diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index 23a1ac9..a524f77 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -13,55 +13,75 @@ const colors: string[] = [ export class Toolbox { - private readonly tools: Map = new Map(); + public readonly _toolboxBaseNode: TransformNode; + private readonly _tools: Map = new Map(); + private readonly _logger = log.getLogger('Toolbox'); + private readonly _handle: Handle; + private readonly _scene: Scene; constructor() { - this.scene = DefaultScene.Scene; - this.toolboxBaseNode = new TransformNode("toolbox", this.scene); - this.handle = new Handle(this.toolboxBaseNode); - this.toolboxBaseNode.position.y = .2; - this.toolboxBaseNode.scaling = new Vector3(0.6, 0.6, 0.6); + this._scene = DefaultScene.Scene; + this._toolboxBaseNode = new TransformNode("toolbox", this._scene); + this._handle = new Handle(this._toolboxBaseNode, 'Toolbox'); + this._toolboxBaseNode.position.y = .2; + this._toolboxBaseNode.scaling = new Vector3(0.6, 0.6, 0.6); this.buildToolbox(); Toolbox._instance = this; } - private readonly logger = log.getLogger('Toolbox'); private index = 0; - public readonly toolboxBaseNode: TransformNode; - private readonly scene: Scene; private colorPicker: TransformNode; private changing = false; - private readonly handle: Handle; - public static _instance; + public static _instance: Toolbox; public static get instance() { return Toolbox._instance; } - public isTool(mesh: AbstractMesh) { - return this.tools.has(mesh.id); + public get handleMesh(): AbstractMesh { + return this._handle.mesh; + } + + public isTool(mesh: AbstractMesh) { + return this._tools.has(mesh.id); + } + + private buildToolbox() { + this.setupPointerObservable(); + this.buildColorPicker(); + if (this._toolboxBaseNode.parent) { + const platform = this._scene.getMeshById("platform"); + if (platform) { + this.assignHandleParentAndStore(platform); + } else { + const observer = this._scene.onNewMeshAddedObservable.add((mesh: AbstractMesh) => { + if (mesh && mesh.id == "platform") { + this.assignHandleParentAndStore(mesh); + this._scene.onNewMeshAddedObservable.remove(observer); + } + }, -1, false, this, false); + } + } } - private nodePredicate = (node: Node) => { - return node.getClassName() == "InstancedMesh" && - node.isEnabled(false) == true - }; private setupPointerObservable() { - this.scene.onPointerObservable.add((pointerInfo) => { + this._scene.onPointerObservable.add((pointerInfo) => { + const pickedMesh = pointerInfo?.pickInfo?.pickedMesh; if (pointerInfo.type == 1 && - pointerInfo.pickInfo.pickedMesh?.metadata?.tool == 'color') { + pickedMesh?.metadata?.tool == 'color') { if (this.changing) { + this._logger.debug('changing'); this.colorPicker.setEnabled(true); return; } else { - const active = pointerInfo.pickInfo.pickedMesh?.parent.getChildren(this.nodePredicate, true); + const active = pickedMesh?.parent.getChildren(this.nodePredicate, true); for (const node of active) { node.setEnabled(false); } - const nodes = pointerInfo.pickInfo.pickedMesh?.metadata?.tools; + const nodes = pickedMesh?.metadata?.tools; if (nodes) { for (const node of nodes) { - this.scene.getNodeById(node)?.setEnabled(true); + this._scene.getNodeById(node)?.setEnabled(true); } } } @@ -69,14 +89,19 @@ export class Toolbox { }); } + private nodePredicate = (node: Node) => { + return node.getClassName() == "InstancedMesh" && + node.isEnabled(false) == true + }; + private buildColorPicker() { let initial = true; for (const c of colors) { - const cnode = buildColor(Color3.FromHexString(c), this.scene, this.toolboxBaseNode, this.index++, this.tools); + const cnode = buildColor(Color3.FromHexString(c), this._scene, this._toolboxBaseNode, this.index++, this._tools); if (initial) { initial = false; for (const id of cnode.metadata.tools) { - this.scene.getNodeById(id)?.setEnabled(true); + this._scene.getNodeById(id)?.setEnabled(true); } } @@ -88,7 +113,7 @@ export class Toolbox { const offset = new Vector3(-.50, 1.6, .38); const rotation = new Vector3(.5, -.6, .18); - const handle = this.handle; + const handle = this._handle; handle.mesh.parent = mesh; if (!handle.idStored) { handle.mesh.position = offset; @@ -96,26 +121,5 @@ export class Toolbox { } } - - private buildToolbox() { - this.setupPointerObservable(); - this.buildColorPicker(); - if (this.toolboxBaseNode.parent) { - const platform = this.scene.getMeshById("platform"); - if (platform) { - this.assignHandleParentAndStore(platform); - } else { - const observer = this.scene.onNewMeshAddedObservable.add((mesh: AbstractMesh) => { - if (mesh && mesh.id == "platform") { - this.assignHandleParentAndStore(mesh); - this.scene.onNewMeshAddedObservable.remove(observer); - } - }, -1, false, this, false); - } - } - } - public get handleMesh(): AbstractMesh { - return this.handle.mesh; - } } diff --git a/src/vrApp.ts b/src/vrApp.ts index 83e2550..7b725cf 100644 --- a/src/vrApp.ts +++ b/src/vrApp.ts @@ -15,7 +15,8 @@ import {Introduction} from "./tutorial/introduction"; const webGpu = false; -log.setLevel('debug', false); +log.setLevel('error', false); +log.getLogger('Handle').setLevel('debug'); const canvas = (document.querySelector('#gameCanvas') as HTMLCanvasElement); export class VrApp {