Caio Sabino
22Mar/10Off

Ragdoll Using Bullet Physics Engine

This article describes a simple approach on how to animate a skinned mesh with a limited number of bones using rigid bodies. The implementation uses a skinned mesh file created by the author. In the provided method, there is no need for any kind of bone hierarchy once we are dealing with meshes with low bones count. The author provides the full source code for a demo application, which also contains editing tools for providing external data for the ragdoll.

In this article, I am providing a simple and effective way of creating a ragdoll using Bullet Physics engine and linking it to a skinned mesh. Even though the idea can be applied to any skinned mesh file format, I used one of my own, which you can find a fully description here.

The code for the ragdoll itself is quite small and it needs values that should be provided for the user. Assuming we are dealing with skinned mesh that do not have too many bones (the one I used has 18), it shouldn't take more than a few minutes for the user to provide these values, also, the demo provides tools for tweaking them

Ragdolls are commonly used for simulating character animation that should react to the physical environment surrounding them, it is not practical to have animations generated by artists to achieve what we do with ragdolls. The video below shows the demo application I created.

Full source code here.

For obvious reasons, no artist can predict how the character should be animated on the environment above, since only during runtime the character will know what is affecting it's behavior (for example, the bullet trajectory on the demo).

So here is what it is needed to create a ragdoll:

1 - Create a simplified physical representation of the character.
2 - Update the skinned mesh bones according to the physical representation on runtime

Ragdoll's rigid bodies

For the first mentioned, the user should be responsible for providing data that will describe the ragdolls's physical representation, such as the rigid bodies dimensions and offsets, the constraints rotation axis and position offsets. All those attributes could also be calculated automatically during the ragdoll setup, and it probably should have if you are dealing with a great amount of bones, since that's not the case here, the code provides a few editing modes for tweaking those values. Finding a basic setup for the ragdoll shouldn't take more than a few minutes with this approach.

The first thing needed is to provide the rigid bodies dimensions, and tell which bones those bodies are going to be linked to. In Game.cpp, the rigid bodies dimensions are defined by calling the CreateShape method, passing the dimensions as arguments:

//creates the ragdoll parts
//pelvis
CreateShape(0,true,1.4f, 2.5f, 1.1f);
//head
CreateShape(1,true,0.7f, 0.8f, 0.8f);
//upper arms
CreateShape(2,false,0.4f, 1.1f, 0.5f);
CreateShape(3,false,0.4f, 1.1f, 0.5f);
//lower arms
CreateShape(4,false,0.5f, 1.6f, 0.4f);
CreateShape(5,false,0.5f, 1.6f, 0.4f);
//upperlegs
CreateShape(6,false,0.6f, 1.8f, 0.7f);
CreateShape(7,false,0.6f, 1.8f, 0.7f);
//lower leg
CreateShape(8,false,0.5f, 1.9f, 0.5f);
CreateShape(9,false,0.5f, 1.9f, 0.5f);

This only stores the values in arrays so we can later create the rigid bodies. We achieve that by calling the RagDoll method SetPart. This method is called on Game.cpp on the CreateRagDoll method. See the code below:

//sets up a part of the ragdoll
//int index = the index number of the part
//int setMeshBoneTransformIndex = the bone index that this part is linked to,
//float offsetX, float offsetY, float offsetZ = translatin offset for the part in bone local space
//float mass = part's mass,
//btCollisionShape * a_shape = part's collision shape
void RagDoll::SetPart(int index, int setMeshBoneTransformIndex, float offsetX, float offsetY, float offsetZ,float mass, btCollisionShape * a_shape)
{
	m_boneIndicesToFollow[setMeshBoneTransformIndex] = index;

	//we set the parts position according to the skinned mesh current position

	D3DXMATRIX t_poseMatrix = m_skinnedMeshContainer->GetPoseMatrix()[setMeshBoneTransformIndex];
	D3DXMATRIX *t_boneWorldRestMatrix = m_skinnedMesh->GetBoneWorldRestMatrix(setMeshBoneTransformIndex);

	D3DXMATRIX t_boneWorldPosition;
	D3DXMatrixMultiply(&t_boneWorldPosition, t_boneWorldRestMatrix, &t_poseMatrix);

	D3DXVECTOR3 * t_head = m_skinnedMesh->GetBoneHead(setMeshBoneTransformIndex);
	D3DXVECTOR3 * t_tail = m_skinnedMesh->GetBoneTail(setMeshBoneTransformIndex);				

	float tx = t_tail->x - t_head->x;
	float ty = t_tail->y - t_head->y;
	float tz = t_tail->z - t_head->z;

	//part's world matrix
	D3DXMATRIX *t_partMatrix = new D3DXMATRIX();
	*t_partMatrix = t_boneWorldPosition;

	D3DXMATRIX *t_centerOffset = new D3DXMATRIX();
	D3DXMatrixIdentity(t_centerOffset);
	D3DXMatrixTranslation(t_centerOffset, (tx / 2.0f) + offsetX, (ty / 2.0f) + offsetY, (tz/2.0f) + offsetZ);
	D3DXMatrixMultiply(t_partMatrix, t_partMatrix, t_centerOffset);

	D3DXVECTOR3 t_pos;
	D3DXVECTOR3 t_scale;
	D3DXQUATERNION t_rot;

	D3DXMatrixDecompose(&t_scale, &t_rot, &t_pos, t_partMatrix);

	btRigidBody* body = PhysicsFactory::GetInstance()->CreateRigidBody(mass,t_pos.x, t_pos.y, t_pos.z, t_rot.x, t_rot.y, t_rot.z, t_rot.w, a_shape);

	D3DXMATRIX t_partInverse;
	D3DXMatrixInverse(&t_partInverse, NULL, t_partMatrix);

	//puts the bone's matrix in part's local space, and store it in m_boneToPartTransforms
	D3DXMatrixMultiply(m_boneToPartTransforms[setMeshBoneTransformIndex], &t_boneWorldPosition, &t_partInverse);

	m_ragdollBodies[index] = body;

	delete t_partMatrix;
	t_partMatrix = NULL;

	delete t_centerOffset;
	t_centerOffset = NULL;

}


The picture above shows the relation between a bone and a rigid body. This offset transform is stored so that we can later update the bones position and still maintain that offset

The bone index is informed, and we use the bone rest matrix as a reference for the rigid body that is going to be create. Keep in mind that Bullet Physics rigid bodies usually have their pivot point in the center of the body, so we need to place the rigid body in the middle position of the bone, in addition to that, the user can provide some translation offsets. We first store the rigid body world position considering those offset translations in t_partMatrix:

*t_partMatrix = t_boneWorldPosition;

D3DXMATRIX *t_centerOffset = new D3DXMATRIX();
D3DXMatrixIdentity(t_centerOffset);
D3DXMatrixTranslation(t_centerOffset, (tx / 2.0f) + offsetX, (ty / 2.0f) + offsetY, (tz/2.0f) + offsetZ);
D3DXMatrixMultiply(t_partMatrix, t_partMatrix, t_centerOffset);

But the objective here is to store those offsets in the rigid body local space, so we can apply those offsets to the bones later, when the rigid bodies have updated their positions on the physic world. We do that in the code below:

//puts the bone's matrix in part's local space, and store it in m_boneToPartTransforms
D3DXMatrixMultiply(m_boneToPartTransforms[setMeshBoneTransformIndex], &t_boneWorldPosition, &t_partInverse);

Most likely, a ragdoll is going to have a few rigid bodies in comparison to the amount of bones in the skinned mesh. I have seen ragdoll codes that relies on bone hierarchy for calculating bones transforms that doesn't have a rigid body directly attached to it, traversing the hierachy tree and multiplying the bones matrices to come up with a final matrix. The approach in use here is quite simpler, there is no need to traverse the hierarchy, instead, the offset matrix is created as the SetPart method does, but taking a different bone as reference. We also keep track of the bones relations to the rigid bodies so that we can later retrieve the correct matrices, storing them at m_boneIndicesToFollow. This whole process is described in the RagDoll method SetBoneRelation. In this case, the user is responsible for passing correctly the bone relation, see example below:

//for the bones that have no direct part linked to, we inform which bone that has a linked part
//this bone should be following
m_ragdoll->SetBoneRelation(4,0);
m_ragdoll->SetBoneRelation(2,0);
m_ragdoll->SetBoneRelation(8,0);
m_ragdoll->SetBoneRelation(1,0);
m_ragdoll->SetBoneRelation(7,6);
m_ragdoll->SetBoneRelation(10,17);
m_ragdoll->SetBoneRelation(13,12);
m_ragdoll->SetBoneRelation(16,15);
void RagDoll::SetBoneRelation(int realBoneIndex, int followBoneIndex)
{
	//it is going to the same thing the setPart method does, but the bone it is going to take
	//as a reference is the one passed as followBoneIndex and the the part's matrix is below
	//by calling GetPartForBoneIndex. Still there is going to be a new entry in m_boneToPartTransforms
	//which is the bone transform in the part's local space
	int partToFollowIndex = GetPartForBoneIndex(followBoneIndex);

	m_boneIndicesToFollow[realBoneIndex] = partToFollowIndex;

	D3DXMATRIX t_poseMatrix = m_skinnedMeshContainer->GetPoseMatrix()[realBoneIndex];
	D3DXMATRIX *t_boneWorldRestMatrix = m_skinnedMesh->GetBoneWorldRestMatrix(realBoneIndex);

	D3DXMATRIX t_boneWorldPosition;
	D3DXMatrixMultiply(&t_boneWorldPosition, t_boneWorldRestMatrix, &t_poseMatrix);

	D3DXMATRIX *t_partMatrix = new D3DXMATRIX();
	btTransform t_partTransform = m_ragdollBodies[partToFollowIndex]->getWorldTransform();
	*t_partMatrix = BT2DX_MATRIX(t_partTransform);

	D3DXMATRIX t_partInverse;
	D3DXMatrixInverse(&t_partInverse, NULL, t_partMatrix);

	D3DXMatrixMultiply(m_boneToPartTransforms[realBoneIndex], &t_boneWorldPosition, &t_partInverse);		

	delete t_partMatrix;
	t_partMatrix = NULL;	

}

Bone relations

The image above shows the relations of 4 bones to a single rigid body.

Once the rigid bodies dimensions and offsets are set, we move on to the constraints. I am not going to give a full description of the constraints available on the Bullet Physics engine, you can find it here. In this demo, we use the cone twist and the hinge constraint, which should be enough for most humanoid shaped ragdolls. I have created a helper method for setting up the constraints local transforms, it is  the CalculateConstraintTransform method, available at Game.cpp.

void Game::CalculateConstraintTransform(int partAIndex, int partBIndex, float partAOffsetX, float partAOffsetY, float partAOffsetZ, float partBOffsetX, float partBOffsetY, float partBOffsetZ, float quatX, float quatY, float quatZ, float quatW,  btTransform * a_transform, btTransform *b_transform, int constraintIndex)
{
	btTransform t_bodyTransformA = m_ragdoll->GetRadollParts()[partAIndex]->getWorldTransform();
	D3DXMATRIX t_bodyAMtx = BT2DX_MATRIX(t_bodyTransformA);
	btTransform t_bodyTransformB = m_ragdoll->GetRadollParts()[partBIndex]->getWorldTransform();
	D3DXMATRIX t_bodyBMtx = BT2DX_MATRIX(t_bodyTransformB);

	D3DXVECTOR3 t_offsetA = D3DXVECTOR3(partAOffsetX, partAOffsetY, partAOffsetZ);
	D3DXVECTOR3 t_offsetB = D3DXVECTOR3(partBOffsetX, partBOffsetY, partBOffsetZ);

	//puts the translation offsets in world space
	D3DXVECTOR4 t_pointAVec;
	D3DXVECTOR4 t_pointBVec;
	D3DXVec3Transform(&t_pointAVec, &t_offsetA, &t_bodyAMtx);
	D3DXVec3Transform(&t_pointBVec, &t_offsetB, &t_bodyBMtx);

	//constraint origin in world space
	btVector3 t_pointA = btVector3(t_pointAVec.x,t_pointAVec.y,t_pointAVec.z);
	btVector3 t_pointB = btVector3(t_pointBVec.x,t_pointBVec.y,t_pointBVec.z);
	btVector3 t_constraintOrigin = btVector3(	t_pointA.x() + ((t_pointB.x() - t_pointA.x()) / 2.0f),
												t_pointA.y() + ((t_pointB.y() - t_pointA.y()) / 2.0f),
												t_pointA.z() + ((t_pointB.z() - t_pointA.z()) / 2.0f));

	//inside this if clause, the matrix for editing mode is setup up with the constraint world position
	//and rotation
	if(constraintIndex > -1)
	{

		g_testConstraintMatrix[constraintIndex] = new D3DXMATRIX();
		D3DXMatrixIdentity(g_testConstraintMatrix[constraintIndex]);

		D3DXMATRIX t_pos;
		D3DXMatrixIdentity(&t_pos);
		D3DXMatrixTranslation(&t_pos, t_constraintOrigin.x(), t_constraintOrigin.y(), t_constraintOrigin.z());

		D3DXMATRIX t_rot;
		D3DXMatrixIdentity(&t_rot);
		D3DXMatrixRotationQuaternion(&t_rot, &D3DXQUATERNION(quatX, quatY, quatZ, quatW));

//		D3DXMatrixMultiply(g_testConstraintMatrix[constraintIndex], g_testConstraintMatrix[constraintIndex], &t_pos);
		D3DXMatrixMultiply(g_testConstraintMatrix[constraintIndex], &t_rot, &t_pos);

	}

	//constraint world matrix
	btTransform t_constraintWorld;
	t_constraintWorld.setIdentity();
	t_constraintWorld.setOrigin(t_constraintOrigin);
	t_constraintWorld.setRotation(btQuaternion(quatX, quatY, quatZ, quatW));

	//puts the constraint in the local space of their corresponding parts
	a_transform->setIdentity();
	*a_transform = t_bodyTransformA.inverse() * t_constraintWorld;

	b_transform->setIdentity();
	*b_transform = t_bodyTransformB.inverse() * t_constraintWorld;

}

The reason I created this method, is because it is easier to visualize on the editors the connecting points of the constraints on the rigid bodies, rather then placing the joint in the correct position. First, we apply the rigid bodies world transform to the offset values, so that they are also in world space. Then, we calculate the middle point between those two connecting points, this is going to be the constraint world position. Then we need to put the constraint position in the rigid bodies local space, so that the Bullet Physics engine can simulate it properly. The constraint rotation axis is provided in the form of a quaternion
and it is applied in the constraint world matrix.

With all that, we conclude the set up of the physical representation of the ragdoll. The only thing we need now is to update the bones position in order to follow the ragdoll's rigid body.

We are going to do that on every frame update of the game on the Update method of Game.cpp. The code is simple:

int totalBones = m_ragdollMeshContainer->GetMesh()->GetTotalBones();
for(int i = 0; i < totalBones; i++) {	 	D3DXMATRIX * t_transform = m_ragdoll->GetBoneWorldTransform(i);
	m_ragdollMeshContainer->SetBoneTransform(t_transform, i);

}

It simply retrieves the world transform for a given bone from the ragdoll, and updates it on the skinned mesh. Here is how we calculate the bone world matrix from a ragdoll:

//This method will return the world position that the given bone should have
D3DXMATRIX * RagDoll::GetBoneWorldTransform(int boneIndex)
{
	//the part world matrix is fetched, and then we apply the bone transform offset to obtain
	//the bone's world position
	int t_partIndex = GetPartForBoneIndex(boneIndex);

	btTransform  t_transform = m_ragdollBodies[t_partIndex]->getWorldTransform();
	D3DXMATRIX t_partMatrix = BT2DX_MATRIX(t_transform);

	D3DXMatrixIdentity(m_bonesCurrentWorldPosition[boneIndex]);
	D3DXMatrixMultiply(m_bonesCurrentWorldPosition[boneIndex], m_boneToPartTransforms[boneIndex], &t_partMatrix);

	return m_bonesCurrentWorldPosition[boneIndex];
}

If the SetPart and SetBoneRelation has been clearly described, this method should look quite obvious. We fetch which rigid body the given bone should be following ( GetPartForBoneIndex(boneIndex) ), then we retrieve the rigid body's world matrix (m_ragdollBodies[t_partIndex]->getWorldTransform()), and finally we apply the previously calculated offset
( m_boneToPartTransforms[boneIndex] ) to the rigid body's world matrix, in order to maintain the offset, no matter what is the rigid body's position at the time.

That concludes the whole ragdoll creation and simulation process presented on the Demo.

About the Source Code

The source code provided here, is focused solely on the ragdoll. The rendering engine was developed by me some time ago, and it hasn't any kind of optimization. There are far more complex and better render engines available, proprietary or free. The objective here is to create a clean ragdoll implementation that can be plugged in easily. The whole ragdoll code presented here can be found on Game.cpp and Ragdoll.cpp classes, and that's all you need to create a similar example.

I used a male body model on the demo, which I simply lost track where I found it. I had to modify it a bit in order to meet the exporter requirements, and I also had to paint the vertices weight myself (in case you are wondering why sometimes you can see spiky corners on the mesh, I am not an artist). If you would like to try it with a different mesh, I would be glad to see it.

If you find this code useful, please refer to my website whenever you use it. If you feel like, you can donate to my paypal account on the right hand side.

Hope you enjoyed it. I sure did 🙂

References

Granberg, Carl, Character animation With Direct3D. Charles River Media, 2009.

Luna, Frank D. , Introduction to 3D Game Programming With DirectX 9.0c. Wordware Publishing Inc., 2006

16Feb/10Off

Skinned Mesh Exporter for Blender

We all know there are several types of 3D models file formats, and the one I present here is just another one. It turns out that people need to create their own file format or modify existing ones quite often, be it a requirement from their bosses or for educational purposes. I have faced this situation twice, first, when I started studying DirectX 9, I couldn't find a DirectX file format exporter that would work properly with my models on Blender, and that's pretty much how this code was born. The second time, I was part of an IPhone project on the company I work, and our 3d engine was pretty much new and it had no support for skinned meshes so far, so I had to do it myself, and of course an exporter was also necessary. This time the exporter was written for 3DS Max and I simply can't post it here because it is company property now.

I had a hard time finding good examples of the Blender API, and I do think the best way to learn something is by checking the examples and trying them yourself. Omari's DirectXExporter (www.omariben.too.it) helped me a lot in this process, and of course the official Blender documentation site (http://www.blender.org/documentation/249PythonDoc/API_intro-module.html), but still I had to put a lot of effort to create my own file format.

So this post is not about skinned meshes, it is about the exporter. I think Blender is a wonderful tool, I just can't believe how they managed to come up with such a great software and put it out free, but that's another discussion. So I wrote a script to export skinned meshes in a format that my 3D engine would understand. I tried to keep it as simple as possible, and you should be able able to understand most of the code by reading the comments.

Here is a short reference for my file formats:

File Type 1: Mesh Geometry and Bones references:

Description Size in bytes Type
Optmized Flag (irrelevant) 4 Int
Vertex count 4 Int
Index count 4 Int
Index 0 4 Int
... ... ...
Index n 4 Int
Vertex 0 x 4 Float
Vertex 0 y 4 Float
Vertex 0 z 4 Float
Vertex 0 normal x 4 Float
Vertex 0 normal y 4 Float
Vertex 0 normal z 4 Float
Vertex 0 texture u 4 Float
Vertex 0 texture v 4 Float
Vertex 0 bone 0 weight 4 Float
Vertex 0 bone 0 index 4 Int
Vertex 0 bone 1 index 4 Int
... ... ...
Vertex n x 4 Float
Vertex n y 4 Float
Vertex n z 4 Float
Vertex n normal x 4 Float
Vertex n normal y 4 Float
Vertex n normal z 4 Float
Vertex n texture u 4 Float
Vertex n texture v 4 Float
Vertex n bone 0 weight 4 Float
Vertex n bone 0 index 4 Int
Vertex n bone 1 index 4 Int

File Type 2: Bones matrices for animation:

Description Size in bytes Type
Total frames 4 Int
Total bones 4 Int
Bone 0 offset matrix 4*16 16 floats
... ... ...
Bone n offset matrix 4*16 16 floats
Bone 0 head position 4*3 3 floats
Bone 0 tail position 4*3 3 floats
... ... ...
Bone n head position 4*3 3 floats
Bone n tail position 4*3 3 floats
Frame 0, bone 0 pose matrix 4*16 16 floats
... ... ...
Frame 0, bone n pose matrix 4*16 16 floats
... ... ...
Frame n, bone 0 pose matrix 4*16 16 floats
... ... ...
Frame n, bone n pose matrix 4*16 16 floats
Total animations (irrelevant) 4 .Int
Total frames (irrelevant) 4 Int
Bone 0 parent index 4 Int
... ... ...
Bone n parent index 4 Int

As I said, the file format is not the point, and you should easily modify the script to output data in a way that suits you best.

The idea is to have two different files, one that will hold the geometry data and bone references for each vertex, and another that will contain the matrices needed to modify the vertices positions as the animation is played.

Enough with the theory, here is the code for both scripts:

Exporter 1: Geometry and bones influence

# Author: Caio Cintra Sabino (caiocsabino@gmail.com)
# As a reference I used some pieces of code from Omari's DirectXExporter.py version 3.0 (www.omariben.too.it)

import Blender
from Blender import Types, Object, NMesh, Material,Armature,Mesh
from Blender.Mathutils import *
from Blender import Draw, BGL
from Blender.BGL import *
from Blender import Window

import math
import struct, string
from types import *

bone_list =[]
index_list = []
bones_order = []

def event(evt, val):
		if evt == Draw.ESCKEY:
			Draw.Exit()
			return

def button_event(evt):
	if evt == 0:
		t_export = Exporter()
		t_export.start()
	if evt == 1:
		Draw.Exit()

def draw():

		glClearColor(0.55,0.6,0.6,1)
		glClear(BGL.GL_COLOR_BUFFER_BIT)
		#external box
		glColor3f(0.2,0.3,0.3)
		rect(10,402,300,382)

		glColor3f(0.5,0.75,0.65)
		rect(14,398,292,30)
		#--
		glColor3f(0.5,0.75,0.65)
		rect(14,366,292,160)
		#--
		glColor3f(0.5,0.75,0.65)
		rect(14,202,292,60)
		#--
		glColor3f(0.5,0.75,0.65)
		rect(14,138,292,40)
		#--
		glColor3f(0.5,0.75,0.65)
		rect(14,94,292,70)

		glColor3f(0.8,.8,0.6)
		glRasterPos2i(20, 380)
		Draw.Text("C3DE Skinned Mesh Exporter",'large')

		sel_butt = Draw.Button("Start",0,120, 155, 75, 30, "Start")
		exit_butt = Draw.Button("Exit",1,220, 155, 75, 30, "Exit")

def rect(x,y,width,height):
		glBegin(GL_LINE_LOOP)
		glVertex2i(x,y)
		glVertex2i(x+width,y)
		glVertex2i(x+width,y-height)
		glVertex2i(x,y-height)
		glEnd()

def rectFill(x,y,width,height):
		glBegin(GL_POLYGON)
		glVertex2i(x,y)
		glVertex2i(x+width,y)
		glVertex2i(x+width,y-height)
		glVertex2i(x,y-height)
		glEnd()				

Draw.Register(draw, event, button_event)

class Exporter:

	def start(self):
		tex = []

		Window.EditMode(0)

		for obj in Blender.Scene.GetCurrent().objects:
			if obj.type == 'Mesh':
				mesh = obj.data
				self.writeMesh(obj)

			else:
				print("not a mesh %s" % obj.type)
		Draw.Exit()

	def writeMesh(self, obj):
		global index_list,flip_z
		mesh = NMesh.GetRawFromObject(obj.name)
		path = ("..\SkinnedMeshOut0.c3d")
		file = open(path, "wb")						

		file2 = open("..\SkinnedMeshOut.txt", "wb")			

		me = Mesh.New()
		me.getFromObject(obj.name)
		hasTexture = me.faceUV

		objData = obj.data

		vert_uvsU =[]
		vert_uvsV =[]	

		vertex_groups_names = objData.getVertGroupNames()

		#this will hold a list of vertex indices list for every vertex group
		#[ (1,2,3,4), (4,5,6) ... ]
		vertices_groups = []			

		file2.write("Vertex group names: [vertex indices]\n\n")

		#store vertice groups order,
		vertice_group_iterator = 0
		for vertex_group_name in vertex_groups_names:
			bones_order.append(vertex_group_name)
			vertice_group_indices = objData.getVertsFromGroup(vertex_group_name)
			vertices_groups.append(vertice_group_indices)
			file2.write("%s: %s\n" %(vertex_group_name, vertice_group_indices))

		verts = me.verts[:]

		me.verts = verts   

		indices = []
		new_vert_coords = []
		new_vert_normals = []
		new_vert_uvs = []
		indice_it = 0;	

		#list of lists of bones influenced for each vertex
		#[ (1,2), (1,-1), (2,3), (2-1)... ]
		g_bone_indices = []
		g_bone_weights = []			

		#" " "
		for f in me.faces:
			iteration  = range(0,3)		

			for iterator in iteration:
				vertice	= f.v[iterator]
				new_vert_coords.append(vertice.co)
				new_vert_normals.append(vertice.no)
				indices.append(indice_it)
				indice_it += 1

				#will hold a list of bones indices that this vertex is influenced by

				bone_indices = []

				bone_indices.append(-1)
				bone_indices.append(-1)
				bone_indices.append(-1)
				bone_indices.append(-1)
				bone_indices.append(-1)

				bone_weights = []

				bone_weights.append(-1)
				bone_weights.append(-1)
				bone_weights.append(-1)
				bone_weights.append(-1)
				bone_weights.append(-1)																						

				valid_entry_iterator = 0
				bone_indices_iterator = 0
				bone_weight_indices_iterator = 0

				influenceList = objData.getVertexInfluences(vertice.index)

				for vertex_group in vertices_groups:
					for vertex_group_entry in vertex_group:
						if(vertice.index == vertex_group_entry) :
							bone_indices[valid_entry_iterator] = bone_indices_iterator
							bone_weights[valid_entry_iterator] = influenceList[valid_entry_iterator][1]
							valid_entry_iterator += 1
					bone_indices_iterator += 1									

				g_bone_indices.append(bone_indices)
				g_bone_weights.append(bone_weights)

				if hasTexture:
					new_vert_uvs.append(f.uv[iterator])

		#0 means the mesh is not optimized, this has something to do with my own 3d engine
		data = struct.pack("i", (0))
		file.write(data)

		format = "i"
		data = struct.pack(format, indice_it)
		file.write(data)
		file.write(data)

		file2.write("\nnumber of vertices %i\n" % len(new_vert_coords))
		file2.write("\nnumber of indices %i\n" % len(new_vert_coords))

		file2.write("\nVertices; index , x, y, z, nx, ny, nz, u, v, bone 0 weight, bone index 0, bone index 1\n\n")

		vert_iterator = 0;
		for vv in new_vert_coords:
			format = "fffffffffii"                   # one integer
			if hasTexture:
				data = struct.pack(format, vv[0], vv[1], vv[2], new_vert_normals[vert_iterator][0], new_vert_normals[vert_iterator][1], new_vert_normals[vert_iterator][2], new_vert_uvs[vert_iterator][0], (1.0 - new_vert_uvs[vert_iterator][1]), g_bone_weights[vert_iterator][0], g_bone_indices[vert_iterator][0], g_bone_indices[vert_iterator][1]) # pack integer in a binary string
				#file2.write("%f %f %f %f %f %f %f %f %f %i %i\n" % (vv[0], vv[1], vv[2], new_vert_normals[vert_iterator][0], new_vert_normals[vert_iterator][1], new_vert_normals[vert_iterator][2], new_vert_uvs[vert_iterator][0], (1.0 - new_vert_uvs[vert_iterator][1]), g_bone_weights[vert_iterator][0], g_bone_indices[vert_iterator][0], g_bone_indices[vert_iterator][1]))
			else:
				data = struct.pack(format, vv[0], vv[1], vv[2], new_vert_normals[vert_iterator][0], new_vert_normals[vert_iterator][1], new_vert_normals[vert_iterator][2], 0.0, 0.0) # pack integer in a binary string

			file.write(data)
			vert_iterator += 1

Exporter 2: Animation

# Author: Caio Cintra Sabino (caiocsabino@gmail.com)
# As a reference I used some pieces of code from Omari's DirectXExporter.py version 3.0 (www.omariben.too.it)

import Blender
from Blender import Types, Object, NMesh, Material,Armature,Mesh
from Blender.Mathutils import *
from Blender import Draw, BGL
from Blender.BGL import *
from Blender import Window

import math
import struct, string
from types import *

bone_list =[]
index_list = []
mat_dict = {}

bones_order = []
bones_matrix_combinations = []

def event(evt, val):
		if evt == Draw.ESCKEY:
			Draw.Exit()
			return

def button_event(evt): 

	if evt == 0:

		t_export = Exporter()
		t_export.start()
	if evt == 1:
		Draw.Exit()

def draw():
		global animsg,flipmsg,swapmsg,anim_tick
		global flip_z,swap_yz,flip_norm,anim,ticks,speed,recalc_norm,Bl_norm,no_light
		glClearColor(0.55,0.6,0.6,1)
		glClear(BGL.GL_COLOR_BUFFER_BIT)
		#external box
		glColor3f(0.2,0.3,0.3)
		rect(10,402,300,382)
		#--
		#glColor3f(0.3,0.4,0.4)
		#rect(11,399,298,398)
		#--
		glColor3f(0.5,0.75,0.65)
		rect(14,398,292,30)
		#--
		glColor3f(0.5,0.75,0.65)
		rect(14,366,292,160)
		#--
		glColor3f(0.5,0.75,0.65)
		rect(14,202,292,60)
		#--
		glColor3f(0.5,0.75,0.65)
		rect(14,138,292,40)
		#--
		glColor3f(0.5,0.75,0.65)
		rect(14,94,292,70)

		glColor3f(0.8,.8,0.6)
		glRasterPos2i(20, 380)
		Draw.Text("C3DE Skinned Mesh Exporter (2)",'large')

		sel_butt = Draw.Button("Start",0,120, 155, 75, 30, "Start")
		exit_butt = Draw.Button("Exit",1,220, 155, 75, 30, "Exit")

def rect(x,y,width,height):
		glBegin(GL_LINE_LOOP)
		glVertex2i(x,y)
		glVertex2i(x+width,y)
		glVertex2i(x+width,y-height)
		glVertex2i(x,y-height)
		glEnd()

def rectFill(x,y,width,height):
		glBegin(GL_POLYGON)
		glVertex2i(x,y)
		glVertex2i(x+width,y)
		glVertex2i(x+width,y-height)
		glVertex2i(x,y-height)
		glEnd()

Draw.Register(draw, event, button_event)

class Exporter:	

	def start(self):

		tex = []

		Window.EditMode(0)

		iterator = 0		

		for obj in Blender.Scene.GetCurrent().objects:
			print("pass")
			if obj.type == 'Mesh':
				self.writeMeshBonesOrder(obj)
				iterator += 1
			else:
				print("not a mesh %s" % obj.type)

		for obj2 in Blender.Scene.GetCurrent().objects:
			if obj2.type == 'Armature':
				self.writeBones(obj2)
		print "...finished"
		Draw.Exit()

	def writeMeshBonesOrder(self, obj):		

		mesh = NMesh.GetRawFromObject(obj.name)								

		me = Mesh.New()              # Create a new mesh

		me.getFromObject(obj.name)    # Get the object's mesh data

		hasTexture = me.faceUV							

		vertex_groups_names = obj.data.getVertGroupNames()												

		for vertex_group_name in vertex_groups_names:
			bones_order.append(vertex_group_name)	

	def writeBones(self, obj):				

		file = open("..\SkinnedMeshOutBones.c3d", "wb")
		file2 = open("..\SkinnedMeshOutBones.txt", "wb")		

		armature_obj = obj.getData()				

		bones = armature_obj.bones.values()			

		file2.write("bones original position\n\n")
		for bone in bones:
			print("bones %s  %s\n\n\n" % (bone.name, bone.matrix))
			file2.write("%s %s\n\n" % (bone.name,	bone.matrix))

		totalBones = len(bones_order)		

		#define here the animation frames
		frameStart = 1
		frameEnd = 60

		totalFrames = frameEnd - frameStart

		format = "ii"
		data = struct.pack(format, totalFrames, totalBones) # pack integer in a binary string
		file.write(data)		

		bones = armature_obj.bones.values()			

		#offset matrix for each bone

		for bone_name in bones_order:
			for bone in bones:
				if(bone.name == bone_name) :
					t_matrix = bone.matrix["ARMATURESPACE"]
					t_matrix2 = bone.matrix["BONESPACE"]

					#if(bone.name == "Pelvis" or bone.name == "Back") :
					print("\n\n-----BONE %s in armature space %s \n" % (bone.name, t_matrix))
					print("\n\n-----BONE %s in local space %s \n" % (bone.name, t_matrix2))

					it = range(0,16)
					for count in it:
						data = struct.pack("f", t_matrix[count/4][count%4])
						file.write(data)

		#bones heads and tails

		for bone_name in bones_order:
			t_poseObject = obj.getPose()									

			#HERE
			t_poseBones = t_poseObject.bones
			bone = t_poseBones[bone_name]
			print("bone %s head %f %f %f" % (bone_name, bone.head[0], bone.head[1], bone.head[2]))
			print("bone %s tail %f %f %f" % (bone_name, bone.tail[0], bone.tail[1], bone.tail[2]))
			data = struct.pack("ffffff", bone.head[0], bone.head[1], bone.head[2],bone.tail[0], bone.tail[1], bone.tail[2]) # pack integer in a binary string
			file.write(data)	

		iterator = 0												

		framesIteration = range(frameStart,frameEnd)

		Blender.Set('curframe', frameStart)

		file2.write("\n\nbones new positions\n\n")
		for count in framesIteration:

			armature_obj = obj.getData()
			file2.write("\n\nframe %i \n\n" % (count))

			Blender.Set('curframe', count)
			poseObject = obj.getPose()									

			#HERE
			poseBones = poseObject.bones									

			iterator = 0

			for bone_name in bones_order:				

				#localPose = poseBones[bone_name]

				#Blender api provides this local matrix from the PoseBone object, it represents the final transformation
				#the vertex will need to use in order to modify it's position relative to the bone position. Depending
				#on the bone's weight of the vertex, you just need to balance the matrices according to their weights.
				#Example using a mesh influenced by 2 bones at maximum:

				#finalVertexPosition = (weight0 * (originalPosition * poseMatrixForBone0)) + (weight1 * (originalPosition * poseMatrixForBone1))

				finalMatrix = poseBones[bone_name].localMatrix	

				file2.write("pose %s %s\n\n" % (bone_name,	poseBones[bone_name].localMatrix))																														

				format = "ffffffffffffffff"

				data = struct.pack(format, finalMatrix[0][0], finalMatrix[0][1], finalMatrix[0][2], finalMatrix[0][3], finalMatrix[1][0], finalMatrix[1][1], finalMatrix[1][2], finalMatrix[1][3], finalMatrix[2][0], finalMatrix[2][1], finalMatrix[2][2], finalMatrix[2][3], finalMatrix[3][0], finalMatrix[3][1], finalMatrix[3][2], finalMatrix[3][3]) # pack integer in a binary string
				file.write(data)								

				iterator += 1											

		format = "ii"
		data = struct.pack(format, 1, totalFrames) # pack integer in a binary string
		file.write(data)

		#writes the parent bone index for every bone. -1 for orphan bones
		for bone in bones:

			parent = bone.parent
			iterator = 0
			for bone2 in bones:
				if(parent == bone2):
					print("bones parent %s %s %i \n\n\n" % (bone.name, bone2.name, iterator))
					data = struct.pack("i", iterator)
					file.write(data)
				iterator += 1

			if(str(parent) == "None"):
				print("orphan bones  %s \n\n\n" % (bone.name))
				data = struct.pack("i", -1)
				file.write(data)

			#print("bones parent %s %s %s\n\n\n" % (bone.name,	parent, bone.matrix))

		Draw.Exit()

Before running the script:

  • Make sure your mesh has all faces converted to triangles.
  • Make sure the mesh vertex groups have the same name as the bones in the armature.
  • Make sure that every vertex group in the mesh has a correspondent bone in the armature.
  • Make sure the model is in the "rest" position when you use the first script.
  • Make sure the model is NOT in the "rest" position when you use the second script.
  • Keep your file simple. The exporter should work with high poly count meshes, but I am not a blender expert, so if you use some advanced features it might break something. The script supports just pure geometry, uv texturing and bones.

How to run the script:

  1. In Blender, open a new Text Editor view.
  2. Open the first script in this view.
  3. Put the Model in the "rest" position
  4. Press Alt + p.
  5. Press Start.
  6. Disable the "rest" position.
  7. Open the second script in the Text Editor.
  8. Press Alt + p.
  9. Press Start.

You can test the scripts on the file below. I got it probably from some Blender model repository  long time ago, but I simply lost the site of the author after I have modified it so much. If you know the link to where people can download the original file, please post a comment.

characterMotion.blend

16Feb/10Off

The Start

Hello,

I am not much of a blog guy, but I decided to start this one to post some of the stuff I create on my free time. Most of it will be game programming code excerpts and ideas that I may think somebody will find useful. You can use anything you find here, just provide the link to this site. I may also use some code from other people that I think somebody may benefit from.

Cheers,

Filed under: Uncategorized 1 Comment