3D Chain Physics Havok

3D Chain Physics Havok script details
Author
matas
Type
Typescript logo image
typescript
App Version
0.15.10
Visibility
public
Date Created
Nov 10, 2023, 2:04:49 PM
Last Edit Date
Jun 10, 2024, 4:34:31 PM

Script Details

The Code
const engine = bitbybit.babylon.engine.getEngine(); const scene = bitbybit.babylon.scene.getScene(); const camera = scene.activeCamera as BABYLON.ArcRotateCamera; camera.position = new BABYLON.Vector3(80, 45, 10); camera.target = new BABYLON.Vector3(0, 25, 0); const start = async () => { const chainModel = await createChain(); const ballChain = await createBallChain(); chainModel.collisionMesh.isVisible = false; ballChain.collisionMesh.isVisible = false; bitbybit.babylon.scene.enablePhysics({ vector: [0, -9.81, 0] }); const boxMeshRes = await createBox(); const boxMeshCollision = boxMeshRes.getChildMeshes()[0] as BABYLON.Mesh; const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 8, segments: 32 }, scene); const material = new BABYLON.PBRMetallicRoughnessMaterial("spheres", scene); material.baseColor = new BABYLON.Color3(0, 0, 1); material.metallic = 0.7; material.roughness = 0.1; sphere.position.y = 4; sphere.material = material; const step = 10; const limit = 5; for (let x = 0; x < limit; x++) { for (let z = 0; z < limit; z++) { const sph = sphere.clone(`${x}-${z}`); sph.position = new BABYLON.Vector3(x * step - 60, 20, z * step - (step * limit / 2)); new BABYLON.PhysicsAggregate(sph, BABYLON.PhysicsShapeType.SPHERE, { mass: 0.01, restitution: 0.75 }, scene); } } new BABYLON.PhysicsAggregate(sphere, BABYLON.PhysicsShapeType.SPHERE, { mass: 0.3, restitution: 0.75 }, scene); new BABYLON.PhysicsAggregate(boxMeshCollision, BABYLON.PhysicsShapeType.MESH, { mass: 0 }, scene); const positionStep = 4.5; const times = 8; for (let i = 0; i < times; i++) { const cloneVisual = chainModel.visualMesh.clone(`m-${i}`); cloneVisual.getChildMeshes().forEach(cm => { if (cm instanceof BABYLON.LinesMesh) { cm.enableEdgesRendering(); cm.edgesWidth = 1; } else { cm.material.zOffset = 2; } }) const cloneCollision = chainModel.collisionMesh.clone(`m-${i}`); cloneVisual.position = new BABYLON.Vector3(i * positionStep, 45, 0); cloneCollision.position = new BABYLON.Vector3(i * positionStep, 45, 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.5, restitution: 0.75 }, 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.5, 45, 0); ballChain.collisionMesh.position = new BABYLON.Vector3(times * positionStep - 0.5, 45, 0); new BABYLON.PhysicsAggregate(ballChain.visualMesh, BABYLON.PhysicsShapeType.MESH, { mesh: ballChain.collisionMesh, mass: 2, restitution: 0.75 }, scene) const light = new Bit.Inputs.BabylonScene.DirectionalLightDto(); light.intensity = 1; light.direction = [200, -200, 200]; light.shadowGeneratorMapSize = 2056; bitbybit.babylon.scene.drawDirectionalLight(light); } start(); async function createChain() { const chainOCCT = await createOCCTChains(); const options = new Bit.Inputs.Draw.DrawOcctShapeOptions(); options.precision = 0.005; 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: 4, center: [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; const visualMesh = await bitbybit.draw.drawAnyAsync({ entity: shapeVisual, options }); options.precision = 0.1; 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 createBox() { const box = await bitbybit.occt.shapes.solid.createBox({ width: 200, length: 200, height: 30, center: [0, 13, 0], }) const boxCutout = await bitbybit.occt.shapes.solid.createBox({ width: 190, length: 190, height: 30, center: [0, 15, 0], }) const diff = await bitbybit.occt.booleans.difference({ shape: box, shapes: [boxCutout], keepEdges: false }) const boxDrawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions(); boxDrawOptions.precision = 0.1; boxDrawOptions.faceColour = "#ff00ff"; const boxMesh = await bitbybit.draw.drawAnyAsync({ entity: diff, options: boxDrawOptions }) return boxMesh } async function createOCCTChains() { const chainOffset = 0.5; const chaingRadiusMinor = 1; const chainRadiusMajor = 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 }; }