const { occt } = bitbybit;
const engine = bitbybit.babylon.engine.getEngine();
const scene = bitbybit.babylon.scene.getScene();
const camera = scene.activeCamera as BABYLON.ArcRotateCamera;
camera.position = new BABYLON.Vector3(-10, 1.6, -15);
camera.target = new BABYLON.Vector3(0, 2.5, 0);
const start = async () => {
const skyboxDto = new Bit.Inputs.BabylonScene.SkyboxDto();
skyboxDto.skybox = Bit.Inputs.Base.skyboxEnum.clearSky;
bitbybit.babylon.scene.enableSkybox(skyboxDto);
const chainModel = await createChain();
const ballChain = await createBallChain();
chainModel.collisionMesh.isVisible = false;
ballChain.collisionMesh.isVisible = false;
bitbybit.babylon.scene.enablePhysics({ vector: [0, -0.0981, 0] });
const boxMeshRes = await createCylBase();
const boxMeshCollision = boxMeshRes.getChildMeshes()[0] as BABYLON.Mesh;
const asset = await bitbybit.asset.getFile({ fileName: "matas.splat" });
const assetUrl = await bitbybit.asset.createObjectURL({ file: asset });
const gs = await bitbybit.babylon.gaussianSplatting.create({
url: assetUrl
});
gs.rotate(new BABYLON.Vector3(0, 1, 0), Math.PI);
gs.position = new BABYLON.Vector3(0, 0.3, 0);
const podium = BABYLON.MeshBuilder.CreateCylinder("sphere", { height: 0.3, diameter: 0.8, subdivisions: 10 }, scene);
const podiumShadow = BABYLON.MeshBuilder.CreateCylinder("sphere", { height: 3, diameter: 0.8, subdivisions: 10 }, scene);
podiumShadow.position.y = 1.84;
podiumShadow.layerMask = 0x10000000;
podium.position.y = 0.15;
const cylinder = BABYLON.MeshBuilder.CreateCylinder("sphere", { height: 3, diameter: 0.8, subdivisions: 10 }, scene);
cylinder.position.y = 1.5;
cylinder.visibility = 0;
cylinder.addChild(gs);
cylinder.addChild(podiumShadow);
cylinder.addChild(podium);
const step = 2;
const limit = 3;
const positions = [];
for (let x = 0; x < limit; x++) {
for (let z = 0; z < limit; z++) {
positions.push(new BABYLON.Vector3(x * step - 8, 1.5, z * step - (step * limit / 2) + step / 2));
}
}
const gaussianPromises = positions.map(_ => {
return bitbybit.babylon.gaussianSplatting.create({
url: assetUrl
});
});
const gaussians = await Promise.all(gaussianPromises);
console.log(gaussians);
gaussians.forEach((g, index) => {
const cyl = BABYLON.MeshBuilder.CreateCylinder("sphere", { height: 3.0, diameter: 0.8, subdivisions: 1.0 }, scene);
cyl.position = positions[index];
cyl.visibility = 1;
cyl.visibility = 0;
g.position.y = -1.2;
g.parent = cyl;
g.rotate(new BABYLON.Vector3(0, 1, 0), Math.PI);
const clonedPodiumShadow = podiumShadow.clone();
clonedPodiumShadow.parent = cyl;
const clonedPodium = podium.clone();
clonedPodium.parent = cyl;
new BABYLON.PhysicsAggregate(cyl, BABYLON.PhysicsShapeType.CYLINDER, { mass: 0.001, restitution: 0.75 }, scene);
});
new BABYLON.PhysicsAggregate(cylinder, BABYLON.PhysicsShapeType.CYLINDER, { mass: 0.03, restitution: 0.75 }, scene);
new BABYLON.PhysicsAggregate(boxMeshCollision, BABYLON.PhysicsShapeType.MESH, { mass: 0 }, scene);
const positionStep = 0.45;
const times = 8;
const chaingHeight = 5;
for (let i = 0; i < times; i++) {
const cloneVisual = chainModel.visualMesh.clone(`m-${i}`);
const cloneCollision = chainModel.collisionMesh.clone(`m-${i}`);
cloneVisual.position = new BABYLON.Vector3(i * positionStep, chaingHeight, 0);
cloneCollision.position = new BABYLON.Vector3(i * positionStep, chaingHeight, 0);
if (i % 2 === 0) {
cloneVisual.rotateAround(cloneVisual.position, new BABYLON.Vector3(1, 0, 0), Math.PI / 2);
cloneCollision.rotateAround(cloneCollision.position, new BABYLON.Vector3(1, 0, 0), Math.PI / 2);
}
cloneCollision.isVisible = false;
if (i !== 0) {
new BABYLON.PhysicsAggregate(cloneVisual, BABYLON.PhysicsShapeType.MESH, { mesh: cloneCollision, mass: 0.05, restitution: 0.5 }, scene)
} else {
new BABYLON.PhysicsAggregate(cloneVisual, BABYLON.PhysicsShapeType.MESH, { mesh: cloneCollision, mass: 0, restitution: 0.75 }, scene)
}
}
chainModel.visualMesh.getChildMeshes().forEach(m => m.isVisible = false);
ballChain.visualMesh.rotateAround(ballChain.visualMesh.position, new BABYLON.Vector3(1, 0, 0), Math.PI / 2);
ballChain.collisionMesh.rotateAround(ballChain.collisionMesh.position, new BABYLON.Vector3(1, 0, 0), Math.PI / 2);
ballChain.visualMesh.position = new BABYLON.Vector3(times * positionStep - 0.05, chaingHeight, 0);
ballChain.collisionMesh.position = new BABYLON.Vector3(times * positionStep - 0.05, chaingHeight, 0);
new BABYLON.PhysicsAggregate(ballChain.visualMesh, BABYLON.PhysicsShapeType.MESH, { mesh: ballChain.collisionMesh, mass: 0.2, restitution: 0.75 }, scene)
const light = new Bit.Inputs.BabylonScene.DirectionalLightDto();
light.intensity = 3;
light.direction = [-100, -100, -100];
light.shadowGeneratorMapSize = 4000;
light.shadowDarkness = 0;
bitbybit.babylon.scene.drawDirectionalLight(light);
}
start();
async function createChain() {
const chainOCCT = await createOCCTChains();
const options = new Bit.Inputs.Draw.DrawOcctShapeOptions();
options.precision = 0.001;
options.drawEdges = false;
options.faceColour = "#0000ff";
const visualMesh = await bitbybit.draw.drawAnyAsync({
entity: chainOCCT.fillet,
options
});
options.precision = 0.1;
options.drawEdges = false;
const collisionMeshRes = await bitbybit.draw.drawAnyAsync({
entity: chainOCCT.extruded,
options
});
const collisionMesh = collisionMeshRes.getChildMeshes()[0] as BABYLON.Mesh;
return { visualMesh, collisionMesh }
}
async function createBallChain() {
const chainOCCT = await createOCCTChains();
const sphere = await bitbybit.occt.shapes.solid.createSphere({ radius: 0.4, center: [0.6, 0, 0] });
const shapeCollision = await bitbybit.occt.booleans.union({
shapes: [chainOCCT.extruded, sphere],
keepEdges: false
})
const shapeVisual = await bitbybit.occt.booleans.union({
shapes: [chainOCCT.fillet, sphere],
keepEdges: false
})
const options = new Bit.Inputs.Draw.DrawOcctShapeOptions();
options.precision = 0.005;
options.drawEdges = false;
options.faceColour = "#0000ff";
const visualMesh = await bitbybit.draw.drawAnyAsync({
entity: shapeVisual,
options
});
options.precision = 0.01;
options.drawEdges = false;
const collisionMeshRes = await bitbybit.draw.drawAnyAsync({
entity: shapeCollision,
options
});
const collisionMesh = collisionMeshRes.getChildMeshes()[0] as BABYLON.Mesh;
return { visualMesh, collisionMesh }
}
async function createCylBase() {
const cyl = await bitbybit.occt.shapes.solid.createCylinder({
radius: 15,
height: 0.1,
center: [-4, -0.1, 0],
})
const boxDrawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();
boxDrawOptions.precision = 0.01;
boxDrawOptions.drawEdges = false;
boxDrawOptions.faceColour = "#7766ff";
const boxMesh = await bitbybit.draw.drawAnyAsync({
entity: cyl,
options: boxDrawOptions
})
return boxMesh
}
async function createOCCTChains() {
const chainOffset = 0.05;
const chaingRadiusMinor = 0.1;
const chainRadiusMajor = 0.3;
const radiuses = [[chaingRadiusMinor, chainRadiusMajor - chainOffset], [chaingRadiusMinor + chainOffset, chainRadiusMajor + chainOffset]];
const contourEllipses = await Promise.all(radiuses.map((r) => {
return bitbybit.occt.shapes.wire.createEllipseWire({
radiusMinor: r[0],
radiusMajor: r[1],
center: [0, 0, -chainOffset / 2],
direction: [0, 0, 1],
})
}));
const loft = await bitbybit.occt.operations.loft({
shapes: contourEllipses,
makeSolid: false
});
const extruded = await bitbybit.occt.operations.extrude({
shape: loft,
direction: [0, 0, chainOffset],
});
const fillet = await bitbybit.occt.fillets.filletEdges({
shape: extruded,
radius: chainOffset / 3
});
return { extruded, fillet };
}