updated menu positioning, added b-button to enable resetting positions.

This commit is contained in:
Michael Mainguy 2024-04-24 14:24:22 -05:00
parent 5c22c15076
commit 36e4b04957
17 changed files with 158 additions and 90 deletions

View File

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta content="An immersive vr diagramming experience based using webxr version 0.3" name="description">
<meta content="An immersive vr diagramming experience based using webxr version 0.4" name="description">
<meta content="width=device-width, initial-scale=1, height=device-height" name="viewport">
<link href="/styles.css" rel="stylesheet">
<link href="/assets/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png">

View File

@ -1,7 +1,7 @@
{
"name": "immersive",
"private": true,
"version": "0.0.3",
"version": "0.0.4",
"type": "module",
"engines": {
"node": ">=18.0.0"

View File

@ -71,7 +71,7 @@ export class Base {
//@TODO THis works, but it uses initGrip, not sure if this is the best idea
this.xrInputSource.onMotionControllerInitObservable.add(motionControllerObserver, -1, false, this);
this.controllers.controllerObserver.add((event) => {
this.controllers.controllerObservable.add((event) => {
logger.debug(event);
switch (event.type) {
case ControllerEventType.PULSE:

View File

@ -37,5 +37,5 @@ export enum ControllerEventType {
export class Controllers {
public movable: TransformNode | AbstractMesh;
public readonly controllerObserver: Observable<ControllerEvent> = new Observable();
public readonly controllerObservable: Observable<ControllerEvent> = new Observable();
}

View File

@ -46,7 +46,7 @@ export class Left extends Base {
init.components['xr-standard-thumbstick'].onButtonStateChangedObservable.add((value) => {
if (value.pressed) {
logger.trace('Left', 'thumbstick changed');
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.DECREASE_VELOCITY,
value: value.value
});
@ -63,7 +63,7 @@ export class Left extends Base {
.onButtonStateChangedObservable
.add((button) => {
logger.trace('trigger pressed');
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.TRIGGER,
value: button.value,
controller: this.xrInputSource
@ -77,7 +77,7 @@ export class Left extends Base {
xbutton.onButtonStateChangedObservable.add((button) => {
if (button.pressed) {
logger.trace('X button pressed');
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.X_BUTTON,
value: button.value
});
@ -91,7 +91,7 @@ export class Left extends Base {
ybutton.onButtonStateChangedObservable.add((button) => {
if (button.pressed) {
logger.trace('Y button pressed');
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.Y_BUTTON,
value: button.value
});
@ -115,7 +115,7 @@ export class Left extends Base {
private moveRig(value: { x: number, y: number }) {
if (Math.abs(value.x) > .1) {
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.LEFT_RIGHT,
value: value.x * this.speedFactor
});
@ -124,7 +124,7 @@ export class Left extends Base {
Base.stickVector.x = 0;
}
if (Math.abs(value.y) > .1) {
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.FORWARD_BACK,
value: value.y * this.speedFactor
});
@ -134,8 +134,8 @@ export class Left extends Base {
}
if (Base.stickVector.equals(Vector3.Zero())) {
this.controllers.controllerObserver.notifyObservers({type: ControllerEventType.LEFT_RIGHT, value: 0});
this.controllers.controllerObserver.notifyObservers({type: ControllerEventType.FORWARD_BACK, value: 0});
this.controllers.controllerObservable.notifyObservers({type: ControllerEventType.LEFT_RIGHT, value: 0});
this.controllers.controllerObservable.notifyObservers({type: ControllerEventType.FORWARD_BACK, value: 0});
} else {
}

View File

@ -24,7 +24,7 @@ export class Right extends Base {
bbutton.onButtonStateChangedObservable.add((button) => {
if (button.pressed) {
logger.debug('B Button Pressed');
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.B_BUTTON,
value: button.value
});
@ -67,7 +67,7 @@ export class Right extends Base {
.onButtonStateChangedObservable
.add((button) => {
logger.debug("right trigger pressed");
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.TRIGGER,
value: button.value,
controller: this.xrInputSource
@ -81,7 +81,7 @@ export class Right extends Base {
abutton.onButtonStateChangedObservable.add((value) => {
if (value.pressed) {
logger.debug('A button pressed');
this.controllers.controllerObserver.notifyObservers({type: ControllerEventType.MENU});
this.controllers.controllerObservable.notifyObservers({type: ControllerEventType.MENU});
}
});
}
@ -96,7 +96,7 @@ export class Right extends Base {
thumbstick.onButtonStateChangedObservable.add((value) => {
if (value.pressed) {
logger.trace('Right', `thumbstick changed ${value.value}`);
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.INCREASE_VELOCITY,
value: value.value
});
@ -107,22 +107,22 @@ export class Right extends Base {
private moveRig(value) {
if (Math.abs(value.x) > .1) {
this.controllers.controllerObserver.notifyObservers({type: ControllerEventType.TURN, value: value.x});
this.controllers.controllerObservable.notifyObservers({type: ControllerEventType.TURN, value: value.x});
} else {
this.controllers.controllerObserver.notifyObservers({type: ControllerEventType.TURN, value: 0});
this.controllers.controllerObservable.notifyObservers({type: ControllerEventType.TURN, value: 0});
}
if (Math.abs(value.y) > .1) {
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.UP_DOWN,
value: value.y * this.speedFactor
});
Base.stickVector.z = 1;
} else {
this.controllers.controllerObserver.notifyObservers({type: ControllerEventType.UP_DOWN, value: 0});
this.controllers.controllerObservable.notifyObservers({type: ControllerEventType.UP_DOWN, value: 0});
Base.stickVector.z = 0;
}
if (Base.stickVector.equals(Vector3.Zero())) {
this.controllers.controllerObserver.notifyObservers({type: ControllerEventType.UP_DOWN, value: 0});
this.controllers.controllerObservable.notifyObservers({type: ControllerEventType.UP_DOWN, value: 0});
}
}
}

View File

@ -113,7 +113,7 @@ export class Rigplatform {
private registerObserver() {
if (!this.registered) {
this.registered = true;
this.controllers.controllerObserver.add((event: ControllerEvent) => {
this.controllers.controllerObservable.add((event: ControllerEvent) => {
logger.debug(event);
switch (event.type) {
case ControllerEventType.INCREASE_VELOCITY:

View File

@ -92,7 +92,7 @@ export class WebController {
case " ":
if (kbInfo.event.ctrlKey) {
if (this.controllers) {
this.controllers.controllerObserver.notifyObservers(
this.controllers.controllerObservable.notifyObservers(
{type: ControllerEventType.X_BUTTON, value: 1}
)
}

View File

@ -33,7 +33,7 @@ export class DiagramManager {
this._scene = DefaultScene.Scene;
this._config = new AppConfig();
this._controllers = new Controllers();
this._diagramMenuManager = new DiagramMenuManager(this.onDiagramEventObservable, this._controllers);
this._diagramMenuManager = new DiagramMenuManager(this.onDiagramEventObservable, this._controllers, this._config);
this._diagramEntityActionManager = buildEntityActionManager(this._controllers);
this.onDiagramEventObservable.add(this.onDiagramEvent, DiagramEventObserverMask.FROM_DB, true, this);
logger.debug("DiagramManager constructed");

View File

@ -1,28 +1,34 @@
import {DiagramEvent, DiagramEventType} from "./types/diagramEntity";
import {AbstractMesh, ActionEvent, Observable, Scene, TransformNode} from "@babylonjs/core";
import {AbstractMesh, ActionEvent, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core";
import {DiagramEventObserverMask} from "./diagramManager";
import {InputTextView} from "../information/inputTextView";
import {toDiagramEntity} from "./functions/toDiagramEntity";
import {DefaultScene} from "../defaultScene";
import {Controllers} from "../controllers/controllers";
import {ControllerEvent, ControllerEventType, Controllers} from "../controllers/controllers";
import log from "loglevel";
import {Toolbox} from "../toolbox/toolbox";
import {ScaleMenu} from "../menus/scaleMenu";
import {ClickMenu} from "../menus/clickMenu";
import {ConfigMenu} from "../menus/configMenu";
import {AppConfig} from "../util/appConfig";
const logger = log.getLogger('DiagramMenuManager');
export class DiagramMenuManager {
public readonly toolbox: Toolbox;
public readonly scaleMenu: ScaleMenu;
public readonly configMenu: ConfigMenu;
private readonly _notifier: Observable<DiagramEvent>;
private readonly _inputTextView: InputTextView;
private readonly _scene: Scene;
constructor(notifier: Observable<DiagramEvent>, controllers: Controllers) {
constructor(notifier: Observable<DiagramEvent>, controllers: Controllers, config: AppConfig) {
this._scene = DefaultScene.Scene;
this._notifier = notifier;
this._inputTextView = new InputTextView(controllers);
this.configMenu = new ConfigMenu(config);
this._inputTextView.onTextObservable.add((evt) => {
const mesh = this._scene.getMeshById(evt.id);
if (mesh) {
@ -41,6 +47,33 @@ export class DiagramMenuManager {
position.y = mesh.getBoundingInfo().boundingBox.maximumWorld.y + .1;
this.scaleMenu.changePosition(position);
});
controllers.controllerObservable.add((event: ControllerEvent) => {
if (event.type == ControllerEventType.B_BUTTON) {
if (event.value > .8) {
const platform = this._scene.getMeshByName("platform");
if (!platform) {
return;
}
const cameraPos = this._scene.activeCamera.globalPosition;
const localCamera = Vector3.TransformCoordinates(cameraPos, platform.getWorldMatrix());
const toolY = this.toolbox.handleMesh.absolutePosition.y;
if (toolY > (cameraPos.y - .2)) {
this.toolbox.handleMesh.position.y = localCamera.y - .2;
}
const inputY = this._inputTextView.handleMesh.absolutePosition.y;
if (inputY > (cameraPos.y - .2)) {
this._inputTextView.handleMesh.position.y = localCamera.y - .2;
}
const configY = this._inputTextView.handleMesh.absolutePosition.y;
if (configY > (cameraPos.y - .2)) {
this.configMenu.handleMesh.position.y = localCamera.y - .2;
}
}
}
});
}
public editText(mesh: AbstractMesh) {

View File

@ -10,7 +10,7 @@ export function buildEntityActionManager(controllers: Controllers) {
new PlaySoundAction(ActionManager.OnPointerOverTrigger, sounds.tick));*/
actionManager.registerAction(
new ExecuteCodeAction(ActionManager.OnPointerOverTrigger, (evt) => {
controllers.controllerObserver.notifyObservers({
controllers.controllerObservable.notifyObservers({
type: ControllerEventType.PULSE,
gripId: evt?.additionalData?.pickResult?.gripTransform?.id
})

View File

@ -25,9 +25,10 @@ export class InputTextView {
constructor(controllers: Controllers) {
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.inputMesh.position.y = .06;
this.inputMesh.position.z = .02;
this.createKeyboard();
}
@ -44,9 +45,18 @@ export class InputTextView {
logger.debug(mesh.metadata);
}
public get handleMesh(): AbstractMesh {
return this.handle.mesh;
}
private hide() {
this.handle.mesh.setEnabled(false);
this.diagramMesh = null;
}
public createKeyboard() {
const platform = this.scene.getMeshById('platform');
const position = new Vector3(0, 1.66, .5);
const position = new Vector3(0, 1.66, .53);
const rotation = new Vector3(.9, 0, 0);
const handle = this.handle;
/*if (handle.mesh.position.x != 0 && handle.mesh.position.y != 0 && handle.mesh.position.z != 0) {
@ -107,7 +117,7 @@ export class InputTextView {
logger.debug(eventData);
const gripId = eventState?.userInfo?.pickInfo?.gripTransform?.id;
if (gripId) {
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.PULSE,
gripId: gripId
});
@ -134,9 +144,4 @@ export class InputTextView {
this.keyboard = keyboard;
this.handle.mesh.setEnabled(false);
}
private hide() {
this.handle.mesh.setEnabled(false);
this.diagramMesh = null;
}
}

View File

@ -1,13 +1,13 @@
import {AdvancedDynamicTexture, CheckboxGroup, RadioGroup, SelectionPanel, StackPanel} from "@babylonjs/gui";
import {MeshBuilder, TransformNode, Vector3, WebXRDefaultExperience} from "@babylonjs/core";
import {AbstractMesh, MeshBuilder, Scene, TransformNode, Vector3} from "@babylonjs/core";
import {AppConfig} from "../util/appConfig";
import {Controllers} from "../controllers/controllers";
import {AbstractMenu} from "./abstractMenu";
import log from "loglevel";
import {DefaultScene} from "../defaultScene";
import {Handle} from "../objects/handle";
const logger = log.getLogger('ConfigMenu');
export class ConfigMenu extends AbstractMenu {
export class ConfigMenu {
private config: AppConfig;
private readonly baseTransform: TransformNode;
private gridSnaps: Array<{ label: string, value: number }> = [
@ -17,54 +17,26 @@ export class ConfigMenu extends AbstractMenu {
{label: "0.5", value: 0.5},
{label: "1", value: 1},
]
private _handle: Handle;
private rotationSnaps: Array<{ label: string, value: number }> = [
{label: "Off", value: 0},
{label: "22.5", value: 22.5},
{label: "45", value: 45},
{label: "90", value: 90},
]
private readonly _scene: Scene;
constructor(xr: WebXRDefaultExperience, controllers: Controllers, config: AppConfig) {
super(xr, controllers);
this.baseTransform = new TransformNode("configMenuBase", this.scene);
constructor(config: AppConfig) {
this._scene = DefaultScene.Scene;
this.baseTransform = new TransformNode("configMenuBase", this._scene);
this._handle = new Handle(this.baseTransform);
this.config = config;
this.buildMenu();
}
private buildMenu() {
const configPlane = MeshBuilder
.CreatePlane("configMenuPlane",
{
width: .6,
height: .3
}, this.scene);
configPlane.setParent(this.baseTransform);
this.createHandle(this.baseTransform, new Vector3(1, 1.6, .5), new Vector3(Math.PI / 4, Math.PI / 4, 0));
configPlane.position.y = .2;
const configTexture = AdvancedDynamicTexture.CreateForMesh(configPlane, 2048, 1024);
const columnPanel = new StackPanel('columns');
columnPanel.isVertical = false;
columnPanel.fontSize = "48px";
configTexture.addControl(columnPanel);
const selectionPanel1 = new SelectionPanel("selectionPanel1");
selectionPanel1.width = "500px";
columnPanel.addControl(selectionPanel1);
this.buildGridSizeControl(selectionPanel1);
this.buildCreateScaleControl(selectionPanel1);
const selectionPanel2 = new SelectionPanel("selectionPanel2");
selectionPanel2.width = "500px";
columnPanel.addControl(selectionPanel2);
this.buildRotationSnapControl(selectionPanel2);
this.buildTurnSnapControl(selectionPanel2);
const selectionPanel3 = new SelectionPanel("selectionPanel3");
selectionPanel3.width = "768px";
columnPanel.addControl(selectionPanel3);
this.buildFlyModeControl(selectionPanel3);
this.baseTransform.parent.setEnabled(true);
public get handleMesh(): AbstractMesh {
return this._handle.mesh;
}
private adjustRadio(radio: RadioGroup | CheckboxGroup) {
@ -157,4 +129,66 @@ export class ConfigMenu extends AbstractMenu {
this.config.setGridSnap(this.gridSnaps[value].value);
}
private buildMenu() {
const configPlane = MeshBuilder
.CreatePlane("configMenuPlane",
{
width: .6,
height: .3
}, this._scene);
configPlane.parent = this.baseTransform;
//this.createHandle(this.baseTransform, new Vector3(1, 1.6, .5), new Vector3(Math.PI / 4, Math.PI / 4, 0));
configPlane.position.y = .2;
const configTexture = AdvancedDynamicTexture.CreateForMesh(configPlane, 2048, 1024);
const columnPanel = new StackPanel('columns');
columnPanel.isVertical = false;
columnPanel.fontSize = "48px";
configTexture.addControl(columnPanel);
const selectionPanel1 = new SelectionPanel("selectionPanel1");
selectionPanel1.width = "500px";
columnPanel.addControl(selectionPanel1);
this.buildGridSizeControl(selectionPanel1);
this.buildCreateScaleControl(selectionPanel1);
const selectionPanel2 = new SelectionPanel("selectionPanel2");
selectionPanel2.width = "500px";
columnPanel.addControl(selectionPanel2);
this.buildRotationSnapControl(selectionPanel2);
this.buildTurnSnapControl(selectionPanel2);
const selectionPanel3 = new SelectionPanel("selectionPanel3");
selectionPanel3.width = "768px";
columnPanel.addControl(selectionPanel3);
this.buildFlyModeControl(selectionPanel3);
const offset = new Vector3(.50, 1.6, .38);
const rotation = new Vector3(.5, .6, 0);
const platform = this._scene.getMeshById('platform');
if (platform) {
this._handle.mesh.parent = platform;
if (!this._handle.idStored) {
this._handle.mesh.position = offset;
this._handle.mesh.rotation = rotation;
}
} else {
const handler = this._scene.onNewMeshAddedObservable.add((mesh) => {
if (mesh && mesh.id == 'platform') {
this._handle.mesh.parent = mesh;
if (!this._handle.idStored) {
this._handle.mesh.position = offset;
this._handle.mesh.rotation = rotation;
}
//this._scene.onNewMeshAddedObservable.remove(handler);
}
}, -1, true, this);
}
this.baseTransform.parent.setEnabled(true);
}
}

View File

@ -28,7 +28,7 @@ export class Ball {
public setControllers(controllers: Controllers) {
this.controllers = controllers;
this.controllers.controllerObserver.add((event) => {
this.controllers.controllerObservable.add((event) => {
if (event.type == ControllerEventType.MOTION) {
}

View File

@ -28,7 +28,7 @@ export class SoccerMenu extends AbstractMenu {
this.field = new Field(this.scene);
this.manager = new GUI3DManager(this.scene);
this.controllers.controllerObserver.add(this.controllerEventHandler, -1, false, this);
this.controllers.controllerObservable.add(this.controllerEventHandler, -1, false, this);
this.buildMenu();
}
@ -100,7 +100,7 @@ export class SoccerMenu extends AbstractMenu {
//end.applyRotationQuaternionInPlace(Quaternion.FromEulerAngles(0, e.y, 0));
if (this.startTime && this.startPosition) {
const duration = new Date().getTime() - this.startTime;
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.MOTION,
startPosition: this.startPosition,
endPosition: end,
@ -127,7 +127,7 @@ export class SoccerMenu extends AbstractMenu {
}, 1500);
if (pickInfo?.pickedMesh?.name == 'Football Ball.001') {
this.controllers.controllerObserver.notifyObservers({
this.controllers.controllerObservable.notifyObservers({
type: ControllerEventType.GAZEPOINT,
endPosition: pickInfo.pickedPoint,
startPosition: this.xr.baseExperience.camera.globalPosition

View File

@ -98,13 +98,6 @@ export class Toolbox {
if (platform) {
const handle = this.handle;
handle.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 (!handle.idStored) {
handle.mesh.position = offset;
handle.mesh.rotation = rotation;
@ -130,5 +123,9 @@ export class Toolbox {
/*setMenuPosition(this.toolboxBaseNode.parent as Mesh, this.scene,
Vector3.Zero());*/
}
public get handleMesh(): AbstractMesh {
return this.handle.mesh;
}
}

View File

@ -1,7 +1,6 @@
import {AbstractMesh, WebXRDefaultExperience, WebXRMotionControllerManager, WebXRState} from "@babylonjs/core";
import log from "loglevel";
import {WebController} from "../../controllers/webController";
import {ConfigMenu} from "../../menus/configMenu";
import {Rigplatform} from "../../controllers/rigplatform";
import {DiagramManager} from "../../diagram/diagramManager";
import {Spinner} from "../../objects/spinner";
@ -76,7 +75,7 @@ export async function groundMeshObserver(ground: AbstractMesh,
rig.flyMode = config.flyMode;
rig.turnSnap = config.turnSnap;
}, -1, false, this);
const config = new ConfigMenu(xr, diagramManager.controllers, diagramManager.config);
const webController = new WebController(ground.getScene(), rig, diagramManager, diagramManager.controllers);
}