diff --git a/package-lock.json b/package-lock.json index a04d981..46c0fec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,9 @@ "@babylonjs/inspector": "^6.12.3", "@maptiler/client": "^1.5.0", "axios": "^1.4.0", + "dexie": "^3.2.4", + "dexie-observable": "^4.0.1-beta.13", + "earcut": "^2.2.4", "express": "^4.18.2", "express-http-proxy": "^1.6.3", "google-static-maps-tile": "1.0.0", @@ -1759,6 +1762,22 @@ "node": ">=8" } }, + "node_modules/dexie": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-3.2.4.tgz", + "integrity": "sha512-VKoTQRSv7+RnffpOJ3Dh6ozknBqzWw/F3iqMdsZg958R0AS8AnY9x9d1lbwENr0gzeGJHXKcGhAMRaqys6SxqA==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/dexie-observable": { + "version": "4.0.1-beta.13", + "resolved": "https://registry.npmjs.org/dexie-observable/-/dexie-observable-4.0.1-beta.13.tgz", + "integrity": "sha512-axmgPk7yjoPwj+0DdQIE5s1MBXi+6XcIFIjBKdQAmSGpsLQSth/LHvMOQ3q3Wj6pwIE5hqIxg2GL75sVqQbhEw==", + "peerDependencies": { + "dexie": "^3.0.2 || ^4.0.1-alpha.5" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1782,6 +1801,11 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/package.json b/package.json index 361feca..ff639b6 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,13 @@ "ring-client-api": "^11.8.0", "@maptiler/client": "^1.5.0", "axios": "^1.4.0", + "dexie": "^3.2.4", + "dexie-observable": "^4.0.1-beta.13", "google-static-maps-tile": "1.0.0", "query-string": "^8.1.0", "vite-express": "^0.9.1", "express-http-proxy": "^1.6.3", + "earcut": "^2.2.4", "uuid": "^9.0.0" }, "devDependencies": { diff --git a/src/controllers/base.ts b/src/controllers/base.ts index 1213944..d6ea3ae 100644 --- a/src/controllers/base.ts +++ b/src/controllers/base.ts @@ -1,13 +1,18 @@ -import {Vector3, WebXRInputSource} from "@babylonjs/core"; +import {AbstractMesh, Scene, Vector3, WebXRInputSource} from "@babylonjs/core"; export class Base { static stickVector = Vector3.Zero(); protected controller: WebXRInputSource; protected speedFactor = 4; - + protected readonly scene: Scene; + protected currentMesh: AbstractMesh = null; constructor(controller: - WebXRInputSource) { + WebXRInputSource, scene: Scene) { this.controller = controller; + this.scene= scene; + this.scene.registerAfterRender(() => { + this.currentMesh= this.scene.getPointerOverMesh(); + }); this.controller.onMotionControllerInitObservable.add((init) => { if (init.components['xr-standard-trigger']) { init.components['xr-standard-trigger'] diff --git a/src/controllers/left.ts b/src/controllers/left.ts index 8a582fb..cbdaab8 100644 --- a/src/controllers/left.ts +++ b/src/controllers/left.ts @@ -1,4 +1,4 @@ -import {Vector3, WebXRInputSource} from "@babylonjs/core"; +import {Scene, Vector3, WebXRInputSource} from "@babylonjs/core"; import {Base} from "./base"; import {Controllers} from "./controllers"; @@ -7,8 +7,9 @@ export class Left extends Base { public static instance: Left; constructor(controller: - WebXRInputSource) { - super(controller); + WebXRInputSource, scene: Scene) { + + super(controller, scene); Left.instance = this; this.controller.onMotionControllerInitObservable.add((init) => { diff --git a/src/controllers/right.ts b/src/controllers/right.ts index 54bb12f..31be1ef 100644 --- a/src/controllers/right.ts +++ b/src/controllers/right.ts @@ -1,19 +1,30 @@ import {Base} from "./base"; -import {Angle, Observable, Vector3, WebXRControllerComponent, WebXRInputSource} from "@babylonjs/core"; +import {Angle, Scene, Vector3, WebXRControllerComponent, WebXRInputSource} from "@babylonjs/core"; import {Bmenu} from "../menus/bmenu"; import {DiagramManager} from "../diagram/diagramManager"; import {ControllerMovementMode, Controllers} from "./controllers"; import {BmenuState} from "../menus/MenuState"; import {DiagramEvent, DiagramEventType} from "../diagram/diagramEntity"; - - export class Right extends Base { private bmenu: Bmenu; public static instance: Right; private down: boolean = false; + constructor(controller: + WebXRInputSource, scene: Scene) { + super(controller, scene); + Right.instance = this; + this.controller.onMotionControllerInitObservable.add((init) => { + this.initTrigger(init.components['xr-standard-trigger']); + this.initBButton(init.components['b-button']); + this.initAButton(init.components['a-button']); + this.initThumbstick(init.components['xr-standard-thumbstick']); + this.initGrip(init.components['xr-standard-squeeze']); + + }); + } private initBButton(bbutton: WebXRControllerComponent) { if (bbutton) { bbutton.onButtonStateChangedObservable.add((value) => { @@ -103,23 +114,16 @@ export class Right extends Base { } } - constructor(controller: - WebXRInputSource) { - super(controller); - Right.instance = this; - this.controller.onMotionControllerInitObservable.add((init) => { - this.initTrigger(init.components['xr-standard-trigger']); - this.initBButton(init.components['b-button']); - this.initAButton(init.components['a-button']); - this.initThumbstick(init.components['xr-standard-thumbstick']); - this.initGrip(init.components['xr-standard-squeeze']); - }); - } + private initGrip(grip: WebXRControllerComponent) { grip.onButtonStateChangedObservable.add((value) => { if (value.value > .5) { - if (this.controller.pointer.collider.collidedMesh) { - console.log(this.controller.pointer.collider.collidedMesh.id); + if (this.currentMesh) { + this.currentMesh.setParent(this.controller.pointer); + } + } else { + if (this.currentMesh) { + this.currentMesh.setParent(null); } } @@ -133,12 +137,12 @@ export class Right extends Base { private rotateMovable(value: { x: number; y: number }) { if (Math.abs(value.y) > .1) { Controllers.movable.rotation.x += - Angle.FromDegrees(Math.sign(value.y) * 1).radians(); + Angle.FromDegrees(Math.sign(value.y)).radians(); Controllers.movable.rotation.x = this.fixRadians(Controllers.movable.rotation.x); } if (Math.abs(value.x) > .1) { Controllers.movable.rotation.z += - Angle.FromDegrees(Math.sign(value.x) * 1).radians(); + Angle.FromDegrees(Math.sign(value.x)).radians(); Controllers.movable.rotation.z = this.fixRadians(Controllers.movable.rotation.z); } } diff --git a/src/controllers/rigplatform.ts b/src/controllers/rigplatform.ts index fd85c98..857f99e 100644 --- a/src/controllers/rigplatform.ts +++ b/src/controllers/rigplatform.ts @@ -43,7 +43,7 @@ export class Rigplatform { this.bMenu = new Bmenu(scene, xr.baseExperience); this.camera = scene.activeCamera; this.rigMesh = MeshBuilder.CreateBox("platform", {width: 2, height: .02, depth: 2}, scene); - const hud = new Hud(this.rigMesh, scene); + new Hud(this.rigMesh, scene); for (const cam of scene.cameras) { cam.parent = this.rigMesh; cam.position = new Vector3(0, 1.6, 0); @@ -72,6 +72,7 @@ export class Rigplatform { this.camera = s.activeCamera; this.camera.parent = this.rigMesh; }); + } public forwardback(val: number) { @@ -129,7 +130,7 @@ export class Rigplatform { let controller; switch (source.inputSource.handedness) { case "right": - Right.instance = new Right(source); + Right.instance = new Right(source, this.scene); Right.instance.setBMenu(this.bMenu); Controllers.controllerObserver.add((event: { type: string, value: number }) => { switch (event.type) { @@ -154,7 +155,7 @@ export class Rigplatform { }); break; case "left": - Left.instance = new Left(source); + Left.instance = new Left(source, this.scene); break; } @@ -175,13 +176,13 @@ export class Rigplatform { window.addEventListener("keydown", (ev) => { switch (ev.key) { case "w": - this.forwardback(1 * Rigplatform.LINEAR_VELOCITY); + this.forwardback(Rigplatform.LINEAR_VELOCITY); break; case "s": this.forwardback(-1 * Rigplatform.LINEAR_VELOCITY); break; case "a": - this.leftright(1 * Rigplatform.LINEAR_VELOCITY); + this.leftright(Rigplatform.LINEAR_VELOCITY); break; case "d": this.leftright(-1 * Rigplatform.LINEAR_VELOCITY); @@ -190,13 +191,13 @@ export class Rigplatform { this.turn(-1 * Rigplatform.ANGULAR_VELOCITY); break; case "e": - this.turn(1 * Rigplatform.ANGULAR_VELOCITY); + this.turn(Rigplatform.ANGULAR_VELOCITY); break; case "W": this.updown(-1 * Rigplatform.LINEAR_VELOCITY); break; case "S": - this.updown(1 * Rigplatform.LINEAR_VELOCITY); + this.updown(Rigplatform.LINEAR_VELOCITY); break; case " ": this.bMenu.toggle(this.rigMesh) diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index ec0f633..274779e 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -1,12 +1,12 @@ import { AbstractMesh, - Angle, - Color3, + Color3, DynamicTexture, Mesh, MeshBuilder, - Observable, Scene, + Observable, + Scene, StandardMaterial, - Vector3, WebXRExperienceHelper + WebXRExperienceHelper } from "@babylonjs/core"; import {v4 as uuidv4} from 'uuid'; import {DiagramEntity, DiagramEvent, DiagramEventType} from "./diagramEntity"; @@ -15,7 +15,7 @@ import {PersistenceManager} from "./persistenceManager"; export class DiagramManager { private persistenceManager: PersistenceManager = new PersistenceManager(); static onDiagramEventObservable = new Observable(); - private scene: Scene; + private readonly scene: Scene; private xr: WebXRExperienceHelper; static currentMesh: AbstractMesh; @@ -33,11 +33,16 @@ export class DiagramManager { DiagramManager.onDiagramEventObservable.add(this.#onDiagramEvent, -1, true, this); } } + + #onRemoteEvent(event: DiagramEntity) { const mesh = this.#createMesh(event); - const material = new StandardMaterial("material-" + event.id, this.scene); - material.diffuseColor = Color3.FromHexString(event.color); - mesh.material = material; + if (!mesh.material) { + const material = new StandardMaterial("material-" + event.id, this.scene); + material.diffuseColor = Color3.FromHexString(event.color); + mesh.material = material; + } + } #onDiagramEvent(event: DiagramEvent) { @@ -53,8 +58,10 @@ export class DiagramManager { switch (event.type) { case DiagramEventType.CLEAR: - DiagramManager.currentMesh.dispose(); - DiagramManager.currentMesh = null; + if (DiagramManager.currentMesh) { + DiagramManager.currentMesh.dispose(); + DiagramManager.currentMesh = null; + } break; case DiagramEventType.DROPPED: break; @@ -62,10 +69,11 @@ export class DiagramManager { if (DiagramManager.currentMesh) { this.persistenceManager.add(DiagramManager.currentMesh); const newName = uuidv4(); - const newMesh = DiagramManager.currentMesh.clone("id"+newName, DiagramManager.currentMesh.parent); - const newMaterial = DiagramManager.currentMesh.material.clone("material"+newName); - newMesh.material=newMaterial; + const newMesh = DiagramManager.currentMesh.clone("id" + newName, DiagramManager.currentMesh.parent); + + newMesh.material = DiagramManager.currentMesh.material.clone("material" + newName); DiagramManager.currentMesh.setParent(null); + //DiagramManager.currentMesh.billboardMode = Mesh.BILLBOARDMODE_Y; DiagramManager.currentMesh = newMesh; DiagramManager.onDiagramEventObservable.notifyObservers({ type: DiagramEventType.DROPPED, @@ -82,9 +90,6 @@ export class DiagramManager { } else { mesh = this.#createMesh(entity); if (!material) { - material = new StandardMaterial("material-" + event.entity.id, this.scene); - material.diffuseColor = Color3.FromHexString(event.entity.color); - mesh.material = material; } if (!mesh) { @@ -98,11 +103,6 @@ export class DiagramManager { } else { if (!material) { - material = new StandardMaterial("material-" + event.entity.id, this.scene); - material.diffuseColor = Color3.FromHexString(event.entity.color); - if (mesh) { - mesh.material = material; - } } mesh.material = material; @@ -127,6 +127,42 @@ export class DiagramManager { } let mesh: Mesh; switch (entity.template) { + case "#plane-template": + + case "#text-template": + + const material = new StandardMaterial("material-" + entity.id, this.scene); + material.backFaceCulling = false; + const font_size = 48; + const font = "bold 48px roboto"; + const planeHeight=1; + const DTHeight = 1.5*font_size; + const ratio = planeHeight / DTHeight; + const text = 'This is some text to put on a plane'; + const tempText = new DynamicTexture("dynamic texture", 64, this.scene); + const tempContext = tempText.getContext(); + tempContext.font = font; + const DTWidth = tempContext.measureText(text).width; + const planeWidth = DTWidth * ratio; + + + const myDynamicTexture = new DynamicTexture("dynamic texture", + {width: DTWidth, height: DTHeight}, + this.scene, false); + mesh= MeshBuilder.CreatePlane(entity.id, { + width: planeWidth, + height: planeHeight + }, this.scene); + + myDynamicTexture.drawText('This is some short text', + null, null, + + font, "#000000", "#FFFFFF", + true, true); + material.diffuseTexture = myDynamicTexture; + mesh.material = material; + + break; case "#box-template": mesh = MeshBuilder.CreateBox(entity.id, { @@ -150,7 +186,9 @@ export class DiagramManager { } if (mesh) { mesh.metadata = {template: entity.template}; - + if (entity.text) { + mesh.metadata.text = entity.text; + } if (entity.position) { mesh.position = entity.position; } @@ -163,6 +201,11 @@ export class DiagramManager { if (entity.scale) { mesh.scaling = entity.scale; } + if (!mesh.material) { + const material = new StandardMaterial("material-" + entity.id, this.scene); + material.diffuseColor = Color3.FromHexString(entity.color); + mesh.material = material; + } } return mesh; diff --git a/src/diagram/persistenceManager.ts b/src/diagram/persistenceManager.ts index 52a3c02..33e202b 100644 --- a/src/diagram/persistenceManager.ts +++ b/src/diagram/persistenceManager.ts @@ -1,26 +1,32 @@ -import {AbstractMesh, Mesh, Observable, StandardMaterial, Vector3} from "@babylonjs/core"; +import {AbstractMesh, Observable, StandardMaterial, Vector3} from "@babylonjs/core"; import {DiagramEntity} from "./diagramEntity"; +import Dexie from "dexie"; export class PersistenceManager { public updateObserver: Observable = new Observable(); - + private db: Dexie = new Dexie("diagram"); constructor() { - + this.db.version(1).stores({entities: "id,position,rotation,last_seen,template,text,scale,color"}); } public add(mesh: AbstractMesh) { const entity = {}; entity.id = mesh.id; - entity.position = mesh.position.toString(); - entity.rotation = mesh.rotation.toString(); + entity.position = this.vectoxys(mesh.position); + entity.rotation = this.vectoxys(mesh.rotation); entity.last_seen = new Date().getDate(); - entity.template = "default"; - entity.text = ""; - entity.scale = mesh.scaling.toString(); + entity.template = mesh?.metadata?.template; + entity.text = mesh?.metadata?.text; + entity.scale = this.vectoxys(mesh.scaling); if (mesh.material) { entity.color = (mesh.material as StandardMaterial).diffuseColor.toHexString(); } - - console.log(entity); + this.db["entities"].add(entity); + } + private vectoxys(v: Vector3): {x, y ,z} { + return {x: v.x, y: v.y, z: v.z}; + } + private xyztovec(xyz: {x, y, z}): Vector3 { + return new Vector3(xyz.x, xyz.y, xyz.z); } public remove() { @@ -30,19 +36,26 @@ export class PersistenceManager { } public initialize() { + this.db['entities'].each((e: DiagramEntity) => { + e.position = this.xyztovec(e.position); + e.rotation = this.xyztovec(e.rotation); + e.scale = this.xyztovec(e.scale); + console.log(e); + this.updateObserver.notifyObservers(e); + }); + } + + private dummyEntity(): DiagramEntity { const entity: DiagramEntity = {}; entity.id = "test"; - entity.position = new Vector3(0,2,-2); + entity.position = new Vector3(0,2,-5); entity.rotation = Vector3.Zero(); entity.last_seen = new Date(); - entity.scale = Vector3.One(); + entity.scale = new Vector3(.1,.1,.1); entity.color = "#ff0000"; entity.text = "test"; entity.parent = null; - entity.template = "#box-template"; - - this.updateObserver.notifyObservers(entity); + entity.template = "#text-template"; + return entity; } - - } \ No newline at end of file diff --git a/src/information/hud.ts b/src/information/hud.ts index 74f4fac..9823fc7 100644 --- a/src/information/hud.ts +++ b/src/information/hud.ts @@ -5,7 +5,7 @@ import {Controllers} from "../controllers/controllers"; export class Hud { private scene: Scene; private parent: AbstractMesh; - private hudPlane: AbstractMesh; + private readonly hudPlane: AbstractMesh; constructor(parent: AbstractMesh, scene: Scene) { this.scene = scene; this.parent = parent; diff --git a/src/menus/bmenu.ts b/src/menus/bmenu.ts index e9cf293..704db43 100644 --- a/src/menus/bmenu.ts +++ b/src/menus/bmenu.ts @@ -1,8 +1,6 @@ import { AbstractMesh, - Color3, Scene, - StandardMaterial, Vector3, WebXRExperienceHelper, WebXRInputSource @@ -16,7 +14,7 @@ import {DiagramEntity, DiagramEvent, DiagramEventType} from "../diagram/diagramE export class Bmenu { private state: BmenuState = BmenuState.NONE; private manager: GUI3DManager; - private scene: Scene; + private readonly scene: Scene; private rightController: AbstractMesh; private xr: WebXRExperienceHelper; @@ -52,6 +50,7 @@ export class Bmenu { public setState(state: BmenuState) { this.state = state; } + toggle(mesh: AbstractMesh) { console.log(mesh.name); if (this.manager) { @@ -64,13 +63,13 @@ export class Bmenu { const follower = panel.defaultBehavior.followBehavior; follower.maxViewHorizontalDegrees = 45; follower.useFixedVerticalOffset = true; - follower.fixedVerticalOffset = 1; + follower.fixedVerticalOffset = 1; follower.defaultDistance = 2; follower.maximumDistance = 3; follower.minimumDistance = 1; panel.backPlateMargin = .01; - panel.scaling= new Vector3(.5, .5, .1); + panel.scaling = new Vector3(.5, .5, .1); panel.margin = .01; //panel.scaling.x = .5; //panel.scaling.y = .5; @@ -79,6 +78,7 @@ export class Bmenu { panel.addButton(this.makeButton("Add Box", "addBox")); panel.addButton(this.makeButton("Add Sphere", "addSphere")); panel.addButton(this.makeButton("Add Cylinder", "addCylinder")); + panel.addButton(this.makeButton("Add Text", "addText")); panel.addButton(this.makeButton("Done Adding", "doneAdding")); this.manager.controlScaling = .5; @@ -91,11 +91,11 @@ export class Bmenu { const id = this?.rightController?.id || null; let entity: DiagramEntity = { template: null, - position: new Vector3(-.01, -.1, .14), + position: new Vector3(-0.02, -.090, .13), rotation: new Vector3(76.04, 0, 0), scale: new Vector3(.1, .1, .1), color: "#CC0000", - text: "test", + text: "text", last_seen: new Date(), parent: id }; @@ -113,6 +113,10 @@ export class Bmenu { entity.template = "#cylinder-template"; this.state = BmenuState.ADDING; break; + case "addText": + entity.template = "#text-template"; + this.state = BmenuState.ADDING; + break; case "doneAdding": this.state = BmenuState.NONE; @@ -133,14 +137,5 @@ export class Bmenu { } DiagramManager.onDiagramEventObservable.notifyObservers(event); } - - - - } - - #createDefaultMaterial() { - const myMaterial = new StandardMaterial("myMaterial", this.scene); - myMaterial.diffuseColor = Color3.FromHexString("#CEE"); - return myMaterial; } } \ No newline at end of file diff --git a/src/server/ring/ringCamera.ts b/src/server/ring/ringCamera.ts index 593e68c..b1722e4 100644 --- a/src/server/ring/ringCamera.ts +++ b/src/server/ring/ringCamera.ts @@ -4,12 +4,12 @@ export class RingCamera { private ringApi: RingApi; constructor() { - const ringApi = new RingApi({ + this.ringApi = new RingApi({ refreshToken: process.env.RING_TOKEN, cameraStatusPollingSeconds: 30, debug: true }); - this.ringApi = ringApi; + } public async getCameras() { diff --git a/src/util/mapt.ts b/src/util/mapt.ts index 8bacb99..3e87ee2 100644 --- a/src/util/mapt.ts +++ b/src/util/mapt.ts @@ -9,8 +9,8 @@ export class Mapt { } buildMapImage() { - const apiKey = '073I3Pfe4lzoSf8tNriR'; - maptilerClient.config.apiKey = apiKey; + + maptilerClient.config.apiKey = '073I3Pfe4lzoSf8tNriR'; const link = maptilerClient.staticMaps.centered( [-88.8711198, 42.3370588], @@ -19,7 +19,6 @@ export class Mapt { ); const plane = MeshBuilder.CreatePlane("plane", {width: 10, height: 10}, this.scene); const materialPlane = new StandardMaterial("texturePlane", this.scene); - const zoom = 10; const sphere = MeshBuilder.CreateSphere("cams", {diameter: .1}, this.scene); sphere.position.y = 0.2; sphere.position.z = -5;