immersive2/src/toolbox/toolbox.ts

246 lines
8.9 KiB
TypeScript

import {
AbstractMesh,
Color3,
InstancedMesh,
Mesh,
MeshBuilder,
Scene,
StandardMaterial,
TransformNode,
Vector3,
WebXRExperienceHelper
} from "@babylonjs/core";
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",
Sphere="#sphere-template",
Cylinder="#cylinder-template",
Cone ="#cone-template",
PLANE ="#plane-template",
OBJECT ="#object-template",
}
export class Toolbox {
private index = 0;
public static instance: Toolbox;
private readonly scene: Scene;
private readonly xr: WebXRExperienceHelper;
public readonly node: TransformNode;
private readonly diagramManager: DiagramManager;
private readonly manager: GUI3DManager;
private readonly gridsize = 5;
private readonly addPanel: StackPanel3D;
private readonly controllers: Controllers;
private xObserver;
constructor(scene: Scene, xr: WebXRExperienceHelper, diagramManager: DiagramManager, controllers: Controllers) {
this.scene = scene;
this.controllers = controllers;
this.diagramManager = diagramManager;
this.diagramManager.onDiagramEventObservable.add((evt) => {
if (evt?.entity?.color && evt.type == DiagramEventType.CHANGECOLOR) {
this.updateToolbox(evt.entity.color);
}
}, -1, true, this);
this.addPanel = new StackPanel3D();
this.manager = new GUI3DManager(scene);
this.manager.addControl(this.addPanel);
this.node = new TransformNode("toolbox", this.scene);
const handle = MeshBuilder.CreateCapsule("handle", {
radius: .05,
orientation: Vector3.Right(),
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 = Vector3.Zero();
this.node.parent = handle;
this.xr = xr;
if (!this.scene.activeCamera) {
return;
} else {
this.buildToolbox();
}
Toolbox.instance = this;
if (!this.xObserver) {
this.xObserver = this.controllers.controllerObserver.add((evt) => {
if (evt.type == 'x-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, 0, 0));
}
}
});
}
}
public buildTool(tool: ToolType, parent: AbstractMesh) {
let newItem: Mesh;
const id = this.toolId(tool, (parent.material as StandardMaterial).diffuseColor);
const material = parent.material;
const toolname = "tool-" + id;
switch (tool) {
case ToolType.BOX:
newItem = MeshBuilder.CreateBox(toolname, {width: 1, height: 1, depth: 1}, this.scene);
break;
case ToolType.Sphere:
newItem = MeshBuilder.CreateSphere(toolname, {diameter: 1}, this.scene);
break;
case ToolType.Cylinder:
newItem = MeshBuilder.CreateCylinder(toolname, {height: 1, diameter: 1}, this.scene);
break;
case ToolType.Cone:
newItem = MeshBuilder.CreateCylinder(toolname, {diameterTop: 0, height: 1, diameterBottom: 1}, this.scene);
break;
case ToolType.PLANE:
newItem = MeshBuilder.CreatePlane(toolname, {width: 1, height: 1}, this.scene);
break;
case ToolType.OBJECT:
break;
}
if (newItem) {
newItem.material = material;
newItem.id = toolname
if (tool === ToolType.PLANE) {
newItem.material.backFaceCulling = false;
}
newItem.scaling = new Vector3(Toolbox.WIDGET_SIZE,
Toolbox.WIDGET_SIZE,
Toolbox.WIDGET_SIZE);
newItem.parent = parent;
if (!newItem.material) {
newItem.material = parent.material;
}
if (newItem.metadata) {
newItem.metadata.template = tool;
} else {
newItem.metadata = {template: tool};
}
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);
newItem.onEnabledStateChangedObservable.add(() => {
instance.setEnabled(false);
});
return instance;
} else {
return null;
}
}
private toolId(tool: ToolType, color: Color3) {
return tool + "-" + color.toHexString();
}
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;
mesh.metadata = {tool: 'color'};
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[];
}