Updated P2P code, moved some classes to increase modularity.
This commit is contained in:
parent
f44d84b7be
commit
77f5a2543f
31
src/app.ts
31
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');
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -10,8 +10,6 @@ export enum DiagramEventType {
|
||||
CLEAR,
|
||||
CHANGECOLOR,
|
||||
COPY
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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<TextEvent> = new Observable<TextEvent>();
|
||||
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);
|
||||
|
||||
@ -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 {
|
||||
@ -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";
|
||||
|
||||
@ -5,40 +5,33 @@ export class PeerjsNetworkConnection {
|
||||
private logger: log.Logger = log.getLogger('PeerjsNetworkConnection');
|
||||
private dataChannel: P2PDataChannel<any>;
|
||||
|
||||
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) => {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
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<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
|
||||
return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[];
|
||||
}
|
||||
@ -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 = {
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user