immersive2/src/controllers/base.ts

253 lines
10 KiB
TypeScript

import {
AbstractMesh,
Scene,
Vector3,
WebXRControllerComponent,
WebXRDefaultExperience,
WebXRInputSource
} from "@babylonjs/core";
import {DiagramManager} from "../diagram/diagramManager";
import {DiagramEvent, DiagramEventType} from "../diagram/types/diagramEntity";
import log from "loglevel";
import {ControllerEventType, Controllers} from "./controllers";
import {grabAndClone} from "./functions/grabAndClone";
import {ClickMenu} from "../menus/clickMenu";
import {displayDebug} from "../util/displayDebug";
import {motionControllerObserver} from "./functions/motionControllerObserver";
import {DefaultScene} from "../defaultScene";
import {DiagramEventObserverMask} from "../diagram/types/diagramEventObserverMask";
import {DiagramObject} from "../objects/diagramObject";
import {snapAll} from "./functions/snapAll";
import {MeshTypeEnum} from "../diagram/types/meshTypeEnum";
import {getMeshType} from "./functions/getMeshType";
const CLICK_TIME = 300;
export class Base {
static stickVector = Vector3.Zero();
protected readonly scene: Scene;
protected readonly xr: WebXRDefaultExperience;
protected readonly diagramManager: DiagramManager;
protected xrInputSource: WebXRInputSource;
protected speedFactor = 4;
protected grabbedObject: DiagramObject = null;
protected grabbedMesh: AbstractMesh = null;
protected grabbedMeshType: MeshTypeEnum = null;
protected controllers: Controllers;
private readonly _logger = log.getLogger('Base');
private _clickStart: number = 0;
private _clickMenu: ClickMenu;
private _pickPoint: Vector3 = new Vector3();
private _meshUnderPointer: AbstractMesh;
constructor(controller: WebXRInputSource,
xr: WebXRDefaultExperience,
diagramManager: DiagramManager) {
this._logger.debug('Base Controller Constructor called');
this.xrInputSource = controller;
this.controllers = diagramManager.controllers;
this.scene = DefaultScene.Scene;
this.xr = xr;
this.scene.onPointerObservable.add((pointerInfo) => {
if (pointerInfo?.pickInfo?.gripTransform?.id == this.xrInputSource?.grip?.id) {
if (pointerInfo.pickInfo.pickedMesh) {
this._pickPoint.copyFrom(pointerInfo.pickInfo.pickedPoint);
this._meshUnderPointer = pointerInfo.pickInfo.pickedMesh;
} else {
this._meshUnderPointer = null;
}
}
});
this.diagramManager = diagramManager;
//@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.controllerObservable.add((event) => {
this._logger.debug(event);
switch (event.type) {
case ControllerEventType.PULSE:
if (event.gripId == this?.xrInputSource?.grip?.id) {
this.xrInputSource?.motionController?.pulse(.35, 50)
.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.xrInputSource.motionController.rootMesh.setEnabled(false)
this.xrInputSource.pointer.setEnabled(false);
}
public enable() {
this.scene.preventDefaultOnPointerDown = false;
this.xrInputSource.motionController.rootMesh.setEnabled(true);
this.xrInputSource.pointer.setEnabled(true)
}
protected initClicker(trigger: WebXRControllerComponent) {
this._logger.debug("initTrigger");
trigger.onButtonStateChangedObservable.add(() => {
if (trigger.changes.pressed) {
if (trigger.pressed) {
if (this._clickStart == 0) {
this._clickStart = Date.now();
window.setTimeout(() => {
if (this._clickStart > 0) {
this._logger.debug("grabbing and cloning");
const clone = grabAndClone(this.diagramManager, this._meshUnderPointer, this.xrInputSource.motionController.rootMesh);
this.grabbedObject = clone;
this.grabbedMesh = clone.mesh;
this.grabbedMeshType = getMeshType(clone.mesh, this.diagramManager);
this._meshUnderPointer = clone.mesh;
clone.grabbed = true;
}
}, 300, this);
}
} else {
const clickEnd = Date.now();
if (this._clickStart > 0 && (clickEnd - this._clickStart) < CLICK_TIME) {
this.click();
} else {
if (this.grabbedObject || this.grabbedMesh) {
this.drop();
}
}
this._clickStart = 0;
}
}
}, -1, false, this);
}
private grab() {
let mesh = this._meshUnderPointer
if (!mesh) {
return;
}
this.grabbedMesh = mesh;
this.grabbedMeshType = getMeshType(mesh, this.diagramManager);
displayDebug(mesh);
switch (this.grabbedMeshType) {
case MeshTypeEnum.ENTITY:
const diagramObject = this.diagramManager.getDiagramObject(mesh.id);
if (diagramObject.isGrabbable) {
diagramObject.baseTransform.setParent(this.xrInputSource.motionController.rootMesh);
diagramObject.grabbed = true;
this.grabbedObject = diagramObject;
}
break;
case MeshTypeEnum.HANDLE:
this.grabbedMesh.setParent(this.xrInputSource.motionController.rootMesh);
break;
case MeshTypeEnum.TOOL:
const clone = grabAndClone(this.diagramManager, mesh, this.xrInputSource.motionController.rootMesh);
this.grabbedObject = clone;
this.grabbedMesh = clone.mesh;
clone.grabbed = true;
}
}
private drop() {
const mesh = this.grabbedMesh;
if (!mesh) {
return;
}
const diagramObject = this.grabbedObject;
switch (this.grabbedMeshType) {
case MeshTypeEnum.ENTITY:
if (diagramObject) {
diagramObject.baseTransform.setParent(null);
snapAll(this.grabbedObject.baseTransform, this.diagramManager.config, this._pickPoint);
diagramObject.mesh.computeWorldMatrix(true);
const event: DiagramEvent =
{
type: DiagramEventType.DROP,
entity: diagramObject.diagramEntity
}
this.diagramManager.onDiagramEventObservable.notifyObservers(event, DiagramEventObserverMask.ALL);
diagramObject.mesh.computeWorldMatrix(false);
diagramObject.grabbed = false;
}
this.grabbedObject = null;
this.grabbedMesh = null;
this.grabbedMeshType = null;
break;
case MeshTypeEnum.TOOL:
this.grabbedObject.baseTransform.setParent(null);
snapAll(this.grabbedObject.baseTransform, this.diagramManager.config, this._pickPoint);
diagramObject.mesh.computeWorldMatrix(true);
const event: DiagramEvent =
{
type: DiagramEventType.DROP,
entity: diagramObject.diagramEntity
}
this.diagramManager.onDiagramEventObservable.notifyObservers(event, DiagramEventObserverMask.ALL);
diagramObject.mesh.computeWorldMatrix(false);
this.grabbedObject.grabbed = false;
this.grabbedObject = null;
this.grabbedMesh = null;
this.grabbedMeshType = null;
break;
case MeshTypeEnum.HANDLE:
mesh.setParent(this.scene.getMeshByName("platform"));
const location = {
position: {x: mesh.position.x, y: mesh.position.y, z: mesh.position.z},
rotation: {x: mesh.rotation.x, y: mesh.rotation.y, z: mesh.rotation.z}
}
localStorage.setItem(mesh.id, JSON.stringify(location));
this.grabbedMesh = null;
this.grabbedMeshType = null;
this.grabbedObject = null;
break;
}
}
private click() {
let mesh = this.xr.pointerSelection.getMeshUnderPointer(this.xrInputSource.uniqueId);
if (this.diagramManager.isDiagramObject(mesh)) {
this._logger.debug("click on " + mesh.id);
if (this.diagramManager.diagramMenuManager.connectionPreview) {
this.diagramManager.diagramMenuManager.connect(mesh);
} else {
if (this._clickMenu) {
this._clickMenu.dispose();
}
this._clickMenu = this.diagramManager.diagramMenuManager.createClickMenu(mesh, this.xrInputSource);
}
} else {
this._logger.debug("click on nothing");
}
}
private initGrip(grip: WebXRControllerComponent) {
grip.onButtonStateChangedObservable.add(() => {
if (grip.changes.pressed) {
if (grip.pressed) {
this.grab();
} else {
this.drop();
}
}
});
}
}