Bowling Pin

Bowling Pin script details
Type
Typescript logo image
typescript
App Version
0.15.15
Visibility
public
Date Created
Jun 14, 2024, 10:40:03 AM
Last Edit Date
Aug 7, 2024, 9:28:34 AM

Script Details

The Code
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 }; }