Sign in to follow this  

Need help with my skeletal animation system

Recommended Posts

Drunken_Monkey    157
I've been working on this skeletal animation thingy for fun, and am having some problems.

The first is when rotating a child bone, it "spins" in the correct direction as its parent, but it "orbits" in the opposite.

The other is when calculating the absolute coordinates of child bones, they're being transformed by its parents pose coordinates.

For example, if a root bone's pose coordinates is at (0, 0, -1) with no local transformation, the absolute coordinates will be at (0, 0, -1). That's correct. If its child bone is at pose (0, 0, 0) with no local transformation, it's absolute will be at (0, 0, -1), when it should be at (0, 0, 0). It's the same with rotations as well. It doesn't seem to affect the vertex transformation, but rendering the bone structure will be useless.

EDIT: Forgot to ask: How are weights calculated? I have the weights in the data file and class structure, they're just not implemented.

Class Declarations
class bone {
//unimportant modifiers, accessors, and constructors
//This should be a nested class instead of just friend'ing actor
friend actor;
//Locally transform a bone
void transform(const vector3<double>& pos, const quaternion<double>& rot);
//Recurse into each child and deform it depending on this bones deformation
void child_transform();
//Keep a copy of the bone's pose translation/rotation and local/global translation/rotation
const vector3<double> _b_orig_pos;
vector3<double> _b_loc_pos, _b_abs_pos;
const quaternion<double> _b_orig_rot;
quaternion<double> _b_loc_rot, _b_abs_rot;
//Map each vertex with a weight
//Ignoring the weights for now
std::map<const vector3<double>*, double > _verts;
//Keep track of each vertex's copy for bone deformation
std::map<const vector3<double>*, vector3<double>* > _b_verts;
bone* _parent;
std::vector<bone*> _children;

class actor {
//unimportant modifiers, accessors, and constructors
void anim_render();
void transform_abs_coords();
std::string _name;
//Original vertex locations for rendering the skin in pose
std::vector<const vector3<double>* > _verts;
//Don't directly modify the original verticies, instead create a copy and map them. This is used for animations
std::map<const vector3<double>*, vector3<double>* > _b_verts;
//Keep a list of faces for rendering
std::vector<face<const vector3<double>* >* > _faces;
//Bones that have parents and bones that do not (root bones)
std::vector<bone*> _bones;
std::vector<bone*> _root_bones;

Main function
actor act("Cube.skan");
int main(int argc, char** argv) {

while(getInput()) {

return 0;

Member functions
void bone::transform(const vector3<double>& pos, const quaternion<double>& rot) {
_b_loc_pos += pos;
_b_loc_rot *= rot;

void actor::transform_abs_coords() {
//Loop through the root bones, set their absolute deformation, and deform its children
for(auto i = _root_bones.begin(); i != _root_bones.end(); i++) {
bone& b = (**i);
b._b_abs_pos = b._b_loc_pos + b._b_orig_pos;
b._b_abs_rot = b._b_loc_rot * b._b_orig_rot;
for(auto j = b._children.begin(); j != b._children.end(); j++) {

void bone::child_transform() {
_b_abs_pos = _b_loc_pos;
_b_abs_pos *= _parent->_b_abs_rot;
_b_abs_pos += _parent->_b_abs_pos;

_b_abs_rot = _b_loc_rot * _parent->_b_abs_rot;
for(auto i = _children.begin(); i != _children.end(); i++) {

void actor::anim_render() {
//Reset all the vertex locations
for(auto i = _verts.begin(); i != _verts.end(); i++) {
*_b_verts[*i] = **i;
//Loop through the bones and deform its attached verticies
for(auto i = _bones.begin(); i != _bones.end(); i++) {
for(auto j = (**i)._verts.begin(); j != (**i)._verts.end(); j++) {
const vector3<double>& vert = *j->first;
const bone& b = **i;
vector3<double>& b_vert = *(**i)._b_verts[&vert];
const quaternion<double>& bone_rot = b._b_abs_rot;

quaternion<double> result = bone_rot * quaternion<double>(0, vert.x(), vert.y(), vert.z()) * bone_rot.conjugate();
b_vert += vector3<double>(result.x(), result.y(), result.z()) + b._b_abs_pos - b_vert;
//Render to the screen
for(auto i = _faces.begin(); i != _faces.end(); i++) {
const face<const vector3<double>* >& f = **i;
glNormal3d(f.nX(), f.nY(), f.nZ());
for(unsigned int j = 0; j < (**i).size(); j++) {
const vector3<double>& v = *_b_verts[f[j]];
glVertex3d(v.x(), v.y(), v.z());

Quaternions & Vectors
template <typename T> void vector3<T>::operator *= (const quaternion<T>& quat) {
quaternion<T> temp = quat.conjugate();
temp *= *this;
temp *= quat;
_x = temp.x();
_y = temp.y();
_z = temp.z();

template <typename T> void vector3<T>::operator += (const vector3<T>& v) {
_x += v._x;
_y += v._y;
_z += v._z;

template <typename T> vector3<T> vector3<T>::operator + (const vector3<T>& v) const {
return vector3<T>(_x+v._x,_y+v._y,_z+v._z);

template <typename T> vector3<T> vector3<T>::operator - (const vector3<T>& v) const {
return vector3<T>(_x-v._x,_y-v._y,_z-v._z);

//I should just restrict the typenames in quaternions to float/double. Why would you use anything else?
template<typename T> void quaternion<T>::operator *= (const vector3<T>& vert) {
quaternion<T> q = *this;
_x = q._w * vert.x() + q._y * vert.z() - q._z * vert.y();
_y = q._w * vert.y() - q._x * vert.z() + q._z * vert.x();
_z = q._w * vert.z() + q._x * vert.y() - q._y * vert.x();
_w =-q._x * vert.x() - q._y * vert.y() - q._z * vert.z();

template<typename T> quaternion<T> quaternion<T>::operator * (const quaternion<T>& i) const {
return quaternion<T>(
_w*i._w - _x*i._x - _y*i._y - _z*i._z,
_w*i._x + _x*i._w + _y*i._z - _z*i._y,
_w*i._y + _y*i._w + _z*i._x - _x*i._z,
_w*i._z + _z*i._w + _x*i._y - _y*i._x);
template<typename T> quaternion<T> quaternion<T>::conjugate() const {
return quaternion<T>(_w, -_x, -_y, -_z);

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this