#include "player.h"
#include "skeleton.h"
+#include "shaders/viewchar.h"
static struct
{
struct skeleton skele;
struct skeleton_anim *yay;
+
+ glmesh mesh;
}
animtest;
{
mdl_header *johannes = mdl_load( "models/ch_new.mdl" );
skeleton_setup( &animtest.skele, johannes );
- animtest.yay = skeleton_get_anim( &animtest.skele, "yay" );
+ animtest.yay = skeleton_get_anim( &animtest.skele, "pose_stand" );
+ mdl_unpack_glmesh( johannes, &animtest.mesh );
free( johannes );
}
m4x3f transform;
m4x3_identity( transform );
+
+ v4f qt;
+ q_axis_angle( qt, (v3f){0.0f,1.0f,0.0f}, vg_time*1.2f );
+ q_m3x3( qt, transform );
+
skeleton_apply_frame( transform, &animtest.skele, animtest.yay, vg_time );
+ skeleton_apply_ik_pass( &animtest.skele );
skeleton_debug( &animtest.skele );
}
-static void anim_test_render(void)
+static void anim_test_render( vg_tex2d *tex )
{
m4x4f world_4x4;
m4x3_expand( player.camera_inverse, world_4x4 );
m4x4_mul( vg_pv, world_4x4, vg_pv );
glEnable( GL_DEPTH_TEST );
+ shader_viewchar_use();
+ vg_tex2d_bind( tex, 0 );
+ shader_viewchar_uTexMain( 0 );
+ shader_viewchar_uPv( vg_pv );
+ glUniformMatrix4x3fv( _uniform_viewchar_uTransforms,
+ animtest.skele.bone_count,
+ 0,
+ (float *)animtest.skele.final_transforms );
+
+ mesh_bind( &animtest.mesh );
+ mesh_draw( &animtest.mesh );
+
glDisable( GL_DEPTH_TEST );
vg_lines_drawall( (float *)vg_pv );
}
class classtype_skeleton(Structure):
_pack_ = 1
_fields_ = [("channels",c_uint32),
+ ("ik_count",c_uint32),
("anim_start",c_uint32),
("anim_count",c_uint32)]
_pack_ = 1
_fields_ = [("deform",c_uint32)]
+class classtype_ik_bone(Structure):
+ _pack_ = 1
+ _fields_ = [("deform",c_uint32),
+ ("target",c_uint32),
+ ("pole",c_uint32)]
+
# Exporter
# ==============================================================================
if n.type == 'ARMATURE':
tree["bones"] = [None] # None is the root transform
+ tree["ik_count"] = 0
def _extendb( p, n, d ):
nonlocal tree
btree["children"] = []
btree["depth"] = d
btree["parent"] = p
-
- if n.use_deform:
- tree["bones"] += [n.name]
+ tree["bones"] += [n.name]
for c in n.children:
_extendb( btree, c, d+1 )
+ for c in tree['obj'].pose.bones[n.name].constraints:
+ if c.type == 'IK':
+ btree["target"] = c.subtarget
+ btree["pole"] = c.pole_subtarget
+ tree["ik_count"] += 1
+
btree['deform'] = n.use_deform
p['children'] += [btree]
node.parent = node_def["parent"]["uid"]
if objt == 'BONE':
- classtype = 'k_classtype_bone'
+ if 'target' in node_def:
+ classtype = 'k_classtype_ik_bone'
+ else:
+ classtype = 'k_classtype_bone'
elif objt == 'ARMATURE':
classtype = 'k_classtype_skeleton'
else:
if mod.type == 'ARMATURE':
classtype = 'k_classtype_skin'
armature_def = graph_lookup[mod.object]
+ armature_def['obj'].data.pose_position = 'REST'
if can_use_cache and obj.data.name in mesh_cache:
ref = mesh_cache[obj.data.name]
s005 = ""
if classtype == 'k_classtype_skin':
+ armature_def['obj'].data.pose_position = 'POSE'
s005 = F" [armature -> {armature_def['obj'].cv_data.uid}]"
- scmp = F"{s002:<32} {s003:<16} {s004} {s005}"
+ scmp = F"{s002:<32} {s003:<22} {s004} {s005}"
print( scmp )
if classtype == 'k_classtype_INSTANCE' or \
armature_def = graph_lookup[obj]
armature = obj
bones = armature_def['bones']
- print( bones )
skeleton.channels = len(bones)
+ skeleton.ik_count = armature_def["ik_count"]
if armature.animation_data:
previous_frame = bpy.context.scene.frame_current
rq = lc_m.to_quaternion()
kf = mdl_keyframe()
- kf.co[0] = loc[0]
- kf.co[1] = loc[2]
- kf.co[2] = -loc[1]
+ kf.co[0] = final_pos[0]
+ kf.co[1] = final_pos[1]
+ kf.co[2] = final_pos[2]
kf.q[0] = rq[1]
kf.q[1] = rq[3]
entdata_length += sizeof( classtype_bone )
bone = classtype_bone()
- bone.use_deform = node_def['deform']
+ bone.deform = node_def['deform']
entdata_buffer += [bone]
+ elif classtype == 'k_classtype_ik_bone':
+ node.classtype = 13
+ entdata_length += sizeof( classtype_ik_bone )
+
+ ikbone = classtype_ik_bone()
+ ikbone.target = armature_def['bones'].index( node_def['target'] )
+ ikbone.pole = armature_def['bones'].index( node_def['pole'] )
+ ikbone.deform = node_def['deform']
+
+ entdata_buffer += [ikbone]
+
elif classtype == 'k_classtype_gate':
node.classtype = 1
entdata_length += sizeof( classtype_gate )
k_classtype_route_node = 8,
k_classtype_route = 9,
k_classtype_bone = 10,
- k_classtype_skeleton = 11
+ k_classtype_skeleton = 11,
+ k_classtype_skin = 12,
+ k_classtype_ik_bone = 13
};
/* TODO: he needs a home somewhere */
shader_standard_register();
shader_vblend_register();
shader_unlit_register();
+ shader_viewchar_register();
world_register();
character_register();
}
else if( sv_scene == 2 )
{
- anim_test_render();
+ anim_test_render( &tex_pallet );
}
#endif
}
u32 deform;
};
+struct classtype_ik_bone
+{
+ u32 deform,
+ target,
+ pole;
+};
+
struct classtype_skeleton
{
u32 channels,
+ ik_count,
anim_start,
anim_count;
};
shader route standard.vs route.fs
shader scoretext scoretext.vs vblend.fs
shader routeui routeui.vs routeui.fs
+shader viewchar standard_skinned.vs viewchar.fs
cd shaders
../bin/linux/tools/shader $target_shaders
--- /dev/null
+#include "vertex_standard.glsl"
+
+uniform mat4 uPv;
+uniform mat4x3 uTransforms[32];
+
+out vec4 aColour;
+out vec2 aUv;
+out vec3 aNorm;
+out vec3 aCo;
+out vec3 aWorldCo;
+
+void main()
+{
+ vec4 co_local = vec4( a_co, 1.0 );
+ vec3 co0 = uTransforms[ a_groups[0] ] * co_local;
+ vec3 co1 = uTransforms[ a_groups[1] ] * co_local;
+ vec3 co2 = uTransforms[ a_groups[2] ] * co_local;
+ vec3 n0 = mat3(uTransforms[ a_groups[0] ]) * a_norm;
+ vec3 n1 = mat3(uTransforms[ a_groups[1] ]) * a_norm;
+ vec3 n2 = mat3(uTransforms[ a_groups[2] ]) * a_norm;
+
+ vec3 world_pos = co0*a_weights[0] + co1*a_weights[1] + co2*a_weights[2];
+ vec3 world_normal = n0*a_weights[0] + n1*a_weights[1] + n2*a_weights[2];
+
+ gl_Position = uPv * vec4( world_pos, 1.0 );
+ aColour = a_colour;
+ aUv = a_uv;
+ aNorm = world_normal;
+ aCo = a_co;
+ aWorldCo = world_pos;
+}
--- /dev/null
+out vec4 FragColor;
+
+uniform sampler2D uTexMain;
+
+in vec4 aColour;
+in vec2 aUv;
+in vec3 aNorm;
+in vec3 aCo;
+in vec3 aWorldCo;
+
+#include "common_world.glsl"
+
+void main()
+{
+ vec3 vfrag = texture( uTexMain, aUv ).rgb;
+
+ // Lighting
+ //vec3 halfview = uCamera - aWorldCo;
+ //float fdist = length( halfview );
+ //halfview /= fdist;
+
+ //vfrag = do_light_diffuse( vfrag, aNorm );
+ //vfrag = do_light_spec( vfrag, aNorm, halfview, 0.1 );
+ //vfrag = do_light_shadowing( vfrag );
+ //vfrag = apply_fog( vfrag, fdist );
+
+ FragColor = vec4(aNorm,1.0);
+}
--- /dev/null
+#ifndef SHADER_viewchar_H
+#define SHADER_viewchar_H
+static void shader_viewchar_link(void);
+static void shader_viewchar_register(void);
+static struct vg_shader _shader_viewchar = {
+ .name = "viewchar",
+ .link = shader_viewchar_link,
+ .vs =
+{
+.orig_file = "../../shaders/standard_skinned.vs",
+.static_src =
+"layout (location=0) in vec3 a_co;\n"
+"layout (location=1) in vec3 a_norm;\n"
+"layout (location=2) in vec2 a_uv;\n"
+"layout (location=3) in vec4 a_colour;\n"
+"layout (location=4) in vec4 a_weights;\n"
+"layout (location=5) in ivec4 a_groups;\n"
+"\n"
+"#line 2 0 \n"
+"\n"
+"uniform mat4 uPv;\n"
+"uniform mat4x3 uTransforms[32];\n"
+"\n"
+"out vec4 aColour;\n"
+"out vec2 aUv;\n"
+"out vec3 aNorm;\n"
+"out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
+"\n"
+"void main()\n"
+"{\n"
+" vec4 co_local = vec4( a_co, 1.0 );\n"
+" vec3 co0 = uTransforms[ a_groups[0] ] * co_local;\n"
+" vec3 co1 = uTransforms[ a_groups[1] ] * co_local;\n"
+" vec3 co2 = uTransforms[ a_groups[2] ] * co_local;\n"
+" vec3 n0 = mat3(uTransforms[ a_groups[0] ]) * a_norm;\n"
+" vec3 n1 = mat3(uTransforms[ a_groups[1] ]) * a_norm;\n"
+" vec3 n2 = mat3(uTransforms[ a_groups[2] ]) * a_norm;\n"
+"\n"
+" vec3 world_pos = co0*a_weights[0] + co1*a_weights[1] + co2*a_weights[2];\n"
+" vec3 world_normal = n0*a_weights[0] + n1*a_weights[1] + n2*a_weights[2];\n"
+"\n"
+" gl_Position = uPv * vec4( world_pos, 1.0 );\n"
+" aColour = a_colour;\n"
+" aUv = a_uv;\n"
+" aNorm = world_normal;\n"
+" aCo = a_co;\n"
+" aWorldCo = world_pos;\n"
+"}\n"
+""},
+ .fs =
+{
+.orig_file = "../../shaders/viewchar.fs",
+.static_src =
+"out vec4 FragColor;\n"
+"\n"
+"uniform sampler2D uTexMain;\n"
+"\n"
+"in vec4 aColour;\n"
+"in vec2 aUv;\n"
+"in vec3 aNorm;\n"
+"in vec3 aCo;\n"
+"in vec3 aWorldCo;\n"
+"\n"
+"#line 1 1 \n"
+"layout (std140) uniform ub_world_lighting\n"
+"{\n"
+" vec4 g_light_colours[3];\n"
+" vec4 g_light_directions[3];\n"
+" vec4 g_ambient_colour;\n"
+"\n"
+" vec4 g_water_plane;\n"
+" vec4 g_depth_bounds;\n"
+" float g_water_fog;\n"
+" int g_light_count;\n"
+" int g_light_preview;\n"
+"};\n"
+"\n"
+"uniform sampler2D g_world_depth;\n"
+"\n"
+"// Standard diffuse + spec models\n"
+"// ==============================\n"
+"\n"
+"vec3 do_light_diffuse( vec3 vfrag, vec3 wnormal )\n"
+"{\n"
+" vec3 vtotal = g_ambient_colour.rgb;\n"
+"\n"
+" for( int i=0; i<g_light_count; i++ )\n"
+" {\n"
+" vec3 vcolour = g_light_colours[i].rgb;\n"
+" vec3 vdir = g_light_directions[i].xyz;\n"
+"\n"
+" float flight = max(dot( vdir, wnormal )*0.75+0.25,0.0);\n"
+" vtotal += vcolour*flight;\n"
+" }\n"
+"\n"
+" return vfrag * vtotal;\n"
+"}\n"
+"\n"
+"vec3 do_light_spec( vec3 vfrag, vec3 wnormal, vec3 halfview, float fintensity )\n"
+"{\n"
+" vec3 vcolour = g_light_colours[0].rgb;\n"
+" vec3 vdir = g_light_directions[0].xyz;\n"
+"\n"
+" vec3 specdir = reflect( -vdir, wnormal );\n"
+" float spec = pow(max(dot( halfview, specdir ), 0.0), 10.0);\n"
+" return vfrag + vcolour*spec*fintensity;\n"
+"}\n"
+"\n"
+"float world_depth_sample( vec3 pos )\n"
+"{\n"
+" vec2 depth_coord = (pos.xz - g_depth_bounds.xy) * g_depth_bounds.zw; \n"
+" return texture( g_world_depth, depth_coord ).r;\n"
+"}\n"
+"\n"
+"float shadow_sample( vec3 vdir )\n"
+"{\n"
+" vec3 sample_pos = aWorldCo + vdir;\n"
+" float height_sample = world_depth_sample( sample_pos );\n"
+"\n"
+" float fdelta = height_sample - sample_pos.y;\n"
+" return clamp( fdelta, 0.1, 0.2 )-0.1;\n"
+"}\n"
+"\n"
+"vec3 do_light_shadowing_old( vec3 vfrag )\n"
+"{\n"
+" float faccum = 0.0;\n"
+" faccum += shadow_sample( vec3( 0.0, 0.5, 0.0 ));\n"
+" faccum += shadow_sample( vec3( 2.0, 0.3, 0.0 ));\n"
+" faccum += shadow_sample( vec3( 3.0, 1.0, 0.0 ));\n"
+" faccum += shadow_sample( vec3( 5.0, 1.0, 0.0 ));\n"
+" faccum += shadow_sample( vec3( 0.0, 0.5, 0.0 )*1.5);\n"
+" faccum += shadow_sample( vec3( 2.0, 0.3, 0.0 )*1.5);\n"
+" faccum += shadow_sample( vec3( 3.0, 1.0, 0.0 )*1.5);\n"
+" faccum += shadow_sample( vec3( 5.0, 1.0, 0.0 )*1.5);\n"
+" return mix( vfrag, g_ambient_colour.rgb, faccum );\n"
+"}\n"
+"\n"
+"vec3 do_light_shadowing( vec3 vfrag )\n"
+"{\n"
+" float fspread = g_light_colours[0].w;\n"
+" vec3 vdir = g_light_directions[0].xyz;\n"
+" float flength = g_light_directions[0].w;\n"
+"\n"
+" float famt = 0.0;\n"
+" famt+=shadow_sample((vdir+vec3(-0.563, 0.550, 0.307)*fspread)*flength*0.1);\n"
+" famt+=shadow_sample((vdir+vec3( 0.808, 0.686, 0.346)*fspread)*flength*0.2);\n"
+" famt+=shadow_sample((vdir+vec3( 0.787, 0.074,-0.065)*fspread)*flength*0.3);\n"
+" famt+=shadow_sample((vdir+vec3(-0.593, 0.071,-0.425)*fspread)*flength*0.4);\n"
+" famt+=shadow_sample((vdir+vec3(-0.790,-0.933,-0.875)*fspread)*flength*0.5);\n"
+" famt+=shadow_sample((vdir+vec3( 0.807,-0.690, 0.472)*fspread)*flength*0.6);\n"
+" famt+=shadow_sample((vdir+vec3( 0.522,-0.379, 0.350)*fspread)*flength*0.7);\n"
+" famt+=shadow_sample((vdir+vec3( 0.483, 0.201, 0.306)*fspread)*flength*0.8);\n"
+" return mix( vfrag, g_ambient_colour.rgb, famt );\n"
+"}\n"
+"\n"
+"vec3 apply_fog( vec3 vfrag, float fdist )\n"
+"{\n"
+" float dist = pow(fdist*0.0008,1.2);\n"
+" return mix( vfrag, vec3(0.55,0.76,1.0), min( 1.0, dist ) );\n"
+"}\n"
+"\n"
+"#line 12 0 \n"
+"\n"
+"void main()\n"
+"{\n"
+" vec3 vfrag = texture( uTexMain, aUv ).rgb;\n"
+"\n"
+" // Lighting\n"
+" //vec3 halfview = uCamera - aWorldCo;\n"
+" //float fdist = length( halfview );\n"
+" //halfview /= fdist;\n"
+"\n"
+" //vfrag = do_light_diffuse( vfrag, aNorm );\n"
+" //vfrag = do_light_spec( vfrag, aNorm, halfview, 0.1 );\n"
+" //vfrag = do_light_shadowing( vfrag );\n"
+" //vfrag = apply_fog( vfrag, fdist );\n"
+"\n"
+" FragColor = vec4(vfrag,1.0);\n"
+"}\n"
+""},
+};
+
+static GLuint _uniform_viewchar_uPv;
+static GLuint _uniform_viewchar_uTransforms;
+static GLuint _uniform_viewchar_uTexMain;
+static GLuint _uniform_viewchar_g_world_depth;
+static void shader_viewchar_uPv(m4x4f m){
+ glUniformMatrix4fv( _uniform_viewchar_uPv, 1, GL_FALSE, (float *)m );
+}
+static void shader_viewchar_uTexMain(int i){
+ glUniform1i( _uniform_viewchar_uTexMain, i );
+}
+static void shader_viewchar_g_world_depth(int i){
+ glUniform1i( _uniform_viewchar_g_world_depth, i );
+}
+static void shader_viewchar_register(void){
+ vg_shader_register( &_shader_viewchar );
+}
+static void shader_viewchar_use(void){ glUseProgram(_shader_viewchar.id); }
+static void shader_viewchar_link(void){
+ _uniform_viewchar_uPv = glGetUniformLocation( _shader_viewchar.id, "uPv" );
+ _uniform_viewchar_uTransforms = glGetUniformLocation( _shader_viewchar.id, "uTransforms" );
+ _uniform_viewchar_uTexMain = glGetUniformLocation( _shader_viewchar.id, "uTexMain" );
+ _uniform_viewchar_g_world_depth = glGetUniformLocation( _shader_viewchar.id, "g_world_depth" );
+}
+#endif /* SHADER_viewchar_H */
struct skeleton_bone
{
v3f co, end;
- u32 children; /* maybe remove */
u32 parent;
-
+
+ /* info, not real */
+ int deform, ik;
+
mdl_keyframe kf;
}
*bones;
m4x3f *final_transforms;
+ struct skeleton_ik
+ {
+ u32 lower, upper, target, pole;
+ }
+ *ik;
+
struct skeleton_anim
{
float rate;
*anims;
u32 bone_count,
- anim_count;
+ ik_count,
+ anim_count,
+ bindable_count; /* TODO: try to place IK last in the rig from export
+ so that we dont always upload transforms for
+ useless cpu IK bones. */
};
static void skeleton_apply_frame( m4x3f transform,
struct skeleton_anim *anim,
float time )
{
- u32 frame = time*anim->rate;
- frame = frame % anim->length;
+ float animtime = time*anim->rate;
+
+ u32 frame = ((u32)animtime) % anim->length,
+ next = (frame+1) % anim->length;
+
+ float t = vg_fractf( animtime );
+
+ mdl_keyframe *base = anim->anim_data + (skele->bone_count-1)*frame,
+ *nbase = anim->anim_data + (skele->bone_count-1)*next;
- mdl_keyframe *base = anim->anim_data + (skele->bone_count-1)*frame;
m4x3_copy( transform, skele->final_transforms[0] );
for( int i=1; i<skele->bone_count; i++ )
v3f temp_delta;
v3_sub( skele->bones[i].co, skele->bones[sb->parent].co, temp_delta );
-
/* pose matrix */
- mdl_keyframe *kf = base+i-1;
+ mdl_keyframe *kf = base+i-1,
+ *nkf = nbase+i-1;
+
+ v3f co;
+ v4f q;
+ v3f s;
- q_m3x3( kf->q, posemtx );
- v3_copy( kf->co, posemtx[3] );
+ v3_lerp( kf->co, nkf->co, t, co );
+ q_nlerp( kf->q, nkf->q, t, q );
+ v3_lerp( kf->s, nkf->s, t, s );
+
+ q_m3x3( q, posemtx );
+ v3_copy( co, posemtx[3] );
v3_add( temp_delta, posemtx[3], posemtx[3] );
/* final matrix */
}
}
+/*
+ * Get transformed position of bone
+ */
+static void skeleton_bone_posepos( struct skeleton *skele, u32 id, v3f co )
+{
+ m4x3_mulv( skele->final_transforms[id], skele->bones[id].co, co );
+}
+
+/*
+ * creates the reference inverse matrix for an IK bone, as it has an initial
+ * intrisic rotation based on the direction that the IK is setup..
+ */
+static void skeleton_inverse_for_ik( struct skeleton *skele,
+ v3f ivaxis,
+ u32 id, m4x3f inverse )
+{
+ v3_copy( ivaxis, inverse[0] );
+ v3_copy( skele->bones[id].end, inverse[1] );
+ v3_normalize( inverse[1] );
+ v3_cross( inverse[0], inverse[1], inverse[2] );
+ v3_copy( skele->bones[id].co, inverse[3] );
+ m4x3_invert_affine( inverse, inverse );
+}
+
+/*
+ * Apply all IK modifiers (2 bone ik reference from blender is supported)
+ */
+static void skeleton_apply_ik_pass( struct skeleton *skele )
+{
+ for( int i=0; i<skele->ik_count; i++ )
+ {
+ struct skeleton_ik *ik = &skele->ik[i];
+
+ v3f v0, /* base -> target */
+ v1, /* base -> pole */
+ vaxis;
+
+ v3f co_base,
+ co_target,
+ co_pole;
+
+ skeleton_bone_posepos( skele, ik->lower, co_base );
+ skeleton_bone_posepos( skele, ik->target, co_target );
+ skeleton_bone_posepos( skele, ik->pole, co_pole );
+
+ v3_sub( co_target, co_base, v0 );
+ v3_sub( co_pole, co_base, v1 );
+ v3_cross( v0, v1, vaxis );
+ v3_normalize( vaxis );
+ v3_normalize( v0 );
+ v3_cross( vaxis, v0, v1 );
+
+ /* localize problem into [x:v0,y:v1] 2d plane */
+ v2f base = { v3_dot( v0, co_base ), v3_dot( v1, co_base ) },
+ end = { v3_dot( v0, co_target ), v3_dot( v1, co_target ) },
+ knee;
+
+ /* Compute angles (basic trig)*/
+ v2f delta;
+ v2_sub( end, base, delta );
+
+ float
+ l1 = v3_length( skele->bones[ik->lower].end ),
+ l2 = v3_length( skele->bones[ik->upper].end ),
+ d = vg_clampf( v2_length(delta), fabsf(l1 - l2), l1+l2-0.00001f ),
+ c = acosf( (l1*l1 + d*d - l2*l2) / (2.0f*l1*d) ),
+ rot = atan2f( delta[1], delta[0] ) + c - VG_PIf/2.0f;
+
+ knee[0] = sinf(-rot) * l1;
+ knee[1] = cosf(-rot) * l1;
+
+ m4x3_identity( skele->final_transforms[ik->lower] );
+ m4x3_identity( skele->final_transforms[ik->upper] );
+
+ /* inverse matrix axis '(^axis,^bone,...)[base] */
+ m4x3f inverse;
+ v3f iv0, iv1, ivaxis;
+ v3_sub( skele->bones[ik->target].co, skele->bones[ik->lower].co, iv0 );
+ v3_sub( skele->bones[ik->pole].co, skele->bones[ik->lower].co, iv1 );
+ v3_cross( iv0, iv1, ivaxis );
+ v3_normalize( ivaxis );
+
+ skeleton_inverse_for_ik( skele, ivaxis, ik->lower, inverse );
+
+ /* create rotation matrix */
+ v3f co_knee;
+ v3_muladds( co_base, v0, knee[0], co_knee );
+ v3_muladds( co_knee, v1, knee[1], co_knee );
+ vg_line( co_base, co_knee, 0xff00ff00 );
+
+ m4x3f transform;
+ v3_copy( vaxis, transform[0] );
+ v3_muls( v0, knee[0], transform[1] );
+ v3_muladds( transform[1], v1, knee[1], transform[1] );
+ v3_normalize( transform[1] );
+ v3_cross( transform[0], transform[1], transform[2] );
+ v3_copy( co_base, transform[3] );
+
+ m4x3_mul( transform, inverse, skele->final_transforms[ik->lower] );
+
+ /* 'upper' or knee bone */
+ skeleton_inverse_for_ik( skele, ivaxis, ik->upper, inverse );
+
+ v3_copy( vaxis, transform[0] );
+ v3_sub( co_target, co_knee, transform[1] );
+ v3_normalize( transform[1] );
+ v3_cross( transform[0], transform[1], transform[2] );
+ v3_copy( co_knee, transform[3] );
+
+ m4x3_mul( transform, inverse, skele->final_transforms[ik->upper] );
+ }
+}
+
static struct skeleton_anim *skeleton_get_anim( struct skeleton *skele,
const char *name )
{
/* Setup an animated skeleton from model */
static int skeleton_setup( struct skeleton *skele, mdl_header *mdl )
{
- u32 bone_count = 1, skeleton_root = 0;
+ u32 bone_count = 1, skeleton_root = 0, ik_count = 0;
skele->bone_count = 0;
skele->bones = NULL;
skele->final_transforms = NULL;
if( skele->bone_count )
{
vg_error( "Multiple skeletons in model file\n" );
- free( skele->bones );
- return 0;
+ goto error_dealloc;
}
skele->bone_count = inf->channels;
+ skele->ik_count = inf->ik_count;
skele->bones = malloc(sizeof(struct skeleton_bone)*skele->bone_count);
+ skele->ik = malloc(sizeof(struct skeleton_ik)*skele->ik_count);
skeleton_root = i;
}
else if( skele->bone_count )
{
- if( pnode->classtype == k_classtype_bone )
+ int is_ik = pnode->classtype == k_classtype_ik_bone,
+ is_bone = (pnode->classtype == k_classtype_bone) || is_ik;
+
+ if( is_bone )
{
- struct skeleton_bone *sb = &skele->bones[bone_count ++];
+ if( bone_count == skele->bone_count )
+ {
+ vg_error( "too many bones (%u/%u) @%s!\n",
+ bone_count, skele->bone_count,
+ mdl_pstr( mdl, pnode->pstr_name ));
+
+ goto error_dealloc;
+ }
+
+ struct skeleton_bone *sb = &skele->bones[bone_count];
+
v3_copy( pnode->co, sb->co );
v3_copy( pnode->s, sb->end );
sb->parent = pnode->parent-skeleton_root;
+
+ if( is_ik )
+ {
+ struct classtype_ik_bone *ik_inf = mdl_get_entdata( mdl, pnode );
+ sb->deform = ik_inf->deform;
+ sb->ik = 1; /* TODO: place into new IK array */
+ skele->bones[ sb->parent ].ik = 1;
+
+ if( ik_count == skele->ik_count )
+ {
+ vg_error( "Too many ik bones, corrupt model file\n" );
+ goto error_dealloc;
+ }
+
+ struct skeleton_ik *ik = &skele->ik[ ik_count ++ ];
+ ik->upper = bone_count;
+ ik->lower = sb->parent;
+ ik->target = ik_inf->target;
+ ik->pole = ik_inf->pole;
+ }
+ else
+ {
+ struct classtype_bone *bone_inf = mdl_get_entdata( mdl, pnode );
+ sb->deform = bone_inf->deform;
+ sb->ik = 0;
+ }
+
+ bone_count ++;
}
else
{
if( bone_count != skele->bone_count )
{
vg_error( "Loaded %u bones out of %u\n", bone_count, skele->bone_count );
- return 0;
+ goto error_dealloc;
+ }
+
+ if( ik_count != skele->ik_count )
+ {
+ vg_error( "Loaded %u ik bones out of %u\n", ik_count, skele->ik_count );
+ goto error_dealloc;
}
/* fill in implicit root bone */
vg_success( "Loaded skeleton with %u bones\n", skele->bone_count );
return 1;
+
+error_dealloc:
+ free( skele->bones );
+ free( skele->ik );
+ return 0;
}
static void skeleton_debug( struct skeleton *skele )
v3f p0, p1;
v3_copy( sb->co, p0 );
v3_add( p0, sb->end, p1 );
- vg_line( p0, p1, 0xffffffff );
+ //vg_line( p0, p1, 0xffffffff );
m4x3_mulv( skele->final_transforms[i], p0, p0 );
m4x3_mulv( skele->final_transforms[i], p1, p1 );
- vg_line( p0, p1, 0xff0000ff );
+
+ if( sb->deform )
+ {
+ if( sb->ik )
+ {
+ vg_line( p0, p1, 0xff0000ff );
+ }
+ else
+ {
+ vg_line( p0, p1, 0xffcccccc );
+ }
+ }
+ else
+ vg_line( p0, p1, 0xff00ffff );
}
}