# HG changeset patch # User John Tsiombikas # Date 1389976067 -7200 # Node ID cb5414f406eb68d4619ca07abe719fcbaff94a33 # Parent fa5c52ea9d59296db3b72c84ff309a2d94021813# Parent 0be413ac2e0ab8ef53f49ba16e161de0b8d7937f merged diff -r fa5c52ea9d59 -r cb5414f406eb Makefile --- a/Makefile Fri Jan 17 18:16:09 2014 +0200 +++ b/Makefile Fri Jan 17 18:27:47 2014 +0200 @@ -37,7 +37,7 @@ CC = clang CXX = clang++ CXXFLAGS = -pedantic -Wall $(dbg) $(opt) $(pic) $(extinc) -LDFLAGS = $(extlibs) +LDFLAGS = $(extlibs) -lpthread .PHONY: all all: $(lib_so) $(lib_a) @@ -48,15 +48,19 @@ $(lib_a): $(obj) $(extlibs) $(AR) rcs $@ $(obj) $(extlibs) +.PHONY: $(openctm) $(openctm): $(MAKE) -C libs/openctm +.PHONY: $(tinyxml2) $(tinyxml2): $(MAKE) -C libs/tinyxml2 +.PHONY: $(vmath) $(vmath): $(MAKE) -C libs/vmath +.PHONY: $(anim) $(anim): $(MAKE) -C libs/anim diff -r fa5c52ea9d59 -r cb5414f406eb libs/anim/anim.c --- a/libs/anim/anim.c Fri Jan 17 18:16:09 2014 +0200 +++ b/libs/anim/anim.c Fri Jan 17 18:27:47 2014 +0200 @@ -8,7 +8,7 @@ static void invalidate_cache(struct anm_node *node); -int anm_init_node(struct anm_node *node) +int anm_init_animation(struct anm_animation *anim) { int i, j; static const float defaults[] = { @@ -17,19 +17,59 @@ 1.0f, 1.0f, 1.0f /* default scale factor */ }; + anim->name = 0; + + for(i=0; itracks + i) == -1) { + for(j=0; jtracks + i); + } + } + anm_set_track_default(anim->tracks + i, defaults[i]); + } + return 0; +} + +void anm_destroy_animation(struct anm_animation *anim) +{ + int i; + for(i=0; itracks + i); + } + free(anim->name); +} + +void anm_set_animation_name(struct anm_animation *anim, const char *name) +{ + char *newname = malloc(strlen(name) + 1); + if(!newname) return; + + strcpy(newname, name); + + free(anim->name); + anim->name = newname; +} + +/* ---- node implementation ----- */ + +int anm_init_node(struct anm_node *node) +{ memset(node, 0, sizeof *node); - for(i=0; itracks + i) == -1) { - for(j=0; jtracks + i); - } - } - anm_set_track_default(node->tracks + i, defaults[i]); + node->cur_anim[1] = -1; + + if(!(node->animations = dynarr_alloc(1, sizeof *node->animations))) { + return -1; + } + if(anm_init_animation(node->animations) == -1) { + dynarr_free(node->animations); + return -1; } - node->cache.time = ANM_TIME_INVAL; - node->cache.inv_time = ANM_TIME_INVAL; + /* initialize thread-local matrix cache */ + pthread_key_create(&node->cache_key, 0); + pthread_mutex_init(&node->cache_list_lock, 0); + return 0; } @@ -39,7 +79,17 @@ free(node->name); for(i=0; itracks + i); + anm_destroy_animation(node->animations + i); + } + dynarr_free(node->animations); + + /* destroy thread-specific cache */ + pthread_key_delete(node->cache_key); + + while(node->cache_list) { + struct mat_cache *tmp = node->cache_list; + node->cache_list = tmp->next; + free(tmp); } } @@ -113,26 +163,6 @@ return node->name ? node->name : ""; } -void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in) -{ - int i; - - for(i=0; itracks + i, in); - } - invalidate_cache(node); -} - -void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) -{ - int i; - - for(i=0; itracks + i, ex); - } - invalidate_cache(node); -} - void anm_link_node(struct anm_node *p, struct anm_node *c) { c->next = p->child; @@ -165,40 +195,398 @@ return -1; } +void anm_set_pivot(struct anm_node *node, vec3_t piv) +{ + node->pivot = piv; +} + +vec3_t anm_get_pivot(struct anm_node *node) +{ + return node->pivot; +} + + +/* animation management */ + +int anm_use_node_animation(struct anm_node *node, int aidx) +{ + if(aidx == node->cur_anim[0] && node->cur_anim[1] == -1) { + return 0; /* no change, no invalidation */ + } + + if(aidx < 0 || aidx >= anm_get_animation_count(node)) { + return -1; + } + + node->cur_anim[0] = aidx; + node->cur_anim[1] = -1; + node->cur_mix = 0; + node->blend_dur = -1; + + invalidate_cache(node); + return 0; +} + +int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t) +{ + int num_anim; + + if(node->cur_anim[0] == aidx && node->cur_anim[1] == bidx && + fabs(t - node->cur_mix) < 1e-6) { + return 0; /* no change, no invalidation */ + } + + num_anim = anm_get_animation_count(node); + if(aidx < 0 || aidx >= num_anim) { + return anm_use_animation(node, bidx); + } + if(bidx < 0 || bidx >= num_anim) { + return anm_use_animation(node, aidx); + } + node->cur_anim[0] = aidx; + node->cur_anim[1] = bidx; + node->cur_mix = t; + + invalidate_cache(node); + return 0; +} + +int anm_use_animation(struct anm_node *node, int aidx) +{ + struct anm_node *child; + + if(anm_use_node_animation(node, aidx) == -1) { + return -1; + } + + child = node->child; + while(child) { + if(anm_use_animation(child, aidx) == -1) { + return -1; + } + child = child->next; + } + return 0; +} + +int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t) +{ + struct anm_node *child; + + if(anm_use_node_animations(node, aidx, bidx, t) == -1) { + return -1; + } + + child = node->child; + while(child) { + if(anm_use_animations(child, aidx, bidx, t) == -1) { + return -1; + } + child = child->next; + } + return 0; + +} + +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which) +{ + if(which < 0 || which >= 2) { + return; + } + node->cur_anim_offset[which] = offs; +} + +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which) +{ + if(which < 0 || which >= 2) { + return 0; + } + return node->cur_anim_offset[which]; +} + +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which) +{ + struct anm_node *c = node->child; + while(c) { + anm_set_animation_offset(c, offs, which); + c = c->next; + } + + anm_set_node_animation_offset(node, offs, which); +} + +int anm_get_active_animation_index(const struct anm_node *node, int which) +{ + if(which < 0 || which >= 2) return -1; + return node->cur_anim[which]; +} + +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which) +{ + int idx = anm_get_active_animation_index(node, which); + if(idx < 0 || idx >= anm_get_animation_count(node)) { + return 0; + } + return node->animations + idx; +} + +float anm_get_active_animation_mix(const struct anm_node *node) +{ + return node->cur_mix; +} + +int anm_get_animation_count(const struct anm_node *node) +{ + return dynarr_size(node->animations); +} + +int anm_add_node_animation(struct anm_node *node) +{ + struct anm_animation newanim; + anm_init_animation(&newanim); + + node->animations = dynarr_push(node->animations, &newanim); + return 0; +} + +int anm_remove_node_animation(struct anm_node *node, int idx) +{ + fprintf(stderr, "anm_remove_animation: unimplemented!"); + abort(); + return 0; +} + +int anm_add_animation(struct anm_node *node) +{ + struct anm_node *child; + + if(anm_add_node_animation(node) == -1) { + return -1; + } + + child = node->child; + while(child) { + if(anm_add_animation(child)) { + return -1; + } + child = child->next; + } + return 0; +} + +int anm_remove_animation(struct anm_node *node, int idx) +{ + struct anm_node *child; + + if(anm_remove_node_animation(node, idx) == -1) { + return -1; + } + + child = node->child; + while(child) { + if(anm_remove_animation(child, idx) == -1) { + return -1; + } + child = child->next; + } + return 0; +} + +struct anm_animation *anm_get_animation(struct anm_node *node, int idx) +{ + if(idx < 0 || idx > anm_get_animation_count(node)) { + return 0; + } + return node->animations + idx; +} + +struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name) +{ + return anm_get_animation(node, anm_find_animation(node, name)); +} + +int anm_find_animation(struct anm_node *node, const char *name) +{ + int i, count = anm_get_animation_count(node); + for(i=0; ianimations[i].name, name) == 0) { + return i; + } + } + return -1; +} + +/* all the rest act on the current animation(s) */ + +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in) +{ + int i; + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + for(i=0; itracks + i, in); + } + invalidate_cache(node); +} + +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) +{ + int i; + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + for(i=0; itracks + i, ex); + } + invalidate_cache(node); +} + +void anm_set_node_active_animation_name(struct anm_node *node, const char *name) +{ + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + anm_set_animation_name(anim, name); +} + +void anm_set_active_animation_name(struct anm_node *node, const char *name) +{ + struct anm_node *child; + + anm_set_node_active_animation_name(node, name); + + child = node->child; + while(child) { + anm_set_active_animation_name(child, name); + child = child->next; + } +} + +const char *anm_get_active_animation_name(struct anm_node *node) +{ + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(anim) { + return anim->name; + } + return 0; +} + +/* ---- high level animation blending ---- */ +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur) +{ + struct anm_node *c = node->child; + + if(anmidx == node->cur_anim[0]) { + return; + } + + while(c) { + anm_transition(c, anmidx, start, dur); + c = c->next; + } + + anm_node_transition(node, anmidx, start, dur); +} + +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur) +{ + if(anmidx == node->cur_anim[0]) { + return; + } + + node->cur_anim[1] = anmidx; + node->cur_anim_offset[1] = start; + node->blend_dur = dur; +} + + +#define BLEND_START_TM node->cur_anim_offset[1] + +static anm_time_t animation_time(struct anm_node *node, anm_time_t tm, int which) +{ + float t; + + if(node->blend_dur >= 0) { + /* we're in transition... */ + t = (float)(tm - BLEND_START_TM) / (float)node->blend_dur; + if(t < 0.0) t = 0.0; + + node->cur_mix = t; + + if(t > 1.0) { + /* switch completely over to the target animation and stop blending */ + anm_use_node_animation(node, node->cur_anim[1]); + node->cur_anim_offset[0] = node->cur_anim_offset[1]; + } + } + + return tm - node->cur_anim_offset[which]; +} + + void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm) { - anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x); - anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y); - anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z); + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + anm_set_value(anim->tracks + ANM_TRACK_POS_X, tm, pos.x); + anm_set_value(anim->tracks + ANM_TRACK_POS_Y, tm, pos.y); + anm_set_value(anim->tracks + ANM_TRACK_POS_Z, tm, pos.z); invalidate_cache(node); } + vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm) { vec3_t v; - v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm); - v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm); - v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm); + anm_time_t tm0 = animation_time(node, tm, 0); + struct anm_animation *anim0 = anm_get_active_animation(node, 0); + struct anm_animation *anim1 = anm_get_active_animation(node, 1); + + if(!anim0) { + return v3_cons(0, 0, 0); + } + + v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm0); + v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm0); + v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm0); + + if(anim1) { + vec3_t v1; + anm_time_t tm1 = animation_time(node, tm, 1); + v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm1); + v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm1); + v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm1); + + v.x = v.x + (v1.x - v.x) * node->cur_mix; + v.y = v.y + (v1.y - v.y) * node->cur_mix; + v.z = v.z + (v1.z - v.z) * node->cur_mix; + } + return v; } void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm) { - anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x); - anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y); - anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z); - anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w); + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + anm_set_value(anim->tracks + ANM_TRACK_ROT_X, tm, rot.x); + anm_set_value(anim->tracks + ANM_TRACK_ROT_Y, tm, rot.y); + anm_set_value(anim->tracks + ANM_TRACK_ROT_Z, tm, rot.z); + anm_set_value(anim->tracks + ANM_TRACK_ROT_W, tm, rot.w); invalidate_cache(node); } -quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) +static quat_t get_node_rotation(struct anm_node *node, anm_time_t tm, struct anm_animation *anim) { #ifndef ROT_USE_SLERP quat_t q; - q.x = anm_get_value(node->tracks + ANM_TRACK_ROT_X, tm); - q.y = anm_get_value(node->tracks + ANM_TRACK_ROT_Y, tm); - q.z = anm_get_value(node->tracks + ANM_TRACK_ROT_Z, tm); - q.w = anm_get_value(node->tracks + ANM_TRACK_ROT_W, tm); + q.x = anm_get_value(anim->tracks + ANM_TRACK_ROT_X, tm); + q.y = anm_get_value(anim->tracks + ANM_TRACK_ROT_Y, tm); + q.z = anm_get_value(anim->tracks + ANM_TRACK_ROT_Z, tm); + q.w = anm_get_value(anim->tracks + ANM_TRACK_ROT_W, tm); return q; #else int idx0, idx1, last_idx; @@ -207,10 +595,10 @@ struct anm_track *track_x, *track_y, *track_z, *track_w; quat_t q, q1, q2; - track_x = node->tracks + ANM_TRACK_ROT_X; - track_y = node->tracks + ANM_TRACK_ROT_Y; - track_z = node->tracks + ANM_TRACK_ROT_Z; - track_w = node->tracks + ANM_TRACK_ROT_W; + track_x = anim->tracks + ANM_TRACK_ROT_X; + track_y = anim->tracks + ANM_TRACK_ROT_Y; + track_z = anim->tracks + ANM_TRACK_ROT_Z; + track_w = anim->tracks + ANM_TRACK_ROT_W; if(!track_x->count) { q.x = track_x->def_val; @@ -267,20 +655,66 @@ #endif } +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) +{ + quat_t q; + anm_time_t tm0 = animation_time(node, tm, 0); + struct anm_animation *anim0 = anm_get_active_animation(node, 0); + struct anm_animation *anim1 = anm_get_active_animation(node, 1); + + if(!anim0) { + return quat_identity(); + } + + q = get_node_rotation(node, tm0, anim0); + + if(anim1) { + anm_time_t tm1 = animation_time(node, tm, 1); + quat_t q1 = get_node_rotation(node, tm1, anim1); + + q = quat_slerp(q, q1, node->cur_mix); + } + return q; +} + void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm) { - anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x); - anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y); - anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z); + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + anm_set_value(anim->tracks + ANM_TRACK_SCL_X, tm, scl.x); + anm_set_value(anim->tracks + ANM_TRACK_SCL_Y, tm, scl.y); + anm_set_value(anim->tracks + ANM_TRACK_SCL_Z, tm, scl.z); invalidate_cache(node); } vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm) { vec3_t v; - v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm); - v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm); - v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm); + anm_time_t tm0 = animation_time(node, tm, 0); + struct anm_animation *anim0 = anm_get_active_animation(node, 0); + struct anm_animation *anim1 = anm_get_active_animation(node, 1); + + if(!anim0) { + return v3_cons(1, 1, 1); + } + + v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm0); + v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm0); + v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm0); + + if(anim1) { + vec3_t v1; + anm_time_t tm1 = animation_time(node, tm, 1); + v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm1); + v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm1); + v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm1); + + v.x = v.x + (v1.x - v.x) * node->cur_mix; + v.y = v.y + (v1.y - v.y) * node->cur_mix; + v.z = v.z + (v1.z - v.z) * node->cur_mix; + } + return v; } @@ -324,16 +758,6 @@ return v3_mul(s, ps); } -void anm_set_pivot(struct anm_node *node, vec3_t piv) -{ - node->pivot = piv; -} - -vec3_t anm_get_pivot(struct anm_node *node) -{ - return node->pivot; -} - void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) { int i; @@ -371,43 +795,102 @@ m4_inverse(mat, tmp); } +void anm_eval_node(struct anm_node *node, anm_time_t tm) +{ + anm_get_node_matrix(node, node->matrix, tm); +} + +void anm_eval(struct anm_node *node, anm_time_t tm) +{ + struct anm_node *c; + + anm_eval_node(node, tm); + + if(node->parent) { + /* due to post-order traversal, the parent matrix is already evaluated */ + m4_mult(node->matrix, node->parent->matrix, node->matrix); + } + + /* recersively evaluate all children */ + c = node->child; + while(c) { + anm_eval(c, tm); + c = c->next; + } +} + void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) { - if(node->cache.time != tm) { - anm_get_node_matrix(node, node->cache.matrix, tm); + struct mat_cache *cache = pthread_getspecific(node->cache_key); + if(!cache) { + cache = malloc(sizeof *cache); + assert(cache); + + pthread_mutex_lock(&node->cache_list_lock); + cache->next = node->cache_list; + node->cache_list = cache; + pthread_mutex_unlock(&node->cache_list_lock); + + cache->time = ANM_TIME_INVAL; + cache->inv_time = ANM_TIME_INVAL; + pthread_setspecific(node->cache_key, cache); + } + + if(cache->time != tm) { + anm_get_node_matrix(node, cache->matrix, tm); if(node->parent) { mat4_t parent_mat; anm_get_matrix(node->parent, parent_mat, tm); - m4_mult(node->cache.matrix, parent_mat, node->cache.matrix); + m4_mult(cache->matrix, parent_mat, cache->matrix); } - node->cache.time = tm; + cache->time = tm; } - m4_copy(mat, node->cache.matrix); + m4_copy(mat, cache->matrix); } void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) { - if(node->cache.inv_time != tm) { + struct mat_cache *cache = pthread_getspecific(node->cache_key); + if(!cache) { + cache = malloc(sizeof *cache); + assert(cache); + + pthread_mutex_lock(&node->cache_list_lock); + cache->next = node->cache_list; + node->cache_list = cache; + pthread_mutex_unlock(&node->cache_list_lock); + + cache->inv_time = ANM_TIME_INVAL; + cache->inv_time = ANM_TIME_INVAL; + pthread_setspecific(node->cache_key, cache); + } + + if(cache->inv_time != tm) { anm_get_matrix(node, mat, tm); - m4_inverse(node->cache.inv_matrix, mat); - node->cache.inv_time = tm; + m4_inverse(cache->inv_matrix, mat); + cache->inv_time = tm; } - m4_copy(mat, node->cache.inv_matrix); + m4_copy(mat, cache->inv_matrix); } anm_time_t anm_get_start_time(struct anm_node *node) { - int i; + int i, j; struct anm_node *c; anm_time_t res = LONG_MAX; - for(i=0; itracks[i].count) { - anm_time_t tm = node->tracks[i].keys[0].time; - if(tm < res) { - res = tm; + for(j=0; j<2; j++) { + struct anm_animation *anim = anm_get_active_animation(node, j); + if(!anim) break; + + for(i=0; itracks[i].count) { + anm_time_t tm = anim->tracks[i].keys[0].time; + if(tm < res) { + res = tm; + } } } } @@ -425,15 +908,20 @@ anm_time_t anm_get_end_time(struct anm_node *node) { - int i; + int i, j; struct anm_node *c; anm_time_t res = LONG_MIN; - for(i=0; itracks[i].count) { - anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time; - if(tm > res) { - res = tm; + for(j=0; j<2; j++) { + struct anm_animation *anim = anm_get_active_animation(node, j); + if(!anim) break; + + for(i=0; itracks[i].count) { + anm_time_t tm = anim->tracks[i].keys[anim->tracks[i].count - 1].time; + if(tm > res) { + res = tm; + } } } } @@ -451,5 +939,8 @@ static void invalidate_cache(struct anm_node *node) { - node->cache.time = node->cache.inv_time = ANM_TIME_INVAL; + struct mat_cache *cache = pthread_getspecific(node->cache_key); + if(cache) { + cache->time = cache->inv_time = ANM_TIME_INVAL; + } } diff -r fa5c52ea9d59 -r cb5414f406eb libs/anim/anim.h --- a/libs/anim/anim.h Fri Jan 17 18:16:09 2014 +0200 +++ b/libs/anim/anim.h Fri Jan 17 18:27:47 2014 +0200 @@ -3,6 +3,8 @@ #include "config.h" +#include + #include #include #include @@ -25,17 +27,35 @@ ANM_NUM_TRACKS }; +struct anm_animation { + char *name; + struct anm_track tracks[ANM_NUM_TRACKS]; +}; + struct anm_node { char *name; - struct anm_track tracks[ANM_NUM_TRACKS]; + int cur_anim[2]; + anm_time_t cur_anim_offset[2]; + float cur_mix; + + /* high-level animation blending transition duration */ + anm_time_t blend_dur; + + struct anm_animation *animations; vec3_t pivot; /* matrix cache */ struct mat_cache { mat4_t matrix, inv_matrix; anm_time_t time, inv_time; - } cache; + struct mat_cache *next; + } *cache_list; + pthread_key_t cache_key; + pthread_mutex_t cache_list_lock; + + /* matrix calculated by anm_eval functions (no locking, meant as a pre-pass) */ + mat4_t matrix; struct anm_node *parent; struct anm_node *child; @@ -46,6 +66,14 @@ extern "C" { #endif +int anm_init_animation(struct anm_animation *anim); +void anm_destroy_animation(struct anm_animation *anim); + +void anm_set_animation_name(struct anm_animation *anim, const char *name); + + +/* ---- node/hierarchy management ---- */ + /* node constructor and destructor */ int anm_init_node(struct anm_node *node); void anm_destroy_node(struct anm_node *node); @@ -66,13 +94,82 @@ int anm_set_node_name(struct anm_node *node, const char *name); const char *anm_get_node_name(struct anm_node *node); -void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in); -void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex); - /* link and unlink nodes with parent/child relations */ void anm_link_node(struct anm_node *parent, struct anm_node *child); int anm_unlink_node(struct anm_node *parent, struct anm_node *child); +void anm_set_pivot(struct anm_node *node, vec3_t pivot); +vec3_t anm_get_pivot(struct anm_node *node); + +/* ---- multiple animations and animation blending ---- */ + +/* set active animation(s) */ +int anm_use_node_animation(struct anm_node *node, int aidx); +int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t); +/* recursive variants */ +int anm_use_animation(struct anm_node *node, int aidx); +int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t); + +/* set/get current animation offset(s) */ +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which); +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which); +/* recursive variant */ +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which); + +/* returns the requested current animation index, which can be 0 or 1 */ +int anm_get_active_animation_index(const struct anm_node *node, int which); +/* returns the requested current animation, which can be 0 or 1 */ +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which); +float anm_get_active_animation_mix(const struct anm_node *node); + +int anm_get_animation_count(const struct anm_node *node); + +/* add/remove an animation to the specified node */ +int anm_add_node_animation(struct anm_node *node); +int anm_remove_node_animation(struct anm_node *node, int idx); + +/* add/remove an animation to the specified node and all it's descendants */ +int anm_add_animation(struct anm_node *node); +int anm_remove_animation(struct anm_node *node, int idx); + +struct anm_animation *anm_get_animation(struct anm_node *node, int idx); +struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name); + +int anm_find_animation(struct anm_node *node, const char *name); + +/* set the interpolator for the (first) currently active animation */ +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in); +/* set the extrapolator for the (first) currently active animation */ +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex); + +/* set the name of the currently active animation of this node only */ +void anm_set_node_active_animation_name(struct anm_node *node, const char *name); +/* recursively set the name of the currently active animation for this node + * and all it's descendants */ +void anm_set_active_animation_name(struct anm_node *node, const char *name); +/* get the name of the currently active animation of this node */ +const char *anm_get_active_animation_name(struct anm_node *node); + + +/* ---- high level animation blending interface ---- */ +/* XXX this convenience interface assumes monotonically increasing time values + * in all subsequent calls to anm_get_* and anm_eval_* functions. + * + * anmidx: index of the animation to transition to + * start: when to start the transition + * dur: transition duration + * + * sets up a transition from the current animation (cur_anim[0]) to another animation. + * at time start + dur, the transition will be completed, cur_anim[0] will be the new + * animation and cur_anim_offset[0] will be equal to start. + */ +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur); +/* non-recursive variant, acts on a single node (you probably DON'T want to use this) */ +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur); + + +/* ---- keyframes / PRS interpolation ---- */ + void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm); vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm); @@ -87,13 +184,27 @@ quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm); vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm); -void anm_set_pivot(struct anm_node *node, vec3_t pivot); -vec3_t anm_get_pivot(struct anm_node *node); +/* those return the start and end times of the whole tree */ +anm_time_t anm_get_start_time(struct anm_node *node); +anm_time_t anm_get_end_time(struct anm_node *node); + + +/* ---- transformation matrices ---- */ /* these calculate the matrix and inverse matrix of this node alone */ void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); +/* ---- top-down matrix calculation interface ---- */ + +/* calculate and set the matrix of this node */ +void anm_eval_node(struct anm_node *node, anm_time_t tm); +/* calculate and set the matrix of this node and all its children recursively */ +void anm_eval(struct anm_node *node, anm_time_t tm); + + +/* ---- bottom-up lazy matrix calculation interface ---- */ + /* These calculate the matrix and inverse matrix of this node taking hierarchy * into account. The results are cached in thread-specific storage and returned * if there's no change in time or tracks from the last query... @@ -101,10 +212,6 @@ void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); -/* those return the start and end times of the whole tree */ -anm_time_t anm_get_start_time(struct anm_node *node); -anm_time_t anm_get_end_time(struct anm_node *node); - #ifdef __cplusplus } #endif diff -r fa5c52ea9d59 -r cb5414f406eb libs/anim/dynarr.h --- a/libs/anim/dynarr.h Fri Jan 17 18:16:09 2014 +0200 +++ b/libs/anim/dynarr.h Fri Jan 17 18:27:47 2014 +0200 @@ -1,8 +1,6 @@ #ifndef DYNARR_H_ #define DYNARR_H_ -#include "config.h" - void *dynarr_alloc(int elem, int szelem); void dynarr_free(void *da); void *dynarr_resize(void *da, int elem); diff -r fa5c52ea9d59 -r cb5414f406eb libs/anim/track.c --- a/libs/anim/track.c Fri Jan 17 18:16:09 2014 +0200 +++ b/libs/anim/track.c Fri Jan 17 18:27:47 2014 +0200 @@ -69,7 +69,7 @@ free(track); } -void anm_copy_track(struct anm_track *dest, struct anm_track *src) +void anm_copy_track(struct anm_track *dest, const struct anm_track *src) { free(dest->name); if(dest->keys) { diff -r fa5c52ea9d59 -r cb5414f406eb libs/anim/track.h --- a/libs/anim/track.h Fri Jan 17 18:16:09 2014 +0200 +++ b/libs/anim/track.h Fri Jan 17 18:27:47 2014 +0200 @@ -59,7 +59,7 @@ /* copies track src to dest * XXX: dest must have been initialized first */ -void anm_copy_track(struct anm_track *dest, struct anm_track *src); +void anm_copy_track(struct anm_track *dest, const struct anm_track *src); int anm_set_track_name(struct anm_track *track, const char *name); const char *anm_get_track_name(struct anm_track *track); diff -r fa5c52ea9d59 -r cb5414f406eb src/goat3d_read.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/goat3d_read.cc Fri Jan 17 18:27:47 2014 +0200 @@ -0,0 +1,13 @@ +#include "goat3d_impl.h" + +using namespace g3dimpl; + +bool Scene::load(goat3d_io *io) +{ + return false; +} + +bool Scene::load_anim(goat3d_io *io) +{ + return false; +} diff -r fa5c52ea9d59 -r cb5414f406eb src/goat3d_readxml.cc --- a/src/goat3d_readxml.cc Fri Jan 17 18:16:09 2014 +0200 +++ b/src/goat3d_readxml.cc Fri Jan 17 18:27:47 2014 +0200 @@ -110,6 +110,12 @@ return true; } +bool Scene::load_anim_xml(goat3d_io *io) +{ + return false; +} + + static Material *read_material(Scene *scn, XMLElement *xml_mtl) { Material *mtl = new Material; diff -r fa5c52ea9d59 -r cb5414f406eb src/goat3d_write.cc --- a/src/goat3d_write.cc Fri Jan 17 18:16:09 2014 +0200 +++ b/src/goat3d_write.cc Fri Jan 17 18:27:47 2014 +0200 @@ -225,3 +225,8 @@ return size; } #endif + +bool Scene::save_anim(const XFormNode *node, goat3d_io *io) const +{ + return false; +} diff -r fa5c52ea9d59 -r cb5414f406eb src/scene.cc --- a/src/scene.cc Fri Jan 17 18:16:09 2014 +0200 +++ b/src/scene.cc Fri Jan 17 18:27:47 2014 +0200 @@ -204,12 +204,7 @@ return (int)nodes.size(); } - -bool Scene::load(goat3d_io *io) -{ - return false; -} - +// Scene::load is defined in goat3d_read.cc // Scene::loadxml is defined in goat3d_readxml.cc // Scene::save is defined in goat3d_write.cc // Scene::savexml is defined in goat3d_writexml.cc