updated menu placement and handle class.

This commit is contained in:
Michael Mainguy 2024-04-15 16:14:19 -05:00
parent 888e858578
commit 8cf12bc91c
18 changed files with 162 additions and 230 deletions

View File

@ -1,6 +1,5 @@
import {
AbstractMesh,
HavokPlugin,
Mesh,
PhysicsMotionType,
Scene,
@ -18,10 +17,15 @@ import {setupTransformNode} from "./functions/setupTransformNode";
import {reparent} from "./functions/reparent";
import {snapGridVal} from "../util/functions/snapGridVal";
import {snapRotateVal} from "../util/functions/snapRotateVal";
import {grabAndClone} from "./functions/grab";
import {grabAndClone} from "./functions/grabAndClone";
import {isDiagramEntity} from "../diagram/functions/isDiagramEntity";
import {ClickMenu} from "../menus/clickMenu";
import {displayDebug} from "../util/displayDebug";
import {beforeRenderObserver} from "./functions/beforeRenderObserver";
import {motionControllerObserver} from "./functions/motionControllerObserver";
import {handleWasGrabbed} from "./functions/handleWasGrabbed";
import {buildDrop} from "./functions/buildDrop";
import {pointable} from "./functions/pointable";
const CLICK_TIME = 300;
export class Base {
@ -53,37 +57,8 @@ export class Base {
this.scene = diagramManager.scene;
this.xr = xr;
this.diagramManager = diagramManager;
this.scene.onBeforeRenderObservable.add(() => {
if (this?.grabbedMesh?.physicsBody) {
const hk = (this.scene.getPhysicsEngine().getPhysicsPlugin() as HavokPlugin);
this.lastPosition = this?.grabbedMesh?.physicsBody?.transformNode.absolutePosition.clone();
if (this.grabbedMeshParentId) {
const parent = this.scene.getTransformNodeById(this.grabbedMeshParentId);
if (parent) {
hk.setPhysicsBodyTransformation(this.grabbedMesh.physicsBody, parent);
hk.sync(this.grabbedMesh.physicsBody);
} else {
this.logger.error("parent not found for " + this.grabbedMeshParentId);
}
} else {
this.logger.warn("no parent id");
}
}
}, -1, false, this);
this.controller.onMotionControllerInitObservable.add((init) => {
this.logger.debug(init.components);
if (init.components['xr-standard-squeeze']) {
this.initGrip(init.components['xr-standard-squeeze'])
}
if (init.components['xr-standard-trigger']) {
this.initClicker(init.components['xr-standard-trigger']);
}
}, -1, false, this);
this.scene.onBeforeRenderObservable.add(beforeRenderObserver, -1, false, this);
this.controller.onMotionControllerInitObservable.add(motionControllerObserver, -1, false, this);
this.controllers.controllerObserver.add((event) => {
this.logger.debug(event);
switch (event.type) {
@ -152,7 +127,7 @@ export class Base {
let player = false;
displayDebug(mesh);
if (!isDiagramEntity(mesh)) {
if (this.handleWasGrabbed(mesh)) {
if (handleWasGrabbed(mesh)) {
mesh && mesh.setParent(this.controller.motionController.rootMesh);
this.grabbedMesh = mesh;
} else {
@ -165,13 +140,13 @@ export class Base {
} else {
return;
}
}
} else {
if (mesh?.metadata?.template == '#connection-template') {
return;
}
}
this.previousParentId = mesh?.parent?.id;
this.logger.warn("grabbed " + mesh?.id + " parent " + this.previousParentId);
this.previousRotation = mesh?.rotation.clone();
@ -203,20 +178,12 @@ export class Base {
}
}
private handleWasGrabbed(mesh: AbstractMesh): boolean {
if (isDiagramEntity(mesh)) {
return false;
} else {
return (mesh?.metadata?.handle == true);
}
}
private drop() {
const mesh = this.grabbedMesh;
if (!mesh) {
return;
}
if (this.handleWasGrabbed(mesh)) {
if (handleWasGrabbed(mesh)) {
mesh.setParent(this.scene.getMeshByName("platform"));
const location = {
position: {x: mesh.position.x, y: mesh.position.y, z: mesh.position.z},
@ -239,12 +206,7 @@ export class Base {
if (isDiagramEntity(mesh) && (mesh?.metadata?.template.indexOf('#') == -1)) {
return;
}
const entity = toDiagramEntity(mesh);
const event: DiagramEvent = {
type: DiagramEventType.DROP,
entity: entity
}
const event: DiagramEvent = buildDrop(mesh);
const body = mesh?.physicsBody;
if (body) {
@ -294,7 +256,3 @@ export class Base {
});
}
}
function pointable(mesh: AbstractMesh): boolean {
return (mesh && mesh.metadata?.template && !mesh.metadata?.tool && !mesh.metadata?.handle && !mesh.metadata?.grabbable && !mesh.metadata?.grabClone);
}

View File

@ -0,0 +1,21 @@
import {HavokPlugin} from "@babylonjs/core";
export function beforeRenderObserver() {
if (this?.grabbedMesh?.physicsBody) {
const hk = (this.scene.getPhysicsEngine().getPhysicsPlugin() as HavokPlugin);
this.lastPosition = this?.grabbedMesh?.physicsBody?.transformNode.absolutePosition.clone();
if (this.grabbedMeshParentId) {
const parent = this.scene.getTransformNodeById(this.grabbedMeshParentId);
if (parent) {
hk.setPhysicsBodyTransformation(this.grabbedMesh.physicsBody, parent);
hk.sync(this.grabbedMesh.physicsBody);
} else {
this.logger.error("parent not found for " + this.grabbedMeshParentId);
}
} else {
this.logger.warn("no parent id");
}
}
}

View File

@ -0,0 +1,12 @@
import {DiagramEvent, DiagramEventType} from "../../diagram/types/diagramEntity";
import {toDiagramEntity} from "../../diagram/functions/toDiagramEntity";
import {AbstractMesh} from "@babylonjs/core";
export function buildDrop(mesh: AbstractMesh): DiagramEvent {
const entity = toDiagramEntity(mesh);
return {
type: DiagramEventType.DROP,
entity: entity
}
}

View File

@ -0,0 +1,10 @@
import {AbstractMesh} from "@babylonjs/core";
import {isDiagramEntity} from "../../diagram/functions/isDiagramEntity";
export function handleWasGrabbed(mesh: AbstractMesh): boolean {
if (isDiagramEntity(mesh)) {
return false;
} else {
return (mesh?.metadata?.handle == true);
}
}

View File

@ -0,0 +1,9 @@
export function motionControllerObserver(init) {
this.logger.debug(init.components);
if (init.components['xr-standard-squeeze']) {
this.initGrip(init.components['xr-standard-squeeze'])
}
if (init.components['xr-standard-trigger']) {
this.initClicker(init.components['xr-standard-trigger']);
}
}

View File

@ -0,0 +1,9 @@
import {AbstractMesh} from "@babylonjs/core";
export function pointable(mesh: AbstractMesh): boolean {
return (mesh && mesh.metadata?.template &&
!mesh.metadata?.tool &&
!mesh.metadata?.handle &&
!mesh.metadata?.grabbable &&
!mesh.metadata?.grabClone);
}

View File

@ -159,12 +159,12 @@ export class Rigplatform {
switch (source.inputSource.handedness) {
case RIGHT:
if (!this.rightController) {
this.rightController = new Right(source, this.xr, this.diagramManager, this.controllers);
this.rightController = new Right(source, this.xr, this.diagramManager);
}
break;
case LEFT:
if (!this.leftController) {
this.leftController = new Left(source, this.xr, this.diagramManager, this.controllers);
this.leftController = new Left(source, this.xr, this.diagramManager);
}
break;
}

View File

@ -1,6 +1,5 @@
import {Scene, TransformNode, Vector3, WebXRDefaultExperience} from "@babylonjs/core";
import {Controllers} from "../controllers/controllers";
import {Button3D, TextBlock} from "@babylonjs/gui";
import {Handle} from "../objects/handle";
export abstract class AbstractMenu {
@ -15,25 +14,10 @@ export abstract class AbstractMenu {
this.controllers = controllers;
}
protected makeButton(name: string, id: string) {
const button = new Button3D(name);
button.scaling = new Vector3(.1, .1, .1);
button.name = id;
const text = new TextBlock(name, name);
text.fontSize = "48px";
text.color = "#ffffff";
text.alpha = 1;
button.content = text;
return button;
protected createHandle(mesh: TransformNode, offset: Vector3, rotation: Vector3) {
this.handle = new Handle(mesh, offset, rotation);
}
protected createHandle(mesh: TransformNode) {
this.handle = new Handle(mesh);
}
public toggle() {
throw new Error("AbstractMenu.toggle() not implemented");
}
}

View File

@ -1,10 +1,11 @@
import {Button3D, GUI3DManager, PlanePanel, TextBlock} from "@babylonjs/gui";
import {GUI3DManager, PlanePanel} from "@babylonjs/gui";
import {AbstractMesh, Tools, TransformNode, Vector3} from "@babylonjs/core";
import {DiagramEvent, DiagramEventType} from "../diagram/types/diagramEntity";
import {toDiagramEntity} from "../diagram/functions/toDiagramEntity";
import {DiagramManager} from "../diagram/diagramManager";
import {DiagramConnection} from "../diagram/diagramConnection";
import {isDiagramEntity} from "../diagram/functions/isDiagramEntity";
import {makeButton} from "./functions/makeButton";
export class ClickMenu {
private static readonly sounds;
@ -34,10 +35,12 @@ export class ClickMenu {
panel.margin = .1;
manager.addControl(panel);
panel.linkToTransformNode(transform);
panel.addControl(this.makeButton("Remove", "remove", grip));
panel.addControl(this.makeButton("Label", "label", grip));
panel.addControl(this.makeButton("Connect", "connect", grip));
panel.addControl(this.makeButton("Close", "close", grip));
manager.controlScaling = .1;
panel.updateLayout();
this.transform = transform;
@ -72,14 +75,7 @@ export class ClickMenu {
}
private makeButton(name: string, id: string, grip: TransformNode) {
const button = new Button3D(name);
button.scaling = new Vector3(.1, .1, .1);
button.name = id;
const text = new TextBlock(name, name);
text.fontSize = "48px";
text.color = "#ffffee";
text.alpha = 1;
button.content = text;
const button = makeButton(id, name);
button.onPointerClickObservable.add(() => {
switch (id) {
case "close":
@ -100,7 +96,6 @@ export class ClickMenu {
break;
case "connect":
this.createMeshConnection(this.entity, grip);
}
}, -1, false, this, true);
return button;
@ -114,6 +109,5 @@ export class ClickMenu {
this.manager.onPickingObservable.clear();
this.manager.dispose();
this.transform.dispose();
}
}

View File

@ -1,10 +1,9 @@
import {AdvancedDynamicTexture, CheckboxGroup, RadioGroup, SelectionPanel, StackPanel} from "@babylonjs/gui";
import {MeshBuilder, Scene, TransformNode, Vector3, WebXRDefaultExperience} from "@babylonjs/core";
import {AppConfig} from "../util/appConfig";
import {ControllerEventType, Controllers} from "../controllers/controllers";
import {Controllers} from "../controllers/controllers";
import {DiaSounds} from "../util/diaSounds";
import {AbstractMenu} from "./abstractMenu";
import {setMenuPosition} from "../util/functions/setMenuPosition";
import log from "loglevel";
const logger = log.getLogger('ConfigMenu');
@ -32,30 +31,8 @@ export class ConfigMenu extends AbstractMenu {
super(scene, xr, controllers);
this.baseTransform = new TransformNode("configMenuBase", scene);
this.config = config;
this.sounds = new DiaSounds(scene);
this.controllers.controllerObserver.add((event) => {
if (event.type == ControllerEventType.Y_BUTTON) {
logger.debug('Y Button Pressed');
this.toggle();
}
});
this.buildMenu();
}
public toggle() {
if (this.baseTransform.parent.isEnabled()) {
this.sounds.exit.play();
this.baseTransform.parent.setEnabled(false);
} else {
this.sounds.enter.play();
this.baseTransform.parent.setEnabled(true);
}
setMenuPosition(this.handle.mesh, this.scene, new Vector3(.6, .1, 0));
}
private buildMenu() {
const configPlane = MeshBuilder
.CreatePlane("configMenuPlane",
@ -63,23 +40,17 @@ export class ConfigMenu extends AbstractMenu {
width: .6,
height: .3
}, this.scene);
configPlane.rotation.y = Math.PI;
configPlane.setParent(this.baseTransform);
this.createHandle(this.baseTransform);
this.baseTransform.position.set(0, .2, 0);
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);
//configTexture.background = "#00ffff";
const columnPanel = new StackPanel('columns');
columnPanel.isVertical = false;
//columnPanel.width = 1;
columnPanel.fontSize = "48px";
//columnPanel.background = "#ff0000";
//
configTexture.addControl(columnPanel);
const selectionPanel1 = new SelectionPanel("selectionPanel1");
selectionPanel1.width = "500px";
//selectionPanel1.width = .3;
columnPanel.addControl(selectionPanel1);
this.buildGridSizeControl(selectionPanel1);
this.buildCreateScaleControl(selectionPanel1);
@ -94,12 +65,8 @@ export class ConfigMenu extends AbstractMenu {
const selectionPanel3 = new SelectionPanel("selectionPanel3");
selectionPanel3.width = "768px";
columnPanel.addControl(selectionPanel3);
this.buildFlyModeControl(selectionPanel3);
setMenuPosition(this.handle.mesh, this.scene, new Vector3(.6, .1, 0));
this.baseTransform.parent.setEnabled(false);
this.baseTransform.parent.setEnabled(true);
}
private adjustRadio(radio: RadioGroup | CheckboxGroup) {

View File

@ -0,0 +1,14 @@
import {Button3D, TextBlock} from "@babylonjs/gui";
import {Vector3} from "@babylonjs/core";
export function makeButton(id: string, name: string): Button3D {
const button = new Button3D(name);
button.scaling = new Vector3(.1, .1, .1);
button.name = id;
const text = new TextBlock(name, name);
text.fontSize = "48px";
text.color = "#ffffee";
text.alpha = 1;
button.content = text;
return button;
}

View File

@ -2,7 +2,7 @@ import {AbstractMesh, MeshBuilder, Scene, WebXRDefaultExperience} from "@babylon
import {Controllers} from "../controllers/controllers";
import {AbstractMenu} from "./abstractMenu";
import {AdvancedDynamicTexture, Grid, TextBlock} from "@babylonjs/gui";
import {setMenuPosition} from "../util/functions/setMenuPosition";
export class IntegrationMenu extends AbstractMenu {
private plane: AbstractMesh = null;
@ -28,6 +28,6 @@ export class IntegrationMenu extends AbstractMenu {
grid.addControl(labelText1, 0, 0);
const labelText2 = new TextBlock("labelText1", "New Relic Account");
grid.addControl(labelText2, 1, 0);
setMenuPosition(this.plane, this.scene);
}
}

View File

@ -1,6 +1,7 @@
import {AbstractMenu} from "./abstractMenu";
import {Scene, WebXRDefaultExperience} from "@babylonjs/core";
import {Controllers} from "../controllers/controllers";
import {makeButton} from "./functions/makeButton";
//a class called SequenceMenu that extends AbstraceMenu and has three buttons labeled '1', '2', and '3'
export class SequenceMenu extends AbstractMenu {
@ -10,9 +11,9 @@ export class SequenceMenu extends AbstractMenu {
}
private buildMenu() {
const button1 = this.makeButton("1", "1");
const button2 = this.makeButton("2", "1");
const button3 = this.makeButton("3", "1");
const button1 = makeButton("1", "1");
const button2 = makeButton("2", "1");
const button3 = makeButton("3", "1");
}
}

View File

@ -3,11 +3,15 @@ import {buildStandardMaterial} from "../materials/functions/buildStandardMateria
export class Handle {
public mesh: AbstractMesh;
private readonly transformNode: TransformNode;
private readonly menuItem: TransformNode;
private _isStored: boolean = false;
private offset: Vector3;
private rotation: Vector3;
constructor(mesh: TransformNode) {
this.transformNode = mesh;
constructor(mesh: TransformNode, offset: Vector3 = Vector3.Zero(), rotation: Vector3 = Vector3.Zero()) {
this.menuItem = mesh;
this.offset = offset;
this.rotation = rotation;
this.buildHandle();
}
@ -15,10 +19,10 @@ export class Handle {
return this._isStored;
}
private buildHandle() {
const scene: Scene = this.transformNode.getScene();
const handle = getHandleMesh("handle-" + this.transformNode.id + "-mesh", scene);
if (this.transformNode) {
this.transformNode.setParent(handle);
const scene: Scene = this.menuItem.getScene();
const handle = getHandleMesh("handle-" + this.menuItem.id + "-mesh", scene);
if (this.menuItem) {
this.menuItem.setParent(handle);
}
const stored = localStorage.getItem(handle.id);
if (stored) {
@ -32,12 +36,48 @@ export class Handle {
handle.position = Vector3.Zero();
}
} else {
handle.position = Vector3.Zero();
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");

View File

@ -4,6 +4,7 @@ import {ControllerEvent, ControllerEventType, Controllers} from "../controllers/
import {Control3D, GUI3DManager, PlanePanel, Slider3D} from "@babylonjs/gui";
import log, {Logger} from "loglevel";
import {Field} from "./field";
import {makeButton} from "../menus/functions/makeButton";
enum SoccerMenuState {
@ -160,7 +161,7 @@ export class SoccerMenu extends AbstractMenu {
}
makeButton(name: string, id: string) {
const button = super.makeButton(name, id);
const button = makeButton(name, id);
button.onPointerClickObservable.add(this.handleClick, -1, false, this);
return button;
}
@ -169,10 +170,10 @@ export class SoccerMenu extends AbstractMenu {
const panel = new PlanePanel();
this.manager.addControl(panel);
panel.columns = 10;
panel.addControl(this.makeButton("Play", "play"));
panel.addControl(this.makeButton("Plan", "plan"));
panel.addControl(this.makeButton("Train", "Train"));
panel.addControl(this.makeButton("Modify", "modify"));
panel.addControl(makeButton("Play", "play"));
panel.addControl(makeButton("Plan", "plan"));
panel.addControl(makeButton("Train", "Train"));
panel.addControl(makeButton("Modify", "modify"));
const slider = panel.addControl(this.makeSlider("force"));
this.manager.rootContainer.children[0].node.position.y = .2;

View File

@ -34,11 +34,6 @@ export class Toolbox {
this.toolboxBaseNode = new TransformNode("toolbox", this.scene);
this.handle = new Handle(this.toolboxBaseNode);
this.toolboxBaseNode.position.y = .2;
//this.toolboxBaseNode.position.z = .05;
/*this.axes = new AxesViewer(this.scene);
this.axes.xAxis.parent = this.toolboxBaseNode;
this.axes.yAxis.parent = this.toolboxBaseNode;
this.axes.zAxis.parent = this.toolboxBaseNode; */
this.toolboxBaseNode.scaling = new Vector3(0.6, 0.6, 0.6);
this.buildToolbox();
}
@ -92,7 +87,7 @@ export class Toolbox {
}
}
//this.toolboxBaseNode.parent.setEnabled(false);
const offset = new Vector3(-.50, 1.6, .38);
const rotation = new Vector3(.5, -.6, .18);

View File

@ -1,83 +0,0 @@
import {Scene, TransformNode, Vector3, WebXRCamera} from "@babylonjs/core";
import log from "loglevel";
const logger = log.getLogger('setMenuPosition');
export function setMenuPosition(node: TransformNode, scene: Scene, offset: Vector3 = Vector3.Zero()) {
/*
scene.onActiveCameraChanged.add(() => {
if (scene.activeCamera) {
switch (scene.activeCamera.getClassName()) {
case "WebXRCamera":
node.parent = null;
const front = getFrontPosition(.8, scene);
//front.y = scene.activeCamera.globalPosition.y;
node.position = front;
node.position.addInPlace(offset);
node.position.y -= .5;
node.lookAt(scene.activeCamera.globalPosition);
node.rotation.y = node.rotation.y + Math.PI;
break;
case "FreeCamera":
case "DeviceOrientationCamera":
case "ArcRotateCamera":
case "UniversalCamera":
node.parent = scene.activeCamera;
const width = scene.getEngine().getRenderWidth();
const height = scene.getEngine().getRenderHeight();
node.position.z = 3;
node.position.y = -.8;
break;
}
}
});
*/
if (scene.activeCamera) {
//setPosition(node, scene, offset);
} else {
scene.onActiveCameraChanged.add((scene: Scene) => {
//setPosition(node, scene, offset);
});
logger.error("No active camera");
}
}
const debug = false;
function setPosition(node: TransformNode, scene: Scene, offset: Vector3 = Vector3.Zero()) {
const platform = scene.getNodeById("platform");
switch (scene.activeCamera.getClassName()) {
case "WebXRCamera":
//const oldParent = node.parent;
window.setTimeout(() => {
node.setParent(null);
const camera = scene.activeCamera as WebXRCamera;
const front = camera.getFrontPosition(.7);
const camPos = camera.globalPosition.clone();
const newPos = new Vector3(front.x + offset.x, front.y + offset.y - .3, front.z + offset.z);
node.position = newPos;
node.lookAt(camPos);
node.setParent(platform);
}, 1000);
break;
case "FreeCamera":
case "DeviceOrientationCamera":
case "ArcRotateCamera":
case "UniversalCamera":
node.parent = scene.activeCamera;
const width = scene.getEngine().getRenderWidth();
const height = scene.getEngine().getRenderHeight();
node.position.z = 2;
node.position.y = -.8 + offset.y;
node.position.x = offset.x;
break;
}
logger.debug('menu position set');
}