public static void processMeshMeshCollision(RigidBody mesh1,
			RigidBody mesh2, double restitution, double timeStep) {
		// Predicted positions and velocities
		Vector3d mesh1Position = new Vector3d();
		Vector3d mesh2Position = new Vector3d();
		Quat4d mesh1Rotation = new Quat4d();
		Quat4d mesh2Rotation = new Quat4d();
		Vector3d mesh1LinearVelocity = new Vector3d();
		Vector3d mesh2LinearVelocity = new Vector3d();
		Vector3d mesh1AngularVelocity = new Vector3d();
		Vector3d mesh2AngularVelocity = new Vector3d();
		mesh1.predictCollisionPosition(timeStep, mesh1Position, mesh1Rotation);
		mesh2.predictCollisionPosition(timeStep, mesh2Position, mesh2Rotation);
		Matrix3d mesh1RotationMatrix = new Matrix3d();
		mesh1RotationMatrix.set(mesh1Rotation);
		Matrix3d mesh2RotationMatrix = new Matrix3d();
		mesh2RotationMatrix.set(mesh1Rotation);
		mesh1LinearVelocity = mesh1.getLinearVelocity();
		mesh2LinearVelocity = mesh2.getLinearVelocity();
		mesh1AngularVelocity = mesh1.getAngularVelocity();
		mesh2AngularVelocity = mesh2.getAngularVelocity();

		// Interpenetration points
		Vector3d[] mesh1LocalVertices = ((MeshObject) mesh1
				.getCollisionObject()).getLocalVertices();
		Vector3d[] mesh2LocalVertices = ((MeshObject) mesh2
				.getCollisionObject()).getLocalVertices();
		PriorityQueue<InterpenetrationPoint> interpentrationPoints = new PriorityQueue<InterpenetrationPoint>();
		// Mesh 1 vertices
		for (Vector3d vertex : mesh1LocalVertices) {
			Vector3d worldPoint = getWorldPoint(mesh1Position,
					mesh1RotationMatrix, vertex);
			try {
				double signedDistance = mesh2.getCollisionObject()
						.getLocalSignedDistance(mesh2Position,
								mesh2RotationMatrix, worldPoint);
				if (signedDistance < 0.0) {
					// mesh 2 LOCAL normal
					Vector3d worldNormal = ((MeshObject) mesh2
							.getCollisionObject()).getLocalNormal(
							mesh2Position, mesh2RotationMatrix, worldPoint);
					// Transform to world normal
					mesh2RotationMatrix.transform(worldNormal);
					worldNormal.normalize();
					// 2 relative to 1
					worldNormal.scale(-1.0);
					// Create interpenetration pair
					interpentrationPoints.add(new InterpenetrationPoint(
							worldPoint, signedDistance, worldNormal));
				}
			} catch (IllegalArgumentException e) {
			}
		}

		// Mesh 2 vertices
		for (Vector3d vertex : mesh2LocalVertices) {
			Vector3d worldPoint = getWorldPoint(mesh2Position,
					mesh2RotationMatrix, vertex);
			try {
				double signedDistance = mesh1.getCollisionObject()
						.getLocalSignedDistance(mesh1Position,
								mesh1RotationMatrix, worldPoint);
				if (signedDistance < 0.0) {
					// mesh 1 LOCAL normal
					Vector3d worldNormal = ((MeshObject) mesh1
							.getCollisionObject()).getLocalNormal(
							mesh1Position, mesh1RotationMatrix, worldPoint);
					// Transform to world normal
					mesh1RotationMatrix.transform(worldNormal);
					worldNormal.normalize();
					// Create interpenetration pair
					interpentrationPoints.add(new InterpenetrationPoint(
							worldPoint, signedDistance, worldNormal));
				}
			} catch (IllegalArgumentException e) {
			}
		}

		// Collision math
		while (!interpentrationPoints.isEmpty()) {
			InterpenetrationPoint interpenetrationPoint = interpentrationPoints
					.poll();
			// Intersecting points and cross product matrices
			Vector3d r1 = new Vector3d();
			r1.sub(interpenetrationPoint.worldPoint, mesh1Position);
			Vector3d r2 = new Vector3d();
			r2.sub(interpenetrationPoint.worldPoint, mesh2Position);
			Matrix3d cross1 = new Matrix3d(0, -r1.z, r1.y, r1.z, 0, -r1.x,
					-r1.y, r1.x, 0);
			Matrix3d cross2 = new Matrix3d(0, -r2.z, r2.y, r2.z, 0, -r2.x,
					-r2.y, r2.x, 0);

			// Relative velocity
			Vector3d r1AngularVelocity = new Vector3d();
			r1AngularVelocity.cross(mesh1AngularVelocity, r1);
			Vector3d r2AngularVelocity = new Vector3d();
			r2AngularVelocity.cross(mesh2AngularVelocity, r2);
			Vector3d relativeVelocity = new Vector3d();
			relativeVelocity.sub(r2AngularVelocity, r1AngularVelocity);
			relativeVelocity.add(mesh2LinearVelocity);
			relativeVelocity.sub(mesh1LinearVelocity);

			// Separating velocity
			if (relativeVelocity.dot(interpenetrationPoint.worldNormal) > 0) {
				return;
			}

			/*
			 * K = I/m + transpose(r*) I^-1 r*
			 */

			Matrix3d k1 = new Matrix3d();
			if (mesh1.getDynamicsType() == RigidBody.DYNAMIC) {
				k1.set(cross1);
				k1.transpose();
				k1.mul(mesh1.getInertiaLocalInverse());
				k1.mul(cross1);
				k1.add(new Matrix3d(1 / mesh1.getMass(), 0, 0, 0, 1 / mesh1
						.getMass(), 0, 0, 0, 1 / mesh1.getMass()));
			}
			Matrix3d k2 = new Matrix3d();
			if (mesh2.getDynamicsType() == RigidBody.DYNAMIC) {
				k2.set(cross2);
				k2.transpose();
				k2.mul(mesh2.getInertiaLocalInverse());
				k2.mul(cross2);
				k2.add(new Matrix3d(1 / mesh2.getMass(), 0, 0, 0, 1 / mesh2
						.getMass(), 0, 0, 0, 1 / mesh2.getMass()));
			}
			Matrix3d k = new Matrix3d();
			k.add(k1, k2);

			processCollision(mesh1, r1, mesh2, r2, relativeVelocity,
					interpenetrationPoint.worldNormal, k, restitution, timeStep);
		}
	}