235 lines
8.8 KiB
TypeScript
235 lines
8.8 KiB
TypeScript
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();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
} |