immersive2/src/diagram/meshConverter.ts

211 lines
6.8 KiB
TypeScript

import {DiagramEntity} from "./diagramEntity";
import {
AbstractMesh,
Color3,
DynamicTexture,
InstancedMesh,
Mesh,
MeshBuilder,
PhysicsAggregate,
PhysicsBody,
PhysicsMotionType,
PhysicsShapeType,
Quaternion,
Scene,
StandardMaterial
} from "@babylonjs/core";
import {v4 as uuidv4} from 'uuid';
import log from "loglevel";
export class MeshConverter {
private static logger = log.getLogger('MeshConverter');
public static toDiagramEntity(mesh: AbstractMesh): DiagramEntity {
if (!mesh) {
this.logger.error("toDiagramEntity: mesh is null");
return null;
}
const entity = <DiagramEntity>{};
if ("new" == mesh?.id) {
mesh.id = "id" + uuidv4();
}
entity.id = mesh.id;
entity.position = mesh.position;
entity.rotation = mesh.rotation;
entity.last_seen = new Date();
entity.template = mesh?.metadata?.template;
entity.text = mesh?.metadata?.text;
entity.scale = mesh.scaling;
if (mesh.material) {
entity.color = (mesh.material as any).diffuseColor.toHexString();
} else {
this.logger.error("toDiagramEntity: mesh.material is null");
}
return entity;
}
public static fromDiagramEntity(entity: DiagramEntity, scene: Scene): AbstractMesh {
if (!entity) {
this.logger.error("fromDiagramEntity: entity is null");
return null;
}
if (!entity.id) {
entity.id = "id" + uuidv4();
}
let mesh = scene.getMeshById(entity.id);
if (mesh) {
log.debug('mesh already exists');
} else {
mesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color);
if (mesh) {
if (mesh.isAnInstance) {
log.debug('error: mesh is an instance');
} else {
mesh = new InstancedMesh(entity.id, (mesh as Mesh));
}
} else {
log.debug('no mesh found for ' + entity.template + "-" + entity.color);
}
}
if (mesh) {
mesh.metadata = {template: entity.template};
if (entity.position) {
mesh.position = entity.position;
}
if (entity.rotation) {
if (mesh.rotationQuaternion) {
mesh.rotationQuaternion = Quaternion.FromEulerAngles(entity.rotation.x, entity.rotation.y, entity.rotation.z);
} else {
mesh.rotation = entity.rotation;
}
}
if (entity.parent) {
mesh.parent = scene.getNodeById(entity.parent);
}
if (entity.scale) {
mesh.scaling = entity.scale;
}
if (!mesh.material) {
const material = new StandardMaterial("material-" + entity.id, scene);
material.diffuseColor = Color3.FromHexString(entity.color);
mesh.material = material;
}
if (entity.text) {
mesh.metadata.text = entity.text;
this.updateTextNode(mesh, entity.text);
}
/*
const sphereAggregate = new PhysicsAggregate(mesh, PhysicsShapeType.BOX, {
mass: 10,
restitution: 0.1,
startAsleep: false
}, scene);
*/
} else {
this.logger.error("fromDiagramEntity: mesh is null after it should have been created");
}
return mesh;
}
public static applyPhysics(mesh: AbstractMesh, scene: Scene): PhysicsBody {
if (!mesh?.metadata?.template || !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;
}
const aggregate = new PhysicsAggregate(mesh,
shapeType, {mass: 20, restitution: .2, friction: .9}, scene);
const body = aggregate.body;
body.setMotionType(PhysicsMotionType.ANIMATED);
body.setLinearDamping(.9);
body.setAngularDamping(.5);
body.setGravityFactor(0);
return aggregate.body;
}
public static updateTextNode(mesh: AbstractMesh, text: string) {
if (!mesh) {
this.logger.error("updateTextNode: mesh is null");
return null;
}
let textNode = (mesh.getChildren((node) => {
return node.name == 'text'
})[0] as Mesh);
if (textNode) {
textNode.dispose(false, true);
}
if (!text) {
return null;
}
//Set font
const height = 0.125;
const font_size = 24;
const font = "bold " + font_size + "px Arial";
//Set height for dynamic texture
const DTHeight = 1.5 * font_size; //or set as wished
//Calc Ratio
const ratio = height / DTHeight;
//Use a temporary dynamic texture to calculate the length of the text on the dynamic texture canvas
const temp = new DynamicTexture("DynamicTexture", 32, mesh.getScene());
const tmpctx = temp.getContext();
tmpctx.font = font;
const DTWidth = tmpctx.measureText(text).width + 8;
//Calculate width the plane has to be
const planeWidth = DTWidth * ratio;
//Create dynamic texture and write the text
const dynamicTexture = new DynamicTexture("DynamicTexture", {
width: DTWidth,
height: DTHeight
}, mesh.getScene(), false);
const mat = new StandardMaterial("mat", mesh.getScene());
mat.diffuseTexture = dynamicTexture;
dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true);
//Create plane and set dynamic texture as material
const plane = MeshBuilder.CreatePlane("text", {width: planeWidth, height: height}, mesh.getScene());
plane.material = mat;
plane.billboardMode = Mesh.BILLBOARDMODE_ALL;
const yOffset = mesh.getBoundingInfo().boundingSphere.radius;
plane.parent = mesh;
plane.position.y = yOffset;
plane.scaling.y = 1 / mesh.scaling.y;
plane.scaling.x = 1 / mesh.scaling.x;
plane.scaling.z = 1 / mesh.scaling.z;
return plane;
}
}