import { AbstractMesh, HavokPlugin, Mesh, PhysicsMotionType, Scene, Vector3, WebXRControllerComponent, WebXRDefaultExperience, WebXRInputSource } from "@babylonjs/core"; import {DiagramManager} from "../diagram/diagramManager"; import {DiagramEvent, DiagramEventType} from "../diagram/diagramEntity"; import log from "loglevel"; import {ControllerEventType, Controllers} from "./controllers"; import {toDiagramEntity} from "../diagram/functions/toDiagramEntity"; 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"; export class Base { static stickVector = Vector3.Zero(); protected controller: WebXRInputSource; protected speedFactor = 4; protected readonly scene: Scene; protected grabbedMesh: AbstractMesh = null; protected grabbedMeshParentId: string = null; protected previousParentId: string = null; protected previousRotation: Vector3 = null; protected previousScaling: Vector3 = null; protected previousPosition: Vector3 = null; protected readonly xr: WebXRDefaultExperience; protected readonly diagramManager: DiagramManager; private logger: log.Logger; private lastPosition: Vector3 = null; protected controllers: Controllers; constructor(controller: WebXRInputSource, scene: Scene, xr: WebXRDefaultExperience, controllers: Controllers, diagramManager: DiagramManager) { this.logger = log.getLogger('Base'); this.controller = controller; this.controllers = controllers; this.scene = scene; 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.xr = xr; this.diagramManager = diagramManager; this.controller.onMotionControllerInitObservable.add((init) => { if (init.components['xr-standard-squeeze']) { this.initGrip(init.components['xr-standard-squeeze']) } }, -1, false, this); this.controllers.controllerObserver.add((event) => { this.logger.debug(event); switch (event.type) { case ControllerEventType.PULSE: if (event.gripId == this?.controller?.grip?.id) { this.controller?.motionController?.pulse(.25, 30) .then(() => { this.logger.debug("pulse done"); }); } ; break; case ControllerEventType.HIDE: this.disable(); break; case ControllerEventType.SHOW: this.enable(); break; } }); } public disable() { this.scene.preventDefaultOnPointerDown = true; this.controller.motionController.rootMesh.setEnabled(false) this.controller.pointer.setEnabled(false); } public enable() { this.scene.preventDefaultOnPointerDown = false; this.controller.motionController.rootMesh.setEnabled(true); this.controller.pointer.setEnabled(true) } private grab() { let mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId); if (!mesh) { return; } let player = false; const template = mesh?.metadata?.template; if (!template) { if (mesh?.metadata?.handle == true) { mesh && mesh.setParent(this.controller.motionController.rootMesh); this.grabbedMesh = mesh; } else { if (mesh?.parent?.parent?.metadata?.grabbable) { if (mesh?.parent?.parent?.parent) { mesh = (mesh?.parent?.parent?.parent as Mesh); this.grabbedMesh = mesh; player = true; } } else { return; } } } else { if (template == '#connection-template') { return; } } this.previousParentId = mesh?.parent?.id; this.logger.warn("grabbed " + mesh?.id + " parent " + this.previousParentId); this.previousRotation = mesh?.rotation.clone(); this.previousScaling = mesh?.scaling.clone(); this.previousPosition = mesh?.position.clone(); if (("toolbox" != mesh?.parent?.parent?.id) || player) { if (mesh.physicsBody) { const transformNode = setupTransformNode(mesh, this.controller.motionController.rootMesh); mesh.physicsBody.setMotionType(PhysicsMotionType.ANIMATED); this.grabbedMeshParentId = transformNode.id; } else { mesh.setParent(this.controller.motionController.rootMesh); } this.grabbedMesh = mesh; } else { const clone = grabAndClone(this.diagramManager, mesh, this.controller.motionController.rootMesh); this.grabbedMeshParentId = clone.transformNode.id; this.grabbedMesh = clone.newMesh; this.previousParentId = null; const event: DiagramEvent = { type: DiagramEventType.ADD, entity: toDiagramEntity(clone.newMesh) } this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1); } } private toolboxHandleWasGrabbed(mesh: AbstractMesh): boolean { if (!mesh?.metadata?.template && mesh?.metadata?.handle == true) { this.grabbedMesh = null; this.previousParentId = null; mesh.setParent(null); return true; } else { return false; } } private drop() { const mesh = this.grabbedMesh; if (!mesh) { return; } if (this.toolboxHandleWasGrabbed(mesh)) { return; } reparent(mesh, this.previousParentId, this.grabbedMeshParentId); this.grabbedMeshParentId = null; if (!mesh.physicsBody) { mesh.position = snapGridVal(mesh.position, this.diagramManager.config.current.gridSnap); mesh.rotation = snapRotateVal(mesh.rotation, this.diagramManager.config.current.rotateSnap); } this.previousParentId = null; this.previousScaling = null; this.previousRotation = null; this.previousPosition = null; this.grabbedMesh = null; if (mesh?.metadata?.template && (mesh?.metadata?.template.indexOf('#') == -1)) { return; } const entity = toDiagramEntity(mesh); const event: DiagramEvent = { type: DiagramEventType.DROP, entity: entity } const body = mesh?.physicsBody; if (body) { body.setMotionType(PhysicsMotionType.DYNAMIC); this.logger.debug(body.transformNode.absolutePosition); this.logger.debug(this.lastPosition); if (this.lastPosition) { body.setLinearVelocity(body.transformNode.absolutePosition.subtract(this.lastPosition).scale(20)); //body.setLinearVelocity(this.lastPosition.subtract(body.transformNode.absolutePosition).scale(20)); this.logger.debug(this.lastPosition.subtract(body.transformNode.absolutePosition).scale(20)); } } this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1); } private initGrip(grip: WebXRControllerComponent) { grip.onButtonStateChangedObservable.add(() => { if (grip.changes.pressed) { if (grip.pressed) { this.grab(); } else { this.drop(); } } }); } }