@RaananW
i shared you my whole code. Sadly i cant share you the glb files i used, because of an NDA.
Inside the setupScene()
you can find the Physics code
import "@babylonjs/loaders/glTF";
import "@babylonjs/core/Physics/physicsEngine";
import HavokPhysics from '@babylonjs/havok';
import { HavokPlugin, SceneLoader, Mesh, Engine, Scalar, CreatePlane, Scene, Tools, ShadowGenerator, SpotLight, StandardMaterial, CreateGround, Vector3, Color3, ArcRotateCamera, ExecuteCodeAction, ActionManager, PBRMaterial, Texture, HighlightLayer, PointerDragBehavior, CreateSphere } from "@babylonjs/core";
import { Button, AdvancedDynamicTexture } from "@babylonjs/gui";
import { Inspector } from '@babylonjs/inspector';
class Configurator extends HTMLElement {
constructor() {
super();
this.html = document.querySelector("html");
this.canvas = this.querySelector("canvas");
this.engine = new Engine(this.canvas, true, { stencil: true });
this.scene = new Scene(this.engine);
this.fullScreenUi = AdvancedDynamicTexture.CreateFullscreenUI("configuratorUi");
this.lights = [];
this.materals = [];
this.walls = [];
this.modules = [];
this.floor = null;
this.camera = null;
this.setupScene();
}
setupFloorMaterial() {
const floorMaterial = new PBRMaterial("floor_material", this.scene);
const albedoTexture = new Texture(livom.configurator.floorMaterial.map, this.scene);
const bumpTexture = new Texture(livom.configurator.floorMaterial.normalMap, this.scene);
const metallicTexture = new Texture(livom.configurator.floorMaterial.roughnessMap, this.scene);
albedoTexture.uScale = 1.5;
albedoTexture.vScale = 1.5;
bumpTexture.uScale = 1.5;
bumpTexture.vScale = 1.5;
metallicTexture.uScale = 1.5;
metallicTexture.vScale = 1.5;
floorMaterial.albedoTexture = albedoTexture;
floorMaterial.bumpTexture = bumpTexture;
floorMaterial.metallicTexture = metallicTexture;
this.materals.push(floorMaterial);
return floorMaterial;
}
setupWallMaterial() {
let wallMaterial = new StandardMaterial("wall_material", this.scene);
wallMaterial.diffuseColor = Color3.White();
wallMaterial.emissiveColor = Color3.White();
wallMaterial.backFaceCulling = true;
this.materals.push(wallMaterial);
}
setupFloor() {
this.floor = CreateGround("ground", { width: 6, height: 6, updatable: true }, this.scene);
this.floor.receiveShadows = true;
this.floor.material = this.setupFloorMaterial();
}
setupCamera() {
const camera = new ArcRotateCamera("camera", 1, 1, 10, new Vector3(0.0, 0.5, 0.0), this.scene);
camera.keysUp.push(87);
camera.keysLeft.push(65);
camera.keysDown.push(83);
camera.keysRight.push(68);
camera.attachControl(this.canvas, true);
camera.ellipsoid = new Vector3(0.5, 1.0, 0.5);
this.camera = camera;
}
setupLight() {
const spotLight = new SpotLight("SpotLight", new Vector3(0, 5, 0), new Vector3(0, -1, 0), Math.PI, 10, this.scene);
spotLight.intensity = 100;
spotLight.shadowEnabled = true;
this.lights.push(spotLight);
return spotLight;
}
setupWalls() {
const wallHeight = 3;
const wallLength = 6;
const walls = [
{
position: new Vector3(0, wallHeight / 2, wallLength / 2),
rotationY: Tools.ToRadians(180)
},
{
position: new Vector3(0, wallHeight / 2, (wallLength / 2) * -1),
rotationY: Tools.ToRadians(0)
},
{
position: new Vector3(wallLength / 2, wallHeight / 2, 0),
rotationY: Tools.ToRadians(-90)
},
{
position: new Vector3((wallLength / 2) * -1, wallHeight / 2, 0),
rotationY: Tools.ToRadians(90)
}
]
for (const [index, wall] of walls.entries()) {
let wallPlane = CreatePlane("wall_" + index, { height: wallHeight, width: wallLength, sideOrientation: Mesh.BACKSIDE }, this.scene);
wallPlane.position = wall.position;
wallPlane.rotation.y = wall.rotationY;
wallPlane.checkCollisions = false;
wallPlane.isPickable = false;
wallPlane.receiveShadows = true;
this.walls.push(wallPlane);
}
}
setupHighlightLayer () {
return new HighlightLayer("highlightLayer", this.scene);
}
shadowGenerator(light) {
const shadowGenerator = new ShadowGenerator(4096, light);
return shadowGenerator;
}
async importModule(moduleContainer, moduleObject) {
const url = new URL("https:" + moduleObject.src);
const obj = await SceneLoader.ImportMeshAsync("", url.origin + url.pathname.split("/LI")[0] + "/", "LI" + url.pathname.split("/LI")[1] + "?" + url.searchParams, this.scene);
const minMax = Mesh.MinMax(obj.meshes.filter((mesh) => mesh.id !== "__root__" && mesh.id !== "bottom"));
const size = {
width: minMax.max.x - minMax.min.x,
height: minMax.max.y - minMax.min.y,
depth: minMax.max.z - minMax.min.z
}
obj.size = size;
obj.id = moduleObject.id;
obj.name = moduleContainer.modell;
this.modules.push(obj);
return obj;
}
enableDragOfModules (obj, highlightLayer) {
const getAllMeshesForHightlight = obj.meshes.filter((mesh) => mesh.id !== "__root__");
const dragBehavior = new PointerDragBehavior({
dragPlaneNormal: new Vector3(0, 1, 0),
});
dragBehavior.useObjectOrientationForDragging = false;
dragBehavior.onDragStartObservable.add((e) => {
for (const mesh of getAllMeshesForHightlight) {
highlightLayer.addMesh(mesh, Color3.Green());
}
});
dragBehavior.onDragObservable.add((e) => { });
dragBehavior.validateDrag = (targetPosition) => {
const bounds = this.floor.getBoundingInfo().boundingBox;
targetPosition.x = Scalar.Clamp(targetPosition.x, bounds.minimum.x, bounds.maximum.x);
targetPosition.y = Scalar.Clamp(targetPosition.y, bounds.minimum.y, bounds.maximum.y);
targetPosition.z = Scalar.Clamp(targetPosition.z, bounds.minimum.z, bounds.maximum.z);
return true;
}
dragBehavior.onDragEndObservable.add(() => {
for (const mesh of getAllMeshesForHightlight) {
highlightLayer.removeMesh(mesh, Color3.Green());
}
});
dragBehavior.attach(obj.meshes[0]);
}
setupShadowCaster (shadowGenerator) {
const rootMeshes = this.scene.getMeshesById("__root__");
for (const rootMesh of rootMeshes) {
let meshes = rootMesh.getChildMeshes();
for (const mesh of meshes) {
shadowGenerator.addShadowCaster(mesh);
}
}
}
createHotspot(mesh, positions, key) {
const hotspot = new CreatePlane(
`hotspot_${key}_${mesh.name}`,
{
sideOrientation: Mesh.BACKSIDE,
},
this.scene
);
hotspot.position = positions[key];
hotspot.parent = mesh; // Parent the hotspot to the main mesh
const GUI = AdvancedDynamicTexture.CreateForMesh(hotspot);
const button = Button.CreateSimpleButton("but", "Click Me");
button.width = 1;
button.height = 0.4;
button.color = "white";
button.fontSize = 50;
button.background = "green";
button.onPointerUpObservable.add(function() {
alert("you did it!");
});
GUI.addControl(button);
console.log(button);
}
createHotspots(mesh) {
if (mesh.id === "legs" || mesh.id === "bottom" || mesh.id === "__root__") return;
const boundingBox = mesh.getBoundingInfo().boundingBox;
let positions = {
top: new Vector3(boundingBox.center.x, boundingBox.maximumWorld.y + 0.01, boundingBox.center.z),
bottom: new Vector3(boundingBox.center.x, boundingBox.minimumWorld.y - 0.01, boundingBox.center.z),
left: new Vector3(boundingBox.minimumWorld.x - 0.01, boundingBox.center.y, boundingBox.center.z),
right: new Vector3(boundingBox.maximumWorld.x + 0.01, boundingBox.center.y, boundingBox.center.z),
front: new Vector3(boundingBox.center.x, boundingBox.center.y, boundingBox.maximumWorld.z + 0.01),
back: new Vector3(boundingBox.center.x, boundingBox.center.y, boundingBox.minimumWorld.z - 0.01)
};
if (mesh.id === "cushion" || mesh.id === "back_lean") {
positions = {
top: new Vector3(boundingBox.center.x, boundingBox.maximumWorld.y + 0.01, boundingBox.center.z),
}
}
// Create hotspot meshes
Object.keys(positions).forEach(key => {
this.createHotspot(mesh, positions, key);
});
}
async setupScene() {
const hk = await HavokPhysics();
console.log(hk);
this.scene.enablePhysics(new Vector3(0, -10, 0), new HavokPlugin(true, hk));
this.html.style.overflow = "hidden";
this.setupFloor();
this.setupCamera();
this.setupLight();
this.setupWalls();
const shadowGenerator = this.shadowGenerator(this.lights[0]);
const highlightLayer = this.setupHighlightLayer();
for (const moduleContainer of livom.configurator.modules) {
for (const moduleObject of moduleContainer.modules) {
const obj = await this.importModule(moduleContainer, moduleObject);
this.enableDragOfModules(obj, highlightLayer);
for (const mesh of obj.meshes) {
this.createHotspots(mesh);
}
}
}
console.log(this.modules);
this.setupShadowCaster(shadowGenerator);
this.engine.runRenderLoop(() => {
this.scene.render();
});
window.addEventListener('resize', () => {
this.engine.resize();
});
Inspector.Show(this.scene, {
embedMode: true
});
}
}
customElements.define('configurator-canvas', Configurator);