making objects as bones and skinning them

Started by
0 comments, last by RobTheBloke 11 years, 2 months ago

I'm making a 3d snake game clone for studies and prettly much got everything to work, I have a 3d ground object surrounded by walls, and a snake head piece (cube). when you start the game 2 additional snake body (cube) are being added and follow the head in a snake like movement, every time you eat an apple additional snake piece is added.

I want instead of cubes to have a snake object (which I got already) my question is what is the approach to make the cubes act like the bones of the snake object and make the snake object get it's shape from it's "bones".

also maybe there's a way to add bones to the snake object I got in a program like 3dsMax ? and then make the bones act like the cube pieces I got in the game and it will work?

I also tried cutting the body of the snake object I got and adding each snake body cube the cutted snake body object but then it doesn't get the look I want (smooth snake that can turn in 3d) so I think the right idea is to have one big object that get it's positon and rotation based on smaller bone objects.

Advertisement

Start with the snake laid out in a straight line along say, the Z axis (it can be any axis you want). We'll call this pose the 'bind pose'. Compute the world space matrix for each joint, and then invert those matrices. You'll be wanting to store them in an array for later!

Now. Given a vertex V, figure out which are the two closest joints (which since your snake is laid out along the Z axis, you should be able to figure out easily using the 'z' values)

You now need to figure out how far (in the Z axis) the vertex is from each joint, and the distance between the joints:

DistanceBone1 = getDistFromBone1( Vertex );

DistanceBone2 = getDistFromBone2( Vertex );

DistanceBetweenBones == DistanceBone1 + DistanceBone2

From that we can compute some weights....

Weight1 = DistanceBone1 / DistanceBetweenBones

Weight2 = DistanceBone2 / DistanceBetweenBones


You'll probably want to store that information in a structure for later, e.g.

struct Vertex
{
uint32_t boneIndex1;

float weight1;
uint32_t boneIndex2;

float weight2;

Vec3 vertex;

};

And add to your 'bind pose' data you computed earlier:

Matrix InverseBindPose[ NUM_BONES ]

And you'll also need the current world space matrices of each bone:

Matrix currentWorldSpace[NUM_BONES ]

To compute the new position of the vertex V, we first transform into local space of the two bones:

VertexInLocalSpaceOfBone1 = V.vertex * InverseBindPose[ V.boneIndex1 ];

VertexInLocalSpaceOfBone2 = V.vertex * InverseBindPose[ V.boneIndex2 ];

Now they are in local space, we can transform them them into world space using the current pose:

VertexTransformedByBone1 = VertexInLocalSpaceOfBone1 * currentWorldSpace[ V.boneIndex1 ];

VertexTransformedByBone2 = VertexInLocalSpaceOfBone2 * currentWorldSpace[ V.boneIndex2 ];

And finally, interpolate to get the result:


FinalVertexPosition = VertexTransformedByBone1 * V.weight1 + VertexTransformedByBone2 * V.weight2;

Do that for each vertex, and you've skinned your snake....

As an optimisation, before computing the vertices, you can pre-multiply the two matrix arrays together into a single Transform:

for( i = 0; i < numBones; ++i)

SkinTransform = InverseBindPose * currentWorldSpace;

Which now means you only need to transform the vertex by one matrix instead of two:

VertexTransformedByBone1 = V.vertex * SkinTransform[ V.boneIndex1 ];

VertexTransformedByBone2 = V.vertex * SkinTransform[ V.boneIndex2 ];

There are slightly more general ways of doing this (google 'skinning'), and you'll probably find it's much more efficient in a shader (although the CPU will initially be ok for your purposes). If you attach a Skin or Physique modifier to your mesh in 3ds max, then you should be able to edit the skin weights in max directly (if you don't want to compute them manually). To export them, either use Fbx/Collada, or write your own exporter in either max script of the SDK. Hope that helps!

This topic is closed to new replies.

Advertisement