diff --git a/package-lock.json b/package-lock.json index 170a974..0283404 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,10 @@ "hasInstallScript": true, "dependencies": { "@auth0/auth0-spa-js": "^2.0.8", - "@babylonjs/core": "^6.12.3", - "@babylonjs/gui": "^6.12.3", - "@babylonjs/havok": "^1.1.0", - "@babylonjs/inspector": "^6.12.3", + "@babylonjs/core": "^6.14.0", + "@babylonjs/gui": "^6.14.0", + "@babylonjs/havok": "1.1.1", + "@babylonjs/inspector": "^6.14.0", "@maptiler/client": "^1.5.0", "axios": "^1.4.0", "dexie": "^3.2.4", @@ -53,14 +53,14 @@ } }, "node_modules/@babylonjs/core": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.12.3.tgz", - "integrity": "sha512-p1di605M2Pa5+YiHbydGJ3PA4nUWSlmu79agL3mcsq7s8zC5VN/HaK1uNSicYI9LhVfPF6bDsVGHqCXxEkRsFQ==" + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.14.0.tgz", + "integrity": "sha512-ciIfWMMtV5jsnqxqTn+v/CS65yji6CXTP2drmvLlzk+k+IZjE8RfkpMqZgZozN/KNkOmIVn2Li7qRMjg4ZUGlw==" }, "node_modules/@babylonjs/gui": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-6.12.3.tgz", - "integrity": "sha512-Yh9rVWwAymjy23g8dC5PMjeI2c71HWMZ6Lw2G0yjTZS9gf0st2A/OLsv/4ofK51u0fqvHZFBTfFTL5ZOFYNMow==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-6.14.0.tgz", + "integrity": "sha512-rkqPEVDaeza4agyd5xSLSjnWgI1spqcNI/kmNke4gREmweEcZZFxNxjGQd5m/JQMbx7qrj9jtEp8COZ7wNSiWw==", "peerDependencies": { "@babylonjs/core": "^6.0.0" } @@ -78,17 +78,17 @@ } }, "node_modules/@babylonjs/havok": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@babylonjs/havok/-/havok-1.1.0.tgz", - "integrity": "sha512-BNo2d+gfkoCbbEGYOVZgdPWG6NRdo5Tjvd9rpjMs0sWV/EaKQ6kL8hnJUn+HVZx4hdyKrTkq8ThTGFt/nTediA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@babylonjs/havok/-/havok-1.1.1.tgz", + "integrity": "sha512-QIygEnKPYYPCE8QuJDxDLYPMj+W5Yha6NOdUfpvcVfZwtjVDE+eOa2Y5el7muQD563OdeJS+Kzmhyg+5qAZHBA==", "dependencies": { "@types/emscripten": "^1.39.6" } }, "node_modules/@babylonjs/inspector": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-6.12.3.tgz", - "integrity": "sha512-REW+BF4LQhOK0cRRTVfSaOGnjh1LVE1lgwel7BBjjWeRcIjCVBimAprPY8rE0K+EAGMmPlxt9rDIpGoA0PUTqQ==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-6.14.0.tgz", + "integrity": "sha512-8wntNbVb9Yhj0q8Ll4kFGDDJPHk7IeosqVwXTPToTfDYC67pwkVQFXFViEXr1R0HrG0l1FmZzPVLY6szbFSwrw==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/free-regular-svg-icons": "^6.0.0", diff --git a/package.json b/package.json index cad3840..f2fbb08 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,10 @@ "postinstall": "cp ./node_modules/@babylonjs/havok/lib/esm/HavokPhysics.wasm ./node_modules/.vite/deps" }, "dependencies": { - "@babylonjs/core": "^6.12.3", - "@babylonjs/gui": "^6.12.3", - "@babylonjs/havok": "^1.1.0", - "@babylonjs/inspector": "^6.12.3", + "@babylonjs/core": "^6.14.0", + "@babylonjs/gui": "^6.14.0", + "@babylonjs/havok": "1.1.1", + "@babylonjs/inspector": "^6.14.0", "express": "^4.18.2", "@auth0/auth0-spa-js": "^2.0.8", "ring-client-api": "^11.8.0", diff --git a/src/app.ts b/src/app.ts index e06c5a7..ed83514 100644 --- a/src/app.ts +++ b/src/app.ts @@ -40,7 +40,7 @@ export class App { constructor() { const config = AppConfig.config; - log.setLevel('debug'); + log.setLevel('warn'); const canvas = document.createElement("canvas"); canvas.style.width = "100%"; canvas.style.height = "100%"; diff --git a/src/controllers/base.ts b/src/controllers/base.ts index e4f663f..160b77b 100644 --- a/src/controllers/base.ts +++ b/src/controllers/base.ts @@ -1,6 +1,11 @@ import { AbstractMesh, + HavokPlugin, + PhysicsAggregate, + PhysicsMotionType, + PhysicsShapeType, Scene, + TransformNode, Vector3, WebXRControllerComponent, WebXRDefaultExperience, @@ -20,7 +25,8 @@ export class Base { protected speedFactor = 4; protected readonly scene: Scene; protected grabbedMesh: AbstractMesh = null; - protected previousParent: string = null; + protected grabbedMeshParentId: string = null; + protected previousParentId: string = null; protected previousRotation: Vector3 = null; protected previousScaling: Vector3 = null; protected previousPosition: Vector3 = null; @@ -28,6 +34,8 @@ export class Base { protected readonly xr: WebXRDefaultExperience; protected readonly diagramManager: DiagramManager; private logger: log.Logger; + private lastPosition: Vector3 = null; + constructor(controller: WebXRInputSource, scene: Scene, xr: WebXRDefaultExperience, @@ -36,8 +44,33 @@ export class Base { this.controller = controller; this.scene = scene; + this.scene.onAfterRenderObservable.add(() => { + + }, -1, false, this); + + 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-trigger']) { init.components['xr-standard-trigger'] @@ -69,101 +102,176 @@ export class Base { this.controller.pointer.setEnabled(true); } + private buildTransformNode() { + + } + + private grab(mesh) { + + if (this.xr.pointerSelection.getMeshUnderPointer) { + mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId); + } + if (!mesh) { + return; + } + if (!mesh?.metadata?.template) { + if (mesh?.id == "handle") { + mesh && mesh.setParent(this.controller.motionController.rootMesh); + this.grabbedMesh = mesh; + } else { + return; + } + + } + this.previousParentId = mesh?.parent?.id; + this.previousRotation = mesh?.rotation.clone(); + this.previousScaling = mesh?.scaling.clone(); + this.previousPosition = mesh?.position.clone(); + + if ("toolbox" != mesh?.parent?.parent?.id) { + if (mesh.physicsBody) { + const transformNode = new TransformNode("grabAnchor, this.scene"); + transformNode.id = "grabAnchor"; + transformNode.position = mesh.position.clone(); + transformNode.rotationQuaternion = mesh.rotationQuaternion.clone(); + transformNode.setParent(this.controller.motionController.rootMesh); + mesh.physicsBody.setMotionType(PhysicsMotionType.STATIC); + //mesh.setParent(transformNode); + this.grabbedMeshParentId = transformNode.id; + } else { + mesh.setParent(this.controller.motionController.rootMesh); + } + this.grabbedMesh = mesh; + } else { + const config = AppConfig.config; + const newMesh = this.diagramManager.createCopy(mesh); + newMesh.position = mesh.absolutePosition.clone(); + if (mesh.absoluteRotationQuaternion) { + newMesh.rotation = mesh.absoluteRotationQuaternion.toEulerAngles().clone(); + } else { + newMesh.rotation = mesh.absoluteRotation.clone(); + } + + newMesh.scaling = config.createSnapVal; + newMesh.material = mesh.material; + newMesh.metadata = mesh.metadata; + const transformNode = new TransformNode("grabAnchor, this.scene"); + transformNode.id = "grabAnchor"; + transformNode.position = newMesh.position.clone(); + if (newMesh.rotationQuaternion) { + transformNode.rotationQuaternion = newMesh.rotationQuaternion.clone(); + } else { + transformNode.rotation = newMesh.rotation.clone(); + } + + transformNode.setParent(this.controller.motionController.rootMesh); + //newMesh?.physicsBody?.setMotionType(PhysicsMotionType.STATIC); + //mesh.setParent(transformNode); + this.grabbedMeshParentId = transformNode.id; + const aggregate = new PhysicsAggregate(newMesh, + PhysicsShapeType.BOX, {mass: 10, restitution: .1, friction: .9}, this.scene); + aggregate.body.setMotionType(PhysicsMotionType.STATIC); + + + //newMesh && newMesh.setParent(this.controller.motionController.rootMesh); + this.grabbedMesh = newMesh; + this.previousParentId = null; + const event: DiagramEvent = { + type: DiagramEventType.ADD, + entity: MeshConverter.toDiagramEntity(newMesh) + } + this.diagramManager.onDiagramEventObservable.notifyObservers(event); + + } + } + + private handleGrabbed(mesh: AbstractMesh): boolean { + if (!mesh?.metadata?.template + && mesh?.id == "handle") { + //mesh && mesh.setParent(null); + this.grabbedMesh = null; + this.previousParentId = null; + mesh.setParent(null); + return true; + } else { + return false; + } + } + + private reparent(mesh: AbstractMesh) { + const config = AppConfig.config; + const snappedRotation = config.snapRotateVal(mesh.absoluteRotationQuaternion.toEulerAngles().clone()); + const snappedPosition = config.snapGridVal(mesh.absolutePosition.clone()); + + if (this.previousParentId) { + const parent = this.scene.getMeshById(this.previousParentId); + if (parent) { + //mesh && mesh.setParent(this.scene.getMeshById(this.previousParentId)); + log.getLogger("Base").warn("Base", "Have not implemented snapping to parent yet"); + //@note: this is not implemented yet + } else { + //mesh.setParent(null); + mesh.rotation = snappedRotation; + mesh.position = snappedPosition; + mesh?.physicsBody?.setMotionType(PhysicsMotionType.DYNAMIC); + } + } else { + const parent = this.scene.getTransformNodeById(this.grabbedMeshParentId); + if (parent) { + parent.rotation = snappedRotation; + parent.position = snappedPosition; + this.grabbedMeshParentId = null; + parent.dispose(); + } + } + } + + private drop() { + const mesh = this.grabbedMesh; + if (!mesh) { + return; + } + if (this.handleGrabbed(mesh)) { + return; + } + this.reparent(mesh); + this.previousParentId = null; + this.previousScaling = null; + this.previousRotation = null; + this.previousPosition = null; + this.grabbedMesh = null; + + const entity = MeshConverter.toDiagramEntity(mesh); + const event: DiagramEvent = { + type: DiagramEventType.DROP, + entity: entity + } + + this.diagramManager.onDiagramEventObservable.notifyObservers(event); + 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)); + } + + + } + + + } + private initGrip(grip: WebXRControllerComponent) { grip.onButtonStateChangedObservable.add(() => { if (grip.changes.pressed) { if (grip.pressed) { - let mesh = this.scene.meshUnderPointer; - if (this.xr.pointerSelection.getMeshUnderPointer) { - mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId); - } - if (!mesh) { - return; - } - if (!mesh?.metadata?.template) { - if (mesh.id == "handle") { - mesh && mesh.setParent(this.controller.motionController.rootMesh); - this.grabbedMesh = mesh; - } else { - return; - } - - } - this.previousParent = mesh?.parent?.id; - this.previousRotation = mesh?.rotation.clone(); - this.previousScaling = mesh?.scaling.clone(); - this.previousPosition = mesh?.position.clone(); - - if ("toolbox" != mesh?.parent?.parent?.id) { - mesh && mesh.setParent(this.controller.motionController.rootMesh); - this.grabbedMesh = mesh; - } else { - const config = AppConfig.config; - const newMesh = this.diagramManager.createCopy(mesh); - newMesh.position = mesh.absolutePosition.clone(); - newMesh.rotation = mesh.absoluteRotationQuaternion.toEulerAngles().clone(); - newMesh.scaling = config.createSnapVal; - newMesh.material = mesh.material; - newMesh.metadata = mesh.metadata; - newMesh && newMesh.setParent(this.controller.motionController.rootMesh); - const event: DiagramEvent = { - type: DiagramEventType.ADD, - entity: MeshConverter.toDiagramEntity(newMesh) - } - this.diagramManager.onDiagramEventObservable.notifyObservers(event); - this.grabbedMesh = newMesh; - this.previousParent = null; - } + this.grab(this.scene.meshUnderPointer); } else { - let mesh = this.scene.meshUnderPointer; - if (this.xr.pointerSelection.getMeshUnderPointer) { - mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId); - } - if (!this.grabbedMesh) { - log.debug("controllers", "no grabbed mesh"); - return; - } - if (mesh?.id != this?.grabbedMesh?.id) { - log.debug("controllers", "not the same mesh"); - } - mesh = this.grabbedMesh; - if (!mesh?.metadata?.template) { - if (mesh.id == "handle") { - mesh && mesh.setParent(null); - this.grabbedMesh = null; - this.previousParent = null; - return; - } - } - const config = AppConfig.config; - const snappedRotation = config.snapRotateVal(mesh.absoluteRotationQuaternion.toEulerAngles().clone()); - const snappedPosition = config.snapGridVal(mesh.absolutePosition.clone()); - if (this.previousParent) { - const p = this.scene.getMeshById(this.previousParent); - if (p) { - mesh && mesh.setParent(this.scene.getMeshById(this.previousParent)); - log.getLogger("Base").warn("Base", "Have not implemented snapping to parent yet"); - //@note: this is not implemented yet - } else { - mesh && mesh.setParent(null); - mesh.rotation = snappedRotation; - mesh.position = snappedPosition; - } - } else { - mesh && mesh.setParent(null); - mesh.rotation = snappedRotation; - mesh.position = snappedPosition; - } - const entity = MeshConverter.toDiagramEntity(mesh); - const event: DiagramEvent = { - type: DiagramEventType.DROP, - entity: entity - } - this.previousParent = null; - this.previousScaling = null; - this.previousRotation = null; - this.previousPosition = null; - this.diagramManager.onDiagramEventObservable.notifyObservers(event); + this.drop(); } } }); diff --git a/src/controllers/rigplatform.ts b/src/controllers/rigplatform.ts index 59b778a..99bfdc4 100644 --- a/src/controllers/rigplatform.ts +++ b/src/controllers/rigplatform.ts @@ -35,6 +35,7 @@ export class Rigplatform { private camera: Camera; private turning: boolean = false; private velocity: Vector3 = Vector3.Zero(); + private logger = log.getLogger('Rigplatform'); private readonly diagramManager: DiagramManager; constructor(scene: Scene, xr: WebXRDefaultExperience, diagramManager: DiagramManager) { @@ -74,7 +75,10 @@ export class Rigplatform { } private registerVelocityObserver() { this.scene.onBeforeRenderObservable.add(() => { - const vel = this.velocity.applyRotationQuaternion(this.camera.absoluteRotation); + const vel = this.velocity.applyRotationQuaternion(this.scene.activeCamera.absoluteRotation); + if (vel.length() > 0) { + this.logger.debug('Velocity', this.velocity, vel, this.scene.activeCamera.absoluteRotation); + } this.body.setLinearVelocity(vel); }); } diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index 796dd2d..140a3f3 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -6,6 +6,9 @@ import { InstancedMesh, Mesh, Observable, + PhysicsAggregate, + PhysicsMotionType, + PhysicsShapeType, PlaySoundAction, Scene, WebXRExperienceHelper @@ -88,6 +91,9 @@ export class DiagramManager { if (event.parent) { mesh.parent = this.scene.getMeshById(event.parent); } + const body = new PhysicsAggregate(mesh, PhysicsShapeType.BOX, {mass: 10, restitution: .1}, this.scene); + body.body.setMotionType(PhysicsMotionType.DYNAMIC); + } private onDiagramEvent(event: DiagramEvent) { @@ -97,6 +103,8 @@ export class DiagramManager { if (entity) { mesh = this.scene.getMeshById(entity.id); } + //const body = mesh?.physicsBody; + switch (event.type) { case DiagramEventType.CLEAR: break; @@ -105,9 +113,11 @@ export class DiagramManager { case DiagramEventType.DROP: this.getPersistenceManager()?.modify(mesh); MeshConverter.updateTextNode(mesh, entity.text); + break; case DiagramEventType.ADD: this.getPersistenceManager()?.add(mesh); + break; case DiagramEventType.MODIFY: this.getPersistenceManager()?.modify(mesh); @@ -134,7 +144,9 @@ export class DiagramManager { case DiagramEventType.REMOVE: if (mesh) { this.getPersistenceManager()?.remove(mesh) + mesh?.physicsBody?.dispose(); mesh.dispose(); + DiaSounds.instance.exit.play(); } break; } diff --git a/src/menus/editMenu.ts b/src/menus/editMenu.ts index 1e58025..f4c3403 100644 --- a/src/menus/editMenu.ts +++ b/src/menus/editMenu.ts @@ -16,6 +16,7 @@ import log from "loglevel"; import {InputTextView} from "../information/inputTextView"; import {Right} from "../controllers/right"; import {Left} from "../controllers/left"; +import {DiaSounds} from "../util/diaSounds"; export class EditMenu { private state: BmenuState = BmenuState.NONE; @@ -180,9 +181,12 @@ export class EditMenu { toggle() { //console.log(mesh.name); if (this.manager) { + DiaSounds.instance.exit.play(); this.manager.dispose(); this.manager = null; + } else { + DiaSounds.instance.enter.play(); this.manager = new GUI3DManager(this.scene); const panel = new StackPanel3D(); this.manager.addControl(panel); @@ -196,6 +200,7 @@ export class EditMenu { this.scene.activeCamera.globalPosition.add(offset); panel.node.lookAt(this.scene.activeCamera.globalPosition); panel.node.rotation.y = panel.node.rotation.y + Math.PI; + } } diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index 9ac5914..a16b687 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -51,16 +51,18 @@ export class Toolbox { this.manager.addControl(this.addPanel); this.node = new TransformNode("toolbox", this.scene); const handle = MeshBuilder.CreateCapsule("handle", { - radius: .01, + radius: .05, orientation: Vector3.Right(), - height: .3 + height: .4 }, this.scene); handle.id = "handle"; const handleMaterial = new StandardMaterial("handle-material", this.scene); handleMaterial.diffuseColor = Color3.FromHexString("#EEEEFF"); + handleMaterial.alpha = .5; handle.material = handleMaterial; handle.position = CameraHelper.getFrontPosition(2, this.scene); handle.position.y = 1.6; + this.node.parent = handle; this.xr = xr; if (!this.scene.activeCamera) { @@ -73,14 +75,14 @@ export class Toolbox { } private buildToolbox() { - this.node.position.y = -.2; - this.node.scaling= new Vector3(0.5, 0.5, 0.5); + this.node.position.y = .1; + this.node.scaling = new Vector3(0.6, 0.6, 0.6); const color = "#7777FF"; this.buildColor(Color3.FromHexString(color)); - const addButton= new Button3D("add-button"); + const addButton = new Button3D("add-button"); const text = new TextBlock("add-button-text", "Add Color"); - text.color="white"; + text.color = "white"; text.fontSize = "48px"; text.text = "Add Color"; addButton.content = text;