How to add Ammo.js Physics to MD2Character?

Hello! I found an example where Quake2 models were loaded in three.js. I wanted to do something like a battle royale but I had a little problem. I wanted to use Ammo.js for physics.
I added physics to the ground so that the character can walk on it.
But when I wanted to do the same with the player I got an error.

I use this code for the rigid bodies:

	let createMeshRigidBody = function(geometry,mesh,mass) {
		// new empty ammo shape
		const shape = new Ammo.btConvexHullShape();
		//new ammo triangles
		let triangle, triangle_mesh = new Ammo.btTriangleMesh;
		//new ammo vectors
		let vectA = new Ammo.btVector3(0,0,0);
		let vectB = new Ammo.btVector3(0,0,0);
		let vectC = new Ammo.btVector3(0,0,0);
		//retrieve vertices positions from object
		let verticesPos = geometry.getAttribute('position').array;
		let triangles = [];
		for ( let i = 0; i < verticesPos.length; i += 3 ) {
			triangles.push({ x:verticesPos[i], y:verticesPos[i+1], z:verticesPos[i+2] })
		//use triangles data to draw ammo shape
		for ( let i = 0; i < triangles.length-3; i += 3 ) {
			triangle_mesh.addTriangle( vectA, vectB, vectC, true );
		let quaternion = mesh.quaternion;
		let position = mesh.position;
		let transform = new Ammo.btTransform();
		transform.setOrigin( new Ammo.btVector3( position.x, position.y, position.z ) );
		transform.setRotation( new Ammo.btQuaternion( quaternion.x, quaternion.y, quaternion.z, quaternion.w ) );
		let defaultMotionState = new Ammo.btDefaultMotionState( transform );
		let localInertia = new Ammo.btVector3( 0, 0, 0 );
		shape.calculateLocalInertia( mass, localInertia );
		let RBody_Info = new Ammo.btRigidBodyConstructionInfo( mass, defaultMotionState, shape, localInertia );
		let RBody = new Ammo.btRigidBody( RBody_Info );
		ammoWorld.addRigidBody( RBody );
		mesh.userData.physicsBody = RBody;

I call: this.createMeshRigidBody( ground_geom, ground_mesh, 0 ); for the ground.

And this function creates a new player:

	createPlayer: function () {
		var that = this;
		player = new THREE.MD2CharacterComplex();
		player.scale = 3;
		player.controls = controls;
		playerConf = Characters[selectedPlayer-1];
		var baseCharacter = new THREE.MD2CharacterComplex();
		baseCharacter.scale = 3;
		baseCharacter.onLoadComplete = function () {
			player.shareParts( baseCharacter );
			player.enableShadows( true );
			player.root.castShadow = true;
			if (playerConf.equipWeapon) player.setWeapon( 0 );
			player.setSkin( 0 );
			player.root.position.x = 0;
			player.root.position.z = 0;
			scene.add( player.root );
			camera.lookAt( player.root.position );
			player.root.add( camera );
			player.root.add( camera2 );
			player.root.add( );
			gameReady = true;
			// physics
			var geom = new THREE.Geometry();
			// i do this with obj
			that.createMeshRigidBody( geom, player.meshBody, 0 );   // i cant get the geometry
		baseCharacter.loadParts( playerConf );

I use MD2CharacterComplex.js to create the player. It’s ok if you just want to move it, but i want it to be able to jump, fall, collide with the walls.

please… can someone help me ?.

seem you are using THREE.Geometry it’s gone since release 125.

The rest is close to my take on the topic (using early templates from Doob+Blue’s articles) so you not that far from a working answer.

My guess would be to keep everything you have, only changing the way you get the geometry data.

Hi. Im working with Revision79, so THREE.Geometry stills there.
I think the meshes may have a reference to their geometry as property.
What I want is to get the geometry with which the mesh was created.
Another thing is the mesh is a THREE.MorphBlendMesh.
Is unknown for me :frowning:

that’s a pretty old version :sweat_smile:
The first step is to output a log of your mesh in your browser, something like: console.log(myMesh)
Ammo need a specific geometry attribute: Float32BufferAttribute, and I fear yours is not compatible.

This is a common issue when using mesh from loaders (like GLTF and other add-ons)

A possible fix is to use a hidden function from BufferGeometryUtils.js
It was included to ease the workflow with physics/BVH engines on custom mesh.
Further informations about the process can be found here: three.js - How to get regular float32 geometry of a mesh compressed using gltfpack - Stack Overflow

But to be honest…your three version is so old you may be forced to rewrite your own attribute quantizing function for THREE.MorphBlendMesh

1 Like

The reason I use R79 is because it is easier to understand. I’m not new to JavaScript, but I am programming in 3D.

Those are screenshots of the scene running.

The solution was adding a box geometry for the player body :sweat_smile:

	this.createPlayerRigidBody = function(player,mass) {
		let scale = 25;
		let playerBox = new THREE.BoxBufferGeometry(scale,scale,scale);
		let weaponBox = new THREE.BoxBufferGeometry(0,0,0);
		let playerBody = this.createMeshRigidBody(player.meshBody,mass,playerBox,0,0,0);
		//let weaponBody = this.createMeshRigidBody(player.meshWeapon,mass,weaponBox,0,5,0);

The problem now is that I can’t directly modify the player’s position. I have to use physics.
and it’s very easy when you use just one mesh. But the md2 character has three 3d objects.

  • a THREE.Object3d that is the root for all models.
  • a THREE.MorphBlendMesh for the character body.
  • and another THREE.MorphBlendMesh for the player’s weapon.

And when i try to move the body it becomes crazy.

A code modification in the MD2CharacterComplex.js file.

		var scalingFactor = 20;
		if (this.usingAmmo) {
			let resultantImpulse = new Ammo.btVector3( Math.sin( this.bodyOrientation ) * forwardDelta, 0, Math.cos( this.bodyOrientation ) * forwardDelta )
			let physicsBody = this.meshBody.userData.physicsBody;
			physicsBody.setLinearVelocity( resultantImpulse );

It doesn’t work :frowning:

What I want to do is use the original code to move the character.

// this is inside the player movement function in MD2CharacterComplex.js
this.root.position.x += Math.sin( this.bodyOrientation ) * forwardDelta;
this.root.position.z += Math.cos( this.bodyOrientation ) * forwardDelta;
this.root.rotation.y = this.bodyOrientation;

And update the position of the physical body every time the xz position changes.
Then ammo can do the collisions. :thinking: