THESIS
2D Skeletal Editor and Inverse Kinematics Animator


void Skeleton::SimulateBonesFromAnimation( int frame, Animation* anim ) { DeselectAllBones(); for( Bone* bone : GetBones() ) { int bone_id = bone->GetID(); float totalAngle = 0.f; int numLayers = 0; for( AnimLayer layer : anim->m_layers ) { if( !layer.m_isActive ) { continue; } int layer_id = layer.m_id; // do nothing if no keyframes std::vector<AnimBoneKeyframe> keyframes = anim->GetKeyframesByBone( layer_id, bone_id ); if( keyframes.size() == 0 ) { continue; } // use keyframe data if KF exists for this frame AnimBoneKeyframe* currentFrameKeyframe = anim->GetBoneKeyframeForIDAndFrame( layer_id, bone_id, frame ); if( currentFrameKeyframe ) { totalAngle += currentFrameKeyframe->m_angle; numLayers++; continue; } // make keyframes for min and max frames int minFrame = anim->m_maxFrame; int maxFrame = anim->m_minFrame; for( AnimBoneKeyframe keyframe : keyframes ) { if( keyframe.m_frame < minFrame ) { minFrame = keyframe.m_frame; } if( keyframe.m_frame > maxFrame ) { maxFrame = keyframe.m_frame; } } AnimBoneKeyframe* lowestKeyframe = anim->GetBoneKeyframeForIDAndFrame( layer_id, bone_id, minFrame ); AnimBoneKeyframe* highestKeyframe = anim->GetBoneKeyframeForIDAndFrame( layer_id, bone_id, maxFrame ); AnimBoneKeyframe minKeyframe({ bone_id, anim->m_minFrame, lowestKeyframe->m_start, lowestKeyframe->m_end, lowestKeyframe->m_angle }); AnimBoneKeyframe maxKeyframe({ bone_id, anim->m_maxFrame, highestKeyframe->m_start, highestKeyframe->m_end, highestKeyframe->m_angle }); // get KFs before and after current frame int lowerFrame = minKeyframe.m_frame; int higherFrame = maxKeyframe.m_frame; for( AnimBoneKeyframe keyframe : keyframes ) { int keyframeFrame = keyframe.m_frame; if( keyframeFrame > lowerFrame && keyframeFrame < frame ) { lowerFrame = keyframeFrame; } if( keyframeFrame > frame && keyframeFrame < higherFrame ) { higherFrame = keyframeFrame; } } AnimBoneKeyframe* lowerKeyframe = (lowerFrame == minKeyframe.m_frame) ? &minKeyframe : anim->GetBoneKeyframeForIDAndFrame( layer_id, bone_id, lowerFrame ); AnimBoneKeyframe* higherKeyframe = (higherFrame == maxKeyframe.m_frame) ? &maxKeyframe : anim->GetBoneKeyframeForIDAndFrame( layer_id, bone_id, higherFrame ); // Interpolate and rotate (currently does NOT translate bone) float t = GetFractionWithin( (float)frame, (float)lowerKeyframe->m_frame, (float)higherKeyframe->m_frame ); float lerpAngle = Interpolate( lowerKeyframe->m_angle, higherKeyframe->m_angle, t ); totalAngle += lerpAngle; numLayers++; } if( numLayers > 0 ) { float avgAngle = totalAngle / (float)numLayers; bone->Rotate( avgAngle - bone->GetAngle(), bone->GetStartPoint(), true ); std::vector<IKTarget*> iks = GetTargetsAttachedToBone( bone ); for( IKTarget* ik : iks ) { ik->Translate( bone->GetEndPoint() - ik->GetPosition() ); } } } }
My thesis is an editor for creating 2D skeletons that can be animated and moved via inverse kinematics.
The main features of the editor are adding, removing, and transforming bones, sprites, and endpoints to create a skeleton. Animations can be made in the timeline by keyframing bone or endpoint movements, and they can be divided into layers. The animation movement can be overrode by moving the endpoints, which moves the attached bones via cyclic coordinate descent.