Accessing and operating bones

From MaratisWiki
Jump to: navigation, search

Code snippet

Here's how you access and operate the bones of a mesh.

In this example I get the position of the bones, I set their position (both relative to the mesh/parent and in world coordinates), and I rotate the bones.

The code as it is has no use of course, but you can use it as a starting point to make meaningful modifications to the mesh armature.

MEngine * engine = MEngine::getInstance(); // get the engine instance
MLevel * level = engine->getLevel(); // get the current level
MScene * scene = level->getSceneByName("MyScene");
MOEntity * entity = scene->getEntityByName("MyEntity");

MOBone * bone;
MVector3 pos;

if(entity)
{	MMesh * mesh = entity->getMesh();
	if(mesh)
	{
		MArmature * armature = mesh->getArmature();
		if(armature)
		{
			unsigned int bones_number = armature->getBonesNumber();
			for(unsigned int i=0; i < bones_number; i++)
			{	bone = armature->getBone(i);
				if(you want to GET the position of the bone)
				{	//get position
					MMatrix4x4 boneWorldMatrix = (*entity->getMatrix()) * (*bone->getMatrix());
					pos = boneWorldMatrix.getTranslationPart();
				}
				else//, if you want to SET the position of the bone
				{
					if(you want to set the relative position)
					{
						pos = MVector3(0,0,0); //relative coordinates P(0,0,0)
						bone->setPosition(pos);
					}
					else//, if you want to set the world position
					{
						pos = MVector3(0,0,0); //world coordinates P(0,0,0)
						MObject3d * parentBone = bone->getParent();
						MVector3 P_entity = entity->getInversePosition(pos); //entity space
						if(parentBone)
						{	MVector3 P_parent = parentBone->getInversePosition(P_entity); //bone parent space
							bone->setPosition(P_parent);
						}
						else bone->setPosition(P_entity); //no parent so the bone is in entity space
					}
				}
				//rotate the bone
				bone->addAxisAngleRotation(MVector3(1, 0, 0), 10);

				//copy the rotation from another object (useful to create a ragdoll)
				MMatrix4x4 myMatrix = entity->getMatrix()->getInverse() * (*object->getMatrix());
				MObject3d * parentBone = bone->getParent();
				if(parentBone)
				{	//calculate object matrix in parentBone's space coordinate
					myMatrix = parentBone->getMatrix()->getInverse() * myMatrix;
				}
				//get rotation coords from myMatrix
				bone->setEulerRotation(myMatrix.getEulerAngles());
			}
		}
	}
}

Armature update (IMPORTANT!)

When you do some modifications on bones, the armature should update itself automatically. However, this does happen only when the dedicated part of the code is run, which is more or less at the end of all the game step processing. For this reason, if you operate with the matrices of bones inside a single game step, you may get old data in return. To avoid this you should use these two lines of code after every bone modification, or before the code that relates on the armature matrix:

armature->processBonesLinking();
armature->updateBonesSkinMatrix();

If you change more than one bone in a single game step, the parent-child relationships may not immediately work because you're retrieving old data. So if you have a loop that moves/rotates all the bones of a mesh you may want to do it like this:

for(int i=0;i<totalNumberOfBones;i++)
{
	//rotate the bone "i":
	//[...]
	if(parentBone)
	{	myMatrix = parentBone->getMatrix()->getInverse() * myMatrix;
	}
	bone[i]->setEulerRotation(myMatrix.getEulerAngles());
	//[...]

	//update the armature
	armature->processBonesLinking();
	armature->updateBonesSkinMatrix();
}

The loop will rotate the bone 0, then it will update the armature. The loop continues, now it will rotate the bone 1. If the bone 1 is a child of bone 0, there won't be any problem because the matrix of its parent has already been updated, so the bone1 will be correctly rotated taking into account the parent rotation.

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox