This is one of those things I’ve been fighting with for the past few days so I thought I would share a bit.
First a bit of a primer for those of you that aren’t all that familiar with bones yet. Bones are a common way of animating 3D geometry. Essentially you add an armature (skeleton) to your scene and bind it to the geometry of your mesh. Moving the bones the compose your armature will then update the bound geometry.
Let’s take a look at an example in Blender. Here is the anatomy of skeletal animation in Blender:
Each bone in turn has a “weight”. This is the amount of influence the bone’s movements have on the geometry. First you need to parent the mesh to the armature. Simply select the mesh, then shift select the bones and press Ctrl+P.
Each bone then has a certain weight attached to it. The colour determines how much influence a bone has over the geometry. Consider the following, it’s the weight mapping for the top most bone in that example:
So now, if in pose mode, I rotate that bone, we see:
As you can see, moving the bone changes the surrounding geometry based on the bones influence area. In a nutshell, that is how bone animation works in Blender. I cover more of the “how to” in the Blender to LibGDX tutorial if you want details.
So that’s bones in Blender, lets take a look at the LibGDX side of the equation. Here is how the skeleton is represented in a g3dj file:
{“id”: “Armature”,
“rotation”: [-0.707107, 0.000000, 0.000000, 0.707107],
“scale”: [ 1.000000, 1.000000, 1.000000],
“translation”: [ 0.012381, -0.935900, -0.017023],
“children”: [
{“id”: “Bone”,
“rotation”: [ 0.500000, -0.500000, 0.500000, 0.500000],
“scale”: [ 1.000000, 1.000000, 1.000000],
“children”: [
{“id”: “Bone_001”,
“rotation”: [ 0.000000, 0.009739, -0.000000, 0.999953],
“scale”: [ 1.000000, 1.000000, 1.000000],
“translation”: [ 1.000000, 0.000000, 0.000000],
“children”: [
{“id”: “Bone_002”,
“rotation”: [-0.000000, -0.013871, 0.000000, 0.999904],
“scale”: [ 1.000000, 1.000000, 1.000000],
“translation”: [ 1.575528, 0.000000, 0.000000]}
]}
]}
]}
]
You can also see the bones as part of the geometry node as well, like so:
{“id”: “Cube”,
“translation”: [-0.012381, -0.017023, 0.935900],
“parts”: [
{“meshpartid”: “shape1_part1”,
“materialid”: “Material”,
“bones”: [
{“node”: “Bone”,
“translation”: [ 0.012381, 0.017023, -0.935900, 0.000000],
“rotation”: [ 0.500000, -0.500000, 0.500000, 0.500000],
“scale”: [ 1.000000, 1.000000, 1.000000, 0.000000]},
{“node”: “Bone_002”,
“translation”: [ 0.012381, 0.047709, 1.639329, 0.000000],
“rotation”: [ 0.502062, -0.502062, 0.497930, 0.497930],
“scale”: [ 1.000000, 1.000000, 1.000000, 0.000000]},
{“node”: “Bone_001”,
“translation”: [ 0.012381, 0.017023, 0.064100, 0.000000],
“rotation”: [ 0.495107, -0.495107, 0.504846, 0.504846],
“scale”: [ 1.000000, 1.000000, 1.000000, 0.000000]}
],
“uvMapping”: [[]]}
]},
The later are the bones that are contained in the mesh “Cube”. This will be relevant in a minute. Instead lets look at the Armature composition.
Each bone within the hierarchy is basically just a series of transforms relative to its parent. The armature itself has a rotation, scale and translation, as do each child. In your ModelInstance, the Armature is a hierarchy of Nodes, like so:
Animations then are simply a series of transforms applied to bones over a period of time, like so:
These values correspond with the keyframe values you set in Blender.
Now there are a couple gotchas to be aware of!
First off, in LibGDX a bone is probably more accurately called a joint. Remember what a bone looked like in Blender:
Only the “bone head” is used. The tail effectively doesn’t exist.
So, positioning relative to a bone will bring you to the base, not the tail. Therefore, if you want to say… use bones for positioning other limbs, you need to create an extra one, and this lead to a problem.
Say I want to create a bone then that I can search for in code to mount a weapon upon. I would then have to do something like this:
This allows me to locate the very tip of my geometry. But there is a catch. If I export it, I can see the new bone Bone_003 is part of my armature:
That said, remember the entry for “Cube” showed the bones it contains… yeah well, that’s a problem.
See… the new bone isn’t actually contained within the geometry.
As a direct result, when working with it in code in LIbGDX, it just doesn’t work. It never returns the proper position, or at least the position I would expect. I’ve also had some weird behaviour where an exported model with only a single bone can’t be programmatically updated as well. I need to investigate this further.
As a result, I’ve decided that bones simply aren’t the way to go about it. Instead what i’ve started doing is putting a null objet in where I want weapon mounts to appear. It doesn’t seem to have the gotchas that bones have so far.
Sorry for the slow rate of updates, I am sick as a dog right now. So if that post seemed a little incoherent, that’s why! 🙂