From Irrlicht mesh to Bullet Physics mesh

By , last updated April 8, 2016

There is typically no physics functionality in graphics engines like Irrlicht. If you are using graphics and physics engines instead of one like Unity or Unreal Engine then you will run into a problem called “Irrlicht physics”. Recall that Irrlicht is just a graphics engine. It renders object on the screen and that is all. In order for an object to be able to have physics properties you would need to integrate physics and graphics engines.

Problem

In our game “Burnt Islands” we saw the need for having a more detailed physics mesh for certain models. Being indie developers, manually making physics mesh for each model is not the way to go.

Instead, I made a method, which extracts the mesh from an Irrlicht node (if it has any), and then converts it into Bullet Physics mesh.

Algorithm

On paper the simplest algorithm is easy.:

  1. Get the mesh from Irrlicht
  2. Make a Bullet Physics btTriangleMesh.
  3. Loop through all meshbuffers
    1. Loop through all vertices and save them
    2. Loop through all indices and save them

However, we can’t do it like that. To keep the code tidy, and I really don’t like to mix Irrlicht code with Bullet Physics code. But for the sake of simplicity, this code sample will do it all-in-one.

Code

btRigidBody * makeBulletMeshFromIrrlichtNode( const irr::scene::ISceneNode * node )
{
	using irr::core::vector2df;
	using irr::core::vector3df;
	using irr::scene::IMesh;
	using irr::scene::IMeshBuffer;
	using irr::scene::IMeshSceneNode;
	using irr::scene::IAnimatedMeshSceneNode;

	// Handy lambda for converting from irr::vector to btVector
	auto toBtVector = [ &]( const vector3df & vec ) -> btVector3
	{
		btVector3 bt( vec.X, vec.Y, vec.Z );

		return bt;
	};

	irr::scene::ESCENE_NODE_TYPE type = node->getType();

	IAnimatedMeshSceneNode * meshnode = nullptr;
	btRigidBody	* body = nullptr;

	switch ( type )
	{
		case irr::scene::ESNT_ANIMATED_MESH:
			{
				meshnode = (IAnimatedMeshSceneNode *)node;
			}
			break;
	}

	if ( meshnode )
	{
		const vector3df nodescale = meshnode->getScale();

		IMesh * mesh = meshnode->getMesh();
		const size_t buffercount = mesh->getMeshBufferCount();

		// Save position
		btVector3 position = toBtVector( meshnode->getPosition() );

		// Save data here
		std::vector<irr::video::S3DVertex>	verticesList;
		std::vector<int>					indicesList;

		for ( size_t i=0; i<buffercount; ++i )
		{
			// Current meshbuffer
			IMeshBuffer * buffer = mesh->getMeshBuffer( i );

			// EVT_STANDARD -> video::S3DVertex
			// EVT_2TCOORDS -> video::S3DVertex2TCoords
			// EVT_TANGENTS -> video::S3DVertexTangents
			const irr::video::E_VERTEX_TYPE vertexType		= buffer->getVertexType();

			// EIT_16BIT
			// EIT_32BIT
			const irr::video::E_INDEX_TYPE	indexType		= buffer->getIndexType();

			// Get working data
			const size_t numVerts		= buffer->getVertexCount();
			const size_t numInd			= buffer->getIndexCount();

			// Resize save buffers
			verticesList.resize( verticesList.size() + numVerts );
			indicesList.resize( indicesList.size() + numInd );

			void * vertices				= buffer->getVertices();
			void * indices				= buffer->getIndices();

			irr::video::S3DVertex			* standard		= reinterpret_cast<irr::video::S3DVertex*>( vertices );
			irr::video::S3DVertex2TCoords	* two2coords	= reinterpret_cast<irr::video::S3DVertex2TCoords*>( vertices );
			irr::video::S3DVertexTangents	* tangents		= reinterpret_cast<irr::video::S3DVertexTangents*>( vertices );

			int16_t	* ind16		= reinterpret_cast<int16_t*>( indices );
			int32_t	* ind32		= reinterpret_cast<int32_t*>( indices );

			for ( size_t v = 0; v < numVerts; ++v )
			{
				auto & vert = verticesList[ v ];

				switch ( vertexType )
				{
					case irr::video::EVT_STANDARD:
						{
							const auto & irrv = standard[ v ];

							vert = irrv;
						}
						break;
					case irr::video::EVT_2TCOORDS:
						{
							const auto & irrv = two2coords[ v ];
							(void)irrv;

							// Not implemented
						}
						//break;
					case irr::video::EVT_TANGENTS:
						{
							const auto & irrv = tangents[ v ];
							(void)irrv;

							// Not implemented
						}
						//break;
					default:
						BOOST_ASSERT( 0 && "unknown vertex type" );
				}

			}

			for ( size_t n = 0; n < numInd; ++n )
			{
				auto & index = indicesList[ n ];

				switch ( indexType )
				{
					case irr::video::EIT_16BIT:
					{
						index = ind16[ n ];
					}
						break;
					case irr::video::EIT_32BIT:
					{
						index = ind32[ n ];
					}
						break;
					default:
						BOOST_ASSERT( 0 && "unkown index type" );
				}

			}

		}

		// Make bullet rigid body
		if ( ! verticesList.empty() && ! indicesList.empty() )
		{
			// Working numbers
			const size_t numIndices		= indicesList.size();
			const size_t numTriangles	= numIndices / 3;

			// Error checking
			BOOST_ASSERT( numTriangles * 3 == numIndices && "Number of indices does not make complete triangles" );

			// Create triangles
			btTriangleMesh * btmesh = new btTriangleMesh();

			// Build btTriangleMesh
			for ( size_t i=0; i<numIndices; i+=3 )
			{
				const btVector3 &A = toBtVector( verticesList[ indicesList[ i+0 ] ].Pos );
				const btVector3 &B = toBtVector( verticesList[ indicesList[ i+1 ] ].Pos );
				const btVector3 &C = toBtVector( verticesList[ indicesList[ i+2 ] ].Pos );

				bool removeDuplicateVertices = true;
				btmesh->addTriangle( A, B, C, removeDuplicateVertices );
			}

			// Give it a default MotionState
			btTransform transform;
			transform.setIdentity();
			transform.setOrigin( position );
			btDefaultMotionState *motionState = new btDefaultMotionState( transform );

			// Create the shape
			btCollisionShape *btShape = new btBvhTriangleMeshShape( btmesh, true );
			btShape->setMargin( 0.05f );

			// Create the rigid body object
			btScalar mass = 0.0f;
			body = new btRigidBody( mass, motionState, btShape );

		}

	}

	// Return Bullet rigid body
	return body;
}

In short, you will have to use the userpointer feature of the physics entities in Bullet to store a pointer to the Irrlicht nodes. On every frame, for all entities that have moved, update the Irrlicht node position (and rotation) from the Bullet physics entity.

Senior Software Engineer developing all kinds of stuff.

Comments

  1. LuciAnus November 14, 2014 Leave a Reply

    Hi,
    I think you have a bug…
    I have a model with mesh->getMeshBufferCount() == 150
    And when I draw triangles only last mesh i draw

    You should change to
    irr::video::S3DVertex & vert = verticesList[ (verticesList.size() – numVerts) + v ];

    and

    int & index = indicesList[ (indicesList.size() – numInd) + n ];

    I put irr::video::S3DVertex because I don’t have C11…

    Cheers

Leave a Reply


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*