Decided to take a break from player/world interactions and look at something I've never tried before, namely attaching objects like weapons to the player skeleton.
The implementation took a bit of messing around and educated guesswork to get it right. Because my game has my own hand-rolled implementation of a skeleton animation system, I was able to extend the [font='courier new']Skeleton[/font] interface to support this.
class Skeleton{public: // snip void setKeyFrame(const KeyFrame &key); const MatrixPalette &palette() const; Vec3 bindPosition(uint index, const Matrix &world) const; Matrix placementTransform(uint index, const Matrix &offset, const Matrix &world) const; // snip};
With the new [font='courier new']placementTransform()[/font] method, you pass in the index of the bone (which can be first looked-up by name), an initial offset matrix to apply to the weapon mesh in its local space and the current world matrix of the player. The result is the matrix you need to set as the transform on the weapon.
So, for example, inside the player character's code, we create the bow mesh on startup and add a scene node linking the bow mesh to the scene. You can have as many scene nodes linked to the same mesh as you wish in my system, each with its own transform and visibility status and so on.
We can then use the above [font='courier new']Skeleton::placementTransform()[/font] method to assign the bow's scene node the correct transform inside of the player's [font='courier new']prepareScene()[/font] method.
void Pc::prepareScene(SceneParams ¶ms, float blend){ KeyFrame key = data.animation.controller.keyFrame(blend, data.animation.map); data.animation.skeleton.setKeyFrame(key); Matrix world = rotationMatrixFromQuaternion(data.rot.value(blend)) * translationMatrix(pos.value(blend) + data.settings.positionOffset); mesh->setTransform(world); mesh->setPalette(data.animation.skeleton.palette()); uint leftHand = data.animation.skeleton.index("Left Hand"); Matrix initialLeft = translationMatrix(Vec3(-0.1f, 0.05f, 0)); bow->setVisible(true); bow->setTransform(data.animation.skeleton.placementTransform(leftHand, initialLeft, world));}
So we take the current interpolated [font='courier new']KeyFrame[/font] from the animation controller and apply this to the player's [font='courier new']Skeleton[/font] which then generates a matrix palette for the shader skinning. But we can also now query the [font='courier new']Skeleton[/font] for the placement matrix for the left hand, for example, and apply this to the scene node representing the bow.
The [font='courier new']placementTransform()[/font] method is implemented quite simply.
Matrix Skeleton::placementTransform(uint index, const Matrix &offset, const Matrix &world) const{ return offset * translationMatrix(bindPosition(index, identityMatrix())) * palette()[index] * world;}
First we do the local offset transform on the object in local space. The [font='courier new']bindPosition()[/font] method returns the position of the bone in the bind pose, which is multipled by the relevant matrix from the skinning palette to move it to the current [font='courier new']KeyFrame[/font] position. Then finally we multiply the result by the overall world matrix of the character.
For simplicity, the weapon meshes are defined with their local origin being the middle of the point at which they are held and rotated so that they match the player in bind pose. We can then tweak this transform with a hardcoded offset matrix to get the position looking visually correct, moving them slightly depending on which hand is holding them for example.
The big challenge, of course, is coming up with animations for attacking, drawing the bow and so on that look good. My 3D model editor, Charm, has a pretty comprehensive animation system for creating these, but my lack of artistic talent is working against me here. Still, we'll see how it goes. Need to decide on how the combat system is going to work before I can do this anyway.
Hope this was of interest to someone. This project is almost entirely hand-rolled due to my coding masochism so if there are any specific aspects of this project that people would like to know more about, please just let me know.
Thanks, as always, for stopping by.
Working with sockets like those is something that I've had to spend a lot of time with. Have you considered yet how you could include them directly in your skeleton data? There are a lot of interesting things that I've seen animators do with them when they're in the data like that and can be directly animated separately from the bones.
Very cool work!