Updated menu system to be more consistent. Change toolbox to fixed colors;

This commit is contained in:
Michael Mainguy 2024-03-08 10:41:18 -06:00
parent 1413a0bda9
commit 73a850613d
17 changed files with 279 additions and 254 deletions

64
package-lock.json generated
View File

@ -8,14 +8,14 @@
"name": "immersive",
"version": "0.0.1",
"dependencies": {
"@babylonjs/core": "^6.21.3",
"@babylonjs/gui": "^6.21.3",
"@babylonjs/havok": "1.1.4",
"@babylonjs/inspector": "^6.21.3",
"@babylonjs/loaders": "^6.21.3",
"@babylonjs/materials": "^6.21.3",
"@babylonjs/procedural-textures": "^6.21.3",
"@babylonjs/serializers": "^6.21.3",
"@babylonjs/core": "^6.45.1",
"@babylonjs/gui": "^6.45.1",
"@babylonjs/havok": "1.3.1",
"@babylonjs/inspector": "^6.45.1",
"@babylonjs/loaders": "^6.45.1",
"@babylonjs/materials": "^6.45.1",
"@babylonjs/procedural-textures": "^6.45.1",
"@babylonjs/serializers": "^6.45.1",
"@cloudflare/workers-types": "^4.20230821.0",
"@netlify/functions": "^2.3.0",
"@typed-mxgraph/typed-mxgraph": "^1.0.8",
@ -49,14 +49,14 @@
}
},
"node_modules/@babylonjs/core": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.21.3.tgz",
"integrity": "sha512-j+UnhCSz3HldEWVHnxRHKGLqhWafju/WGqKaLXncbgo/Fkz48OoFbrec651jv1cBiNx82zdVa59c8VIK6FNSOw=="
"version": "6.45.1",
"resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.45.1.tgz",
"integrity": "sha512-wkORoAqpnZb10bUhrI0vinE9IiW7+gSgH4U4Zp41wO4kSeV0mtJY+Q5Ez6/n9ad9sLykD2FD7650B+Qi5tTMSw=="
},
"node_modules/@babylonjs/gui": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-6.21.3.tgz",
"integrity": "sha512-bsh6GT/tvWIDua1vSg2n3GeMGWnkuW7j9Dpi6+NVRjaXYEKdtbsSuWwND8JVClSoYa23qosIeeNPCqRPB8pq3g==",
"version": "6.45.1",
"resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-6.45.1.tgz",
"integrity": "sha512-lev/3nprv4t8lu3kW1zdlH7VzlWh9dmyZ2PkzybmBI6nB48bswPm7cX2ppaFTkpY8Z904js9TOsrYQotMzsUiw==",
"peerDependencies": {
"@babylonjs/core": "^6.0.0"
}
@ -74,17 +74,17 @@
}
},
"node_modules/@babylonjs/havok": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@babylonjs/havok/-/havok-1.1.4.tgz",
"integrity": "sha512-mK/sgqv4LaI4BjoQgWYGxOBX45hVauZ8PopPkIUIB42DiVPgWkiCZbBQ9pL7r1tbLhThvV1RTokj4z5bVGBayw==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@babylonjs/havok/-/havok-1.3.1.tgz",
"integrity": "sha512-ctaAQ2RN7hzE2vukGiA27//08YE4RNqH4RN26fCd8q0q7Qn+pXg4P61ZgakWYox/YS4VqHrB3ovZUDtPt2Scxg==",
"dependencies": {
"@types/emscripten": "^1.39.6"
}
},
"node_modules/@babylonjs/inspector": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-6.21.3.tgz",
"integrity": "sha512-XJfqYVilSWEDLRy7IaSxkGNAs5U+SHsWBi9+eS49bFsP06fGjcbJjF2ZK1qY7DbMEThbwzwD6VypJtjKaoF9kw==",
"version": "6.45.1",
"resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-6.45.1.tgz",
"integrity": "sha512-4YhJLD2FrVXUFIU+ttBanhevVaCBVLaBxhPuwrxXxKWkuBulFOQz7GkvRT/CJwA3Ad9/66qwS5+vfRT99GLM/w==",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.0",
"@fortawesome/free-regular-svg-icons": "^6.0.0",
@ -102,34 +102,34 @@
}
},
"node_modules/@babylonjs/loaders": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-6.21.3.tgz",
"integrity": "sha512-2GpMOEQFuih8O/l6IIyGd0i0gCYo6x8LPjD10FBFBVX2dSkazhWDaAQSS8HCZNY2Uennmy83ePzuTf5RvaKDaw==",
"version": "6.45.1",
"resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-6.45.1.tgz",
"integrity": "sha512-a75JvRVxT3DROCrl5iigLEpI5/eR7Rh4wdsDzZNn7bv3sXB40Kbw8EL60W3jDBXPdGUqNVzqtrxJF/ec2udg/Q==",
"peerDependencies": {
"@babylonjs/core": "^6.0.0",
"babylonjs-gltf2interface": "^6.0.0"
}
},
"node_modules/@babylonjs/materials": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-6.21.3.tgz",
"integrity": "sha512-oH/ZmiYKTJnvWzaZVNDPSjvekKK9C/eQZaiMcYiPTb39LHJw+bnIJS8NyvWjDLLn4cJ9XfEict69WqMb1XItVA==",
"version": "6.45.1",
"resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-6.45.1.tgz",
"integrity": "sha512-v1jqgG0bfX+8Qezq4eDT2sSjpQYW+ocNU70dbJF0AYrhuhhgf0SpM28C7DAKi9GUOKjMvZrIBEbFmJ6AijLjcQ==",
"peerDependencies": {
"@babylonjs/core": "^6.0.0"
}
},
"node_modules/@babylonjs/procedural-textures": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/@babylonjs/procedural-textures/-/procedural-textures-6.21.3.tgz",
"integrity": "sha512-S1KIMrrVubk06mxuVeLGH8YngUj1W0zhdYjp2tHP2a7gueARG8a2OhqLW4e0MwFxi7XnP9DuFyzmkK95XZMaKw==",
"version": "6.45.1",
"resolved": "https://registry.npmjs.org/@babylonjs/procedural-textures/-/procedural-textures-6.45.1.tgz",
"integrity": "sha512-QpLuPknYIvylfUscSqorkuXO4QSI49atQnScWN43ZpRzr+ecXWnxEaOyAmt7bbf93htshtIkUQdJhwx8w4fqrg==",
"peerDependencies": {
"@babylonjs/core": "^6.0.0"
}
},
"node_modules/@babylonjs/serializers": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-6.21.3.tgz",
"integrity": "sha512-E8jerSLDrDkwkNc8D9a9vmvPlNJiaXUl5m6cIUQ317oUk0/pT06NiM/N//HbyjM8ccnkaPUBkNBq4dxtPW/XSw==",
"version": "6.45.1",
"resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-6.45.1.tgz",
"integrity": "sha512-iSPUdjZhQIbSyi21IFiFC/wm6Bj4zcH9NhsN9i+xqLTbO/Uho49cHU0ew39n0Xn+LRsGRMTJWQLTsp+TPvAvrw==",
"peerDependencies": {
"@babylonjs/core": "^6.0.0",
"babylonjs-gltf2interface": "^6.0.0"

View File

@ -16,14 +16,14 @@
},
"dependencies": {
"axios": "^0.24.0",
"@babylonjs/core": "^6.21.3",
"@babylonjs/gui": "^6.21.3",
"@babylonjs/havok": "1.1.4",
"@babylonjs/inspector": "^6.21.3",
"@babylonjs/loaders": "^6.21.3",
"@babylonjs/materials": "^6.21.3",
"@babylonjs/procedural-textures": "^6.21.3",
"@babylonjs/serializers": "^6.21.3",
"@babylonjs/core": "^6.45.1",
"@babylonjs/gui": "^6.45.1",
"@babylonjs/havok": "1.3.1",
"@babylonjs/inspector": "^6.45.1",
"@babylonjs/loaders": "^6.45.1",
"@babylonjs/materials": "^6.45.1",
"@babylonjs/procedural-textures": "^6.45.1",
"@babylonjs/serializers": "^6.45.1",
"@cloudflare/workers-types": "^4.20230821.0",
"@netlify/functions": "^2.3.0",
"events": "^3.3.0",

View File

@ -143,7 +143,7 @@ export class Base {
this.previousScaling = mesh?.scaling.clone();
this.previousPosition = mesh?.position.clone();
if (("toolbox" != mesh?.parent?.parent?.id) || player) {
if (!mesh.metadata?.grabClone || player) {
if (mesh.physicsBody) {
const transformNode = setupTransformNode(mesh, this.controller.motionController.rootMesh);
mesh.physicsBody.setMotionType(PhysicsMotionType.ANIMATED);
@ -155,6 +155,8 @@ export class Base {
this.grabbedMesh = mesh;
} else {
const clone = grabAndClone(this.diagramManager, mesh, this.controller.motionController.rootMesh);
clone.newMesh.metadata.grabClone = false;
clone.newMesh.metadata.tool = false;
this.grabbedMeshParentId = clone.transformNode.id;
this.grabbedMesh = clone.newMesh;
this.previousParentId = null;

View File

@ -51,9 +51,16 @@ export class WebController {
}
if (kbInfo.type == KeyboardEventTypes.KEYUP) {
this.rig.turn(0);
this.rig.updown(0);
}
if (kbInfo.type == 1) {
switch (kbInfo.event.key) {
case "W":
this.rig.updown(-this.speed);
break;
case "S":
this.rig.updown(this.speed);
break;
case "ArrowUp":
case "w":
this.rig.forwardback(-this.speed);

View File

@ -1,4 +1,4 @@
import {MeshBuilder, Observable, Scene, Vector3, WebXRDefaultExperience} from "@babylonjs/core";
import {MeshBuilder, Observable, Scene, TransformNode, Vector3, WebXRDefaultExperience} from "@babylonjs/core";
import log, {Logger} from "loglevel";
import {AdvancedDynamicTexture, Control, InputText, VirtualKeyboard} from "@babylonjs/gui";
import {ControllerEventType, Controllers} from "../controllers/controllers";
@ -37,8 +37,10 @@ export class InputTextView {
public showVirtualKeyboard() {
const inputBaseNode = new TransformNode("inputBase", this.scene);
const inputMesh = MeshBuilder.CreatePlane("input", {width: 1, height: .5}, this.scene);
inputMesh.parent = inputBaseNode;
inputMesh.rotation.y = Math.PI;
const advancedTexture = AdvancedDynamicTexture.CreateForMesh(inputMesh, 2048, 1024, false);
const input = new InputText();
@ -96,7 +98,7 @@ export class InputTextView {
this.sounds.exit.play();
}
});
setMenuPosition(inputMesh, this.scene, new Vector3(0, .4, 0));
setMenuPosition(inputBaseNode, this.scene, new Vector3(0, .4, 0));
this.sounds.enter.play();
}

View File

@ -1,5 +1,5 @@
import {AdvancedDynamicTexture, CheckboxGroup, RadioGroup, SelectionPanel, StackPanel} from "@babylonjs/gui";
import {MeshBuilder, Scene, Vector3, WebXRDefaultExperience} from "@babylonjs/core";
import {MeshBuilder, Scene, TransformNode, Vector3, WebXRDefaultExperience} from "@babylonjs/core";
import {AppConfig} from "../util/appConfig";
import {ControllerEventType, Controllers} from "../controllers/controllers";
import {DiaSounds} from "../util/diaSounds";
@ -9,6 +9,7 @@ import {setMenuPosition} from "../util/functions/setMenuPosition";
export class ConfigMenu extends AbstractMenu {
private sounds: DiaSounds;
private config: AppConfig;
private readonly baseTransform: TransformNode;
private gridSnaps: Array<{ label: string, value: number }> = [
{label: "Off", value: 0},
{label: "0.01", value: 0.01},
@ -22,11 +23,11 @@ export class ConfigMenu extends AbstractMenu {
{label: "22.5", value: 22.5},
{label: "45", value: 45},
{label: "90", value: 90},
]
constructor(scene: Scene, xr: WebXRDefaultExperience, controllers: Controllers, config: AppConfig) {
super(scene, xr, controllers);
this.baseTransform = new TransformNode("configMenuBase", scene);
this.config = config;
this.sounds = new DiaSounds(scene);
@ -35,56 +36,72 @@ export class ConfigMenu extends AbstractMenu {
this.toggle();
}
});
this.buildMenu();
}
public toggle() {
if (this.handle) {
this.handle.mesh.dispose(false, true);
if (this.baseTransform.parent.isEnabled()) {
this.sounds.exit.play();
this.handle = null;
return;
}
this.baseTransform.parent.setEnabled(false);
} else {
this.sounds.enter.play();
this.baseTransform.parent.setEnabled(true);
}
setMenuPosition(this.handle.mesh, this.scene, new Vector3(.6, .1, 0));
}
private buildMenu() {
const configPlane = MeshBuilder
.CreatePlane("gridSizePlane",
.CreatePlane("configMenuPlane",
{
width: .6,
height: .3
}, this.scene);
this.createHandle(configPlane);
configPlane.rotation.y = Math.PI;
configPlane.setParent(this.baseTransform);
this.createHandle(this.baseTransform);
this.baseTransform.position.set(0, .2, 0);
const configTexture = AdvancedDynamicTexture.CreateForMesh(configPlane, 2048, 1024);
configTexture.background = "white";
//configTexture.background = "#00ffff";
const columnPanel = new StackPanel('columns');
columnPanel.fontSize = "48px";
columnPanel.isVertical = false;
//columnPanel.width = 1;
columnPanel.fontSize = "48px";
//columnPanel.background = "#ff0000";
//
configTexture.addControl(columnPanel);
const selectionPanel1 = new SelectionPanel("selectionPanel1");
selectionPanel1.width = .3;
selectionPanel1.width = "500px";
//selectionPanel1.width = .3;
columnPanel.addControl(selectionPanel1);
this.buildGridSizeControl(selectionPanel1);
this.buildCreateScaleControl(selectionPanel1);
const selectionPanel2 = new SelectionPanel("selectionPanel2");
selectionPanel2.width = .3;
selectionPanel2.width = "500px";
columnPanel.addControl(selectionPanel2);
this.buildRotationSnapControl(selectionPanel2);
this.buildTurnSnapControl(selectionPanel2);
const selectionPanel3 = new SelectionPanel("selectionPanel3");
selectionPanel3.width = .3;
selectionPanel3.width = "768px";
columnPanel.addControl(selectionPanel3);
this.buildFlyModeControl(selectionPanel3);
configPlane.position.set(0, .2, 0);
setMenuPosition(this.handle.mesh, this.scene, new Vector3(.6, .4, 0));
setMenuPosition(this.handle.mesh, this.scene, new Vector3(.6, .1, 0));
this.baseTransform.parent.setEnabled(false);
}
private adjustRadio(radio: RadioGroup | CheckboxGroup) {
radio.groupPanel.height = "512px";
radio.groupPanel.background = "#cccccc";
radio.groupPanel.color = "#000000";
radio.groupPanel.fontSize = "64px";
radio.groupPanel.children[0].height = "70px";
radio.groupPanel.paddingLeft = "16px";
@ -100,16 +117,15 @@ export class ConfigMenu extends AbstractMenu {
private buildCreateScaleControl(selectionPanel: SelectionPanel): RadioGroup {
const radio = new RadioGroup("Create Scale");
selectionPanel.addGroup(radio);
for (const [index, snap] of this.gridSnaps.entries()) {
const selected = (this.config.current.createSnap == snap.value);
console.log(selected);
radio.addRadio(snap.label, this.createVal.bind(this), selected);
}
this.adjustRadio(radio);
return radio;
}
private buildFlyModeControl(selectionPanel: SelectionPanel): CheckboxGroup {
const checkbox = new CheckboxGroup("Fly Mode");
selectionPanel.addGroup(checkbox);
@ -117,7 +133,6 @@ export class ConfigMenu extends AbstractMenu {
this.adjustRadio(checkbox);
return checkbox;
}
private buildRotationSnapControl(selectionPanel: SelectionPanel): RadioGroup {
const radio = new RadioGroup("Rotation Snap");
selectionPanel.addGroup(radio);
@ -128,15 +143,11 @@ export class ConfigMenu extends AbstractMenu {
this.adjustRadio(radio);
return radio;
}
private buildGridSizeControl(selectionPanel: SelectionPanel): RadioGroup {
const radio = new RadioGroup("Grid Snap");
selectionPanel.addGroup(radio);
for (const [index, snap] of this.gridSnaps.entries()) {
const selected = (this.config.current.gridSnap == snap.value);
radio.addRadio(snap.label, this.gridVal.bind(this), selected);
}
this.adjustRadio(radio);

View File

@ -168,20 +168,21 @@ export class EditMenu extends AbstractMenu {
//this.scaleMenu = new ScaleMenu(this.scene, this.xr, this.controllers);
this.sounds = new DiaSounds(scene);
this.diagramManager = diagramManager;
this.gizmoManager = new GizmoManager(scene);
/*this.gizmoManager = new GizmoManager(scene);
this.gizmoManager.boundingBoxGizmoEnabled = true;
this.gizmoManager.gizmos.boundingBoxGizmo.scaleBoxSize = .020;
this.gizmoManager.gizmos.boundingBoxGizmo.rotationSphereSize = .020;
this.gizmoManager.gizmos.boundingBoxGizmo.scaleDragSpeed = 2;
this.gizmoManager.clearGizmoOnEmptyPointerEvent = true;
this.gizmoManager.usePointerToAttachGizmos = false;
this.gizmoManager.usePointerToAttachGizmos = false;*/
this.manager = new GUI3DManager(this.scene);
const panel = new PlanePanel();
const panel = new PlanePanel();
panel.orientation = PlanePanel.FACEFORWARDREVERSED_ORIENTATION;
panel.columns = 4;
this.manager.addControl(panel);
//panel.addControl(this.makeButton("Cameras", "camera"));
panel.addControl(this.makeButton("Modify", "modify"));
//panel.addControl(this.makeButton("Modify", "modify"));
panel.addControl(this.makeButton("Remove", "remove"));
panel.addControl(this.makeButton("Label", "label"));
panel.addControl(this.makeButton("Copy", "copy"));
@ -218,7 +219,7 @@ export class EditMenu extends AbstractMenu {
});
this.panel = panel;
this.createHandle(this.manager.rootContainer.children[0].node);
this.manager.rootContainer.children[0].node.position.y = .2;
this.manager.rootContainer.children[0].node.position.y = .15;
this.isVisible = false;
}

View File

@ -17,6 +17,7 @@ export class Handle {
handle.metadata = {handle: true};
if (this.transformNode) {
this.transformNode.setParent(handle);
//this.transformNode.rotation.y = Math.PI;
}
this.mesh = handle;
}
@ -30,9 +31,9 @@ function getHandleMesh(name: string, scene: Scene): InstancedMesh {
return instance;
}
const handle = MeshBuilder.CreateCapsule("base-handle-mesh", {
radius: .05,
radius: .04,
orientation: Vector3.Right(),
height: .4
height: .3
}, scene);
handle.setEnabled(false);
handle.material = buildStandardMaterial('base-handle-material', scene, "#CCCCDD");

View File

@ -57,8 +57,9 @@ function DiagramList({display, onClick}) {
useEffect(() => {
const listDb = async () => {
const data = await indexedDB.databases();
let i = 0;
setDbList(data.filter((item) => item.name.indexOf('_pouch_') > -1).map((item) => {
return {name: item.name.replace('_pouch_', '')}
return {key: i++, name: item.name.replace('_pouch_', '')}
}));
};
listDb();
@ -71,7 +72,7 @@ function DiagramList({display, onClick}) {
<div id="startCreate"><a href="#" id="startCreateLink" onClick={onClick}>New</a></div>
<div id="diagramListContent">
<ul>
{dbList.map((item) => <li><a href={`/db/${item.name}`}>{item.name}</a></li>)}
{dbList.map((item) => <li key={item.key}><a href={`/db/${item.name}`}>{item.name}</a></li>)}
</ul>
</div>
</div>

View File

@ -1,75 +1,50 @@
import {Color3, MeshBuilder, Observable, Scene, StandardMaterial, TransformNode, Vector3} from "@babylonjs/core";
import {Color3, MeshBuilder, Node, Scene, StandardMaterial, TransformNode, Vector3} from "@babylonjs/core";
import {enumKeys} from "../../util/functions/enumKeys";
import {ToolType} from "../types/toolType";
import {buildTool} from "./buildTool";
import {AdvancedDynamicTexture, ColorPicker} from "@babylonjs/gui";
export function buildColor(color: Color3, scene: Scene, parent: TransformNode, index: number,
colorChangeObservable: Observable<{ oldColor: string, newColor: string }>) {
const width = 1;
const depth = .2;
//const material = new PBRMaterial("material-" + color.toHexString(), scene);
export function buildColor(color: Color3, scene: Scene, parent: TransformNode, index: number): Node {
const width = .1;
const depth = .1;
const height = .01;
const material = new StandardMaterial("material-" + color.toHexString(), scene);
material.diffuseColor = color;
material.roughness = 1;
material.specularPower = 1;
//material.emissiveColor = color;
material.ambientColor = color;
//const material = new StandardMaterial("material-" + color.toHexString(), scene);
//material.albedoColor = color;
//material.metallic = 1;
//material.bumpTexture = new MarbleProceduralTexture("marble", 1024, scene);
//material.bumpTexture.level = 5;
const mesh = MeshBuilder.CreateBox("toolbox-color-" + color.toHexString(), {
//material.emissiveColor = new Color3(.1,.1,.1);
//material.roughness = 1;
//material.specularPower = .0001;
const colorBoxMesh = MeshBuilder.CreateBox("toolbox-color-" + color.toHexString(), {
width: width,
height: .01,
height: height,
depth: depth
}, scene);
mesh.material = material;
mesh.position.z = index / 4;
mesh.parent = parent;
mesh.metadata = {tool: 'color'};
colorBoxMesh.rotation.x = Math.PI / 2;
colorBoxMesh.material = material;
const rowLength = 8;
colorBoxMesh.position.x = -.45 + ((index % rowLength) / rowLength);
colorBoxMesh.position.y = -Math.floor(index / rowLength) * .1;
colorBoxMesh.parent = parent;
colorBoxMesh.metadata = {tool: 'color'};
let i = 0;
const tools = [];
for (const tool of enumKeys(ToolType)) {
const newItem = buildTool(ToolType[tool], mesh);
const newItem = buildTool(ToolType[tool], colorBoxMesh, material);
if (newItem) {
//buildColorPicker(scene, color, newItem, material, i, colorChangeObservable);
newItem.position = new Vector3(calculatePosition(++i), .1, 0);
tools.push(newItem.id);
}
}
const colorPickerPlane = MeshBuilder
.CreatePlane("colorPickerPlane",
{
width: .1,
height: .1
}, scene);
const colorPickerTexture = AdvancedDynamicTexture.CreateForMesh(colorPickerPlane, 1024, 1024);
colorPickerPlane.parent = mesh;
colorPickerPlane.position = new Vector3(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;
colorChangeObservable.notifyObservers({
oldColor: oldColor.toHexString(),
newColor: newColor.toHexString()
});
});
colorPickerTexture.addControl(colorPicker);
colorBoxMesh.metadata.tools = tools;
return colorBoxMesh;
}
const GRID_SIZE = 5;
function calculatePosition(i: number) {

View File

@ -7,11 +7,16 @@ export function buildMesh(type: ToolType, toolname: string, scene: Scene): Mesh
return MeshBuilder.CreateBox(toolname, {width: 1, height: 1, depth: 1}, scene);
case ToolType.SPHERE:
return MeshBuilder.CreateIcoSphere(toolname, {subdivisions: 5, radius: .5}, scene);
return MeshBuilder.CreateIcoSphere(toolname, {subdivisions: 6, radius: .5, flat: false}, scene);
//return MeshBuilder.CreateSphere(toolname, {diameter: 1}, scene);
case ToolType.CYLINDER:
return MeshBuilder.CreateCylinder(toolname, {height: 1, diameter: 1, subdivisions: 1, tessellation: 12}, scene);
return MeshBuilder.CreateCylinder(toolname, {
height: 1,
diameter: 1,
subdivisions: 1,
tessellation: 24
}, scene);
case ToolType.CONE:
return MeshBuilder.CreateCylinder(toolname, {
@ -19,7 +24,7 @@ export function buildMesh(type: ToolType, toolname: string, scene: Scene): Mesh
subdivisions: 1,
height: 1,
diameterBottom: 1,
tessellation: 12
tessellation: 24
}, scene);
case ToolType.PLANE:

View File

@ -1,44 +1,48 @@
import {AbstractMesh, Color3, InstancedMesh, PBRMaterial, StandardMaterial, Vector3} from "@babylonjs/core";
import {AbstractMesh, Color3, InstancedMesh, Material, PBRMaterial, StandardMaterial, Vector3} from "@babylonjs/core";
import {ToolType} from "../types/toolType";
import {buildMesh} from "./buildMesh";
const WIDGET_SIZE = .1;
export function buildTool(tool: ToolType, parent: AbstractMesh) {
export function buildTool(tool: ToolType, colorParent: AbstractMesh, material: Material) {
let id = "ID";
switch (parent.material.getClassName()) {
switch (material.getClassName()) {
case "StandardMaterial":
id = toolId(tool, (parent.material as StandardMaterial).diffuseColor);
id = toolId(tool, (material as StandardMaterial).diffuseColor);
break;
case "PBRMaterial":
id = toolId(tool, (parent.material as PBRMaterial).albedoColor);
id = toolId(tool, (material as PBRMaterial).albedoColor);
break;
default:
this.logger.warn("buildTool: parent.material is null");
}
const newItem = buildMesh(tool, `tool-${id}`, parent.getScene());
const newItem = buildMesh(tool, `tool-${id}`, colorParent.getScene());
if (!newItem) {
return null;
}
newItem.material = parent.material;
newItem.material = material;
if (tool === ToolType.PLANE) {
newItem.material.backFaceCulling = false;
}
newItem.scaling = new Vector3(WIDGET_SIZE,
WIDGET_SIZE,
WIDGET_SIZE);
newItem.parent = parent;
newItem.metadata = {template: tool, tool: true};
newItem.parent = colorParent.parent;
newItem.metadata = {template: tool, tool: true, grabClone: true};
const instance = new InstancedMesh("instance-" + id, newItem);
instance.metadata = {template: tool, tool: true};
instance.parent = parent;
instance.metadata = {template: tool, tool: true, grabClone: true};
instance.parent = colorParent.parent;
instance.setEnabled(false);
newItem.setEnabled(false);
/*
newItem.onEnabledStateChangedObservable.add(() => {
instance.setEnabled(false);
});
*/
return instance;
}

View File

@ -1,20 +0,0 @@
import {Color3, Scene, StandardMaterial, TransformNode} from "@babylonjs/core";
import {enumKeys} from "../util/functions/enumKeys";
import {ToolType} from "./types/toolType";
import {buildMesh} from "./functions/buildMesh";
export class SimpleToolbox {
private scene: Scene;
private transformNode: TransformNode;
constructor(scene: Scene) {
this.scene = scene;
this.transformNode = new TransformNode("SimpleToolbox", this.scene);
}
private buildBaseShapes(color: Color3) {
for (const tool of enumKeys(ToolType)) {
const mesh = buildMesh(ToolType[tool], id = toolId(tool, (parent.material as StandardMaterial).diffuseColor), this.transformNode);
}
}
}

View File

@ -1,57 +1,60 @@
import {AssetContainer, Color3, Mesh, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core";
import {Button3D, GUI3DManager, StackPanel3D, TextBlock} from "@babylonjs/gui";
import {ControllerEventType, Controllers} from "../controllers/controllers";
import {AxesViewer, Color3, Mesh, Node, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core";
import {GUI3DManager, StackPanel3D,} from "@babylonjs/gui";
import {setMenuPosition} from "../util/functions/setMenuPosition";
import {buildColor} from "./functions/buildColor";
import log from "loglevel";
import {Handle} from "../objects/handle";
const colors: string[] = [
"#222222", "#8b4513", "#006400", "#778899",
"#4b0082", "#ff0000", "#ffa500", "#ffff00",
"#00ff00", "#00ffff", "#0000ff", "#ff00ff",
"#1e90ff", "#98fb98", "#ffe4b5", "#ff69b4"
]
export class Toolbox {
private readonly logger = log.getLogger('Toolbox');
private index = 0;
public readonly toolboxBaseNode: TransformNode;
private readonly scene: Scene;
public readonly node: TransformNode;
private colorPicker: TransformNode;
private changing = false;
private readonly manager: GUI3DManager;
private readonly addPanel: StackPanel3D;
private readonly controllers: Controllers;
private readonly xObserver;
public readonly colorChangeObservable: Observable<{ oldColor: string, newColor: string }> =
new Observable<{ oldColor: string; newColor: string }>()
private handle: Handle;
constructor(scene: Scene, controllers: Controllers) {
private axes: AxesViewer;
constructor(scene: Scene) {
this.scene = scene;
this.controllers = controllers;
this.addPanel = new StackPanel3D();
this.manager = new GUI3DManager(scene);
this.manager.addControl(this.addPanel);
this.node = new TransformNode("toolbox", this.scene);
this.handle = new Handle(this.node);
this.node.position.y = .1;
this.node.position.z = .2;
this.node.scaling = new Vector3(0.6, 0.6, 0.6);
this.toolboxBaseNode = new TransformNode("toolbox", this.scene);
this.handle = new Handle(this.toolboxBaseNode);
this.toolboxBaseNode.position.y = .2;
//this.toolboxBaseNode.position.z = .05;
/**this.axes = new AxesViewer(this.scene);
this.axes.xAxis.parent = this.toolboxBaseNode;
this.axes.yAxis.parent = this.toolboxBaseNode;
this.axes.zAxis.parent = this.toolboxBaseNode;*/
this.toolboxBaseNode.scaling = new Vector3(0.6, 0.6, 0.6);
this.buildToolbox();
}
if (!this.xObserver) {
this.xObserver = this.controllers.controllerObserver.add((evt) => {
if (evt.type == ControllerEventType.X_BUTTON) {
if (evt.value == 1) {
this.node.parent.setEnabled(!this.node.parent.isEnabled(false));
setMenuPosition(this.node.parent as Mesh, this.scene,
public toggle() {
this.toolboxBaseNode.parent.setEnabled(!this.toolboxBaseNode.parent.isEnabled(false));
setMenuPosition(this.toolboxBaseNode.parent as Mesh, this.scene,
Vector3.Zero());
}
}
});
}
}
public updateToolbox(color: string) {
if (color) {
if (this.scene.getMeshById("toolbox-color-" + color)) {
return;
} else {
buildColor(Color3.FromHexString(color), this.scene, this.node, this.index++, this.colorChangeObservable);
buildColor(Color3.FromHexString(color), this.scene, this.toolboxBaseNode, this.index++);
}
} else {
this.logger.warn("updateToolbox called with no color");
@ -59,38 +62,48 @@ export class Toolbox {
}
private readonly objectObservable: Observable<AssetContainer> = new Observable();
private nodePredicate = (node: Node) => {
return node.getClassName() == "InstancedMesh" &&
node.isEnabled(false) == true
};
private buildToolbox() {
this.scene.onPointerObservable.add((pointerInfo) => {
if (pointerInfo.type == 1 && pointerInfo.pickInfo.pickedMesh?.metadata?.tool == 'color') {
if (this.changing) {
console.log('changing');
this.colorPicker.setEnabled(true);
return;
} else {
const active = pointerInfo.pickInfo.pickedMesh?.parent.getChildren(this.nodePredicate, true);
for (const node of active) {
node.setEnabled(false);
}
const nodes = pointerInfo.pickInfo.pickedMesh?.metadata?.tools;
if (nodes) {
for (const node of nodes) {
this.scene.getNodeById(node)?.setEnabled(true);
}
}
}
const color = "#7777FF";
buildColor(Color3.FromHexString(color), this.scene, this.node, this.index++, this.colorChangeObservable);
const addButton = createButton();
}
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);
//@TODO: move this somewhere else, just to prototype loading objects.
addButton.onPointerClickObservable.add(() => {
buildColor(Color3.Random(), this.scene, this.node, this.index++, this.colorChangeObservable);
});
//this.node.parent
this.node.parent.setEnabled(false);
setMenuPosition(this.node.parent as Mesh, this.scene,
let initial = true;
for (const c of colors) {
const cnode = buildColor(Color3.FromHexString(c), this.scene, this.toolboxBaseNode, this.index++);
if (initial) {
initial = false;
for (const id of cnode.metadata.tools) {
this.scene.getNodeById(id)?.setEnabled(true);
}
}
}
this.toolboxBaseNode.parent.setEnabled(false);
setMenuPosition(this.toolboxBaseNode.parent as Mesh, this.scene,
Vector3.Zero());
//
}
}
function createButton(): Button3D {
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;
return addButton;
}

View File

@ -33,9 +33,10 @@ export class CustomEnvironment {
if (loading) {
loading.remove();
}
const light = new HemisphericLight("light1", new Vector3(.1, 1, 0), scene);
const light = new HemisphericLight("light1", new Vector3(1, 2, 1), scene);
light.groundColor = new Color3(.1, .1, .1)
light.intensity = .6;
light.diffuse = new Color3(1, 1, 1);
light.intensity = .8;
const physics = new CustomPhysics(this.scene, config);
physics

View File

@ -36,17 +36,31 @@ export function setMenuPosition(node: TransformNode, scene: Scene, offset: Vecto
*/
if (scene.activeCamera) {
setPosition(node, scene, offset);
} else {
scene.onActiveCameraChanged.add((scene: Scene) => {
setPosition(node, scene, offset);
});
console.error("No active camera");
}
}
function setPosition(node: TransformNode, scene: Scene, offset: Vector3 = Vector3.Zero()) {
const platform = scene.getMeshByName("platform");
switch (scene.activeCamera.getClassName()) {
case "WebXRCamera":
node.parent = null;
const front = getFrontPosition(.8, scene);
//front.y = scene.activeCamera.globalPosition.y;
node.position = front;
node.position.addInPlace(offset);
node.position.y -= .5;
node.lookAt(scene.activeCamera.globalPosition);
node.rotation.y = node.rotation.y + Math.PI;
//const oldParent = node.parent;
//console.log(oldParent.name);
node.setParent(null);
const front = getFrontPosition(1, scene);
const camPos = scene.activeCamera.globalPosition.clone();
node.position.x = front.x + offset.x;
node.position.z = front.z + offset.z;
node.position.y = 1.2 + offset.y;
node.lookAt(camPos);
node.setParent(platform);
break;
case "FreeCamera":
case "DeviceOrientationCamera":
@ -56,10 +70,10 @@ export function setMenuPosition(node: TransformNode, scene: Scene, offset: Vecto
const width = scene.getEngine().getRenderWidth();
const height = scene.getEngine().getRenderHeight();
node.position.z = 2;
node.position.y = -.8;
node.position.y = -.8 + offset.y;
node.position.x = offset.x;
break;
}
console.log('menu position set');
}
}

View File

@ -1,4 +1,4 @@
import {Engine, FreeCamera, Scene, Vector3} from "@babylonjs/core";
import {Color3, Engine, FreeCamera, Scene, Vector3} from "@babylonjs/core";
import '@babylonjs/loaders';
import {DiagramManager} from "./diagram/diagramManager";
import {Toolbox} from "./toolbox/toolbox";
@ -6,7 +6,7 @@ import log, {Logger} from "loglevel";
import {AppConfig} from "./util/appConfig";
import {GamepadManager} from "./controllers/gamepadManager";
import {CustomEnvironment} from "./util/customEnvironment";
import {Controllers} from "./controllers/controllers";
import {ControllerEventType, Controllers} from "./controllers/controllers";
import {Spinner} from "./util/spinner";
import {PouchdbPersistenceManager} from "./integration/pouchdbPersistenceManager";
import {addSceneInspector} from "./util/functions/sceneInspctor";
@ -38,13 +38,21 @@ export class VrApp {
}
const scene = new Scene(this.engine);
this.scene = scene;
this.scene.ambientColor = new Color3(.1, .1, .1);
const spinner = new Spinner(scene);
spinner.show();
const config = new AppConfig();
const controllers = new Controllers();
const toolbox = new Toolbox(scene, controllers);
const toolbox = new Toolbox(scene);
controllers.controllerObserver.add((evt) => {
if (evt.type == ControllerEventType.X_BUTTON) {
if (evt.value == 1) {
toolbox.toggle();
}
}
})
const diagramManager = new DiagramManager(scene, controllers, toolbox, config);
const db = new PouchdbPersistenceManager();
db.setDiagramManager(diagramManager);
db.configObserver.add((newConfig) => {