X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=skeleton.h;h=9729b64c04a95b7b052ba1daa3f280f444670a44;hb=5fa590f62aa7e62a8b6b07e10556c2ecc54cdca6;hp=78a0b5604d5fd0facb8b903ece0631d5fcf35a15;hpb=d00b1df8f80e4714dc2f9aa2189d242bb4d09a2f;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/skeleton.h b/skeleton.h index 78a0b56..9729b64 100644 --- a/skeleton.h +++ b/skeleton.h @@ -2,9 +2,8 @@ * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved */ -#ifndef SKELETON_H -#define SKELETON_H - +#pragma once +#include "vg/vg_lines.h" #include "model.h" struct skeleton @@ -14,20 +13,33 @@ struct skeleton v3f co, end; u32 parent; - int deform, ik; + u32 flags; int defer; mdl_keyframe kf; + mdl_bone *orig_bone; - u32 orig_node; - - int collider; + u32 collider; boxf hitbox; - - char name[16]; + const char *name; } *bones; + u32 bone_count; + + struct skeleton_anim + { + const char *name; + u32 length; + + float rate; + mdl_keyframe *anim_data; + } + *anims; + u32 anim_count; + +#if 0 m4x3f *final_mtx; +#endif struct skeleton_ik { @@ -35,74 +47,88 @@ struct skeleton m3x3f ia, ib; } *ik; + u32 ik_count; - struct skeleton_anim - { - float rate; - u32 length; - struct mdl_keyframe *anim_data; - char name[32]; - } - *anims; - - u32 bone_count, - ik_count, + u32 collider_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. */ + bindable_count; }; static u32 skeleton_bone_id( struct skeleton *skele, const char *name ) { - for( u32 i=0; ibone_count; i++ ) - { + for( u32 i=1; ibone_count; i++ ){ if( !strcmp( skele->bones[i].name, name )) return i; } + vg_error( "skeleton_bone_id( *, \"%s\" );\n", name ); + vg_fatal_error( "Bone does not exist\n" ); + return 0; } -static void keyframe_copy_pose( mdl_keyframe *kfa, mdl_keyframe *kfb, int num ) +static void keyframe_copy_pose( mdl_keyframe *kfa, mdl_keyframe *kfb, + int num ) { for( int i=0; ico, offset, co ); + v3_sub( co, origin, v0 ); + q_mulv( q, v0, v0 ); + v3_add( v0, origin, co ); + v3_sub( co, offset, kf->co ); + + q_mul( q, kf->q, kf->q ); + q_normalize( kf->q ); +} + +static void keyframe_lerp( mdl_keyframe *kfa, mdl_keyframe *kfb, f32 t, + mdl_keyframe *kfd ){ + v3_lerp( kfa->co, kfb->co, t, kfd->co ); + q_nlerp( kfa->q, kfb->q, t, kfd->q ); + v3_lerp( kfa->s, kfb->s, t, kfd->s ); +} + /* * Lerp between two sets of keyframes and store in dest. Rotations use Nlerp. */ -static void keyframe_lerp_pose( mdl_keyframe *kfa, mdl_keyframe *kfb, float t, - mdl_keyframe *kfd, int count ) -{ - if( t <= 0.01f ) - { +static void keyframe_lerp_pose( mdl_keyframe *kfa, mdl_keyframe *kfb, + float t, mdl_keyframe *kfd, int count ){ + if( t <= 0.0001f ){ keyframe_copy_pose( kfa, kfd, count ); return; } - else if( t >= 0.99f ) - { + else if( t >= 0.9999f ){ keyframe_copy_pose( kfb, kfd, count ); return; } for( int i=0; ibone_count-1 ); } +static void skeleton_copy_pose( struct skeleton *skele, + mdl_keyframe *kfa, mdl_keyframe *kfd ) +{ + keyframe_copy_pose( kfa, kfd, skele->bone_count-1 ); +} + /* * Sample animation between 2 closest frames using time value. Output is a * keyframe buffer that is allocated with an appropriate size @@ -112,13 +138,13 @@ static void skeleton_sample_anim( struct skeleton *skele, float time, mdl_keyframe *output ) { - float animtime = time*anim->rate; + f32 animtime = fmodf( time*anim->rate, anim->length ), + animframe = floorf( animtime ), + t = animtime - animframe; - u32 frame = ((u32)animtime) % anim->length, + u32 frame = (u32)animframe % 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; @@ -143,30 +169,30 @@ typedef enum anim_apply { k_anim_apply_always, k_anim_apply_defer_ik, - k_anim_apply_deffered_only + k_anim_apply_deffered_only, + k_anim_apply_absolute } anim_apply; -static int should_apply_bone( struct skeleton *skele, u32 id, anim_apply type ) +static +int should_apply_bone( struct skeleton *skele, u32 id, anim_apply type ) { struct skeleton_bone *sb = &skele->bones[ id ], *sp = &skele->bones[ sb->parent ]; - if( type == k_anim_apply_defer_ik ) - { - if( (sp->ik && !sb->ik) || sp->defer ) + if( type == k_anim_apply_defer_ik ){ + if( ((sp->flags & k_bone_flag_ik) && !(sb->flags & k_bone_flag_ik)) + || sp->defer ) { sb->defer = 1; return 0; } - else - { + else{ sb->defer = 0; return 1; } } - else if( type == k_anim_apply_deffered_only ) - { + else if( type == k_anim_apply_deffered_only ){ if( sb->defer ) return 1; else @@ -180,16 +206,27 @@ static int should_apply_bone( struct skeleton *skele, u32 id, anim_apply type ) * Apply block of keyframes to skeletons final pose */ static void skeleton_apply_pose( struct skeleton *skele, mdl_keyframe *pose, - anim_apply passtype ) -{ - m4x3_identity( skele->final_mtx[0] ); + anim_apply passtype, m4x3f *final_mtx ){ + if( passtype == k_anim_apply_absolute ){ + for( u32 i=1; ibone_count; i++ ){ + mdl_keyframe *kf = &pose[i-1]; + + v3f *posemtx = final_mtx[i]; + + q_m3x3( kf->q, posemtx ); + m3x3_scale( posemtx, kf->s ); + v3_copy( kf->co, posemtx[3] ); + } + return; + } + + m4x3_identity( final_mtx[0] ); skele->bones[0].defer = 0; - skele->bones[0].ik = 0; + skele->bones[0].flags &= ~k_bone_flag_ik; - for( int i=1; ibone_count; i++ ) - { + for( u32 i=1; ibone_count; i++ ){ struct skeleton_bone *sb = &skele->bones[i], - *sp = &skele->bones[ sb->parent ]; + *sp = &skele->bones[sb->parent]; if( !should_apply_bone( skele, i, passtype ) ) continue; @@ -205,11 +242,25 @@ static void skeleton_apply_pose( struct skeleton *skele, mdl_keyframe *pose, /* pose matrix */ mdl_keyframe *kf = &pose[i-1]; q_m3x3( kf->q, posemtx ); + m3x3_scale( posemtx, kf->s ); v3_copy( kf->co, posemtx[3] ); v3_add( temp_delta, posemtx[3], posemtx[3] ); /* final matrix */ - m4x3_mul( skele->final_mtx[ sb->parent ], posemtx, skele->final_mtx[i] ); + m4x3_mul( final_mtx[ sb->parent ], posemtx, final_mtx[i] ); + } +} + +/* + * Take the final matrices and decompose it into an absolute positioned anim + */ +static void skeleton_decompose_mtx_absolute( struct skeleton *skele, + mdl_keyframe *anim, + m4x3f *final_mtx ){ + for( u32 i=1; ibone_count; i++ ){ + struct skeleton_bone *sb = &skele->bones[i]; + mdl_keyframe *kf = &anim[i-1]; + m4x3_decompose( final_mtx[i], kf->co, kf->q, kf->s ); } } @@ -234,14 +285,13 @@ static void skeleton_inverse_for_ik( struct skeleton *skele, static void skeleton_create_inverses( struct skeleton *skele ) { /* IK: inverse 'plane-bone space' axis '(^axis,^bone,...)[base] */ - for( int i=0; iik_count; i++ ) - { + for( u32 i=0; iik_count; i++ ){ struct skeleton_ik *ik = &skele->ik[i]; 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_sub( skele->bones[ik->pole].co, skele->bones[ik->lower].co, iv1 ); v3_cross( iv0, iv1, ivaxis ); v3_normalize( ivaxis ); @@ -253,12 +303,13 @@ static void skeleton_create_inverses( struct skeleton *skele ) /* * Apply a model matrix to all bones, should be done last */ -static void skeleton_apply_transform( struct skeleton *skele, m4x3f transform ) +static +void skeleton_apply_transform( struct skeleton *skele, m4x3f transform, + m4x3f *final_mtx ) { - for( int i=0; ibone_count; i++ ) - { + for( u32 i=0; ibone_count; i++ ){ struct skeleton_bone *sb = &skele->bones[i]; - m4x3_mul( transform, skele->final_mtx[i], skele->final_mtx[i] ); + m4x3_mul( transform, final_mtx[i], final_mtx[i] ); } } @@ -266,26 +317,22 @@ static void skeleton_apply_transform( struct skeleton *skele, m4x3f transform ) * Apply an inverse matrix to all bones which maps vertices from bind space into * bone relative positions */ -static void skeleton_apply_inverses( struct skeleton *skele ) -{ - for( int i=0; ibone_count; i++ ) - { +static void skeleton_apply_inverses( struct skeleton *skele, m4x3f *final_mtx ){ + for( u32 i=0; ibone_count; i++ ){ struct skeleton_bone *sb = &skele->bones[i]; m4x3f inverse; m3x3_identity( inverse ); v3_negate( sb->co, inverse[3] ); - m4x3_mul( skele->final_mtx[i], inverse, skele->final_mtx[i] ); + m4x3_mul( final_mtx[i], inverse, final_mtx[i] ); } } /* * 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; iik_count; i++ ) - { +static void skeleton_apply_ik_pass( struct skeleton *skele, m4x3f *final_mtx ){ + for( u32 i=0; iik_count; i++ ){ struct skeleton_ik *ik = &skele->ik[i]; v3f v0, /* base -> target */ @@ -296,9 +343,9 @@ static void skeleton_apply_ik_pass( struct skeleton *skele ) co_target, co_pole; - v3_copy( skele->final_mtx[ik->lower][3], co_base ); - v3_copy( skele->final_mtx[ik->target][3], co_target ); - v3_copy( skele->final_mtx[ik->pole][3], co_pole ); + v3_copy( final_mtx[ik->lower][3], co_base ); + v3_copy( final_mtx[ik->target][3], co_target ); + v3_copy( final_mtx[ik->pole][3], co_pole ); v3_sub( co_target, co_base, v0 ); v3_sub( co_pole, co_base, v1 ); @@ -326,8 +373,8 @@ static void skeleton_apply_ik_pass( struct skeleton *skele ) knee[0] = sinf(-rot) * l1; knee[1] = cosf(-rot) * l1; - m4x3_identity( skele->final_mtx[ik->lower] ); - m4x3_identity( skele->final_mtx[ik->upper] ); + m4x3_identity( final_mtx[ik->lower] ); + m4x3_identity( final_mtx[ik->upper] ); /* create rotation matrix */ v3f co_knee; @@ -344,7 +391,7 @@ static void skeleton_apply_ik_pass( struct skeleton *skele ) v3_copy( co_base, transform[3] ); m3x3_mul( transform, ik->ia, transform ); - m4x3_copy( transform, skele->final_mtx[ik->lower] ); + m4x3_copy( transform, final_mtx[ik->lower] ); /* upper/knee bone */ v3_copy( vaxis, transform[0] ); @@ -354,7 +401,7 @@ static void skeleton_apply_ik_pass( struct skeleton *skele ) v3_copy( co_knee, transform[3] ); m3x3_mul( transform, ik->ib, transform ); - m4x3_copy( transform, skele->final_mtx[ik->upper] ); + m4x3_copy( transform, final_mtx[ik->upper] ); } } @@ -363,219 +410,172 @@ static void skeleton_apply_ik_pass( struct skeleton *skele ) * Pose, IK, Pose(deferred), Inverses, Transform */ static void skeleton_apply_standard( struct skeleton *skele, mdl_keyframe *pose, - m4x3f transform ) -{ - skeleton_apply_pose( skele, pose, k_anim_apply_defer_ik ); - skeleton_apply_ik_pass( skele ); - skeleton_apply_pose( skele, pose, k_anim_apply_deffered_only ); - skeleton_apply_inverses( skele ); - skeleton_apply_transform( skele, transform ); + m4x3f transform, m4x3f *final_mtx ){ + skeleton_apply_pose( skele, pose, k_anim_apply_defer_ik, final_mtx ); + skeleton_apply_ik_pass( skele, final_mtx ); + skeleton_apply_pose( skele, pose, k_anim_apply_deffered_only, final_mtx ); + skeleton_apply_inverses( skele, final_mtx ); + skeleton_apply_transform( skele, transform, final_mtx ); } /* * Get an animation by name */ static struct skeleton_anim *skeleton_get_anim( struct skeleton *skele, - const char *name ) -{ - for( int i=0; ianim_count; i++ ) - { + const char *name ){ + for( u32 i=0; ianim_count; i++ ){ struct skeleton_anim *anim = &skele->anims[i]; if( !strcmp( anim->name, name ) ) return anim; } + vg_error( "skeleton_get_anim( *, \"%s\" )\n", name ); + vg_fatal_error( "Invalid animation name\n" ); + return NULL; } -/* Setup an animated skeleton from model */ -static int skeleton_setup( struct skeleton *skele, mdl_header *mdl ) -{ - u32 bone_count = 1, skeleton_root = 0, ik_count = 0, collider_count = 0; +static void skeleton_alloc_from( struct skeleton *skele, + void *lin_alloc, + mdl_context *mdl, + mdl_armature *armature ){ + skele->bone_count = armature->bone_count+1; + skele->anim_count = armature->anim_count; + skele->ik_count = 0; + skele->collider_count = 0; + + for( u32 i=0; ibone_count; i++ ){ + mdl_bone *bone = mdl_arritm( &mdl->bones, armature->bone_start+i ); + + if( bone->flags & k_bone_flag_ik ) + skele->ik_count ++; + + if( bone->collider ) + skele->collider_count ++; + } + + u32 bone_size = sizeof(struct skeleton_bone) * skele->bone_count, + ik_size = sizeof(struct skeleton_ik) * skele->ik_count, + mtx_size = sizeof(m4x3f) * skele->bone_count, + anim_size = sizeof(struct skeleton_anim) * skele->anim_count; + + skele->bones = vg_linear_alloc( lin_alloc, bone_size ); + skele->ik = vg_linear_alloc( lin_alloc, ik_size ); + //skele->final_mtx = vg_linear_alloc( lin_alloc, mtx_size ); + skele->anims = vg_linear_alloc( lin_alloc, anim_size ); + + memset( skele->bones, 0, bone_size ); + memset( skele->ik, 0, ik_size ); + //memset( skele->final_mtx, 0, mtx_size ); + memset( skele->anims, 0, anim_size ); +} + +static void skeleton_fatal_err(void){ + vg_fatal_error( "Skeleton setup failed" ); +} + +/* Setup an animated skeleton from model. mdl's metadata should stick around */ +static void skeleton_setup( struct skeleton *skele, + void *lin_alloc, mdl_context *mdl ){ + u32 ik_count = 0, collider_count = 0; skele->bone_count = 0; skele->bones = NULL; - skele->final_mtx = NULL; + //skele->final_mtx = NULL; skele->anims = NULL; - struct classtype_skeleton *inf = NULL; + if( !mdl->armatures.count ){ + vg_error( "No skeleton in model\n" ); + skeleton_fatal_err(); + } + + mdl_armature *armature = mdl_arritm( &mdl->armatures, 0 ); + skeleton_alloc_from( skele, lin_alloc, mdl, armature ); - for( u32 i=0; inode_count; i++ ) - { - mdl_node *pnode = mdl_node_from_id( mdl, i ); + for( u32 i=0; ibone_count; i++ ){ + mdl_bone *bone = mdl_arritm( &mdl->bones, armature->bone_start+i ); + struct skeleton_bone *sb = &skele->bones[i+1]; - if( pnode->classtype == k_classtype_skeleton ) - { - inf = mdl_get_entdata( mdl, pnode ); - if( skele->bone_count ) - { - vg_error( "Multiple skeletons in model file\n" ); - goto error_dealloc; - } + v3_copy( bone->co, sb->co ); + v3_copy( bone->end, sb->end ); + + sb->parent = bone->parent; + sb->name = mdl_pstr( mdl, bone->pstr_name ); + sb->flags = bone->flags; + sb->collider = bone->collider; + sb->orig_bone = bone; + + if( sb->flags & k_bone_flag_ik ){ + skele->bones[ sb->parent ].flags |= k_bone_flag_ik; - skele->bone_count = inf->channels; - skele->ik_count = inf->ik_count; - skele->collider_count = inf->collider_count; - skele->bones =vg_alloc(sizeof(struct skeleton_bone)*skele->bone_count); - skele->ik = vg_alloc(sizeof(struct skeleton_ik)*skele->ik_count); - skeleton_root = i; - } - else if( skele->bone_count ) - { - int is_bone = pnode->classtype == k_classtype_bone; - - if( is_bone ) - { - 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]; - struct classtype_bone *bone_inf = mdl_get_entdata( mdl, pnode ); - int is_ik = bone_inf->ik_target; - - v3_copy( pnode->co, sb->co ); - v3_copy( pnode->s, sb->end ); - sb->parent = pnode->parent-skeleton_root; - strncpy( sb->name, mdl_pstr(mdl,pnode->pstr_name), 15 ); - sb->deform = bone_inf->deform; - - if( is_ik ) - { - 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 = bone_inf->ik_target; - ik->pole = bone_inf->ik_pole; - } - else - { - sb->ik = 0; - } - - sb->collider = bone_inf->collider; - sb->orig_node = i; - box_copy( bone_inf->hitbox, sb->hitbox ); - - if( bone_inf->collider ) - { - if( collider_count == skele->collider_count ) - { - vg_error( "Too many collider bones\n" ); - goto error_dealloc; - } - - collider_count ++; - } - - bone_count ++; + if( ik_count == skele->ik_count ){ + vg_error( "Too many ik bones, corrupt model file\n" ); + skeleton_fatal_err(); } - else - { - break; - } - } - } - if( !inf ) - { - vg_error( "No skeleton in model\n" ); - return 0; - } + struct skeleton_ik *ik = &skele->ik[ ik_count ++ ]; + ik->upper = i+1; + ik->lower = bone->parent; + ik->target = bone->ik_target; + ik->pole = bone->ik_pole; + } - if( collider_count != skele->collider_count ) - { - vg_error( "Loaded %u colliders out of %u\n", collider_count, - skele->collider_count ); - goto error_dealloc; - } + box_copy( bone->hitbox, sb->hitbox ); - if( bone_count != skele->bone_count ) - { - vg_error( "Loaded %u bones out of %u\n", bone_count, skele->bone_count ); - goto error_dealloc; - } + if( bone->collider ){ + if( collider_count == skele->collider_count ){ + vg_error( "Too many collider bones\n" ); + skeleton_fatal_err(); + } - if( ik_count != skele->ik_count ) - { - vg_error( "Loaded %u ik bones out of %u\n", ik_count, skele->ik_count ); - goto error_dealloc; + collider_count ++; + } } /* fill in implicit root bone */ v3_zero( skele->bones[0].co ); v3_copy( (v3f){0.0f,1.0f,0.0f}, skele->bones[0].end ); skele->bones[0].parent = 0xffffffff; - skele->bones[0].collider = 0; - - skele->final_mtx = vg_alloc( sizeof(m4x3f) * skele->bone_count ); - skele->anim_count = inf->anim_count; - skele->anims = vg_alloc( sizeof(struct skeleton_anim) * inf->anim_count); + skele->bones[0].flags = 0; + skele->bones[0].name = "[root]"; - for( int i=0; ianim_count; i++ ) - { + /* process animation quick refs */ + for( u32 i=0; ianim_count; i++ ){ mdl_animation *anim = - mdl_animation_from_id( mdl, inf->anim_start+i ); - - skele->anims[i].rate = anim->rate; - skele->anims[i].length = anim->length; - strncpy( skele->anims[i].name, mdl_pstr(mdl, anim->pstr_name), 31 ); + mdl_arritm( &mdl->animations, armature->anim_start+i ); - u32 total_keyframes = (skele->bone_count-1)*anim->length; - size_t block_size = sizeof(mdl_keyframe) * total_keyframes; - mdl_keyframe *dst = vg_alloc( block_size ); + skele->anims[i].rate = anim->rate; + skele->anims[i].length = anim->length; + skele->anims[i].name = mdl_pstr(mdl, anim->pstr_name); + skele->anims[i].anim_data = + mdl_arritm( &mdl->keyframes, anim->offset ); - skele->anims[i].anim_data = dst; - memcpy( dst, mdl_get_animdata( mdl, anim ), block_size ); + vg_info( "animation[ %f, %u ] '%s'\n", anim->rate, + anim->length, + skele->anims[i].name ); } skeleton_create_inverses( skele ); vg_success( "Loaded skeleton with %u bones\n", skele->bone_count ); vg_success( " %u colliders\n", skele->collider_count ); - return 1; - -error_dealloc: - vg_free( skele->bones ); - vg_free( skele->ik ); - return 0; } -static void skeleton_debug( struct skeleton *skele ) -{ - for( int i=0; ibone_count; i ++ ) - { +static void skeleton_debug( struct skeleton *skele, m4x3f *final_mtx ){ + for( u32 i=1; ibone_count; i ++ ){ struct skeleton_bone *sb = &skele->bones[i]; v3f p0, p1; v3_copy( sb->co, p0 ); v3_add( p0, sb->end, p1 ); - //vg_line( p0, p1, 0xffffffff ); - m4x3_mulv( skele->final_mtx[i], p0, p0 ); - m4x3_mulv( skele->final_mtx[i], p1, p1 ); + m4x3_mulv( final_mtx[i], p0, p0 ); + m4x3_mulv( final_mtx[i], p1, p1 ); - if( sb->deform ) - { - if( sb->ik ) - { + if( sb->flags & k_bone_flag_deform ){ + if( sb->flags & k_bone_flag_ik ){ vg_line( p0, p1, 0xff0000ff ); } - else - { + else{ vg_line( p0, p1, 0xffcccccc ); } } @@ -583,5 +583,3 @@ static void skeleton_debug( struct skeleton *skele ) vg_line( p0, p1, 0xff00ffff ); } } - -#endif /* SKELETON_H */