Bone animation is done in the vertex shader.
Bone rotations/positions are worked out in the shader and then the vertices moved appropriately. The stock animation vertex shader for bone based models is this:
attribute highp vec3 position;
attribute mediump vec3 normal;
varying highp vec3 posVarying;
varying mediump vec3 normalVarying;
varying mediump vec3 lightVarying;
mediump vec3 GetVSLighting( mediump vec3 normal, highp vec3 pos );
uniform highp mat4 agk_ViewProj;
attribute highp vec2 uv;
varying highp vec2 uvVarying;
uniform highp vec4 uvBounds0;
attribute highp vec4 boneweights;
attribute mediump vec4 boneindices;
uniform highp vec4 agk_bonequats1[30];
uniform highp vec4 agk_bonequats2[30];
highp vec3 transformDQ( highp vec3 p, highp vec4 q1, highp vec4 q2 )
{
p += 2.0 * cross( q1.xyz, cross(q1.xyz, p) + q1.w*p );
p += 2.0 * (q1.w*q2.xyz - q2.w*q1.xyz + cross(q1.xyz,q2.xyz));
return p;
}
void main()
{
uvVarying = uv * uvBounds0.xy + uvBounds0.zw;
highp vec4 q1 = agk_bonequats1[ int(boneindices.x) ] * boneweights.x;
q1 += agk_bonequats1[ int(boneindices.y) ] * boneweights.y;
q1 += agk_bonequats1[ int(boneindices.z) ] * boneweights.z;
q1 += agk_bonequats1[ int(boneindices.w) ] * boneweights.w;
highp vec4 q2 = agk_bonequats2[ int(boneindices.x) ] * boneweights.x;
q2 += agk_bonequats2[ int(boneindices.y) ] * boneweights.y;
q2 += agk_bonequats2[ int(boneindices.z) ] * boneweights.z;
q2 += agk_bonequats2[ int(boneindices.w) ] * boneweights.w;
highp float len = 1.0/length(q1);
q1 *= len;
q2 = (q2 - q1*dot(q1,q2)) * len;
highp vec4 pos = vec4( transformDQ(position,q1,q2), 1.0 );
gl_Position = agk_ViewProj * pos;
normalVarying = normal + 2.0*cross( q1.xyz, cross(q1.xyz,normal) + q1.w*normal );
posVarying = pos.xyz;
lightVarying = GetVSLighting( normalVarying, posVarying );
}
If you get you model animating correctly with the shaders built into AppGameKit then simply get that shader and save it to a file then you have a good starting point.