Browse Source

Examples: Refactored OimoPhysics example.

Mr.doob 4 years ago
parent
commit
e35862887b
2 changed files with 267 additions and 87 deletions
  1. 243 0
      examples/jsm/physics/OimoPhysics.js
  2. 24 87
      examples/physics_oimo_instancing.html

+ 243 - 0
examples/jsm/physics/OimoPhysics.js

@@ -0,0 +1,243 @@
+import { oimo } from '../libs/OimoPhysics.js';
+
+async function OimoPhysics() {
+
+	const Vec3 = oimo.common.Vec3;
+	const World = oimo.dynamics.World;
+	const RigidBodyType = oimo.dynamics.rigidbody.RigidBodyType;
+	const RigidBodyConfig = oimo.dynamics.rigidbody.RigidBodyConfig;
+	const ShapeConfig = oimo.dynamics.rigidbody.ShapeConfig;
+	const RigidBody = oimo.dynamics.rigidbody.RigidBody;
+	const Shape = oimo.dynamics.rigidbody.Shape;
+	const OBoxGeometry = oimo.collision.geometry.BoxGeometry;
+	const OSphereGeometry = oimo.collision.geometry.SphereGeometry;
+
+	const frameRate = 60;
+
+	const world = new World( 2, new Vec3( 0, - 9.8, 0 ) );
+
+	//
+
+	function getShape( geometry ) {
+
+		const parameters = geometry.parameters;
+
+		// TODO change type to is*
+
+		if ( geometry.type === 'BoxGeometry' ) {
+
+			const sx = parameters.width !== undefined ? parameters.width / 2 : 0.5;
+			const sy = parameters.height !== undefined ? parameters.height / 2 : 0.5;
+			const sz = parameters.depth !== undefined ? parameters.depth / 2 : 0.5;
+
+			return new OBoxGeometry( new Vec3( sx, sy, sz ) );
+
+		} else if ( geometry.type === 'SphereGeometry' || geometry.type === 'IcosahedronGeometry' ) {
+
+			const radius = parameters.radius !== undefined ? parameters.radius : 1;
+
+			return new OSphereGeometry( radius );
+
+		}
+
+		return null;
+
+	}
+
+	const meshes = [];
+	const meshMap = new WeakMap();
+
+	function addMesh( mesh, mass = 0 ) {
+
+		const shape = getShape( mesh.geometry );
+
+		if ( shape !== null ) {
+
+			if ( mesh.isInstancedMesh ) {
+
+				handleInstancedMesh( mesh, mass, shape );
+
+			} else if ( mesh.isMesh ) {
+
+				handleMesh( mesh, mass, shape );
+
+			}
+
+		}
+
+	}
+
+	function handleMesh( mesh, mass, shape ) {
+
+		const shapeConfig = new ShapeConfig();
+		shapeConfig.geometry = shape;
+
+		const bodyConfig = new RigidBodyConfig();
+		bodyConfig.type = mass === 0 ? RigidBodyType.STATIC : RigidBodyType.DYNAMIC;
+		bodyConfig.position = new Vec3( mesh.position.x, mesh.position.y, mesh.position.z );
+
+		const body = new RigidBody( bodyConfig );
+		body.addShape( new Shape( shapeConfig ) );
+		world.addRigidBody( body );
+
+		if ( mass > 0 ) {
+
+			meshes.push( mesh );
+			meshMap.set( mesh, body );
+
+		}
+
+	}
+
+	function handleInstancedMesh( mesh, mass, shape ) {
+
+		const array = mesh.instanceMatrix.array;
+
+		const bodies = [];
+
+		for ( let i = 0; i < mesh.count; i ++ ) {
+
+			const index = i * 16;
+
+			const shapeConfig = new ShapeConfig();
+			shapeConfig.geometry = shape;
+
+			const bodyConfig = new RigidBodyConfig();
+			bodyConfig.type = mass === 0 ? RigidBodyType.STATIC : RigidBodyType.DYNAMIC;
+			bodyConfig.position = new Vec3( array[ index + 12 ], array[ index + 13 ], array[ index + 14 ] );
+
+			const body = new RigidBody( bodyConfig );
+			body.addShape( new Shape( shapeConfig ) );
+			world.addRigidBody( body );
+
+			bodies.push( body );
+
+		}
+
+		if ( mass > 0 ) {
+
+			meshes.push( mesh );
+			meshMap.set( mesh, bodies );
+
+		}
+
+	}
+
+	//
+
+	function setMeshPosition( mesh, position, index = 0 ) {
+
+		if ( mesh.isInstancedMesh ) {
+
+			const bodies = meshMap.get( mesh );
+			const body = bodies[ index ];
+
+			body.setPosition( new Vec3( position.x, position.y, position.z ) );
+
+		} else if ( mesh.isMesh ) {
+
+			const body = meshMap.get( mesh );
+
+			body.setPosition( new Vec3( position.x, position.y, position.z ) );
+
+		}
+
+	}
+
+	//
+
+	let lastTime = 0;
+
+	function step() {
+
+		const time = performance.now();
+
+		if ( lastTime > 0 ) {
+
+			const delta = ( time - lastTime ) / 1000;
+
+			// console.time( 'world.step' );
+			world.step( delta );
+			// console.timeEnd( 'world.step' );
+
+		}
+
+		lastTime = time;
+
+		//
+
+		for ( let i = 0, l = meshes.length; i < l; i ++ ) {
+
+			const mesh = meshes[ i ];
+
+			if ( mesh.isInstancedMesh ) {
+
+				const array = mesh.instanceMatrix.array;
+				const bodies = meshMap.get( mesh );
+
+				for ( let j = 0; j < bodies.length; j ++ ) {
+
+					const body = bodies[ j ];
+
+					compose( body.getPosition(), body.getOrientation(), array, j * 16 );
+
+				}
+
+				mesh.instanceMatrix.needsUpdate = true;
+
+			} else if ( mesh.isMesh ) {
+
+				const body = meshMap.get( mesh );
+
+				mesh.position.copy( body.getPosition() );
+				mesh.quaternion.copy( body.getOrientation() );
+
+			}
+
+		}
+
+	}
+
+	// animate
+
+	setInterval( step, 1000 / frameRate );
+
+	return {
+		addMesh: addMesh,
+		setMeshPosition: setMeshPosition
+		// addCompoundMesh
+	};
+
+}
+
+function compose( position, quaternion, array, index ) {
+
+	const x = quaternion.x, y = quaternion.y, z = quaternion.z, w = quaternion.w;
+	const x2 = x + x, y2 = y + y, z2 = z + z;
+	const xx = x * x2, xy = x * y2, xz = x * z2;
+	const yy = y * y2, yz = y * z2, zz = z * z2;
+	const wx = w * x2, wy = w * y2, wz = w * z2;
+
+	array[ index + 0 ] = ( 1 - ( yy + zz ) );
+	array[ index + 1 ] = ( xy + wz );
+	array[ index + 2 ] = ( xz - wy );
+	array[ index + 3 ] = 0;
+
+	array[ index + 4 ] = ( xy - wz );
+	array[ index + 5 ] = ( 1 - ( xx + zz ) );
+	array[ index + 6 ] = ( yz + wx );
+	array[ index + 7 ] = 0;
+
+	array[ index + 8 ] = ( xz + wy );
+	array[ index + 9 ] = ( yz - wx );
+	array[ index + 10 ] = ( 1 - ( xx + yy ) );
+	array[ index + 11 ] = 0;
+
+	array[ index + 12 ] = position.x;
+	array[ index + 13 ] = position.y;
+	array[ index + 14 ] = position.z;
+	array[ index + 15 ] = 1;
+
+}
+
+export { OimoPhysics };

+ 24 - 87
examples/physics_oimo_instancing.html

@@ -17,42 +17,23 @@
 
 			import * as THREE from '../build/three.module.js';
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { OimoPhysics } from './jsm/physics/OimoPhysics.js';
 			import Stats from './jsm/libs/stats.module.js';
-			// Or use latest version from NPM `oimophysics`
-			import {oimo} from './jsm/libs/OimoPhysics.js';
-
-
-			const Vec3 = oimo.common.Vec3;
-			const World = oimo.dynamics.World;
-			const RigidBodyType = oimo.dynamics.rigidbody.RigidBodyType;
-			const RigidBodyConfig = oimo.dynamics.rigidbody.RigidBodyConfig;
-			const ShapeConfig = oimo.dynamics.rigidbody.ShapeConfig;
-			const RigidBody = oimo.dynamics.rigidbody.RigidBody;
-			const Shape = oimo.dynamics.rigidbody.Shape;
-			const OBoxGeometry = oimo.collision.geometry.BoxGeometry;
-			const OSphereGeometry = oimo.collision.geometry.SphereGeometry;
-
-
 
 			let camera, scene, renderer, stats;
 			let physics, position;
-			let world;
 
 			let boxes, spheres;
-			let boxesPhys, spheresPhys;
-
-			const dummy = new THREE.Object3D();
 
 			init();
 
 			async function init() {
-				world = new World(2, new Vec3(0, -9.8, 0));
-
-				boxesPhys = [];
-				spheresPhys = [];
 
+				physics = await OimoPhysics();
 				position = new THREE.Vector3();
 
+				//
+
 				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 100 );
 				camera.position.set( - 1, 1.5, 2 );
 				camera.lookAt( 0, 0.5, 0 );
@@ -76,8 +57,10 @@
 				);
 				floor.position.y = - 2.5;
 				floor.receiveShadow = true;
-				floor.userData.physics = addRigidBody(world, vec3FromVector3(floor.position), new OBoxGeometry(new Vec3(10 / 2, 5 / 2, 10 / 2)), true);
 				scene.add( floor );
+				physics.addMesh( floor );
+
+				//
 
 				const material = new THREE.MeshLambertMaterial();
 
@@ -85,8 +68,8 @@
 				const color = new THREE.Color();
 
 				// Boxes
-				const boxSideSize = 0.1;
-				const geometryBox = new THREE.BoxGeometry( boxSideSize, boxSideSize, boxSideSize );
+
+				const geometryBox = new THREE.BoxGeometry( 0.1, 0.1, 0.1 );
 				boxes = new THREE.InstancedMesh( geometryBox, material, 100 );
 				boxes.instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame
 				boxes.castShadow = true;
@@ -94,16 +77,17 @@
 				scene.add( boxes );
 
 				for ( let i = 0; i < boxes.count; i ++ ) {
-					position.set(Math.random() - 0.5, Math.random() * 10, Math.random() - 0.5);
-					matrix.setPosition( position.x, position.y, position.z );
+
+					matrix.setPosition( Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5 );
 					boxes.setMatrixAt( i, matrix );
 					boxes.setColorAt( i, color.setHex( 0xffffff * Math.random() ) );
-					// size of the side of the cube should be 2 times smaller
-					boxesPhys.push(addRigidBody(world, vec3FromVector3(position), new OBoxGeometry(new Vec3(boxSideSize / 2, boxSideSize / 2, boxSideSize / 2)), false));
+
 				}
 
+				physics.addMesh( boxes, 1 );
 
 				// Spheres
+
 				const geometrySphere = new THREE.IcosahedronGeometry( 0.075, 3 );
 				spheres = new THREE.InstancedMesh( geometrySphere, material, 100 );
 				spheres.instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame
@@ -112,13 +96,14 @@
 				scene.add( spheres );
 
 				for ( let i = 0; i < spheres.count; i ++ ) {
-					position.set(Math.random() - 0.5, Math.random() * 10, Math.random() - 0.5);
-					matrix.setPosition( position.x, position.y, position.z );
+
+					matrix.setPosition( Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5 );
 					spheres.setMatrixAt( i, matrix );
 					spheres.setColorAt( i, color.setHex( 0xffffff * Math.random() ) );
-					spheresPhys.push(addRigidBody(world, vec3FromVector3(position), new OSphereGeometry(0.075), false));
+
 				}
 
+				physics.addMesh( spheres, 1 );
 
 				//
 
@@ -138,80 +123,32 @@
 				controls.target.y = 0.5;
 				controls.update();
 
-				window.addEventListener('resize', onWindowResize);
-
 				animate();
 
 			}
 
-			function onWindowResize() {
-				camera.aspect = document.body.clientWidth / document.body.clientHeight;
-				camera.updateProjectionMatrix();
-				renderer.setSize(document.body.clientWidth, document.body.clientHeight);
-			}
-
 			function animate() {
 
 				requestAnimationFrame( animate );
 
-				world.step(1 / 60);
-
-				// updating data of visual objects from physical objects
-				// BOXES
-				boxesPhys.forEach((elPhys, index) => {
-					const posVec3 = elPhys.getPosition();
-					dummy.position.set(posVec3.x, posVec3.y, posVec3.z);
-					dummy.setRotationFromQuaternion(quaternionFromQuat(elPhys.getOrientation()));
-					dummy.updateMatrix();
-					boxes.setMatrixAt( index, dummy.matrix );
-				});
+				//
 
 				let index = Math.floor( Math.random() * boxes.count );
-				boxesPhys[index].setPosition( new Vec3(0, Math.random() + 1, 0) );
-				boxes.instanceMatrix.needsUpdate = true;
 
+				position.set( 0, Math.random() + 1, 0 );
+				physics.setMeshPosition( boxes, position, index );
 
-
-				// SPHERES
-				spheresPhys.forEach((elPhys, index) => {
-					const posVec3 = elPhys.getPosition();
-					dummy.position.set(posVec3.x, posVec3.y, posVec3.z);
-					dummy.setRotationFromQuaternion(quaternionFromQuat(elPhys.getOrientation()));
-					dummy.updateMatrix();
-					spheres.setMatrixAt( index, dummy.matrix );
-				});
+				//
 
 				index = Math.floor( Math.random() * spheres.count );
-				spheresPhys[index].setPosition( new Vec3(0, Math.random() + 1, 0) );
-				spheres.instanceMatrix.needsUpdate = true;
 
+				position.set( 0, Math.random() + 1, 0 );
+				physics.setMeshPosition( spheres, position, index );
 
 				renderer.render( scene, camera );
 
 				stats.update();
-			}
-
-			// adding a physical object to the world of physics
-			function addRigidBody(w, center, geom, wall) {
-				const shapeConfig = new ShapeConfig();
-				shapeConfig.geometry = geom;
-				const bodyConfig = new RigidBodyConfig();
-				bodyConfig.type = wall ? RigidBodyType.STATIC : RigidBodyType.DYNAMIC;
-				bodyConfig.position = center;
-				let body = new RigidBody(bodyConfig);
-				body.addShape(new Shape(shapeConfig));
-				w.addRigidBody(body);
-				return body;
-			}
-
-			// convert threejs Vector3 to oimo Vec3
-			function vec3FromVector3(position) {
-				return new Vec3(...position.toArray());
-			}
 
-			// convert oimo Quat to threejs quaternion
-			function quaternionFromQuat(quat) {
-				return new THREE.Quaternion(quat.x, quat.y, quat.z, quat.w);
 			}
 
 		</script>