added capability to create items in diagram.

This commit is contained in:
Michael Mainguy 2023-07-13 10:55:19 -05:00
parent 91144571c0
commit 46efe08535
11 changed files with 453 additions and 126 deletions

1
package-lock.json generated
View File

@ -21,6 +21,7 @@
"google-static-maps-tile": "1.0.0",
"query-string": "^8.1.0",
"ring-client-api": "^11.8.0",
"uuid": "^9.0.0",
"vite-express": "^0.9.1"
},
"devDependencies": {

View File

@ -24,7 +24,8 @@
"google-static-maps-tile": "1.0.0",
"query-string": "^8.1.0",
"vite-express": "^0.9.1",
"express-http-proxy": "^1.6.3"
"express-http-proxy": "^1.6.3",
"uuid": "^9.0.0"
},
"devDependencies": {
"typescript": "^5.0.2",

BIN
public/outdoor_field.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 MiB

View File

@ -8,6 +8,7 @@ import {
HemisphericLight,
MeshBuilder,
PBRMetallicRoughnessMaterial,
PhotoDome,
PhysicsAggregate,
PhysicsShapeType,
Scene,
@ -21,8 +22,8 @@ import {Left} from "./controllers/left";
import {Bmenu} from "./menus/bmenu";
import HavokPhysics from "@babylonjs/havok";
import {Rigplatform} from "./controllers/rigplatform";
import {Cameras} from "./integration/ring/cameras";
import {Mapt} from "./util/mapt";
import {DiagramManager} from "./diagram/diagramManager";
class App {
@ -43,6 +44,7 @@ class App {
const engine = new Engine(canvas, true);
const scene = new Scene(engine);
const diagramManager = new DiagramManager(scene);
const havokInstance = await HavokPhysics();
const havokPlugin = new HavokPlugin(true, havokInstance);
scene.enablePhysics(new Vector3(0, -9.8, 0), havokPlugin);
@ -51,65 +53,21 @@ class App {
camera.attachControl(canvas, true);
new HemisphericLight("light1", new Vector3(1, 1, 0), scene);
const rig = new Rigplatform(scene);
//const envTexture = new CubeTexture("/assets/textures/SpecularHDR.dds", scene);
//scene.createDefaultSkybox(envTexture, true, 1000);
/*const photoDome = new PhotoDome('sky',
const photoDome = new PhotoDome('sky',
'./outdoor_field.jpeg', {},
scene);
*/
const xr = await WebXRDefaultExperience.CreateAsync(scene, {
floorMeshes: [this.createGround(scene)],
optionalFeatures: true
});
xr.baseExperience.camera.parent = rig.rigMesh;
const b = new Bmenu(scene, rig, xr.baseExperience);
//const box = MeshBuilder.CreateBox("box", {size: 1}, scene);
//box.position.z = -4;
/*box.actionManager.registerAction(
new InterpolateValueAction(
ActionManager.OnPointerOverTrigger,box, 'visibility', 0.2, 500
)
);
box.actionManager.registerAction(
new InterpolateValueAction(
ActionManager.OnPointerOutTrigger,box, 'visibility', 1, 200
)
);*/
//const edit = new ObjectEditor(scene, box);
//const edit = new ObjectEditor(scene, box);
const ring = new Cameras(scene, this.token);
ring.getCameras().then(() => ring.createCameras());
const stickVector = Vector3.Zero();
xr.input.onControllerAddedObservable.add((source, state) => {
let controller;
switch (source.inputSource.handedness) {
case "right":
controller = new Right(source);
rig.right = controller;
controller.setBMenu(b);
break;
case "left":
controller = new Left(source);
rig.left = controller;
break;
}
xr.baseExperience.camera.position = new Vector3(0, 1.6, 0);
if (controller) {
controller.setStickVector(stickVector);
controller.setCamera(xr.baseExperience.camera);
controller.setRig(rig.body);
}
console.log(source);
console.log(state);
});
const rig = new Rigplatform(scene, xr);
//const ring = new Cameras(scene, this.token);
//ring.getCameras().then(() => ring.createCameras());
xr.teleportation.detach();
// hide/show the Inspector
@ -123,12 +81,7 @@ class App {
}
}
});
const map = new Mapt(scene);
map.buildMapImage();
//map.createMapTiles(26.443397,-82.111512);
// run the main render loop
engine.runRenderLoop(() => {
scene.render();
});
@ -142,6 +95,7 @@ class App {
groundMaterial.baseTexture = gText;
groundMaterial.metallic = 0;
groundMaterial.roughness = 1;
const ground = MeshBuilder.CreateGround("ground", {width: 100, height: 100, subdivisions: 1}, scene);
ground.material = groundMaterial;
@ -149,5 +103,6 @@ class App {
return ground;
}
}
const app = new App();

View File

@ -1,10 +1,10 @@
import {PhysicsBody, Vector3, WebXRCamera, WebXRInputSource} from "@babylonjs/core";
import {Rigplatform} from "./rigplatform";
export class Base {
static stickVector = Vector3.Zero();
protected controller: WebXRInputSource;
protected stickVector: Vector3;
protected body: PhysicsBody;
protected camera: WebXRCamera;
protected rig: Rigplatform;
protected speedFactor = 4;
constructor(controller:
@ -12,24 +12,17 @@ export class Base {
this.controller = controller;
this.controller.onMotionControllerInitObservable.add((init) => {
if (init.components['xr-standard-trigger']) {
init.components['xr-standard-trigger'].onButtonStateChangedObservable.add((value) => {
if (value.value == 1) {
console.log(value);
}
init.components['xr-standard-trigger']
.onButtonStateChangedObservable
.add((value) => {
if (value.value == 1) {
console.log(value);
}
});
}
});
}
setRig(body: PhysicsBody) {
this.body = body;
}
setCamera(camera: WebXRCamera) {
this.camera = camera;
}
setStickVector(vector: Vector3) {
this.stickVector = vector;
setRig(rig: Rigplatform) {
this.rig = rig;
}
}

View File

@ -1,8 +1,8 @@
import {Quaternion, Vector3, WebXRInputSource} from "@babylonjs/core";
import {Vector3, WebXRInputSource} from "@babylonjs/core";
import {Base} from "./base";
export class Left extends Base {
private x90 = Quaternion.RotationAxis(Vector3.Up(), 1.5708);
constructor(controller:
WebXRInputSource) {
@ -11,26 +11,23 @@ export class Left extends Base {
if (init.components['xr-standard-thumbstick']) {
init.components['xr-standard-thumbstick']
.onAxisValueChangedObservable.add((value) => {
const ray = this.camera.getForwardRay();
if (Math.abs(value.x) > .1) {
const direction = ray.direction.applyRotationQuaternion(this.x90).scale(value.x*this.speedFactor);
this.body.setLinearVelocity(direction);
this.stickVector.x = 1;
this.rig.leftright(value.x*this.speedFactor);
Base.stickVector.x = 1;
} else {
this.stickVector.x = 0;
Base.stickVector.x = 0;
}
if (Math.abs(value.y) > .1) {
let direction = Vector3.Zero();
this.body.getLinearVelocityToRef(direction);
direction.y = (value.y*-1*this.speedFactor);
this.body.setLinearVelocity(direction);
this.stickVector.y = 1;
this.rig.updown(value.y*this.speedFactor);
Base.stickVector.y = 1;
} else {
this.stickVector.y = 0;
Base.stickVector.y = 0;
}
if (this.stickVector.equals(Vector3.Zero())) {
this.body.setLinearVelocity(Vector3.Zero());
if (Base.stickVector.equals(Vector3.Zero())) {
this.rig.updown(0);
this.rig.leftright(0)
} else {
}

View File

@ -1,15 +1,37 @@
import {Base} from "./base";
import {Mesh, Vector3, WebXRInputSource} from "@babylonjs/core";
import {Bmenu} from "../menus/bmenu";
import {Vector3, WebXRInputSource} from "@babylonjs/core";
import {Bmenu, BmenuState} from "../menus/bmenu";
import {DiagramEvent, DiagramEventType, DiagramManager} from "../diagram/diagramManager";
export class Right extends Base {
private bmenu: Bmenu;
private addMesh: Mesh;
private down: boolean = false;
constructor(controller:
WebXRInputSource) {
super(controller);
this.controller.onMotionControllerInitObservable.add((init)=> {
const trigger = init.components['xr-standard-trigger'];
if (trigger) {
trigger
.onButtonStateChangedObservable
.add((value) => {
if (value.value > .4 && !this.down) {
this.down = true;
if (this.bmenu.getState() == BmenuState.ADDING) {
this.bmenu.setState(BmenuState.DROPPING);
const event: DiagramEvent = {
type: DiagramEventType.DROP,
entity: null
}
DiagramManager.onDiagramEventObservable.notifyObservers(event);
}
}
if (value.value < .05) {
this.down = false;
}
});
}
if (init.components['b-button']) {
init.components['b-button'].onButtonStateChangedObservable.add((value)=>{
if (value.pressed) {
@ -21,29 +43,29 @@ export class Right extends Base {
if (init.components['xr-standard-thumbstick']) {
init.components['xr-standard-thumbstick']
.onAxisValueChangedObservable.add((value) => {
const ray = this.camera.getForwardRay();
if (Math.abs(value.x) > .1) {
this.body.setAngularVelocity(Vector3.Up().scale(value.x));
this.rig.turn(value.x);
} else {
this.body.setAngularVelocity(Vector3.Zero());
this.rig.turn(0);
}
if (Math.abs(value.y) > .1) {
this.body.setLinearVelocity(ray.direction.scale(value.y*-1*this.speedFactor));
this.stickVector.z = 1;
this.rig.forwardback(value.y*this.speedFactor);
Base.stickVector.z = 1;
} else {
this.stickVector.z = 0;
Base.stickVector.z = 0;
}
if (this.stickVector.equals(Vector3.Zero())) {
this.body.setLinearVelocity(Vector3.Zero());
if (Base.stickVector.equals(Vector3.Zero())) {
this.rig.forwardback(0);
}
});
}
});
}
public setBMenu(menu: Bmenu) {
this.bmenu = menu;
this.bmenu.setController(this.controller);
}
}

View File

@ -1,40 +1,191 @@
import {
Camera,
Color3,
Mesh,
MeshBuilder,
PhysicsAggregate, PhysicsBody, PhysicsMotionType,
PhysicsShapeType, Quaternion,
PhysicsAggregate,
PhysicsBody,
PhysicsMotionType,
PhysicsShapeType,
Quaternion,
Scene,
StandardMaterial,
Vector3
Vector3,
WebXRDefaultExperience
} from "@babylonjs/core";
import {Right} from "./right";
import {Left} from "./left";
import {Bmenu} from "../menus/bmenu";
export class Rigplatform {
static LINEAR_VELOCITY = 4;
static ANGULAR_VELOCITY = 3;
public bMenu: Bmenu;
static x90 = Quaternion.RotationAxis(Vector3.Up(), 1.5708);
private camera: Camera;
private scene: Scene;
private xr: WebXRDefaultExperience;
public right: Right;
public left: Left;
public body: PhysicsBody;
public rigMesh: Mesh;
constructor(scene: Scene) {
private turning: boolean = false;
constructor(scene: Scene, xr: WebXRDefaultExperience) {
this.xr = xr;
this.bMenu = new Bmenu(scene, this.xr.baseExperience);
this.camera = scene.activeCamera;
this.scene = scene;
this.rigMesh = MeshBuilder.CreateCylinder("platform", {diameter: 1.5, height: .01}, scene);
for (const cam of this.scene.cameras) {
cam.parent = this.rigMesh;
}
const myMaterial = new StandardMaterial("myMaterial", scene);
myMaterial.diffuseColor = Color3.Blue();
this.rigMesh.material = myMaterial;
this.rigMesh.setAbsolutePosition(new Vector3(0, .1, -3));
this.rigMesh.visibility=0;
this.rigMesh.visibility = 1;
const rigAggregate =
new PhysicsAggregate(
this.rigMesh,
PhysicsShapeType.CYLINDER,
{ friction: 1, center: Vector3.Zero(), radius: .5, mass: .1, restitution: .1},
{friction: 1, center: Vector3.Zero(), radius: .5, mass: .1, restitution: .1},
scene);
rigAggregate.body.setMotionType(PhysicsMotionType.ANIMATED);
rigAggregate.body.setGravityFactor(0);
this.#fixRotation();
this.body = rigAggregate.body;
this.#setupKeyboard();
this.#initializeControllers();
this.scene.onActiveCameraChanged.add((s) => {
this.camera = s.activeCamera;
this.camera.parent = this.rigMesh;
console.log('camera changed');
});
}
#initializeControllers() {
this.xr.input.onControllerAddedObservable.add((source, state) => {
let controller;
switch (source.inputSource.handedness) {
case "right":
controller = new Right(source);
this.right = controller;
controller.setBMenu(this.bMenu);
break;
case "left":
controller = new Left(source);
this.left = controller;
break;
}
this.xr.baseExperience.camera.position = new Vector3(0, 1.6, 0);
if (controller) {
controller.setRig(this);
}
console.log(source);
console.log(state);
});
}
public forwardback(val: number) {
const ray = this.camera.getForwardRay();
this.body.setLinearVelocity(ray.direction.scale(val * -1));
}
public leftright(val: number) {
const ray = this.camera.getForwardRay();
const direction = ray.direction.applyRotationQuaternion(Rigplatform.x90).scale(val);
this.body.setLinearVelocity(direction);
console.log(val);
}
public stop() {
this.body.setLinearVelocity(Vector3.Zero());
this.body.setAngularVelocity(Vector3.Zero());
}
public updown(val: number) {
let direction = Vector3.Zero();
this.body.getLinearVelocityToRef(direction);
direction.y = (val * -1);
this.body.setLinearVelocity(direction);
}
public turn(val: number) {
const snap = true;
if (snap) {
if (!this.turning) {
this.turning = true;
const q = this.rigMesh.rotation.y += Math.abs(val) * 22.5;
} else {
if (val < .1) {
this.turning = false;
}
}
} else {
if (Math.abs(val) > .1) {
this.body.setAngularVelocity(Vector3.Up().scale(val));
} else {
this.body.setAngularVelocity(Vector3.Zero());
}
}
}
//create a method to set the camera to the rig
#setupKeyboard() {
///simplify this with a map
window.addEventListener("keydown", (ev) => {
switch (ev.key) {
case "w":
this.forwardback(1 * Rigplatform.LINEAR_VELOCITY);
break;
case "s":
this.forwardback(-1 * Rigplatform.LINEAR_VELOCITY);
break;
case "a":
this.leftright(1 * Rigplatform.LINEAR_VELOCITY);
break;
case "d":
this.leftright(-1 * Rigplatform.LINEAR_VELOCITY);
break;
case "q":
this.turn(-1 * Rigplatform.ANGULAR_VELOCITY);
break;
case "e":
this.turn(1 * Rigplatform.ANGULAR_VELOCITY);
break;
case "W":
this.updown(-1 * Rigplatform.LINEAR_VELOCITY);
break;
case "S":
this.updown(1 * Rigplatform.LINEAR_VELOCITY);
break;
case " ":
this.bMenu.toggle()
}
});
window.addEventListener("keyup", (ev) => {
const keys = "wsadqeWS";
if (keys.indexOf(ev.key) > -1) {
this.stop();
}
});
}
#fixRotation() {

View File

@ -0,0 +1,2 @@

View File

@ -0,0 +1,140 @@
import {AbstractMesh, Color3, Mesh, MeshBuilder, Observable, Scene, StandardMaterial, Vector3} from "@babylonjs/core";
import {v4 as uuidv4} from 'uuid';
import {BmenuState} from "../menus/bmenu";
export enum DiagramEventType {
ADD,
REMOVE,
MODIFY,
DROP,
DROPPED,
}
export type DiagramEvent = {
type: DiagramEventType;
menustate?: BmenuState;
entity?: DiagramEntity;
}
export type DiagramEntity = {
color?: string;
id?: string;
last_seen?: Date;
position?: Vector3;
rotation?: Vector3;
template?: string;
text?: string;
scale?: Vector3;
parent?: string;
}
export class DiagramManager {
static onDiagramEventObservable = new Observable();
static leftController: Mesh;
static currentMesh: AbstractMesh;
static rightController: Mesh;
static state: BmenuState;
private scene: Scene;
constructor(scene: Scene) {
this.scene = scene;
if (DiagramManager.onDiagramEventObservable.hasObservers()) {
} else {
DiagramManager.onDiagramEventObservable.add(this.#onDiagramEvent, -1, true, this);
}
}
#onDiagramEvent(event: DiagramEvent) {
console.log(event);
const entity = event.entity;
let mesh;
let material
if (entity) {
mesh = this.scene.getMeshByName(entity.id);
material = this.scene.getMaterialByName("material-"+entity.id);
if (!material){
material = new StandardMaterial("material-"+event.entity.id, this.scene);
material.ambientColor = Color3.FromHexString(event.entity.color.replace("#",""));
}
}
switch (event.type) {
case DiagramEventType.DROPPED:
break;
case DiagramEventType.DROP:
if (DiagramManager.currentMesh) {
const newMesh = DiagramManager.currentMesh.clone(DiagramManager.currentMesh.name = "id" + uuidv4(), DiagramManager.currentMesh.parent);
DiagramManager.currentMesh.setParent(null);
DiagramManager.currentMesh = newMesh;
DiagramManager.onDiagramEventObservable.notifyObservers({type: DiagramEventType.DROPPED, entity: entity});
}
break;
case DiagramEventType.ADD:
if (mesh) {
return;
} else {
mesh = this.#createMesh(entity);
if (!mesh) {
return;
}
}
case DiagramEventType.MODIFY:
if (!mesh) {
} else {
const rotation = entity.rotation;
const scale = entity.scale;
const position = entity.position;
mesh.material = material;
mesh.position = new Vector3(position.x, position.y, position.z);
mesh.rotation = new Vector3(rotation.x, rotation.y, rotation.z);
if (entity.parent) {
mesh.parent = this.scene.getMeshByName(entity.parent);
}
}
DiagramManager.currentMesh = mesh;
break;
case DiagramEventType.REMOVE:
break;
}
}
#createMesh(entity: DiagramEntity) {
if (!entity.id) {
entity.id = "id" + uuidv4();
}
let mesh: Mesh;
switch (entity.template) {
case "#box-template":
mesh = MeshBuilder.CreateBox( entity.id,
{width: entity.scale.x,
height: entity.scale.y,
depth: entity.scale.z}, this.scene);
break;
case "#sphere-template":
mesh= MeshBuilder.CreateSphere(entity.id, {diameter: entity.scale.x}, this.scene);
break
case "#cylinder-template":
mesh= MeshBuilder.CreateCylinder(entity.id, {diameter: entity.scale.x, height: entity.scale.y}, this.scene);
break;
default:
mesh = null;
}
return mesh;
}
}

View File

@ -1,28 +1,44 @@
import {Angle, FadeInOutBehavior, Scene, TransformNode, Vector3, WebXRExperienceHelper} from "@babylonjs/core";
import {
Container3D,
CylinderPanel,
GUI3DManager,
HandMenu,
HolographicButton, HolographicSlate,
NearMenu, PlanePanel,
SpherePanel
} from "@babylonjs/gui";
import {Rigplatform} from "../controllers/rigplatform";
AbstractMesh,
Angle,
Color3, Mesh,
MeshBuilder,
Scene, SceneSerializer,
StandardMaterial,
TransformNode, Vector3,
WebXRExperienceHelper, WebXRInputSource
} from "@babylonjs/core";
import {GUI3DManager, HolographicButton, PlanePanel} from "@babylonjs/gui";
import {DiagramEntity, DiagramEvent, DiagramEventType, DiagramManager} from "../diagram/diagramManager";
export enum BmenuState {
NONE,
ADDING, // Adding a new entity
DROPPING, // Dropping an entity
}
export class Bmenu {
private scene;
private rig;
private state: BmenuState = BmenuState.NONE;
private xr;
private manager;
private panel;
constructor(scene: Scene, rig: Rigplatform, xr: WebXRExperienceHelper) {
private rightController: AbstractMesh;
constructor(scene: Scene, xr: WebXRExperienceHelper) {
this.scene = scene;
this.rig = rig;
this.manager = new GUI3DManager(scene);
this.xr = xr;
DiagramManager.onDiagramEventObservable.add((event: DiagramEvent) => {
if (event.type === DiagramEventType.DROPPED) {
this.state = BmenuState.ADDING;
}
});
}
setController(controller: WebXRInputSource) {
this.rightController = controller.grip;
}
makeButton(name: string, id: string) {
const button = new HolographicButton(name);
button.text = name;
@ -30,20 +46,69 @@ export class Bmenu {
button.onPointerClickObservable.add(this.#clickhandler, -1, false, this);
return button;
}
#clickhandler(_info, state) {
console.log(state.currentTarget.name);
let entity: DiagramEntity = {
template: null,
position: new Vector3(0,-.040,.13),
rotation: new Vector3(),
scale: new Vector3(.1, .1, .1),
color: "#CEE",
text: "test",
last_seen: new Date(),
parent: this.rightController.id
};
switch (state.currentTarget.name) {
case "addBox":
entity.template = "#box-template";
break;
case "addSphere":
entity.template = "#sphere-template";
break;
case "addCylinder":
entity.template = "#cylinder-template";
break;
default:
console.log("Unknown button");
return;
}
const event: DiagramEvent = {
type: DiagramEventType.ADD,
entity: entity
}
this.state = BmenuState.ADDING;
DiagramManager.onDiagramEventObservable.notifyObservers(event);
}
public getState() {
return this.state;
}
public setState(state: BmenuState) {
this.state = state;
}
#createDefaultMaterial() {
const myMaterial = new StandardMaterial("myMaterial", this.scene);
myMaterial.diffuseColor = Color3.FromHexString("#CEE");
return myMaterial;
}
toggle() {
if (this.panel) {
this.panel.dispose();
this.panel = null;
this.setState(BmenuState.NONE);
} else {
const anchor = new TransformNode("bMenuAnchor");
anchor.rotation.y = Angle.FromDegrees(180).radians();
const cam = this.xr.camera.getFrontPosition(2);
anchor.position = cam;
const panel = new PlanePanel();
panel.margin=.6;
panel.margin = .6;
//panel.scaling.y=.5;
//panel.orientation = Container3D.FACEFORWARDREVERSED_ORIENTATION;