From 77f5a2543f24e9429c30a350a1e044fb0f58613f Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Thu, 3 Aug 2023 11:36:30 -0500 Subject: [PATCH] Updated P2P code, moved some classes to increase modularity. --- src/app.ts | 31 ++- src/controllers/base.ts | 5 - src/diagram/diagramEntity.ts | 2 - src/diagram/diagramManager.ts | 72 ++++++- src/diagram/diagramShapePhysics.ts | 52 ----- src/information/inputTextView.ts | 52 ++++- .../iPersistenceManager.ts | 2 +- .../indexdbPersistenceManager.ts | 4 +- src/integration/peerjsNetworkConnection.ts | 29 +-- src/menus/editMenu.ts | 6 +- src/toolbox/toolbox.ts | 199 +++++++++--------- src/util/appConfig.ts | 2 +- src/util/cameraHelper.ts | 3 +- 13 files changed, 266 insertions(+), 193 deletions(-) delete mode 100644 src/diagram/diagramShapePhysics.ts rename src/{diagram => integration}/iPersistenceManager.ts (94%) rename src/{diagram => integration}/indexdbPersistenceManager.ts (98%) diff --git a/src/app.ts b/src/app.ts index 041dcd3..2115607 100644 --- a/src/app.ts +++ b/src/app.ts @@ -24,6 +24,7 @@ import log from "loglevel"; import {AppConfig} from "./util/appConfig"; import {DiaSounds} from "./util/diaSounds"; import {PeerjsNetworkConnection} from "./integration/peerjsNetworkConnection"; +import {InputTextView} from "./information/inputTextView"; export class App { //preTasks = [havokModule]; @@ -60,6 +61,26 @@ export class App { const engine = new Engine(canvas, true); const scene = new Scene(engine); + const query = Object.fromEntries(new URLSearchParams(window.location.search)); + this.logger.debug('Query', query); + if (query.shareCode) { + scene.onReadyObservable.addOnce(() => { + this.logger.debug('Scene ready'); + const identityView = new InputTextView({scene: scene, text: ""}); + identityView.onTextObservable.add((text) => { + if (text?.text?.trim() != "") { + this.logger.debug('Identity', text.text); + const network = new PeerjsNetworkConnection(query.shareCode, text.text); + if (query.host) { + network.connect(query.host); + } + } + }); + identityView.show(); + }); + } + + this.scene = scene; const sounds = new DiaSounds(scene); sounds.enter.autoplay = true; @@ -114,9 +135,8 @@ export class App { const diagramManager = new DiagramManager(this.scene, xr.baseExperience); this.rig = new Rigplatform(this.scene, xr, diagramManager); const toolbox = new Toolbox(scene, xr.baseExperience, diagramManager); - //const network = new PeerjsNetworkConnection(); - import ('./diagram/indexdbPersistenceManager').then((module) => { + import ('./integration/indexdbPersistenceManager').then((module) => { const persistenceManager = new module.IndexdbPersistenceManager("diagram"); diagramManager.setPersistenceManager(persistenceManager); AppConfig.config.setPersistenceManager(persistenceManager); @@ -206,8 +226,13 @@ export class App { engine.runRenderLoop(() => { scene.render(); - }); + + //const data = window.location.search.replace('?', '') + // .split('&') + // .map((x) => x.split('=')); + //const network = new PeerjsNetworkConnection(); + //network.connect(data[0][1]); this.logger.info('Render loop started'); } diff --git a/src/controllers/base.ts b/src/controllers/base.ts index afbc4d7..d076d90 100644 --- a/src/controllers/base.ts +++ b/src/controllers/base.ts @@ -14,8 +14,6 @@ import {DiagramManager} from "../diagram/diagramManager"; import {DiagramEvent, DiagramEventType} from "../diagram/diagramEntity"; import log from "loglevel"; import {Controllers} from "./controllers"; -import {DiagramShapePhysics} from "../diagram/diagramShapePhysics"; - export class Base { static stickVector = Vector3.Zero(); @@ -151,9 +149,6 @@ export class Base { } transformNode.setParent(this.controller.motionController.rootMesh); this.grabbedMeshParentId = transformNode.id; - DiagramShapePhysics - .applyPhysics(newMesh, this.scene) - .setMotionType(PhysicsMotionType.ANIMATED); //newMesh && newMesh.setParent(this.controller.motionController.rootMesh); diff --git a/src/diagram/diagramEntity.ts b/src/diagram/diagramEntity.ts index 153f041..53dde5e 100644 --- a/src/diagram/diagramEntity.ts +++ b/src/diagram/diagramEntity.ts @@ -10,8 +10,6 @@ export enum DiagramEventType { CLEAR, CHANGECOLOR, COPY - - } diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index 8c239f4..c068522 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -6,19 +6,21 @@ import { InstancedMesh, Mesh, Observable, + PhysicsAggregate, + PhysicsBody, PhysicsMotionType, + PhysicsShapeType, PlaySoundAction, Scene, WebXRExperienceHelper } from "@babylonjs/core"; import {DiagramEntity, DiagramEvent, DiagramEventType} from "./diagramEntity"; -import {IPersistenceManager} from "./iPersistenceManager"; +import {IPersistenceManager} from "../integration/iPersistenceManager"; import {MeshConverter} from "./meshConverter"; import log from "loglevel"; import {Controllers} from "../controllers/controllers"; import {DiaSounds} from "../util/diaSounds"; import {AppConfig} from "../util/appConfig"; -import {DiagramShapePhysics} from "./diagramShapePhysics"; import {TextLabel} from "./textLabel"; export class DiagramManager { @@ -41,8 +43,9 @@ export class DiagramManager { } return this.persistenceManager; } - private readonly actionManager: ActionManager; + private readonly actionManager: ActionManager; + private config: AppConfig; constructor(scene: Scene, xr: WebXRExperienceHelper) { this.scene = scene; this.xr = xr; @@ -86,6 +89,7 @@ export class DiagramManager { } newMesh.material = mesh.material; newMesh.metadata = mesh.metadata; + DiagramShapePhysics.applyPhysics(newMesh, this.scene); return newMesh; } @@ -127,9 +131,14 @@ export class DiagramManager { break; case DiagramEventType.ADD: this.getPersistenceManager()?.add(mesh); + DiagramShapePhysics + .applyPhysics(mesh, this.scene); + break; case DiagramEventType.MODIFY: this.getPersistenceManager()?.modify(mesh); + DiagramShapePhysics + .applyPhysics(mesh, this.scene); break; case DiagramEventType.CHANGECOLOR: if (!event.oldColor) { @@ -160,4 +169,61 @@ export class DiagramManager { break; } } + +} + +class DiagramShapePhysics { + private static logger: log.Logger = log.getLogger('DiagramShapePhysics'); + + public static applyPhysics(mesh: AbstractMesh, scene: Scene): PhysicsBody { + if (!mesh?.metadata?.template) { + this.logger.error("applyPhysics: mesh.metadata.template is null", mesh); + return null; + } + if (!scene) { + this.logger.error("applyPhysics: mesh or scene is null"); + return null; + } + if (mesh.physicsBody) { + mesh.physicsBody.dispose(); + } + + let shapeType = PhysicsShapeType.BOX; + switch (mesh.metadata.template) { + case "#sphere-template": + shapeType = PhysicsShapeType.SPHERE; + break; + case "#cylinder-template": + shapeType = PhysicsShapeType.CYLINDER; + break; + case "#cone-template": + shapeType = PhysicsShapeType.CONVEX_HULL; + break; + + } + let mass = mesh.scaling.x * mesh.scaling.y * mesh.scaling.z * 10; + + const aggregate = new PhysicsAggregate(mesh, + shapeType, {mass: mass, restitution: .02, friction: .9}, scene); + if (mesh.parent) { + aggregate.body + .setMotionType(PhysicsMotionType.ANIMATED); + } else { + aggregate.body + .setMotionType(PhysicsMotionType.DYNAMIC); + } + aggregate.body.setCollisionCallbackEnabled(true); + aggregate.body.getCollisionObservable().add((event, state) => { + if (event.distance > .001 && !DiaSounds.instance.low.isPlaying) { + this.logger.debug(event, state); + DiaSounds.instance.low.play(); + } + }, -1, false, this); + const body = aggregate.body; + body.setMotionType(PhysicsMotionType.ANIMATED); + body.setLinearDamping(.95); + body.setAngularDamping(.99); + body.setGravityFactor(0); + return aggregate.body; + } } \ No newline at end of file diff --git a/src/diagram/diagramShapePhysics.ts b/src/diagram/diagramShapePhysics.ts deleted file mode 100644 index c778aa1..0000000 --- a/src/diagram/diagramShapePhysics.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {AbstractMesh, PhysicsAggregate, PhysicsBody, PhysicsMotionType, PhysicsShapeType, Scene} from "@babylonjs/core"; -import {DiaSounds} from "../util/diaSounds"; -import log from "loglevel"; - -export class DiagramShapePhysics { - private static logger: log.Logger = log.getLogger('DiagramShapePhysics'); - - public static applyPhysics(mesh: AbstractMesh, scene: Scene): PhysicsBody { - if (!mesh?.metadata?.template) { - this.logger.error("applyPhysics: mesh.metadata.template is null", mesh); - return null; - } - if (!scene) { - this.logger.error("applyPhysics: mesh or scene is null"); - return null; - } - if (mesh.physicsBody) { - mesh.physicsBody.dispose(); - } - - let shapeType = PhysicsShapeType.BOX; - switch (mesh.metadata.template) { - case "#sphere-template": - shapeType = PhysicsShapeType.SPHERE; - break; - case "#cylinder-template": - shapeType = PhysicsShapeType.CYLINDER; - break; - case "#cone-template": - shapeType = PhysicsShapeType.CONVEX_HULL; - break; - - } - let mass = mesh.scaling.x * mesh.scaling.y * mesh.scaling.z * 10; - const aggregate = new PhysicsAggregate(mesh, - shapeType, {mass: mass, restitution: .02, friction: .9}, scene); - aggregate.body.setCollisionCallbackEnabled(true); - aggregate.body.getCollisionObservable().add((event, state) => { - if (event.distance > .001 && !DiaSounds.instance.low.isPlaying) { - this.logger.debug(event, state); - DiaSounds.instance.low.play(); - } - - }, -1, false, this); - const body = aggregate.body; - body.setMotionType(PhysicsMotionType.ANIMATED); - body.setLinearDamping(.95); - body.setAngularDamping(.99); - body.setGravityFactor(0); - return aggregate.body; - } -} \ No newline at end of file diff --git a/src/information/inputTextView.ts b/src/information/inputTextView.ts index d4f90da..c91978e 100644 --- a/src/information/inputTextView.ts +++ b/src/information/inputTextView.ts @@ -1,23 +1,65 @@ import {Right} from "../controllers/right"; import {Left} from "../controllers/left"; -import {Observable, WebXRSessionManager} from "@babylonjs/core"; +import {Observable, Scene, WebXRSessionManager} from "@babylonjs/core"; import log from "loglevel"; +import {AdvancedDynamicTexture, InputText} from "@babylonjs/gui"; export type TextEvent = { text: string; } +export type InputTextViewOptions = { + scene?: Scene; + xrSession?: WebXRSessionManager; + text?: string; +} export class InputTextView { public readonly onTextObservable: Observable = new Observable(); private text: string; - private xrSession: WebXRSessionManager; + private readonly scene: Scene; + private readonly xrSession: WebXRSessionManager; - constructor(xrSession: WebXRSessionManager, text: string) { - this.xrSession = xrSession; - this.text = text; + constructor(options: InputTextViewOptions) { + if (options.text) { + this.text = options.text; + } + if (options.xrSession) { + this.xrSession = options.xrSession; + } + if (options.scene) { + this.scene = options.scene; + } } public show() { + if (this?.xrSession?.inXRSession) { + this.showXr(); + } else { + this.showWeb(); + } + } + + public showWeb() { + const textInput = new InputText('identity', this.text); + textInput.width = 0.2; + textInput.height = "40px"; + textInput.color = "white"; + textInput.background = "black"; + textInput.focusedBackground = "black"; + + const advancedTexture = AdvancedDynamicTexture.CreateFullscreenUI("myUI"); + advancedTexture.addControl(textInput); + textInput.onKeyboardEventProcessedObservable.add((evt) => { + if (evt.key === 'Enter') { + this.onTextObservable.notifyObservers({text: textInput.text}); + textInput.dispose(); + advancedTexture.dispose(); + } + + }); + } + + private showXr() { const textInput = document.createElement("input"); textInput.type = "text"; document.body.appendChild(textInput); diff --git a/src/diagram/iPersistenceManager.ts b/src/integration/iPersistenceManager.ts similarity index 94% rename from src/diagram/iPersistenceManager.ts rename to src/integration/iPersistenceManager.ts index ee86940..e2ab8d1 100644 --- a/src/diagram/iPersistenceManager.ts +++ b/src/integration/iPersistenceManager.ts @@ -1,5 +1,5 @@ import {AbstractMesh, Color3, Observable} from "@babylonjs/core"; -import {DiagramEntity} from "./diagramEntity"; +import {DiagramEntity} from "../diagram/diagramEntity"; import {AppConfigType} from "../util/appConfigType"; export enum DiagramListingEventType { diff --git a/src/diagram/indexdbPersistenceManager.ts b/src/integration/indexdbPersistenceManager.ts similarity index 98% rename from src/diagram/indexdbPersistenceManager.ts rename to src/integration/indexdbPersistenceManager.ts index 2163fe7..629a35c 100644 --- a/src/diagram/indexdbPersistenceManager.ts +++ b/src/integration/indexdbPersistenceManager.ts @@ -1,8 +1,8 @@ import {DiagramListing, DiagramListingEvent, DiagramListingEventType, IPersistenceManager} from "./iPersistenceManager"; import {AbstractMesh, Observable, Vector3} from "@babylonjs/core"; -import {DiagramEntity} from "./diagramEntity"; +import {DiagramEntity} from "../diagram/diagramEntity"; import Dexie from "dexie"; -import {MeshConverter} from "./meshConverter"; +import {MeshConverter} from "../diagram/meshConverter"; import log from "loglevel"; import {AppConfigType} from "../util/appConfigType"; diff --git a/src/integration/peerjsNetworkConnection.ts b/src/integration/peerjsNetworkConnection.ts index 2f2d539..ce91e6b 100644 --- a/src/integration/peerjsNetworkConnection.ts +++ b/src/integration/peerjsNetworkConnection.ts @@ -5,40 +5,33 @@ export class PeerjsNetworkConnection { private logger: log.Logger = log.getLogger('PeerjsNetworkConnection'); private dataChannel: P2PDataChannel; - constructor() { + constructor(dataChannel: string, identity: string) { + const config = { debug: false, - dataChannel: 'default', + dataChannel: dataChannel, connectionTimeout: 5000, pingInterval: 4000, pingTimeout: 8000 } - - - const data = window.location.search.replace('?', '') - .split('&') - .map((x) => x.split('=')); - this.dataChannel = new P2PDataChannel(data[0][1], config); + this.dataChannel = new P2PDataChannel(identity, config); this.dataChannel.onConnected((peerId) => { this.logger.debug(peerId, ' connected'); }); this.dataChannel.onMessage((message) => { - this.logger.debug(message.payload, ' received from ', message.sender); + if (message.sender !== this.dataChannel.localPeerId) { + this.logger.debug(message.payload, ' received from ', message.sender); + } }); - this.connect(); - } - private async connect() { + public connect(host: string) { try { - const data = window.location.search.replace('?', '') - .split('&') - .map((x) => x.split('=')); - const connection = await this.dataChannel.connect(data[1][1]).then(() => { - console.log('Connected'); + this.dataChannel.connect(host).then((peerId) => { + this.logger.debug('Broadcasting Join', peerId); + this.dataChannel.broadcast({payload: 'Joined'}); }); - this.dataChannel.broadcast({payload: 'Hello World'}); } catch (err) { this.logger.error(err); } diff --git a/src/menus/editMenu.ts b/src/menus/editMenu.ts index f096f99..246e728 100644 --- a/src/menus/editMenu.ts +++ b/src/menus/editMenu.ts @@ -17,14 +17,11 @@ import {InputTextView} from "../information/inputTextView"; import {DiaSounds} from "../util/diaSounds"; import {CameraHelper} from "../util/cameraHelper"; import {TextLabel} from "../diagram/textLabel"; -import {DiagramShapePhysics} from "../diagram/diagramShapePhysics"; export class EditMenu { private state: EditMenuState = EditMenuState.NONE; private manager: GUI3DManager; private readonly scene: Scene; - private textView: InputTextView; - private textInput: HTMLElement; private readonly logger: log.Logger = log.getLogger('EditMenu'); private gizmoManager: GizmoManager; private readonly xr: WebXRExperienceHelper; @@ -158,7 +155,6 @@ export class EditMenu { if (mesh) { const newMesh = this.diagramManager.createCopy(mesh, true); newMesh.setParent(null); - DiagramShapePhysics.applyPhysics(newMesh, this.scene); } this.logger.warn('copying not implemented', mesh); //@todo implement @@ -170,7 +166,7 @@ export class EditMenu { if (mesh?.metadata?.text) { text = mesh.metadata.text; } - const textInput = new InputTextView(this.xr.sessionManager, text); + const textInput = new InputTextView({xrSession: this.xr.sessionManager, text: text}); textInput.show(); textInput.onTextObservable.addOnce((value) => { this.persist(mesh, value.text); diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index 6cbf165..75e5e8b 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -1,6 +1,5 @@ import { AbstractMesh, - Angle, Color3, InstancedMesh, Mesh, @@ -16,6 +15,7 @@ import {CameraHelper} from "../util/cameraHelper"; import {AdvancedDynamicTexture, Button3D, ColorPicker, GUI3DManager, StackPanel3D, TextBlock} from "@babylonjs/gui"; import {DiagramManager} from "../diagram/diagramManager"; import {DiagramEventType} from "../diagram/diagramEntity"; +import {Controllers} from "../controllers/controllers"; export enum ToolType { BOX ="#box-template", @@ -60,8 +60,7 @@ export class Toolbox { handleMaterial.diffuseColor = Color3.FromHexString("#EEEEFF"); handleMaterial.alpha = .5; handle.material = handleMaterial; - handle.position = CameraHelper.getFrontPosition(2, this.scene); - handle.position.y = 1.6; + handle.position = Vector3.Zero(); this.node.parent = handle; this.xr = xr; @@ -72,97 +71,15 @@ export class Toolbox { } Toolbox.instance = this; - } - - private buildToolbox() { - 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 text = new TextBlock("add-button-text", "Add Color"); - text.color = "white"; - text.fontSize = "48px"; - text.text = "Add Color"; - addButton.content = text; - this.addPanel.node.parent = this.node; - this.addPanel.addControl(addButton); - this.addPanel.node.rotation = - new Vector3( - Angle.FromDegrees(0).radians(), - Angle.FromDegrees(180).radians(), - Angle.FromDegrees(0).radians()); - this.addPanel.node.scaling = new Vector3(.1, .1,.1); - this.addPanel.position = new Vector3(0, 0, .5); - addButton.onPointerClickObservable.add(() => { - this.buildColor(Color3.Random()); - }); - - } - private calculatePosition(i: number) { - return (i/this.gridsize)-.5-(1/this.gridsize/2); - } - private static WIDGET_SIZE = .1; - private buildColor(color: Color3) { - - const width = 1; - const depth = .2; - const material = new StandardMaterial("material-" + color.toHexString(), this.scene); - material.diffuseColor = color; - const mesh = MeshBuilder.CreateBox("toolbox-color-" + color.toHexString(), {width: width, height: .01, depth: depth}, this.scene); - mesh.material = material; - mesh.position.z = this.index++/4; - mesh.parent = this.node; - let i = 0; - for (const tool of enumKeys(ToolType)) { - const newItem = this.buildTool(ToolType[tool], mesh); - if (newItem) { - newItem.position = new Vector3(this.calculatePosition(++i), .1, 0); - } - } - const colorPickerPlane = MeshBuilder - .CreatePlane("myPlane", - { - width: Toolbox.WIDGET_SIZE, - height: Toolbox.WIDGET_SIZE - }, this.scene); - colorPickerPlane.parent = mesh; - colorPickerPlane.position = new Vector3(this.calculatePosition(++i), .1, 0); - - const colorPickerTexture = AdvancedDynamicTexture.CreateForMesh(colorPickerPlane, 1024, 1024); - const colorPicker = new ColorPicker("color-picker"); - colorPicker.scaleY = 5; - colorPicker.scaleX = 5; - colorPicker.value = color; - colorPicker.onValueChangedObservable.add((value) => { - const oldColor = material.diffuseColor.clone(); - const newColor = value.clone(); - material.diffuseColor = newColor; - const newColorHex = newColor.toHexString(); - material.id = "material-" + newColorHex; - material.name = "material-" + newColorHex; - mesh.id = "toolbox-color-" + newColorHex; - mesh.name = "toolbox-color-" + newColorHex; - this.diagramManager.onDiagramEventObservable.notifyObservers( - { - type: DiagramEventType.CHANGECOLOR, - oldColor: oldColor, - newColor: newColor + Controllers.controllerObserver.add((evt) => { + if (evt.type == 'y-button') { + if (evt.value == 1) { + this.node.parent.setEnabled(!this.node.parent.isEnabled(false)); + CameraHelper.setMenuPosition(this.node.parent as Mesh, this.scene, + new Vector3(0, -.5, 0)); } - ); + } }); - - colorPickerTexture.addControl(colorPicker); - this.addPanel.position.z += .25; - this.node.position.z -= .125; - } - public updateToolbox(color: string) { - if (this.scene.getMeshById("toolbox-color-" + color)) { - return; - } else { - this.buildColor(Color3.FromHexString(color)); - } } public buildTool(tool: ToolType, parent: AbstractMesh) { @@ -208,20 +125,112 @@ export class Toolbox { } else { newItem.metadata = {template: tool}; } - const instance = new InstancedMesh("instance-"+id, newItem); + const instance = new InstancedMesh("instance-" + id, newItem); if (instance.metadata) { instance.metadata.template = tool; } else { instance.metadata = {template: tool}; } - instance.parent= parent; - newItem.setEnabled(false) + instance.parent = parent; + newItem.setEnabled(false); + newItem.onEnabledStateChangedObservable.add(() => { + instance.setEnabled(false); + }); return instance; } else { return null; } } + private calculatePosition(i: number) { + return (i/this.gridsize)-.5-(1/this.gridsize/2); + } + private static WIDGET_SIZE = .1; + + private buildToolbox() { + 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 text = new TextBlock("add-button-text", "Add Color"); + text.color = "white"; + text.fontSize = "48px"; + text.text = "Add Color"; + addButton.content = text; + this.addPanel.node.parent = this.node.parent; + this.addPanel.addControl(addButton); + this.addPanel.node.scaling = new Vector3(.1, .1, .1); + this.addPanel.position = new Vector3(-.25, 0, 0); + addButton.onPointerClickObservable.add(() => { + this.buildColor(Color3.Random()); + }); + this.node.parent.setEnabled(false); + + } + public updateToolbox(color: string) { + if (this.scene.getMeshById("toolbox-color-" + color)) { + return; + } else { + this.buildColor(Color3.FromHexString(color)); + } + } + + private buildColor(color: Color3) { + + const width = 1; + const depth = .2; + const material = new StandardMaterial("material-" + color.toHexString(), this.scene); + material.diffuseColor = color; + const mesh = MeshBuilder.CreateBox("toolbox-color-" + color.toHexString(), {width: width, height: .01, depth: depth}, this.scene); + mesh.material = material; + mesh.position.z = this.index++/4; + mesh.parent = this.node; + let i = 0; + for (const tool of enumKeys(ToolType)) { + const newItem = this.buildTool(ToolType[tool], mesh); + if (newItem) { + newItem.position = new Vector3(this.calculatePosition(++i), .1, 0); + } + } + const colorPickerPlane = MeshBuilder + .CreatePlane("colorPickerPlane", + { + width: Toolbox.WIDGET_SIZE, + height: Toolbox.WIDGET_SIZE + }, this.scene); + const colorPickerTexture = AdvancedDynamicTexture.CreateForMesh(colorPickerPlane, 1024, 1024); + colorPickerPlane.parent = mesh; + colorPickerPlane.position = new Vector3(this.calculatePosition(++i), .1, 0); + + + const colorPicker = new ColorPicker("color-picker"); + colorPicker.scaleY = 5; + colorPicker.scaleX = 5; + colorPicker.value = color; + colorPicker.onValueChangedObservable.add((value) => { + const oldColor = material.diffuseColor.clone(); + const newColor = value.clone(); + material.diffuseColor = newColor; + const newColorHex = newColor.toHexString(); + material.id = "material-" + newColorHex; + material.name = "material-" + newColorHex; + mesh.id = "toolbox-color-" + newColorHex; + mesh.name = "toolbox-color-" + newColorHex; + this.diagramManager.onDiagramEventObservable.notifyObservers( + { + type: DiagramEventType.CHANGECOLOR, + oldColor: oldColor, + newColor: newColor + } + ); + }); + + colorPickerTexture.addControl(colorPicker); + + } } + function enumKeys(obj: O): K[] { return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[]; } \ No newline at end of file diff --git a/src/util/appConfig.ts b/src/util/appConfig.ts index c7d5083..5f6de64 100644 --- a/src/util/appConfig.ts +++ b/src/util/appConfig.ts @@ -1,7 +1,7 @@ import {Angle, Vector3} from "@babylonjs/core"; import round from "round"; import log from "loglevel"; -import {IPersistenceManager} from "../diagram/persistenceManager"; +import {IPersistenceManager} from "../integration/iPersistenceManager"; import {AppConfigType} from "./appConfigType"; export type SnapValue = { diff --git a/src/util/cameraHelper.ts b/src/util/cameraHelper.ts index 5b61316..612e8e2 100644 --- a/src/util/cameraHelper.ts +++ b/src/util/cameraHelper.ts @@ -7,9 +7,10 @@ export class CameraHelper { return scene.activeCamera.globalPosition.add(offset); } - public static setMenuPosition(node: TransformNode, scene: Scene) { + public static setMenuPosition(node: TransformNode, scene: Scene, offset: Vector3 = Vector3.Zero()) { node.position = CameraHelper.getFrontPosition(2, scene); + node.position.addInPlace(offset); node.lookAt(scene.activeCamera.globalPosition); node.rotation.y = node.rotation.y + Math.PI; node.position.y += .2;