+#ifndef CHARACTER_H
+#define CHARACTER_H
+
+#include "vg/vg.h"
+#include "model.h"
+#include "ik.h"
+
+#define FOREACH_PART(FN) \
+ FN( body0 ) \
+ FN( body1 ) \
+ FN( neck ) \
+ FN( head ) \
+ FN( arm_l0 ) \
+ FN( arm_l1 ) \
+ FN( hand_l ) \
+ FN( arm_r0 ) \
+ FN( arm_r1 ) \
+ FN( hand_r ) \
+ FN( leg_l0 ) \
+ FN( leg_l1 ) \
+ FN( foot_l ) \
+ FN( leg_r0 ) \
+ FN( leg_r1 ) \
+ FN( foot_r ) \
+ FN( wheels ) \
+ FN( board ) \
+
+#define MAKE_ENUM(ENUM) k_chpart_##ENUM,
+#define MAKE_STRING(STR) #STR,
+#define ADD_ONE(_) +1
+#define PART_COUNT FOREACH_PART(ADD_ONE)
+
+enum character_part
+{
+ FOREACH_PART( MAKE_ENUM )
+};
+
+static const char *character_part_strings[] =
+{
+ FOREACH_PART( MAKE_STRING )
+};
+
+
+struct character
+{
+ glmesh mesh;
+
+ submodel parts[ PART_COUNT ];
+ m4x3f matrices[ PART_COUNT ];
+
+ /* Auxillary information */
+ v3f offsets[ PART_COUNT ];
+
+ /*
+ * Controls
+ * note: - base nodes of IK structures will be filled automatically
+ * - all positions are in local space to the mroot
+ */
+ struct ik_basic ik_arm_l, ik_arm_r,
+ ik_leg_l, ik_leg_r,
+ ik_body;
+
+ v4f qhead;
+ float rhip, rcollar, /* twist of hip and collar controls,
+ these act in the local +y axis of the part */
+ rfootl, rfootr,
+ rhandl, rhandr;
+
+ v3f ground_normal; /* Feet will be aligned to this */
+ m4x3f mroot;
+};
+
+static void character_offset( struct character *ch, enum character_part parent,
+ enum character_part child )
+{
+ v3_sub( ch->parts[ child ].pivot, ch->parts[ parent ].pivot,
+ ch->offsets[ child ] );
+}
+
+static int character_load( struct character *ch, const char *name )
+{
+ char buf[64];
+
+ snprintf( buf, sizeof(buf)-1, "models/%s.mdl", name );
+ model *src = vg_asset_read( buf );
+
+ if( !src )
+ {
+ vg_error( "Could not open 'models/%s.mdl'", name );
+ return 0;
+ }
+
+ int error_count = 0;
+
+ for( int i=0; i<PART_COUNT; i++ )
+ {
+ snprintf( buf, sizeof(buf)-1, "%s_%s", name, character_part_strings[i] );
+ submodel *sm = submodel_get( src, buf );
+
+ if( !sm )
+ {
+ vg_warn( "Character file does not contain an '_%s' part.\n",
+ character_part_strings[i] );
+ error_count ++;
+
+ memset( &ch->parts[i], 0, sizeof(submodel) );
+ continue;
+ }
+
+ ch->parts[i] = *sm;
+ }
+
+ model_unpack( src, &ch->mesh );
+
+ if( !error_count )
+ vg_success( "Loaded character file '%s' with no errors\n", name );
+
+ /* Create the offsets */
+ character_offset( ch, k_chpart_body0, k_chpart_body1 );
+ character_offset( ch, k_chpart_body1, k_chpart_neck );
+ character_offset( ch, k_chpart_neck, k_chpart_head );
+
+ character_offset( ch, k_chpart_body1, k_chpart_arm_l0 );
+ character_offset( ch, k_chpart_arm_l0, k_chpart_arm_l1 );
+ character_offset( ch, k_chpart_arm_l1, k_chpart_hand_l );
+
+ character_offset( ch, k_chpart_body1, k_chpart_arm_r0 );
+ character_offset( ch, k_chpart_arm_r0, k_chpart_arm_r1 );
+ character_offset( ch, k_chpart_arm_r1, k_chpart_hand_r );
+
+ character_offset( ch, k_chpart_body0, k_chpart_leg_l0 );
+ character_offset( ch, k_chpart_leg_l0, k_chpart_leg_l1 );
+ character_offset( ch, k_chpart_leg_l1, k_chpart_foot_l );
+
+ character_offset( ch, k_chpart_body0, k_chpart_leg_r0 );
+ character_offset( ch, k_chpart_leg_r0, k_chpart_leg_r1 );
+ character_offset( ch, k_chpart_leg_r1, k_chpart_foot_r );
+
+ ch->ik_arm_l.l1 = v3_length( ch->offsets[ k_chpart_arm_l1 ] );
+ ch->ik_arm_l.l2 = v3_length( ch->offsets[ k_chpart_hand_l ] );
+ ch->ik_arm_r.l1 = v3_length( ch->offsets[ k_chpart_arm_r1 ] );
+ ch->ik_arm_r.l2 = v3_length( ch->offsets[ k_chpart_hand_r ] );
+
+ ch->ik_leg_l.l1 = v3_length( ch->offsets[ k_chpart_leg_l1 ] );
+ ch->ik_leg_l.l2 = v3_length( ch->offsets[ k_chpart_foot_l ] );
+ ch->ik_leg_r.l1 = v3_length( ch->offsets[ k_chpart_leg_r1 ] );
+ ch->ik_leg_r.l2 = v3_length( ch->offsets[ k_chpart_foot_r ] );
+
+ ch->ik_body.l1 = v3_length( ch->offsets[ k_chpart_body1 ] );
+ ch->ik_body.l2 = v3_length( ch->offsets[ k_chpart_neck ] );
+
+ free( src );
+ return 1;
+}
+
+static void character_eval( struct character *ch )
+{
+ m4x3f *mats = ch->matrices;
+ v3f *offs = ch->offsets;
+
+ ik_basic( &ch->ik_body, mats[k_chpart_body0], mats[k_chpart_body1],
+ k_ikY, k_ikX );
+
+ m3x3f temp;
+ v4f body_rotation;
+ /* TODO: Do this directly via m3x3 */
+
+ q_axis_angle( body_rotation, (v3f){0.0f,1.0f,0.0f}, ch->rhip );
+ q_m3x3( body_rotation, temp );
+ m3x3_mul( mats[k_chpart_body0], temp, mats[k_chpart_body0] );
+
+ q_axis_angle( body_rotation, (v3f){0.0f,1.0f,0.0f}, ch->rcollar );
+ q_m3x3( body_rotation, temp );
+ m3x3_mul( mats[k_chpart_body1], temp, mats[k_chpart_body1] );
+
+ /* Setup aux */
+ m4x3_mulv( mats[k_chpart_body0], offs[k_chpart_leg_l0], ch->ik_leg_l.base );
+ m4x3_mulv( mats[k_chpart_body0], offs[k_chpart_leg_r0], ch->ik_leg_r.base );
+ m4x3_mulv( mats[k_chpart_body1], offs[k_chpart_arm_l0], ch->ik_arm_l.base );
+ m4x3_mulv( mats[k_chpart_body1], offs[k_chpart_arm_r0], ch->ik_arm_r.base );
+
+ /* IK for arms and legs */
+ ik_basic( &ch->ik_arm_l, mats[k_chpart_arm_l0], mats[k_chpart_arm_l1],
+ k_ikZ, k_ikY );
+ ik_basic( &ch->ik_arm_r, mats[k_chpart_arm_r0], mats[k_chpart_arm_r1],
+ k_iknZ, k_ikY );
+ ik_basic( &ch->ik_leg_l, mats[k_chpart_leg_l0], mats[k_chpart_leg_l1],
+ k_ikY, k_iknX );
+ ik_basic( &ch->ik_leg_r, mats[k_chpart_leg_r0], mats[k_chpart_leg_r1],
+ k_ikY, k_iknX );
+
+ /* Feet */
+ m3x3_copy( mats[k_chpart_leg_l1], mats[k_chpart_foot_l] );
+ m3x3_copy( mats[k_chpart_leg_r1], mats[k_chpart_foot_r] );
+ m4x3_mulv( mats[k_chpart_leg_l1], offs[k_chpart_foot_l],
+ mats[k_chpart_foot_l][3] );
+ m4x3_mulv( mats[k_chpart_leg_r1], offs[k_chpart_foot_r],
+ mats[k_chpart_foot_r][3] );
+
+ /* Hands */
+ m3x3_copy( mats[k_chpart_arm_l1], mats[k_chpart_hand_l] );
+ m3x3_copy( mats[k_chpart_arm_r1], mats[k_chpart_hand_r] );
+ m4x3_mulv( mats[k_chpart_arm_l1], offs[k_chpart_hand_l],
+ mats[k_chpart_hand_l][3] );
+ m4x3_mulv( mats[k_chpart_arm_r1], offs[k_chpart_hand_r],
+ mats[k_chpart_hand_r][3] );
+
+ /* Neck / Head */
+ m3x3_copy( mats[k_chpart_body1], mats[k_chpart_neck] );
+ m4x3_mulv( mats[k_chpart_body1], offs[k_chpart_neck],
+ mats[k_chpart_neck][3] );
+
+ m3x3_copy( mats[k_chpart_neck], mats[k_chpart_head] );
+ m4x3_mulv( mats[k_chpart_neck], offs[k_chpart_head], mats[k_chpart_head][3]);
+
+ for( int i=0; i<PART_COUNT; i++ )
+ m4x3_mul( ch->mroot, ch->matrices[i], ch->matrices[i] );
+}
+
+static void character_testpose( struct character *ch, float t )
+{
+ /* Body */
+ float *hips = ch->ik_body.base,
+ *collar = ch->ik_body.end,
+ *pole = ch->ik_body.pole;
+
+ hips[0] = cosf(t*1.325f)*0.25f;
+ hips[1] = (sinf(t)*0.2f+0.6f) * ch->parts[ k_chpart_body0 ].pivot[1];
+ hips[2] = 0.0f;
+
+ collar[0] = hips[0];
+ collar[1] = hips[1] + (ch->ik_body.l1+ch->ik_body.l2)*(sinf(t)*0.05f+0.94f);
+ collar[2] = hips[2] + cosf(t*0.42f)*0.01f;
+
+ v3_add( hips, collar, pole );
+ v3_muls( pole, 0.5f, pole );
+ v3_add( pole, (v3f){ 1.0f, 0.0f, 0.0f }, pole );
+
+ /* Legs */
+ float *footl = ch->ik_leg_l.end,
+ *footr = ch->ik_leg_r.end,
+ *polel = ch->ik_leg_l.pole,
+ *poler = ch->ik_leg_r.pole;
+
+ footl[0] = sinf(t*0.563f);
+ footl[1] = 0.0f;
+ footl[2] = 0.0f;
+
+ footr[0] = 0.0f;
+ footr[1] = 0.0f;
+ footr[2] = cosf(t*0.672f);
+
+ v3_add( hips, footl, polel );
+ v3_muls( polel, 0.4f, polel );
+ v3_add( polel, (v3f){ -1.0f,0.0f,0.0f }, polel );
+
+ v3_add( hips, footr, poler );
+ v3_muls( poler, 0.4f, poler );
+ v3_add( poler, (v3f){ -1.0f,0.0f,0.0f }, poler );
+
+ /* Arms */
+ float *arml = ch->ik_arm_l.end,
+ *armr = ch->ik_arm_r.end;
+ polel = ch->ik_arm_l.pole;
+ poler = ch->ik_arm_r.pole;
+
+ v3_copy( (v3f){ 0.0f, 0.0f, 1.0f }, arml );
+ v3_copy( (v3f){ 0.0f, 0.0f,-1.0f }, armr );
+ v3_copy( (v3f){ 1.0f, 1.0f, 0.5f }, polel );
+ v3_copy( (v3f){ 1.0f, 1.0f,-0.5f }, poler );
+
+ /* Other */
+ ch->rhip = sinf(t*0.2f);
+ ch->rcollar = sinf(t*0.35325f);
+ q_identity( ch->qhead );
+
+ m4x3_identity( ch->matrices[k_chpart_board] );
+ m4x3_identity( ch->matrices[k_chpart_wheels] );
+}
+
+static void character_draw( struct character *ch, int temp )
+{
+ mesh_bind( &ch->mesh );
+
+ glEnable( GL_CULL_FACE );
+ glCullFace( GL_BACK );
+
+ for( int i=0; i<PART_COUNT; i++ )
+ {
+ glUniformMatrix4x3fv( temp, 1, GL_FALSE, (float *)ch->matrices[i] );
+ submodel_draw( &ch->parts[i] );
+ }
+}
+
+#undef FOREACH_PART
+#undef MAKE_ENUM
+#undef MAKE_STRING
+#undef ADD_ONE
+#endif