### Serving Examples with http-server Source: https://github.com/kripken/ammo.js/blob/main/README.md Command to serve the project directory using http-server via npx on port 3000. This is necessary to run the examples which depend on accessing the built ammo.js file from the 'builds' directory. ```bash npx http-server -p 3000 . ``` -------------------------------- ### Create and Run Tumbler Application - JavaScript Source: https://github.com/kripken/ammo.js/blob/main/bullet/Demos/NativeClient/bin_html/index.html Creates a new instance of the tumbler application and starts it, targeting the HTML element with the ID 'tumbler_view' for rendering. ```JavaScript tumbler.application = new tumbler.Application(); tumbler.application.run('tumbler_view'); ``` -------------------------------- ### Initialize Demo and Start Simulation - Ammo.js/CubicVR - JavaScript Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo/ammo.wasm.html The main function to set up the demo. It initializes the CubicVR.js WebGL context and scene, creates materials and meshes for the objects (boxes and floor), sets up the camera and lighting, initializes and communicates with the physics worker, and starts the CubicVR.js main rendering loop. ```JavaScript function startUp(NUM) { var NUMRANGE = []; while (NUMRANGE.length < NUM) NUMRANGE.push(NUMRANGE.length+1); document.getElementById('postdiv').innerHTML = '(' + NUM + ' cubes)'; //document.getElementById('before').style.visibility = 'hidden'; document.getElementById('during').style.visibility = 'visible'; var canvas = document.getElementById("canvas"); canvas.width = screen.width*0.75; canvas.height = screen.height*0.50; var gl = CubicVR.init(canvas); if (!gl) { alert("Sorry, no WebGL support :("); return; }; var quaternion = new CubicVR.Quaternion; var scene = new CubicVR.Scene(canvas.width, canvas.height, 70); var light = new CubicVR.Light({ type:CubicVR.enums.light.type.POINT, intensity: 0.9, areaCeiling: 80, areaFloor: FLOOR_HEIGHT, areaAxis: [15, 10], distance: 60, mapRes: 1024 }); scene.bindLight(light); scene.camera.position = [0, 2.4, 17]; scene.camera.target = [0, 2.4, 0]; var boxMaterials = []; var boxMeshes = []; for (var i = 0; i < 5; i++) { boxMaterials[i] = new CubicVR.Material({ textures: { color: new CubicVR.Texture("cube" + (i+1) + ".jpg") } }); boxMeshes[i] = new CubicVR.primitives.box({ size: 2.0, material: boxMaterials[i], uvmapper: { projectionMode: CubicVR.enums.uv.projection.CUBIC, scale: [2, 2, 2] } }).calcNormals().triangulateQuads().compile().clean(); } for (var i = 0; i < NUM; i++) { boxes[i] = new CubicVR.SceneObject({ mesh: boxMeshes[Math.floor(Math.random()*5)], position: [0, -10000, 0] }); scene.bindSceneObject(boxes[i], true); } var floorMaterial = new CubicVR.Material({ textures: { color: new CubicVR.Texture("cube3.jpg") } }); var floorMesh = new CubicVR.primitives.box({ size: FLOOR_SIZE, material: floorMaterial, uvmapper: { projectionMode: CubicVR.enums.uv.projection.CUBIC, scale: [4, 4, 4] } }).calcNormals().triangulateQuads().compile().clean(); var floor_ = new CubicVR.SceneObject({ mesh: floorMesh, position: [0, FLOOR_HEIGHT, 0] }); scene.bindSceneObject(floor_, true); // Worker if (physicsWorker) physicsWorker.terminate(); physicsWorker = new Worker('worker.wasm.js'); physicsWorker.onmessage = function(event) { var data = event.data; if (data.isReady){ physicsWorker.postMessage(NUM); return } if (data.objects.length != NUM) return; for (var i = 0; i < NUM; i++) { var physicsObject = data.objects[i]; var renderObject = boxes[i]; renderObject.position[0] = physicsObject[0]; renderObject.position[1] = physicsObject[1]; renderObject.position[2] = physicsObject[2]; quaternion.x = physicsObject[3]; quaternion.y = physicsObject[4]; quaternion.z = physicsObject[5]; quaternion.w = physicsObject[6]; renderObject.rotation = quaternion.toEuler(); } currFPS = data.currFPS; allFPS = data.allFPS; }; // Main loop var mvc = new CubicVR.MouseViewController(canvas, scene.camera); CubicVR.MainLoop(function(timer, gl) { var dt = timer.getLastUpdateSeconds(); scene.render(); showFPS(); }); } ``` -------------------------------- ### Initializing Ammo.js and Scene Setup (JavaScript) Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_gimpact_chain/index.html Sets up the main application entry point, loads the Ammo.js physics engine, initializes global variables for graphics and physics, and calls the main initialization and animation loops. ```javascript const gltfLoader = new THREE.GLTFLoader(); const textureLoader = new THREE.TextureLoader(); Ammo().then(function (Ammo) { // Detects webgl if (!Detector.webgl) { Detector.addGetWebGLMessage(); document.getElementById('container').innerHTML = ""; } // - Global variables - // Graphics variables let container, stats; let camera, controls, scene, renderer; let texture; let clock = new THREE.Clock(); // Physics variables let physicsWorld; const margin = 0.01; const rigidBodies = []; let pos = new THREE.Vector3(); let quat = new THREE.Quaternion(); let transformAux1; let tempBtVec3_1; // Models let urls = [ '../models/chain.glb' ]; // - Main code - init(); animate(); function init() { initGraphics(); initPhysics(); createObjects(); } function initGraphics() { container = document.getElementById('container'); scene = new THREE.Scene(); scene.background = new THREE.Color(0xbfd1e5); renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; container.appendChild(renderer.domElement); camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.2, 2000); camera.position.set(-40, 34, 30); controls = new THREE.OrbitControls(camera, renderer.domElement); controls.target.y = 10; controls.update(); const ambientLight = new THREE.AmbientLight(0x404040); scene.add(ambientLight); const light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(- 10, 10, 5); light.castShadow = true; const d = 10; light.shadow.camera.left = - d; light.shadow.camera.right = d; light.shadow.camera.top = d; light.shadow.camera.bottom = - d; light.shadow.camera.near = 2; light.shadow.camera.far = 50; light.shadow.mapSize.x = 1024; light.shadow.mapSize.y = 1024; scene.add(light); stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '0px'; container.appendChild(stats.domElement); window.addEventListener('resize', onWindowResize); } function initPhysics() { // Physics configuration // --------------------- let m_collisionConfiguration = new Ammo.btDefaultCollisionConfiguration(); let m_dispatcher = new Ammo.btCollisionDispatcher(m_collisionConfiguration); let m_broadphase = new Ammo.btDbvtBroadphase(); let m_constraintSolver = new Ammo.btSequentialImpulseConstraintSolver(); physicsWorld = new Ammo.btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_constraintSolver, m_collisionConfiguration); physicsWorld.setGravity(new Ammo.btVector3(0, -9.810, 0)); Ammo.btGImpactCollisionAlgorithm.prototype.registerAlgorithm(physicsWorld.getDispatcher()); transformAux1 = new Ammo.btTransform(); tempBtVec3_1 = new Ammo.btVector3(0, 0, 0); } function loadModels(urls) { urls.forEach(url => { gltfLoader.load( url, (gltf) => { scene.add(gltf.scene); gltf.scene.traverse((child) => { if (child.type == 'Mesh') { child.castShadow = true; child.receiveShadow = true; child.material.side = THREE.FrontSide; let trimesh = createGImpactCollision(child); pos.copy(child.position); quat.copy(child.quaternion); if (child.name == 'Static') { let body = createRigidBody(child, trimesh, 0, pos, quat); body.setCollisionFlags(body.getCollisionFlags() | 1/* btCollisionObject:: CF_STATIC_OBJECT */); } else { createRigidBody(child, trimesh, 1, pos, quat); } }) } ) }); } function createObjects() { // Ground pos.set(0, - 0.5, 0); quat.set(0, 0, 0, 1); const ground = createParalellepipedWithPhysics(40, 1, 40, 0, pos, quat, new THREE.MeshPhongMaterial({ color: 0xFFFFFF })); scene.add(ground); ground.receiveShadow = true; textureLoader.load('../textures/grid.png', function (texture) { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(40, 40); ground.material.map = texture; ground.material.needsUpdate = true; }); // Wall let brickMass = 0.5; let brickLength = 2; let brickDepth = 1; let brickHeight = brickLength * 0.5; let numBricksLength = 6; let numBricksHeight = 8; let z0 = - numBricksLength * brickLength * 0.5; pos.set(0, brickHeight * 0.5, z0); quat.set(0, 0, 0, 1); for (let j = 0; j < numBricksHeight; j++) { let oddRow = (j % 2) == 1; pos.z = z0; if (oddRow) { pos.z -= 0.25 * brickLength; } let nRow = oddRow ? numBricksLength + 1 : numBricksLength; for (let i = 0; i < nRow; i++) { let brickLengthCurrent = brickLength; let brickMassCurrent = brickMass; if (oddRow && (i == 0 || i == nRow - 1)) { brickLengthCurrent *= 0.5; brickMassCurrent *= 0.5; } let brick = createParalellepiped(brickDepth, brickHeight, brickLengthCurrent, brickMassCurrent, pos, quat, createMaterial()); brick.castShadow = true; brick.receiveShadow = true; scene.add(b ``` -------------------------------- ### Running ava Tests Directly with npx Source: https://github.com/kripken/ammo.js/blob/main/README.md Examples of running the ava test runner directly using npx, allowing for more command-line options. The examples show running with verbose output and inspecting node arguments. ```bash npx ava --verbose npx ava --node-arguments inspect ``` -------------------------------- ### Initialize Ammo.js and Setup Demo Environment Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_softbody_volume2/index.html Initializes the Ammo.js physics engine, checks for WebGL support, sets up global variables for graphics (Three.js) and physics (Ammo.js), and calls the main initialization and animation functions. ```javascript Ammo().then(function(Ammo) { // Detects webgl if ( ! Detector.webgl ) { Detector.addGetWebGLMessage(); document.getElementById( 'container' ).innerHTML = ""; } // - Global variables - // Graphics variables var container, stats; var camera, controls, scene, renderer; var textureLoader; var clock = new THREE.Clock(); var clickRequest = false; var mouseCoords = new THREE.Vector2(); var raycaster = new THREE.Raycaster(); var ballMaterial = new THREE.MeshPhongMaterial( { color: 0x202020 } ); var pos = new THREE.Vector3(); var quat = new THREE.Quaternion(); // Physics variables var gravityConstant = -9.8; var collisionConfiguration; var dispatcher; var broadphase; var solver; var physicsWorld; var rigidBodies = []; var softBodies = []; var margin = 0.05; var transformAux1 = new Ammo.btTransform(); var softBodyHelpers = new Ammo.btSoftBodyHelpers(); // - Main code - init(); animate(); // - Functions - function init() { initGraphics(); initPhysics(); createObjects(); initInput(); } function initGraphics() { container = document.getElementById( 'container' ); camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.2, 2000 ); scene = new THREE.Scene(); camera.position.x = -7; camera.position.y = 5; camera.position.z = 8; controls = new THREE.OrbitControls( camera ); controls.target.y = 2; renderer = new THREE.WebGLRenderer(); renderer.setClearColor( 0xbfd1e5 ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.shadowMap.enabled = true; textureLoader = new THREE.TextureLoader(); var ambientLight = new THREE.AmbientLight( 0x404040 ); scene.add( ambientLight ); var light = new THREE.DirectionalLight( 0xffffff, 1 ); light.position.set( -10, 10, 5 ); light.castShadow = true; var d = 20; light.shadow.camera.left = -d; light.shadow.camera.right = d; light.shadow.camera.top = d; light.shadow.camera.bottom = -d; light.shadow.camera.near = 2; light.shadow.camera.far = 50; light.shadow.mapSize.x = 1024; light.shadow.mapSize.y = 1024; scene.add( light ); container.innerHTML = ""; container.appendChild( renderer.domElement ); stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '0px'; container.appendChild( stats.domElement ); // window.addEventListener( 'resize', onWindowResize, false ); } function initPhysics() { // Physics configuration collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration(); dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration ); broadphase = new Ammo.btDbvtBroadphase(); solver = new Ammo.btSequentialImpulseConstraintSolver(); softBodySolver = new Ammo.btDefaultSoftBodySolver(); physicsWorld = new Ammo.btSoftRigidDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration, softBodySolver); physicsWorld.setGravity( new Ammo.btVector3( 0, gravityConstant, 0 ) ); physicsWorld.getWorldInfo().set_m_gravity( new Ammo.btVector3( 0, gravityConstant, 0 ) ); } function createObjects() { // Ground pos.set( 0, - 0.5, 0 ); quat.set( 0, 0, 0, 1 ); var ground = createParalellepiped( 40, 1, 40, 0, pos, quat, new THREE.MeshPhongMaterial( { color: 0xFFFFFF } ) ); ground.castShadow = true; ground.receiveShadow = true; textureLoader.load( "../textures/grid.png", function( texture ) { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set( 40, 40 ); ground.material.map = texture; ground.material.needsUpdate = true; } ); // Create soft volumes var volumeMass = 15; var bx = 2; var by = 1; var bz = 3; var nn = 5; createSoftBox( bx, by, bz, nn * bx, nn * by, nn * bz, 0, 5, 0, volumeMass, 0 ); // Some obstacle pos.set( 0, 0.5, 0 ); quat.set( 0, 0, 0, 1 ); var obstacle = createParalellepiped( 6, 1, 1, 0, pos, quat, new THREE.MeshPhongMaterial( { color: 0x606060 } ) ); obstacle.castShadow = true; obstacle.receiveShadow = true; } function createSoftBox( sizeX, sizeY, sizeZ, numPointsX, numPointsY, numPointsZ, tX, tY, tZ, mass, pressure ) { if ( numPointsX < 2 || numPointsY < 2 || numPointsZ < 2 ) { return; } // Offset is the numbers assigned to 8 vertices of the cube in ascending Z, Y, X in this order. // indexFromOffset is the vertex index increase for a given offset. var indexFromOffset = []; for ( var offset = 0; offset < 8; offset++ ) { var a = offset & 1 ? 1 : 0; var b = offset & 2 ? 1 : 0; var c = offset & 4 ? 1 : 0; var index = a + b * numPointsX + c * numPointsX * numPointsY; indexFromOffset[ offset ] = index; } // Construct BufferGeometry var numVertices = numPointsX * numPointsY * numPointsZ; var numFaces = 4 * ( ( numPointsX - 1 ) * ( numPoint ``` -------------------------------- ### Initialize Ammo.js and Setup Demo Environment - JavaScript Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_vehicle/index.html Loads the Ammo.js physics engine, checks for WebGL support, declares global variables for graphics and physics, and defines the main execution context within the Ammo().then() promise. ```javascript Ammo().then(function(Ammo) { // Detects webgl if ( ! Detector.webgl ) { Detector.addGetWebGLMessage(); document.getElementById( 'container' ).innerHTML = ""; } // - Global variables - var DISABLE_DEACTIVATION = 4; var TRANSFORM_AUX = new Ammo.btTransform(); var ZERO_QUATERNION = new THREE.Quaternion(0, 0, 0, 1); // Graphics variables var container, stats, speedometer; var camera, controls, scene, renderer; var clock = new THREE.Clock(); var materialDynamic, materialStatic, materialInteractive; // Physics variables var collisionConfiguration; var dispatcher; var broadphase; var solver; var physicsWorld; var syncList = []; var time = 0; // Keybord actions var actions = {}; var keysActions = { "KeyW":'acceleration', "KeyS":'braking', "KeyA":'left', "KeyD":'right' }; // - Functions - function initGraphics() { /* ... */ } function onWindowResize() { /* ... */ } function initPhysics() { /* ... */ } function tick() { /* ... */ } function keyup(e) { /* ... */ } function keydown(e) { /* ... */ } function createBox(pos, quat, w, l, h, mass, friction) { /* ... */ } // ... rest of the code ... }); ``` -------------------------------- ### Initialize Ammo.js and Setup Demo Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_softbody_cloth/index.html Initializes the Ammo.js physics engine asynchronously, checks for WebGL support, declares global variables for graphics (Three.js) and physics (Ammo.js), and calls the main initialization and animation functions. ```javascript Ammo().then(function(Ammo) { // Detects webgl if ( ! Detector.webgl ) { Detector.addGetWebGLMessage(); document.getElementById( 'container' ).innerHTML = ""; } // - Global variables - // Graphics variables var container, stats; var camera, controls, scene, renderer; var textureLoader; var clock = new THREE.Clock(); // Physics variables var gravityConstant = -9.8; var collisionConfiguration; var dispatcher; var broadphase; var solver; var physicsWorld; var rigidBodies = []; var margin = 0.05; var hinge; var cloth; var transformAux1 = new Ammo.btTransform(); var time = 0; var armMovement = 0; // - Main code - init(); animate(); // - Functions - function init() { initGraphics(); initPhysics(); createObjects(); initInput(); } function initGraphics() { container = document.getElementById( 'container' ); camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.2, 2000 ); scene = new THREE.Scene(); camera.position.x = -12; camera.position.y = 7; camera.position.z = 4; controls = new THREE.OrbitControls( camera ); controls.target.y = 2; renderer = new THREE.WebGLRenderer(); renderer.setClearColor( 0xbfd1e5 ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.shadowMap.enabled = true; textureLoader = new THREE.TextureLoader(); var ambientLight = new THREE.AmbientLight( 0x404040 ); scene.add( ambientLight ); var light = new THREE.DirectionalLight( 0xffffff, 1 ); light.position.set( -7, 10, 15 ); light.castShadow = true; var d = 10; light.shadow.camera.left = -d; light.shadow.camera.right = d; light.shadow.camera.top = d; light.shadow.camera.bottom = -d; light.shadow.camera.near = 2; light.shadow.camera.far = 50; light.shadow.mapSize.x = 1024; light.shadow.mapSize.y = 1024; light.shadow.bias = -0.01; scene.add( light ); container.innerHTML = ""; container.appendChild( renderer.domElement ); stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '0px'; container.appendChild( stats.domElement ); // window.addEventListener( 'resize', onWindowResize, false ); } function initPhysics() { // Physics configuration collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration(); dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration ); broadphase = new Ammo.btDbvtBroadphase(); solver = new Ammo.btSequentialImpulseConstraintSolver(); softBodySolver = new Ammo.btDefaultSoftBodySolver(); physicsWorld = new Ammo.btSoftRigidDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration, softBodySolver); physicsWorld.setGravity( new Ammo.btVector3( 0, gravityConstant, 0 ) ); physicsWorld.getWorldInfo().set_m_gravity( new Ammo.btVector3( 0, gravityConstant, 0 ) ); } function createObjects() { var pos = new THREE.Vector3(); var quat = new THREE.Quaternion(); // Ground pos.set( 0, - 0.5, 0 ); quat.set( 0, 0, 0, 1 ); var ground = createParalellepiped( 40, 1, 40, 0, pos, quat, new THREE.MeshPhongMaterial( { color: 0xFFFFFF } ) ); ground.castShadow = true; ground.receiveShadow = true; textureLoader.load( "../textures/grid.png", function( texture ) { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set( 40, 40 ); ground.material.map = texture; ground.material.needsUpdate = true; } ); // Wall var brickMass = 0.5; var brickLength = 1.2; var brickDepth = 0.6; var brickHeight = brickLength * 0.5; var numBricksLength = 6; var numBricksHeight = 8; var z0 = - numBricksLength * brickLength * 0.5; pos.set( 0, brickHeight * 0.5, z0 ); quat.set( 0, 0, 0, 1 ); for ( var j = 0; j < numBricksHeight; j ++ ) { var oddRow = ( j % 2 ) == 1; pos.z = z0; if ( oddRow ) { pos.z -= 0.25 * brickLength; } var nRow = oddRow? numBricksLength + 1 : numBricksLength; for ( var i = 0; i < nRow; i ++ ) { var brickLengthCurrent = brickLength; var brickMassCurrent = brickMass; if ( oddRow && ( i == 0 || i == nRow - 1 ) ) { brickLengthCurrent *= 0.5; brickMassCurrent *= 0.5; } var brick = createParalellepiped( brickDepth, brickHeight, brickLengthCurrent, brickMassCurrent, pos, quat, createMaterial() ); brick.castShadow = true; brick.receiveShadow = true; if ( oddRow && ( i == 0 || i == nRow - 2 ) ) { pos.z += 0.75 * brickLength; } else { pos.z += brickLength; } } pos.y += brickHeight; } // The cloth // Cloth graphic object var clothWidth = 4; var clothHeight = 3; var clothNumSegmentsZ = clothWidth * 5; var clothNumSegmentsY = clothHeight * 5; var clothSegmentLengthZ = clothWidth / clothNumSegmentsZ; var clothSegmentLengthY = clothHeight / clothNumSegmentsY; var clothPos = new THREE.Vector3( -3, 3, 2 ); //var ``` -------------------------------- ### Initializing Simulation and Starting Loop (Ammo.js, Three.js) - JavaScript Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_vehicle/index.html This code block represents the main entry point after the physics engine is loaded. It calls functions to initialize the graphics and physics environments, creates all the objects for the simulation, and then starts the main simulation loop (`tick`) which likely handles physics steps and rendering updates. ```javascript initGraphics(); initPhysics(); createObjects(); tick(); ``` -------------------------------- ### Initialize Tumbler Namespace - JavaScript Source: https://github.com/kripken/ammo.js/blob/main/bullet/Demos/NativeClient/bin_html/index.html Initializes an empty object to serve as the namespace for the tumbler application components. ```JavaScript tumbler = {}; ``` -------------------------------- ### Initialize Demo - Ammo.js/Three.js Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_test_ray/index.html The main initialization function that orchestrates the setup of the demo. It calls separate functions to initialize the graphics scene (`initGraphics`), the physics world (`initPhysics`), create the objects within the scene (`createObjects`), and set up input handling (`initInput`). ```javascript function init() { initGraphics(); initPhysics(); createObjects(); initInput(); } ``` -------------------------------- ### Configuring CMake Build Options Source: https://github.com/kripken/ammo.js/blob/main/README.md Examples of specifying key options during the CMake configuration step using the -D flag. Options include enabling closure compilation, setting total memory allocation, and allowing memory growth. ```bash cmake -B builds -DCLOSURE=1 cmake -B builds -DTOTAL_MEMORY=268435456 cmake -B builds -DALLOW_MEMORY_GROWTH=1 ``` -------------------------------- ### Creating a Three.js Box and Ammo.js Rigid Body Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_softbody_volume2/index.html Creates a Three.js `Mesh` with a `BoxGeometry` and a corresponding Ammo.js `btBoxShape`. It then calls `createRigidBody` to finalize the physics setup and returns the Three.js object. Requires `THREE`, `Ammo`, `margin`, `sx`, `sy`, `sz`, `mass`, `pos`, `quat`, `material`. ```javascript function createParalellepiped( sx, sy, sz, mass, pos, quat, material ) { var threeObject = new THREE.Mesh( new THREE.BoxGeometry( sx, sy, sz, 1, 1, 1 ), material ); var shape = new Ammo.btBoxShape( new Ammo.btVector3( sx * 0.5, sy * 0.5, sz * 0.5 ) ); shape.setMargin( margin ); createRigidBody( threeObject, shape, mass, pos, quat ); return threeObject; } ``` -------------------------------- ### Initialize 3D Scene and Physics World (Ammo.js, Three.js) Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_softbody_rope/index.html This code block initializes the graphics environment using Three.js and the physics simulation using Ammo.js. It sets up the camera, scene, renderer, lighting, and configures the physics world with gravity and collision handling. It also starts the main animation loop and initializes input handling, and begins creating scene objects. ```javascript Ammo().then(function(Ammo) { // Detects webgl if ( ! Detector.webgl ) { Detector.addGetWebGLMessage(); document.getElementById( 'container' ).innerHTML = ""; } // - Global variables - // Graphics variables var container, stats; var camera, controls, scene, renderer; var textureLoader; var clock = new THREE.Clock(); // Physics variables var gravityConstant = -9.8; var collisionConfiguration; var dispatcher; var broadphase; var solver; var physicsWorld; var rigidBodies = []; var margin = 0.05; var hinge; var rope; var transformAux1 = new Ammo.btTransform(); var time = 0; var armMovement = 0; // - Main code - init(); animate(); // - Functions - function init() { initGraphics(); initPhysics(); createObjects(); initInput(); } function initGraphics() { container = document.getElementById( 'container' ); camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.2, 2000 ); scene = new THREE.Scene(); camera.position.x = -7; camera.position.y = 5; camera.position.z = 8; controls = new THREE.OrbitControls( camera ); controls.target.y = 2; renderer = new THREE.WebGLRenderer(); renderer.setClearColor( 0xbfd1e5 ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.shadowMap.enabled = true; textureLoader = new THREE.TextureLoader(); var ambientLight = new THREE.AmbientLight( 0x404040 ); scene.add( ambientLight ); var light = new THREE.DirectionalLight( 0xffffff, 1 ); light.position.set( -10, 10, 5 ); light.castShadow = true; var d = 10; light.shadow.camera.left = -d; light.shadow.camera.right = d; light.shadow.camera.top = d; light.shadow.camera.bottom = -d; light.shadow.camera.near = 2; light.shadow.camera.far = 50; light.shadow.mapSize.x = 1024; light.shadow.mapSize.y = 1024; scene.add( light ); container.innerHTML = ""; container.appendChild( renderer.domElement ); stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '0px'; container.appendChild( stats.domElement ); // window.addEventListener( 'resize', onWindowResize, false ); } function initPhysics() { // Physics configuration collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration(); dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration ); broadphase = new Ammo.btDbvtBroadphase(); solver = new Ammo.btSequentialImpulseConstraintSolver(); softBodySolver = new Ammo.btDefaultSoftBodySolver(); physicsWorld = new Ammo.btSoftRigidDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration, softBodySolver); physicsWorld.setGravity( new Ammo.btVector3( 0, gravityConstant, 0 ) ); physicsWorld.getWorldInfo().set_m_gravity( new Ammo.btVector3( 0, gravityConstant, 0 ) ); } function createObjects() { var pos = new THREE.Vector3(); var quat = new THREE.Quaternion(); // Ground pos.set( 0, - 0.5, 0 ); quat.set( 0, 0, 0, 1 ); var ground = createParalellepiped( 40, 1, 40, 0, pos, quat, new THREE.MeshPhongMaterial( { color: 0xFFFFFF } ) ); ground.castShadow = true; ground.receiveShadow = true; textureLoader.load( "../textures/grid.png", function( texture ) { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set( 40, 40 ); ground.material.map = texture; ground.material.needsUpdate = true; } ); // Ball var ballMass = 1.2; var ballRadius = 0.6; var ball = new THREE.Mesh( new THREE.SphereGeometry( ballRadius, 20, 20 ), new THREE.MeshPhongMaterial( { color: 0x202020 } ) ); ball.castShadow = true; ball.receiveShadow = true; var ballShape = new Ammo.btSphereShape( ballRadius ); ballShape.setMargin( margin ); pos.set( -3, 2, 0 ); quat.set( 0, 0, 0, 1 ); createRigidBody( ball, ballShape, ballMass, pos, quat ); ball.userData.physicsBody.setFriction( 0.5 ); // Wall var brickMass = 0.5; var brickLength = 1.2; var brickDepth = 0.6; var brickHeight = brickLength * 0.5; var numBricksLength = 6; var numBricksHeight = 8; var z0 = - numBricksLength * brickLength * 0.5; pos.set( 0, brickHeight * 0.5, z0 ); quat.set( 0, 0, 0, 1 ); for ( var j = 0; j < numBricksHeight; j ++ ) { var oddRow = ( j % 2 ) == 1; pos.z = z0; if ( oddRow ) { pos.z -= 0.25 * brickLength; } var nRow = oddRow? numBricksLength + 1 : numBricksLength; for ( var i = 0; i < nRow; i ++ ) { var brickLengthCurrent = brickLength; var brickMassCurrent = brickMass; if ( oddRow && ( i == 0 || i == nRow - 1 ) ) { brickLengthCurrent *= 0.5; brickMassCurrent *= 0.5; } var brick = createParalellepiped( brickDepth, brickHeight, brickLengthCurrent, brickMassCurrent, pos, quat, createMaterial() ); brick.castShadow = true; brick.receiveShadow = true; if ( oddRow && ( i == 0 || i == nRow - 1 ) ) { ``` -------------------------------- ### Release Process: Running WebGL Demo Source: https://github.com/kripken/ammo.js/blob/main/README.md Command to open the WebGL demo in a browser as part of the release process checklist. Note that Chrome may require a web server. ```bash firefox examples/webgl_demo/ammo.html ``` -------------------------------- ### Starting Animation Loop with requestAnimationFrame Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_terrain/index.html This function sets up the main animation loop using `requestAnimationFrame`. It calls the `render` function on each frame. ```javascript function animate() { requestAnimationFrame( animate ); render(); stats.update(); } ``` -------------------------------- ### Release Process Build and Test Steps Source: https://github.com/kripken/ammo.js/blob/main/README.md Commands included in the checklist for pushing a new build during the release process. This involves configuring with closure, building both libraries, and running tests. ```bash cmake -B builds -DCLOSURE=1 cmake --build builds npm test ``` -------------------------------- ### Troubleshooting: Missing 'new' Keyword Source: https://github.com/kripken/ammo.js/blob/main/README.md An example of incorrect JavaScript code when creating an Ammo.js object without the 'new' keyword. This is a common mistake that can lead to runtime errors like 'Cannot read property 'a' of undefined'. ```javascript var vec = Ammo.btVector3(1,2,3); // This is wrong! Need 'new'! ``` -------------------------------- ### Main Initialization Function - JavaScript Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_terrain/index.html The primary initialization function that orchestrates the setup of the demo. It calls `generateHeight` to create terrain data, `initGraphics` to set up the 3D scene, and `initPhysics` to configure the physics world. ```javascript function init() { heightData = generateHeight( terrainWidth, terrainDepth, terrainMinHeight, terrainMaxHeight ); initGraphics(); initPhysics(); } ``` -------------------------------- ### Setting up Main Render Loop (JavaScript) Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo/ammo.html Initializes a mouse view controller for camera interaction and sets up the main CubicVR.js rendering loop. This loop is executed continuously to render the scene and update the displayed FPS. ```javascript var mvc = new CubicVR.MouseViewController(canvas, scene.camera); CubicVR.MainLoop(function(timer, gl) { var dt = timer.getLastUpdateSeconds(); scene.render(); showFPS(); }); ``` -------------------------------- ### Setting up Room Walls (Physics and Visual) - JavaScript Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_test_ray/index.html This code sets up the physical and visual components for the room walls. It creates a static rigid body using `createRigidBody` and `createBoxShape` for physics and a visual box using `createVisualBox` for rendering. The visual box geometry vertices are then inverted to make the box appear hollow, forming the room walls. ```JavaScript pos.set( roomX * 0.5 + 0.5, roomY * 0.5 + 1, 0 ); quat.set( 0, 0, 0, 1 ); createRigidBody( new THREE.Object3D(), createBoxShape( 1, roomY + 2, roomZ ), 0, pos, quat ); // Walls visual pos.set( 0, roomY * 0.5, 0 ); quat.set( 0, 0, 0, 1 ); var box = createVisualBox( roomX, roomY, roomZ, pos, quat, wallsMaterial ); box.castShadow = false; box.receiveShadow = true; // Invert geometry var vertices = box.geometry.vertices; for ( var i = 0; i < vertices.length; i++ ) { vertices[ i ].multiplyScalar( -1 ); } ``` -------------------------------- ### Initialize Physics - Ammo.js Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_test_ray/index.html Sets up the Ammo.js physics world. This involves creating the collision configuration, dispatcher, broadphase, solver, and soft body solver. It then initializes the `btSoftRigidDynamicsWorld` and sets the gravity vector. The world information's gravity is also explicitly set. ```javascript function initPhysics() { // Physics configuration collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration(); dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration ); broadphase = new Ammo.btDbvtBroadphase(); solver = new Ammo.btSequentialImpulseConstraintSolver(); softBodySolver = new Ammo.btDefaultSoftBodySolver(); physicsWorld = new Ammo.btSoftRigidDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration, softBodySolver); physicsWorld.setGravity( new Ammo.btVector3( 0, gravityConstant, 0 ) ); physicsWorld.getWorldInfo().set_m_gravity( new Ammo.btVector3( 0, gravityConstant, 0 ) ); } ``` -------------------------------- ### Initialize Graphics - Three.js Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_test_ray/index.html Sets up the Three.js graphics environment. This includes creating the scene, camera, OrbitControls for camera manipulation, the WebGL renderer with shadow mapping enabled, texture loader, ambient light, and a directional light with shadow properties. It also appends the renderer's DOM element and a stats panel to the container. ```javascript function initGraphics() { container = document.getElementById( 'container' ); camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.02, 50 ); scene = new THREE.Scene(); camera.position.x = -3; camera.position.y = 2; camera.position.z = 2; controls = new THREE.OrbitControls( camera ); controls.target.y = 1; controls.enablePan = false; renderer = new THREE.WebGLRenderer(); renderer.setClearColor( 0xbfd1e5 ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.shadowMap.enabled = true; textureLoader = new THREE.TextureLoader(); var ambientLight = new THREE.AmbientLight( 0x404040 ); scene.add( ambientLight ); var light = new THREE.DirectionalLight( 0xffffff, 1 ); light.position.set( -10, 10, 5 ); light.castShadow = true; var d = 10; light.shadow.camera.left = -d; light.shadow.camera.right = d; light.shadow.camera.top = d; light.shadow.camera.bottom = -d; light.shadow.camera.near = 2; light.shadow.camera.far = 50; light.shadow.mapSize.x = 1024; light.shadow.mapSize.y = 1024; scene.add( light ); container.innerHTML = ""; container.appendChild( renderer.domElement ); stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '0px'; container.appendChild( stats.domElement ); // window.addEventListener( 'resize', onWindowResize, false ); } ``` -------------------------------- ### Creating Paralellepiped Helper - Ammo.js Three.js JavaScript Source: https://github.com/kripken/ammo.js/blob/main/examples/webgl_demo_softbody_rope/index.html A helper function that creates a Three.js `Mesh` with a `BoxGeometry` and an associated Ammo.js `btBoxShape`. It sets the shape's margin and returns the Three.js object after calling `createRigidBody` to handle the physics setup. ```JavaScript function createParalellepiped( sx, sy, sz, mass, pos, quat, material ) { var threeObject = new THREE.Mesh( new THREE.BoxGeometry( sx, sy, sz, 1, 1, 1 ), material ); var shape = new Ammo.btBoxShape( new Ammo.btVector3( sx * 0.5, sy * 0.5, sz * 0.5 ) ); shape.setMargin( margin ); createRigidBody( threeObject, shape, mass, pos, quat ); return threeObject; } ```