Ver código fonte

Added Ammo.js physics examples (#9153)

* New ammo.js lib version. Physics demos: terrain and rope.

* Physics demo: Cloth and wall.

* Physics demo: Soft volumes.

* Added physics demos to files list.
Juan Jose Luna Espinosa 9 anos atrás
pai
commit
902590bd25

+ 4 - 0
examples/files.js

@@ -178,6 +178,10 @@ var files = {
 		"webgl_performance",
 		"webgl_performance_doublesided",
 		"webgl_performance_static",
+		"webgl_physics_terrain",
+		"webgl_physics_rope",
+		"webgl_physics_cloth",
+		"webgl_physics_volume",
 		"webgl_points_billboards",
 		"webgl_points_billboards_colors",
 		"webgl_points_dynamic",

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
examples/js/libs/ammo.js


BIN
examples/textures/colors.png


BIN
examples/textures/grid.png


+ 467 - 0
examples/webgl_physics_cloth.html

@@ -0,0 +1,467 @@
+<html lang="en">
+    <head>
+        <title>Ammo.js softbody cloth demo</title>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+        <style>
+            body {
+                color: #61443e;
+                font-family:Monospace;
+                font-size:13px;
+                text-align:center;
+
+                background-color: #bfd1e5;
+                margin: 0px;
+                overflow: hidden;
+            }
+
+            #info {
+                position: absolute;
+                top: 0px; width: 100%;
+                padding: 5px;
+            }
+
+            a {
+
+                color: #a06851;
+            }
+
+        </style>
+    </head>
+    <body>
+	<div id="info">Ammo.js physics soft body cloth demo<br>Press Q or A to move the arm.</div>
+        <div id="container"><br /><br /><br /><br /><br />Loading...</div>
+
+	<script src="../build/three.js"></script>
+	<script src="js/libs/ammo.js"></script>
+	<script src="js/controls/OrbitControls.js"></script>
+        <script src="js/Detector.js"></script>
+	<script src="js/libs/stats.min.js"></script>
+
+        <script>
+
+		// 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.shadowBias = -0.003;
+			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 clothGeometry = new THREE.BufferGeometry();
+			var clothGeometry = new THREE.PlaneBufferGeometry( clothWidth, clothHeight, clothNumSegmentsZ, clothNumSegmentsY );
+			clothGeometry.rotateY( Math.PI * 0.5 )
+			clothGeometry.translate( clothPos.x, clothPos.y + clothHeight * 0.5, clothPos.z - clothWidth * 0.5 )
+			//var clothMaterial = new THREE.MeshLambertMaterial( { color: 0x0030A0, side: THREE.DoubleSide } );
+			var clothMaterial = new THREE.MeshLambertMaterial( { color: 0xFFFFFF, side: THREE.DoubleSide } );
+			cloth = new THREE.Mesh( clothGeometry, clothMaterial );
+			cloth.castShadow = true;
+			cloth.receiveShadow = true;
+			scene.add( cloth );
+			textureLoader.load( "textures/grid.png", function( texture ) {
+				texture.wrapS = THREE.RepeatWrapping;
+				texture.wrapT = THREE.RepeatWrapping;
+				texture.repeat.set( clothNumSegmentsZ, clothNumSegmentsY );
+				cloth.material.map = texture;
+				cloth.material.needsUpdate = true;
+			} );
+
+			// Cloth physic object
+			var softBodyHelpers = new Ammo.btSoftBodyHelpers();
+			var clothCorner00 = new Ammo.btVector3( clothPos.x, clothPos.y + clothHeight, clothPos.z );
+			var clothCorner01 = new Ammo.btVector3( clothPos.x, clothPos.y + clothHeight, clothPos.z - clothWidth );
+			var clothCorner10 = new Ammo.btVector3( clothPos.x, clothPos.y, clothPos.z );
+			var clothCorner11 = new Ammo.btVector3( clothPos.x, clothPos.y, clothPos.z - clothWidth );
+			var clothSoftBody = softBodyHelpers.CreatePatch( physicsWorld.getWorldInfo(), clothCorner00, clothCorner01, clothCorner10, clothCorner11, clothNumSegmentsZ + 1, clothNumSegmentsY + 1, 0, true );
+			var sbConfig = clothSoftBody.get_m_cfg();
+			sbConfig.set_viterations( 10 );
+			sbConfig.set_piterations( 10 );
+
+			clothSoftBody.setTotalMass( 0.9, false )
+			Ammo.castObject( clothSoftBody, Ammo.btCollisionObject ).getCollisionShape().setMargin( margin * 3 );
+			physicsWorld.addSoftBody( clothSoftBody, 1, -1 );
+			cloth.userData.physicsBody = clothSoftBody;
+			// Disable deactivation
+			clothSoftBody.setActivationState( 4 );
+
+			// The base
+			var armMass = 2;
+			var armLength = 3 + clothWidth;
+			var pylonHeight = clothPos.y + clothHeight;
+			var baseMaterial = new THREE.MeshPhongMaterial( { color: 0x606060 } );
+			pos.set( clothPos.x, 0.1, clothPos.z - armLength );
+			quat.set( 0, 0, 0, 1 );
+			var base = createParalellepiped( 1, 0.2, 1, 0, pos, quat, baseMaterial );
+			base.castShadow = true;
+			base.receiveShadow = true;
+			pos.set( clothPos.x, 0.5 * pylonHeight, clothPos.z - armLength );
+			var pylon = createParalellepiped( 0.4, pylonHeight, 0.4, 0, pos, quat, baseMaterial );
+			pylon.castShadow = true;
+			pylon.receiveShadow = true;
+			pos.set( clothPos.x, pylonHeight + 0.2, clothPos.z - 0.5 * armLength );
+			var arm = createParalellepiped( 0.4, 0.4, armLength + 0.4, armMass, pos, quat, baseMaterial );
+			arm.castShadow = true;
+			arm.receiveShadow = true;
+
+			// Glue the cloth to the arm
+			var influence = 0.5;
+			clothSoftBody.appendAnchor( 0, arm.userData.physicsBody, false, influence );
+			clothSoftBody.appendAnchor( clothNumSegmentsZ, arm.userData.physicsBody, false, influence );
+
+			// Hinge constraint to move the arm
+			var pivotA = new Ammo.btVector3( 0, pylonHeight * 0.5, 0 );
+			var pivotB = new Ammo.btVector3( 0, -0.2, - armLength * 0.5 );
+			var axis = new Ammo.btVector3( 0, 1, 0 );
+			hinge = new Ammo.btHingeConstraint( pylon.userData.physicsBody, arm.userData.physicsBody, pivotA, pivotB, axis, axis, true );
+			physicsWorld.addConstraint( hinge, true );
+
+		}
+
+		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;
+
+		}
+
+		function createRigidBody( threeObject, physicsShape, mass, pos, quat ) {
+
+			threeObject.position.copy( pos );
+			threeObject.quaternion.copy( quat );
+
+			var transform = new Ammo.btTransform();
+			transform.setIdentity();
+			transform.setOrigin( new Ammo.btVector3( pos.x, pos.y, pos.z ) );
+			transform.setRotation( new Ammo.btQuaternion( quat.x, quat.y, quat.z, quat.w ) );
+			var motionState = new Ammo.btDefaultMotionState( transform );
+
+			var localInertia = new Ammo.btVector3( 0, 0, 0 );
+			physicsShape.calculateLocalInertia( mass, localInertia );
+
+			var rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, physicsShape, localInertia );
+			var body = new Ammo.btRigidBody( rbInfo );
+
+			threeObject.userData.physicsBody = body;
+
+			scene.add( threeObject );
+
+			if ( mass > 0 ) {
+				rigidBodies.push( threeObject );
+
+				// Disable deactivation
+				body.setActivationState( 4 );
+			}
+
+			physicsWorld.addRigidBody( body );
+
+		}
+
+		function createRandomColor() {
+			return Math.floor( Math.random() * ( 1 << 24 ) );
+		}
+
+		function createMaterial() {
+		    return new THREE.MeshPhongMaterial( { color: createRandomColor() } );
+		}
+
+		function initInput() {
+
+		    window.addEventListener( 'keydown', function( event ) {
+
+			    switch ( event.keyCode ) {
+				    // Q
+				    case 81:
+					    armMovement = 1;
+				    break;
+
+				    // A
+				    case 65:
+					    armMovement = - 1;
+				    break;
+			    }
+
+		    }, false );
+
+		    window.addEventListener( 'keyup', function( event ) {
+
+			    armMovement = 0;
+
+		    }, false );
+
+		}
+
+		function onWindowResize() {
+
+			camera.aspect = window.innerWidth / window.innerHeight;
+			camera.updateProjectionMatrix();
+
+			renderer.setSize( window.innerWidth, window.innerHeight );
+
+		}
+
+		function animate() {
+
+			requestAnimationFrame( animate );
+
+			render();
+			stats.update();
+
+		}
+
+		function render() {
+
+			var deltaTime = clock.getDelta();
+
+			updatePhysics( deltaTime );
+
+			controls.update( deltaTime );
+
+			renderer.render( scene, camera );
+
+			time += deltaTime;
+
+		}
+
+		function updatePhysics( deltaTime ) {
+
+			// Hinge control
+			hinge.enableAngularMotor( true, 0.8 * armMovement, 50 );
+
+			// Step world
+			physicsWorld.stepSimulation( deltaTime, 10 );
+
+			// Update cloth
+			var softBody = cloth.userData.physicsBody;
+			var clothPositions = cloth.geometry.attributes.position.array;
+			var numVerts = clothPositions.length / 3;
+			var nodes = softBody.get_m_nodes();
+			var indexFloat = 0;
+			for ( var i = 0; i < numVerts; i ++ ) {
+
+				var node = nodes.at( i );
+				var nodePos = node.get_m_x();
+				clothPositions[ indexFloat++ ] = nodePos.x();
+				clothPositions[ indexFloat++ ] = nodePos.y();
+				clothPositions[ indexFloat++ ] = nodePos.z();
+
+			}
+			cloth.geometry.computeVertexNormals();
+			cloth.geometry.attributes.position.needsUpdate = true;
+			cloth.geometry.attributes.normal.needsUpdate = true;
+
+			// Update rigid bodies
+			for ( var i = 0, il = rigidBodies.length; i < il; i++ ) {
+				var objThree = rigidBodies[ i ];
+				var objPhys = objThree.userData.physicsBody;
+				var ms = objPhys.getMotionState();
+				if ( ms ) {
+
+					ms.getWorldTransform( transformAux1 );
+					var p = transformAux1.getOrigin();
+					var q = transformAux1.getRotation();
+					objThree.position.set( p.x(), p.y(), p.z() );
+					objThree.quaternion.set( q.x(), q.y(), q.z(), q.w() );
+
+				}
+			}
+
+		}
+
+        </script>
+
+    </body>
+</html>

+ 481 - 0
examples/webgl_physics_rope.html

@@ -0,0 +1,481 @@
+<html lang="en">
+    <head>
+        <title>Amjs softbody rope demo</title>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+        <style>
+            body {
+                color: #61443e;
+                font-family:Monospace;
+                font-size:13px;
+                text-align:center;
+
+                background-color: #bfd1e5;
+                margin: 0px;
+                overflow: hidden;
+            }
+
+            #info {
+                position: absolute;
+                top: 0px; width: 100%;
+                padding: 5px;
+            }
+
+            a {
+
+                color: #a06851;
+            }
+
+        </style>
+    </head>
+    <body>
+	<div id="info">Ammo.js physics soft body rope demo<br>Press Q or A to move the arm.</div>
+        <div id="container"><br /><br /><br /><br /><br />Loading...</div>
+
+	<script src="../build/three.js"></script>
+	<script src="js/libs/ammo.js"></script>
+	<script src="js/controls/OrbitControls.js"></script>
+        <script src="js/Detector.js"></script>
+	<script src="js/libs/stats.min.js"></script>
+
+        <script>
+
+		// 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 - 2 ) ) {
+						pos.z += 0.75 * brickLength;
+					}
+					else {
+						pos.z += brickLength;
+					}
+
+				}
+				pos.y += brickHeight;
+			}
+
+			// The rope
+			// Rope graphic object
+			var ropeNumSegments = 10;
+			var ropeLength = 4;
+			var ropeMass = 3;
+			var ropePos = ball.position.clone();
+			ropePos.y += ballRadius;
+
+			var segmentLength = ropeLength / ropeNumSegments;
+			var ropeGeometry = new THREE.BufferGeometry();
+			var ropeMaterial = new THREE.LineBasicMaterial( { color: 0x000000 } );
+			var ropePositions = [];
+			var ropeIndices = [];
+
+			for ( var i = 0; i < ropeNumSegments + 1; i++ ) {
+				ropePositions.push( ropePos.x, ropePos.y + i * segmentLength, ropePos.z );
+			}
+
+			for ( var i = 0; i < ropeNumSegments; i++ ) {
+				ropeIndices.push( i, i + 1 );
+			}
+
+			ropeGeometry.setIndex( new THREE.BufferAttribute( new Uint16Array( ropeIndices ), 1 ) );
+			ropeGeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( ropePositions ), 3 ) );
+			ropeGeometry.computeBoundingSphere();
+			rope = new THREE.LineSegments( ropeGeometry, ropeMaterial );
+			rope.castShadow = true;
+			rope.receiveShadow = true;
+			scene.add( rope );
+
+			// Rope physic object
+			var softBodyHelpers = new Ammo.btSoftBodyHelpers();
+			var ropeStart = new Ammo.btVector3( ropePos.x, ropePos.y, ropePos.z );
+			var ropeEnd = new Ammo.btVector3( ropePos.x, ropePos.y + ropeLength, ropePos.z );
+			var ropeSoftBody = softBodyHelpers.CreateRope( physicsWorld.getWorldInfo(), ropeStart, ropeEnd, ropeNumSegments - 1, 0 );
+			var sbConfig = ropeSoftBody.get_m_cfg();
+			sbConfig.set_viterations( 10 );
+			sbConfig.set_piterations( 10 );
+			ropeSoftBody.setTotalMass( ropeMass, false )
+			Ammo.castObject( ropeSoftBody, Ammo.btCollisionObject ).getCollisionShape().setMargin( margin * 3 );
+			physicsWorld.addSoftBody( ropeSoftBody, 1, -1 );
+			rope.userData.physicsBody = ropeSoftBody;
+			// Disable deactivation
+			ropeSoftBody.setActivationState( 4 );
+
+			// The base
+			var armMass = 2;
+			var armLength = 3;
+			var pylonHeight = ropePos.y + ropeLength;
+			var baseMaterial = new THREE.MeshPhongMaterial( { color: 0x606060 } );
+			pos.set( ropePos.x, 0.1, ropePos.z - armLength );
+			quat.set( 0, 0, 0, 1 );
+			var base = createParalellepiped( 1, 0.2, 1, 0, pos, quat, baseMaterial );
+			base.castShadow = true;
+			base.receiveShadow = true;
+			pos.set( ropePos.x, 0.5 * pylonHeight, ropePos.z - armLength );
+			var pylon = createParalellepiped( 0.4, pylonHeight, 0.4, 0, pos, quat, baseMaterial );
+			pylon.castShadow = true;
+			pylon.receiveShadow = true;
+			pos.set( ropePos.x, pylonHeight + 0.2, ropePos.z - 0.5 * armLength );
+			var arm = createParalellepiped( 0.4, 0.4, armLength + 0.4, armMass, pos, quat, baseMaterial );
+			arm.castShadow = true;
+			arm.receiveShadow = true;
+
+			// Glue the rope extremes to the ball and the arm
+			var influence = 1;
+			ropeSoftBody.appendAnchor( 0, ball.userData.physicsBody, true, influence );
+			ropeSoftBody.appendAnchor( ropeNumSegments, arm.userData.physicsBody, true, influence );
+
+			// Hinge constraint to move the arm
+			var pivotA = new Ammo.btVector3( 0, pylonHeight * 0.5, 0 );
+			var pivotB = new Ammo.btVector3( 0, -0.2, - armLength * 0.5 );
+			var axis = new Ammo.btVector3( 0, 1, 0 );
+			hinge = new Ammo.btHingeConstraint( pylon.userData.physicsBody, arm.userData.physicsBody, pivotA, pivotB, axis, axis, true );
+			physicsWorld.addConstraint( hinge, true );
+
+
+		}
+
+		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;
+
+		}
+
+		function createRigidBody( threeObject, physicsShape, mass, pos, quat ) {
+
+			threeObject.position.copy( pos );
+			threeObject.quaternion.copy( quat );
+
+			var transform = new Ammo.btTransform();
+			transform.setIdentity();
+			transform.setOrigin( new Ammo.btVector3( pos.x, pos.y, pos.z ) );
+			transform.setRotation( new Ammo.btQuaternion( quat.x, quat.y, quat.z, quat.w ) );
+			var motionState = new Ammo.btDefaultMotionState( transform );
+
+			var localInertia = new Ammo.btVector3( 0, 0, 0 );
+			physicsShape.calculateLocalInertia( mass, localInertia );
+
+			var rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, physicsShape, localInertia );
+			var body = new Ammo.btRigidBody( rbInfo );
+
+			threeObject.userData.physicsBody = body;
+
+			scene.add( threeObject );
+
+			if ( mass > 0 ) {
+				rigidBodies.push( threeObject );
+
+				// Disable deactivation
+				body.setActivationState( 4 );
+			}
+
+			physicsWorld.addRigidBody( body );
+
+		}
+
+		function createRandomColor() {
+			return Math.floor( Math.random() * ( 1 << 24 ) );
+		}
+
+		function createMaterial() {
+			return new THREE.MeshPhongMaterial( { color: createRandomColor() } );
+		}
+
+		function initInput() {
+
+			window.addEventListener( 'keydown', function( event ) {
+
+				switch ( event.keyCode ) {
+					// Q
+					case 81:
+						armMovement = 1;
+					break;
+
+					// A
+					case 65:
+						armMovement = - 1;
+					break;
+				}
+
+			}, false );
+
+			window.addEventListener( 'keyup', function( event ) {
+
+				armMovement = 0;
+
+			}, false );
+
+		}
+
+		function onWindowResize() {
+
+			camera.aspect = window.innerWidth / window.innerHeight;
+			camera.updateProjectionMatrix();
+
+			renderer.setSize( window.innerWidth, window.innerHeight );
+
+		}
+
+		function animate() {
+
+			requestAnimationFrame( animate );
+
+			render();
+			stats.update();
+
+		}
+
+		function render() {
+
+			var deltaTime = clock.getDelta();
+
+			updatePhysics( deltaTime );
+
+			controls.update( deltaTime );
+
+			renderer.render( scene, camera );
+
+			time += deltaTime;
+
+		}
+
+		function updatePhysics( deltaTime ) {
+
+			// Hinge control
+			hinge.enableAngularMotor( true, 1.5 * armMovement, 50 );
+
+			// Step world
+			physicsWorld.stepSimulation( deltaTime, 10 );
+
+			// Update rope
+			var softBody = rope.userData.physicsBody;
+			var ropePositions = rope.geometry.attributes.position.array;
+			var numVerts = ropePositions.length / 3;
+			var nodes = softBody.get_m_nodes();
+			var indexFloat = 0;
+			for ( var i = 0; i < numVerts; i ++ ) {
+
+				var node = nodes.at( i );
+				var nodePos = node.get_m_x();
+				ropePositions[ indexFloat++ ] = nodePos.x();
+				ropePositions[ indexFloat++ ] = nodePos.y();
+				ropePositions[ indexFloat++ ] = nodePos.z();
+
+			}
+			rope.geometry.attributes.position.needsUpdate = true;
+
+			// Update rigid bodies
+			for ( var i = 0, il = rigidBodies.length; i < il; i++ ) {
+				var objThree = rigidBodies[ i ];
+				var objPhys = objThree.userData.physicsBody;
+				var ms = objPhys.getMotionState();
+				if ( ms ) {
+
+					ms.getWorldTransform( transformAux1 );
+					var p = transformAux1.getOrigin();
+					var q = transformAux1.getRotation();
+					objThree.position.set( p.x(), p.y(), p.z() );
+					objThree.quaternion.set( q.x(), q.y(), q.z(), q.w() );
+
+				}
+			}
+
+		}
+
+        </script>
+
+    </body>
+</html>

+ 438 - 0
examples/webgl_physics_terrain.html

@@ -0,0 +1,438 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <title>Ammo.js terrain heightfield demo</title>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+        <style>
+            body {
+                color: #61443e;
+                font-family:Monospace;
+                font-size:13px;
+                text-align:center;
+
+                background-color: #bfd1e5;
+                margin: 0px;
+                overflow: hidden;
+            }
+
+            #info {
+                position: absolute;
+                top: 0px; width: 100%;
+                padding: 5px;
+            }
+
+            a {
+
+                color: #a06851;
+            }
+
+        </style>
+    </head>
+    <body>
+        <div id="container"><br /><br /><br /><br /><br />Loading...</div>
+        <div id="info">Ammo.js physics terrain heightfield demo</div>
+
+	<script src="../build/three.js"></script>
+	<script src="js/libs/ammo.js"></script>
+	<script src="js/controls/OrbitControls.js"></script>
+        <script src="js/Detector.js"></script>
+	<script src="js/libs/stats.min.js"></script>
+
+        <script>
+
+		// Detects webgl
+		if ( ! Detector.webgl ) {
+			Detector.addGetWebGLMessage();
+			document.getElementById( 'container' ).innerHTML = "";
+		}
+
+		// - Global variables -
+
+		// Heightfield parameters
+		var terrainWidthExtents = 100;
+		var terrainDepthExtents = 100;
+		var terrainWidth = 128;
+		var terrainDepth = 128;
+		var terrainHalfWidth = terrainWidth / 2;
+		var terrainHalfDepth = terrainDepth / 2;
+		var terrainMaxHeight = 8;
+		var terrainMinHeight = -2;
+
+		// Graphics variables
+		var container, stats;
+		var camera, controls, scene, renderer;
+		var terrainMesh, texture;
+		var clock = new THREE.Clock();
+
+		// Physics variables
+		var collisionConfiguration;
+		var dispatcher;
+		var broadphase;
+		var solver;
+		var physicsWorld;
+		var terrainBody;
+		var dynamicObjects = [];
+		var transformAux1 = new Ammo.btTransform();
+
+		var heightData = null;
+		var ammoHeightData = null;
+
+		var time = 0;
+		var objectTimePeriod = 3;
+		var timeNextSpawn = time + objectTimePeriod;
+		var maxNumObjects = 30;
+
+		// - Main code -
+		init();
+		animate();
+
+		function init() {
+
+			heightData = generateHeight( terrainWidth, terrainDepth, terrainMinHeight, terrainMaxHeight );
+
+			initGraphics();
+
+			initPhysics();
+
+		}
+
+		function initGraphics() {
+
+			container = document.getElementById( 'container' );
+
+			renderer = new THREE.WebGLRenderer();
+			renderer.setClearColor( 0xbfd1e5 );
+			renderer.shadowMap.enabled = true;
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+
+			container.innerHTML = "";
+
+			container.appendChild( renderer.domElement );
+
+			stats = new Stats();
+			stats.domElement.style.position = 'absolute';
+			stats.domElement.style.top = '0px';
+			container.appendChild( stats.domElement );
+
+
+			camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.2, 2000 );
+
+			scene = new THREE.Scene();
+
+			camera.position.y = heightData[ terrainHalfWidth + terrainHalfDepth * terrainWidth ] * ( terrainMaxHeight - terrainMinHeight ) + 5;
+
+			camera.position.z = terrainDepthExtents / 2;
+			camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
+
+			controls = new THREE.OrbitControls( camera );
+
+			var geometry = new THREE.PlaneBufferGeometry( 100, 100, terrainWidth - 1, terrainDepth - 1 );
+			geometry.rotateX( -Math.PI / 2 );
+
+			var vertices = geometry.attributes.position.array;
+
+			for ( var i = 0, j = 0, l = vertices.length; i < l; i++, j += 3 ) {
+
+				// j + 1 because it is the y component that we modify
+				vertices[ j + 1 ] = heightData[ i ];
+
+			}
+
+			geometry.computeVertexNormals();
+
+			var groundMaterial = new THREE.MeshPhongMaterial( { color: 0xC7C7C7 } );
+			terrainMesh = new THREE.Mesh( geometry, groundMaterial );
+			terrainMesh.receiveShadow = true;
+			terrainMesh.castShadow = true;
+
+			scene.add( terrainMesh );
+
+			var textureLoader = new THREE.TextureLoader();
+			textureLoader.load("textures/grid.png", function ( texture ) {
+				texture.wrapS = THREE.RepeatWrapping;
+				texture.wrapT = THREE.RepeatWrapping;
+				texture.repeat.set( terrainWidth - 1, terrainDepth - 1 );
+				groundMaterial.map = texture;
+				groundMaterial.needsUpdate = true;
+
+			});
+
+			var light = new THREE.DirectionalLight( 0xffffff, 1 );
+			light.position.set( 100, 100, 50 );
+			light.castShadow = true;
+			var dLight = 200;
+			var sLight = dLight * 0.25;
+			light.shadow.camera.left = -sLight;
+			light.shadow.camera.right = sLight;
+			light.shadow.camera.top = sLight;
+			light.shadow.camera.bottom = -sLight;
+
+			light.shadow.camera.near = dLight / 30;
+			light.shadow.camera.far = dLight;
+
+			light.shadow.mapSize.x = 1024 * 2;
+			light.shadow.mapSize.y = 1024 * 2;
+
+			scene.add(light);
+
+
+			window.addEventListener( 'resize', onWindowResize, false );
+
+		}
+
+		function onWindowResize() {
+
+			camera.aspect = window.innerWidth / window.innerHeight;
+			camera.updateProjectionMatrix();
+
+			renderer.setSize( window.innerWidth, window.innerHeight );
+
+		}
+
+		function initPhysics() {
+
+			// Physics configuration
+
+			collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
+			dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration );
+			broadphase = new Ammo.btDbvtBroadphase();
+			solver = new Ammo.btSequentialImpulseConstraintSolver();
+			physicsWorld = new Ammo.btDiscreteDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration );
+			physicsWorld.setGravity( new Ammo.btVector3( 0, -6, 0 ) );
+
+			// Create the terrain body
+
+			groundShape = this.createTerrainShape( heightData );
+			var groundTransform = new Ammo.btTransform();
+			groundTransform.setIdentity();
+			// Shifts the terrain, since bullet re-centers it on its bounding box.
+			groundTransform.setOrigin( new Ammo.btVector3( 0, ( terrainMaxHeight + terrainMinHeight ) / 2, 0 ) );
+			var groundMass = 0;
+			var groundLocalInertia = new Ammo.btVector3( 0, 0, 0 );
+			var groundMotionState = new Ammo.btDefaultMotionState( groundTransform );
+			var groundBody = new Ammo.btRigidBody( new Ammo.btRigidBodyConstructionInfo( groundMass, groundMotionState, groundShape, groundLocalInertia ) );
+			physicsWorld.addRigidBody( groundBody );
+
+		}
+
+		function generateHeight( width, depth, minHeight, maxHeight ) {
+
+			// Generates the height data (a sinus wave)
+
+			var size = width * depth;
+			var data = new Float32Array(size);
+
+			var hRange = maxHeight - minHeight;
+			var w2 = width / 2;
+			var d2 = depth / 2;
+			var phaseMult = 12;
+
+			var p = 0;
+			for ( var j = 0; j < depth; j++ ) {
+				for ( var i = 0; i < width; i++ ) {
+
+					var radius = Math.sqrt(
+							Math.pow( ( i - w2 ) / w2, 2.0 ) +
+							Math.pow( ( j - d2 ) / d2, 2.0 ) );
+
+					var height = ( Math.sin( radius * phaseMult ) + 1 ) * 0.5 * hRange + minHeight;
+
+					data[ p ] = height;
+
+					p++;
+				}
+			}
+
+			return data;
+
+		}
+
+		function createTerrainShape() {
+
+			// This parameter is not really used, since we are using PHY_FLOAT height data type and hence it is ignored
+			var heightScale = 1;
+
+			// Up axis = 0 for X, 1 for Y, 2 for Z. Normally 1 = Y is used.
+			var upAxis = 1;
+
+			// hdt, height data type. "PHY_FLOAT" is used. Possible values are "PHY_FLOAT", "PHY_UCHAR", "PHY_SHORT"
+			var hdt = "PHY_FLOAT";
+
+			// Set this to your needs (inverts the triangles)
+			var flipQuadEdges = false;
+
+			// Creates height data buffer in Ammo heap
+			ammoHeightData = Ammo._malloc(4 * terrainWidth * terrainDepth);
+
+			// Copy the javascript height data array to the Ammo one.
+			var p = 0;
+			var p2 = 0;
+			for ( var j = 0; j < terrainDepth; j++ ) {
+				for ( var i = 0; i < terrainWidth; i++ ) {
+
+					// write 32-bit float data to memory
+					Ammo.HEAPF32[ ammoHeightData + p2 >> 2 ] = heightData[ p ];
+
+					p++;
+
+					// 4 bytes/float
+					p2 += 4;
+				}
+			}
+
+			// Creates the heightfield physics shape
+			var heightFieldShape = new Ammo.btHeightfieldTerrainShape(
+				terrainWidth,
+				terrainDepth,
+				ammoHeightData,
+				heightScale,
+				terrainMinHeight,
+				terrainMaxHeight,
+				upAxis,
+				hdt,
+				flipQuadEdges
+				);
+
+			// Set horizontal scale
+			var scaleX = terrainWidthExtents / ( terrainWidth - 1 );
+			var scaleZ = terrainDepthExtents / ( terrainDepth - 1 );
+			heightFieldShape.setLocalScaling( new Ammo.btVector3( scaleX, 1, scaleZ ) );
+
+			heightFieldShape.setMargin( 0.05 );
+
+			return heightFieldShape;
+
+		}
+
+		function generateObject() {
+
+			var numTypes = 4;
+			var objectType = Math.ceil( Math.random() * numTypes );
+
+			var threeObject = null;
+			var shape = null;
+
+			var objectSize = 3;
+			var margin = 0.05;
+
+			switch (objectType) {
+				case 1:
+					// Sphere
+					var radius = 1 + Math.random() * objectSize;
+					threeObject = new THREE.Mesh( new THREE.SphereGeometry( radius, 20, 20 ), createObjectMaterial() );
+					shape = new Ammo.btSphereShape( radius );
+					shape.setMargin( margin );
+					break;
+				case 2:
+					// Box
+					var sx = 1 + Math.random() * objectSize;
+					var sy = 1 + Math.random() * objectSize;
+					var sz = 1 + Math.random() * objectSize;
+					threeObject = new THREE.Mesh( new THREE.BoxGeometry( sx, sy, sz, 1, 1, 1 ), createObjectMaterial() );
+					shape = new Ammo.btBoxShape( new Ammo.btVector3( sx * 0.5, sy * 0.5, sz * 0.5 ) );
+					shape.setMargin( margin );
+					break;
+				case 3:
+					// Cylinder
+					var radius = 1 + Math.random() * objectSize;
+					var height = 1 + Math.random() * objectSize;
+					threeObject = new THREE.Mesh( new THREE.CylinderGeometry( radius, radius, height, 20, 1 ), createObjectMaterial() );
+					shape = new Ammo.btCylinderShape( new Ammo.btVector3( radius, height * 0.5, radius ) );
+					shape.setMargin(margin);
+					break;
+				default:
+					// Cone
+					var radius = 1 + Math.random() * objectSize;
+					var height = 2 + Math.random() * objectSize;
+					threeObject = new THREE.Mesh( new THREE.CylinderGeometry( 0, radius, height, 20, 2 ), createObjectMaterial() );
+					shape = new Ammo.btConeShape( radius, height );
+					break;
+			}
+
+			threeObject.position.set( ( Math.random() - 0.5 ) * terrainWidth * 0.6, terrainMaxHeight + objectSize + 2, ( Math.random() - 0.5 ) * terrainDepth * 0.6 );
+
+			var mass = objectSize * 5;
+			var localInertia = new Ammo.btVector3( 0, 0, 0 );
+			shape.calculateLocalInertia( mass, localInertia );
+			var transform = new Ammo.btTransform();
+			transform.setIdentity();
+			var pos = threeObject.position;
+			transform.setOrigin( new Ammo.btVector3( pos.x, pos.y, pos.z ) );
+			var motionState = new Ammo.btDefaultMotionState( transform );
+			var rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, shape, localInertia );
+			var body = new Ammo.btRigidBody( rbInfo );
+
+			threeObject.userData.physicsBody = body;
+
+			threeObject.receiveShadow = true;
+			threeObject.castShadow = true;
+
+			scene.add( threeObject );
+			dynamicObjects.push( threeObject );
+
+			physicsWorld.addRigidBody( body );
+
+
+
+		}
+
+		function createObjectMaterial() {
+			var c = Math.floor( Math.random() * ( 1 << 24 ) );
+			return new THREE.MeshPhongMaterial( { color: c } );
+		}
+
+		function animate() {
+
+			requestAnimationFrame(animate);
+
+			render();
+			stats.update();
+
+		}
+
+		function render() {
+
+			var deltaTime = clock.getDelta();
+
+			if ( dynamicObjects.length < maxNumObjects && time > timeNextSpawn ) {
+				generateObject();
+				timeNextSpawn = time + objectTimePeriod;
+			}
+
+			updatePhysics( deltaTime );
+
+			controls.update( deltaTime );
+
+			renderer.render( scene, camera );
+
+			time += deltaTime;
+
+		}
+
+		function updatePhysics( deltaTime ) {
+
+			physicsWorld.stepSimulation( deltaTime, 10 );
+
+			// Update objects
+			for ( var i = 0, il = dynamicObjects.length; i < il; i++ ) {
+				var objThree = dynamicObjects[ i ];
+				var objPhys = objThree.userData.physicsBody;
+				var ms = objPhys.getMotionState();
+				if ( ms ) {
+
+					ms.getWorldTransform( transformAux1 );
+					var p = transformAux1.getOrigin();
+					var q = transformAux1.getRotation();
+					objThree.position.set( p.x(), p.y(), p.z() );
+					objThree.quaternion.set( q.x(), q.y(), q.z(), q.w() );
+
+				}
+			}
+		}
+
+        </script>
+
+    </body>
+</html>

+ 558 - 0
examples/webgl_physics_volume.html

@@ -0,0 +1,558 @@
+<html lang="en">
+    <head>
+        <title>Ammo.js softbody volume demo</title>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+        <style>
+            body {
+                color: #61443e;
+                font-family:Monospace;
+                font-size:13px;
+                text-align:center;
+
+                background-color: #bfd1e5;
+                margin: 0px;
+                overflow: hidden;
+            }
+
+            #info {
+                position: absolute;
+                top: 0px; width: 100%;
+                padding: 5px;
+            }
+
+            a {
+
+                color: #a06851;
+            }
+
+        </style>
+    </head>
+    <body>
+	<div id="info">Ammo.js physics soft body volume demo<br>Click to throw a ball</div>
+        <div id="container"><br /><br /><br /><br /><br />Loading...</div>
+
+	<script src="../build/three.js"></script>
+	<script src="js/libs/ammo.js"></script>
+	<script src="js/controls/OrbitControls.js"></script>
+        <script src="js/Detector.js"></script>
+	<script src="js/libs/stats.min.js"></script>
+
+        <script>
+
+		// 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 hinge;
+		var transformAux1 = new Ammo.btTransform();
+		var softBodyHelpers = new Ammo.btSoftBodyHelpers();
+
+		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 = 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 sphereGeometry = new THREE.SphereBufferGeometry( 1.5, 40, 25 );
+			sphereGeometry.translate( 5, 5, 0 );
+			createSoftVolume( sphereGeometry, volumeMass, 250 );
+
+			var boxGeometry = new THREE.BufferGeometry().fromGeometry( new THREE.BoxGeometry( 1, 1, 5, 4, 4, 20 ) );
+			boxGeometry.translate( -2, 5, 0 );
+			createSoftVolume( boxGeometry, volumeMass, 120 );
+
+			// Ramp
+			pos.set( 3, 1, 0 );
+			quat.setFromAxisAngle( new THREE.Vector3( 0, 0, 1 ), 30 * Math.PI / 180 );
+			var obstacle = createParalellepiped( 10, 1, 4, 0, pos, quat, new THREE.MeshPhongMaterial( { color: 0x606060 } ) );
+			obstacle.castShadow = true;
+			obstacle.receiveShadow = true;
+
+		}
+
+		function processGeometry( bufGeometry ) {
+
+			// Obtain a Geometry
+			var geometry = new THREE.Geometry().fromBufferGeometry( bufGeometry );
+
+			// Merge the vertices so the triangle soup is converted to indexed triangles
+			var vertsDiff = geometry.mergeVertices();
+
+			// Convert again to BufferGeometry, indexed
+			var indexedBufferGeom = createIndexedBufferGeometryFromGeometry( geometry );
+
+			// Create index arrays mapping the indexed vertices to bufGeometry vertices
+			mapIndices( bufGeometry, indexedBufferGeom );
+
+		}
+
+		function createIndexedBufferGeometryFromGeometry( geometry ) {
+
+			var numVertices = geometry.vertices.length;
+			var numFaces = geometry.faces.length;
+
+			var bufferGeom = new THREE.BufferGeometry();
+			var vertices = new Float32Array( numVertices * 3 );
+			var indices = new ( numFaces * 3 > 65535 ? Uint32Array : Uint16Array )( numFaces * 3 );
+
+			for ( var i = 0; i < numVertices; i++ ) {
+
+				var p = geometry.vertices[ i ];
+
+				var i3 = i * 3;
+
+				vertices[ i3 ] = p.x;
+				vertices[ i3 + 1 ] = p.y;
+				vertices[ i3 + 2 ] = p.z;
+
+			}
+
+			for ( var i = 0; i < numFaces; i++ ) {
+
+				var f = geometry.faces[ i ];
+
+				var i3 = i * 3;
+
+				indices[ i3 ] = f.a;
+				indices[ i3 + 1 ] = f.b;
+				indices[ i3 + 2 ] = f.c;
+
+			}
+
+			bufferGeom.setIndex( new THREE.BufferAttribute( indices, 1 ) );
+			bufferGeom.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+
+			return bufferGeom;
+		}
+
+		function isEqual( x1, y1, z1, x2, y2, z2 ) {
+
+			var delta = 0.000001;
+			return Math.abs( x2 - x1 ) < delta &&
+			    Math.abs( y2 - y1 ) < delta &&
+			    Math.abs( z2 - z1 ) < delta;
+
+		}
+
+		function mapIndices( bufGeometry, indexedBufferGeom ) {
+
+			// Creates ammoVertices, ammoIndices and ammoIndexAssociation in bufGeometry
+
+			var vertices = bufGeometry.attributes.position.array;
+			var idxVertices = indexedBufferGeom.attributes.position.array;
+			var indices = indexedBufferGeom.index.array;
+
+			var numIdxVertices = idxVertices.length / 3;
+			var numVertices = vertices.length / 3;
+
+			bufGeometry.ammoVertices = idxVertices;
+			bufGeometry.ammoIndices = indices;
+			bufGeometry.ammoIndexAssociation = [];
+
+			for ( var i = 0; i < numIdxVertices; i++ ) {
+
+				var association = [];
+				bufGeometry.ammoIndexAssociation.push( association );
+
+				var i3 = i * 3;
+
+				for ( var j = 0; j < numVertices; j++ ) {
+
+					var j3 = j * 3;
+					if ( isEqual( idxVertices[ i3 ], idxVertices[ i3 + 1 ],  idxVertices[ i3 + 2 ],
+						      vertices[ j3 ], vertices[ j3 + 1 ], vertices[ j3 + 2 ] ) ) {
+					    association.push( j3 );
+					}
+
+				}
+
+			}
+
+		}
+
+		function createSoftVolume( bufferGeom, mass, pressure ) {
+
+			processGeometry( bufferGeom );
+
+			var volume = new THREE.Mesh( bufferGeom, new THREE.MeshPhongMaterial( { color: 0xFFFFFF } ) );
+			volume.castShadow = true;
+			volume.receiveShadow = true;
+			volume.frustumCulled = false;
+			scene.add( volume );
+
+			textureLoader.load( "textures/colors.png", function( texture ) {
+			    volume.material.map = texture;
+			    volume.material.needsUpdate = true;
+			} );
+
+			// Volume physic object
+
+			var volumeSoftBody = softBodyHelpers.CreateFromTriMesh(
+			    physicsWorld.getWorldInfo(),
+			    bufferGeom.ammoVertices,
+			    bufferGeom.ammoIndices,
+			    bufferGeom.ammoIndices.length / 3,
+			    true );
+
+			var sbConfig = volumeSoftBody.get_m_cfg();
+			sbConfig.set_viterations( 40 );
+			sbConfig.set_piterations( 40 );
+
+			// Soft-soft and soft-rigid collisions
+			sbConfig.set_collisions( 0x11 );
+
+			// Friction
+			sbConfig.set_kDF( 0.1 );
+			// Damping
+			sbConfig.set_kDP( 0.01 );
+			// Pressure
+			sbConfig.set_kPR( pressure );
+			// Stiffness
+			volumeSoftBody.get_m_materials().at( 0 ).set_m_kLST( 0.9 );
+			volumeSoftBody.get_m_materials().at( 0 ).set_m_kAST( 0.9 );
+
+			volumeSoftBody.setTotalMass( mass, false )
+			Ammo.castObject( volumeSoftBody, Ammo.btCollisionObject ).getCollisionShape().setMargin( margin );
+			physicsWorld.addSoftBody( volumeSoftBody, 1, -1 );
+			volume.userData.physicsBody = volumeSoftBody;
+			// Disable deactivation
+			volumeSoftBody.setActivationState( 4 );
+
+			softBodies.push( volume );
+
+		}
+
+		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;
+
+		}
+
+		function createRigidBody( threeObject, physicsShape, mass, pos, quat ) {
+
+			threeObject.position.copy( pos );
+			threeObject.quaternion.copy( quat );
+
+			var transform = new Ammo.btTransform();
+			transform.setIdentity();
+			transform.setOrigin( new Ammo.btVector3( pos.x, pos.y, pos.z ) );
+			transform.setRotation( new Ammo.btQuaternion( quat.x, quat.y, quat.z, quat.w ) );
+			var motionState = new Ammo.btDefaultMotionState( transform );
+
+			var localInertia = new Ammo.btVector3( 0, 0, 0 );
+			physicsShape.calculateLocalInertia( mass, localInertia );
+
+			var rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, physicsShape, localInertia );
+			var body = new Ammo.btRigidBody( rbInfo );
+
+			threeObject.userData.physicsBody = body;
+
+			scene.add( threeObject );
+
+			if ( mass > 0 ) {
+
+				rigidBodies.push( threeObject );
+
+				// Disable deactivation
+				body.setActivationState( 4 );
+
+			}
+
+			physicsWorld.addRigidBody( body );
+
+		    return body;
+		}
+
+		function initInput() {
+
+			window.addEventListener( 'mousedown', function( event ) {
+
+				if ( ! clickRequest ) {
+
+					mouseCoords.set(
+						( event.clientX / window.innerWidth ) * 2 - 1,
+						- ( event.clientY / window.innerHeight ) * 2 + 1
+					);
+
+					clickRequest = true;
+
+				}
+
+			}, false );
+
+		}
+
+		function processClick() {
+
+			if ( clickRequest ) {
+
+				raycaster.setFromCamera( mouseCoords, camera );
+
+				// Creates a ball
+				var ballMass = 3;
+				var ballRadius = 0.4;
+
+				var ball = new THREE.Mesh( new THREE.SphereGeometry( ballRadius, 18, 16 ), ballMaterial );
+				ball.castShadow = true;
+				ball.receiveShadow = true;
+				var ballShape = new Ammo.btSphereShape( ballRadius );
+				ballShape.setMargin( margin );
+				pos.copy( raycaster.ray.direction );
+				pos.add( raycaster.ray.origin );
+				quat.set( 0, 0, 0, 1 );
+				var ballBody = createRigidBody( ball, ballShape, ballMass, pos, quat );
+				ballBody.setFriction( 0.5 );
+
+				pos.copy( raycaster.ray.direction );
+				pos.multiplyScalar( 14 );
+				ballBody.setLinearVelocity( new Ammo.btVector3( pos.x, pos.y, pos.z ) );
+
+				clickRequest = false;
+
+			}
+
+		}
+
+		function onWindowResize() {
+
+			camera.aspect = window.innerWidth / window.innerHeight;
+			camera.updateProjectionMatrix();
+
+			renderer.setSize( window.innerWidth, window.innerHeight );
+
+		}
+
+		function animate() {
+
+			requestAnimationFrame( animate );
+
+			render();
+			stats.update();
+
+		}
+
+		function render() {
+
+			var deltaTime = clock.getDelta();
+
+			updatePhysics( deltaTime );
+
+			processClick();
+
+			controls.update( deltaTime );
+
+			renderer.render( scene, camera );
+
+		}
+
+		function updatePhysics( deltaTime ) {
+
+			// Step world
+			physicsWorld.stepSimulation( deltaTime, 10 );
+
+			// Update soft volumes
+			for ( var i = 0, il = softBodies.length; i < il; i++ ) {
+				var volume = softBodies[ i ];
+				var geometry = volume.geometry;
+				var softBody = volume.userData.physicsBody;
+				var volumePositions = geometry.attributes.position.array;
+				var volumeNormals = geometry.attributes.normal.array;
+				var association = geometry.ammoIndexAssociation;
+				var numVerts = association.length;
+				var nodes = softBody.get_m_nodes();
+				for ( var j = 0; j < numVerts; j ++ ) {
+
+					var node = nodes.at( j );
+					var nodePos = node.get_m_x();
+					var x = nodePos.x();
+					var y = nodePos.y();
+					var z = nodePos.z();
+					var nodeNormal = node.get_m_n();
+					var nx = nodeNormal.x();
+					var ny = nodeNormal.y();
+					var nz = nodeNormal.z();
+
+					var assocVertex = association[ j ];
+
+					for ( var k = 0, kl = assocVertex.length; k < kl; k++ ) {
+						var indexVertex = assocVertex[ k ];
+						volumePositions[ indexVertex ] = x;
+						volumeNormals[ indexVertex ] = nx;
+						indexVertex++;
+						volumePositions[ indexVertex ] = y;
+						volumeNormals[ indexVertex ] = ny;
+						indexVertex++;
+						volumePositions[ indexVertex ] = z;
+						volumeNormals[ indexVertex ] = nz;
+					}
+				}
+
+				geometry.attributes.position.needsUpdate = true;
+				geometry.attributes.normal.needsUpdate = true;
+
+			}
+
+			// Update rigid bodies
+			for ( var i = 0, il = rigidBodies.length; i < il; i++ ) {
+				var objThree = rigidBodies[ i ];
+				var objPhys = objThree.userData.physicsBody;
+				var ms = objPhys.getMotionState();
+				if ( ms ) {
+
+					ms.getWorldTransform( transformAux1 );
+					var p = transformAux1.getOrigin();
+					var q = transformAux1.getRotation();
+					objThree.position.set( p.x(), p.y(), p.z() );
+					objThree.quaternion.set( q.x(), q.y(), q.z(), q.w() );
+
+				}
+			}
+
+		}
+
+        </script>
+
+    </body>
+</html>

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff