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.

Next
Next

Sidewalk Showdown