nuclear@1: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "anim.h" nuclear@0: #include "dynarr.h" nuclear@0: nuclear@7: #define ROT_USE_SLERP nuclear@7: nuclear@0: static void invalidate_cache(struct anm_node *node); nuclear@0: nuclear@21: int anm_init_animation(struct anm_animation *anim) nuclear@0: { nuclear@0: int i, j; nuclear@0: static const float defaults[] = { nuclear@0: 0.0f, 0.0f, 0.0f, /* default position */ nuclear@0: 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */ nuclear@0: 1.0f, 1.0f, 1.0f /* default scale factor */ nuclear@0: }; nuclear@0: nuclear@21: anim->name = 0; nuclear@21: nuclear@21: for(i=0; itracks + i) == -1) { nuclear@21: for(j=0; jtracks + i); nuclear@21: } nuclear@21: } nuclear@21: anm_set_track_default(anim->tracks + i, defaults[i]); nuclear@21: } nuclear@21: return 0; nuclear@21: } nuclear@21: nuclear@21: void anm_destroy_animation(struct anm_animation *anim) nuclear@21: { nuclear@21: int i; nuclear@21: for(i=0; itracks + i); nuclear@21: } nuclear@21: free(anim->name); nuclear@21: } nuclear@21: nuclear@22: void anm_set_animation_name(struct anm_animation *anim, const char *name) nuclear@22: { nuclear@22: char *newname = malloc(strlen(name) + 1); nuclear@22: if(!newname) return; nuclear@22: nuclear@23: strcpy(newname, name); nuclear@23: nuclear@22: free(anim->name); nuclear@22: anim->name = newname; nuclear@22: } nuclear@22: nuclear@21: /* ---- node implementation ----- */ nuclear@21: nuclear@21: int anm_init_node(struct anm_node *node) nuclear@21: { nuclear@0: memset(node, 0, sizeof *node); nuclear@0: nuclear@21: node->cur_anim[1] = -1; nuclear@21: nuclear@21: if(!(node->animations = dynarr_alloc(1, sizeof *node->animations))) { nuclear@21: return -1; nuclear@21: } nuclear@21: if(anm_init_animation(node->animations) == -1) { nuclear@21: dynarr_free(node->animations); nuclear@21: return -1; nuclear@21: } nuclear@21: nuclear@0: /* initialize thread-local matrix cache */ nuclear@0: pthread_key_create(&node->cache_key, 0); nuclear@10: pthread_mutex_init(&node->cache_list_lock, 0); nuclear@0: nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void anm_destroy_node(struct anm_node *node) nuclear@0: { nuclear@0: int i; nuclear@0: free(node->name); nuclear@0: nuclear@0: for(i=0; ianimations + i); nuclear@0: } nuclear@21: dynarr_free(node->animations); nuclear@0: nuclear@0: /* destroy thread-specific cache */ nuclear@0: pthread_key_delete(node->cache_key); nuclear@0: nuclear@0: while(node->cache_list) { nuclear@0: struct mat_cache *tmp = node->cache_list; nuclear@0: node->cache_list = tmp->next; nuclear@0: free(tmp); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void anm_destroy_node_tree(struct anm_node *tree) nuclear@0: { nuclear@0: struct anm_node *c, *tmp; nuclear@0: nuclear@0: if(!tree) return; nuclear@0: nuclear@0: c = tree->child; nuclear@0: while(c) { nuclear@0: tmp = c; nuclear@0: c = c->next; nuclear@0: nuclear@0: anm_destroy_node_tree(tmp); nuclear@0: } nuclear@0: anm_destroy_node(tree); nuclear@0: } nuclear@0: nuclear@0: struct anm_node *anm_create_node(void) nuclear@0: { nuclear@0: struct anm_node *n; nuclear@0: nuclear@0: if((n = malloc(sizeof *n))) { nuclear@0: if(anm_init_node(n) == -1) { nuclear@0: free(n); nuclear@0: return 0; nuclear@0: } nuclear@0: } nuclear@0: return n; nuclear@0: } nuclear@0: nuclear@0: void anm_free_node(struct anm_node *node) nuclear@0: { nuclear@0: anm_destroy_node(node); nuclear@0: free(node); nuclear@0: } nuclear@0: nuclear@0: void anm_free_node_tree(struct anm_node *tree) nuclear@0: { nuclear@0: struct anm_node *c, *tmp; nuclear@0: nuclear@0: if(!tree) return; nuclear@0: nuclear@0: c = tree->child; nuclear@0: while(c) { nuclear@0: tmp = c; nuclear@0: c = c->next; nuclear@0: nuclear@0: anm_free_node_tree(tmp); nuclear@0: } nuclear@0: nuclear@0: anm_free_node(tree); nuclear@0: } nuclear@0: nuclear@0: int anm_set_node_name(struct anm_node *node, const char *name) nuclear@0: { nuclear@0: char *str; nuclear@0: nuclear@0: if(!(str = malloc(strlen(name) + 1))) { nuclear@0: return -1; nuclear@0: } nuclear@0: strcpy(str, name); nuclear@0: free(node->name); nuclear@0: node->name = str; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: const char *anm_get_node_name(struct anm_node *node) nuclear@0: { nuclear@0: return node->name ? node->name : ""; nuclear@0: } nuclear@0: nuclear@0: void anm_link_node(struct anm_node *p, struct anm_node *c) nuclear@0: { nuclear@0: c->next = p->child; nuclear@0: p->child = c; nuclear@0: nuclear@0: c->parent = p; nuclear@0: invalidate_cache(c); nuclear@0: } nuclear@0: nuclear@0: int anm_unlink_node(struct anm_node *p, struct anm_node *c) nuclear@0: { nuclear@0: struct anm_node *iter; nuclear@0: nuclear@0: if(p->child == c) { nuclear@0: p->child = c->next; nuclear@0: c->next = 0; nuclear@0: invalidate_cache(c); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: iter = p->child; nuclear@0: while(iter->next) { nuclear@0: if(iter->next == c) { nuclear@0: iter->next = c->next; nuclear@0: c->next = 0; nuclear@0: invalidate_cache(c); nuclear@0: return 0; nuclear@0: } nuclear@0: } nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@21: void anm_set_pivot(struct anm_node *node, vec3_t piv) nuclear@21: { nuclear@21: node->pivot = piv; nuclear@21: } nuclear@21: nuclear@21: vec3_t anm_get_pivot(struct anm_node *node) nuclear@21: { nuclear@21: return node->pivot; nuclear@21: } nuclear@21: nuclear@21: nuclear@21: /* animation management */ nuclear@21: nuclear@21: int anm_use_node_animation(struct anm_node *node, int aidx) nuclear@21: { nuclear@21: if(aidx == node->cur_anim[0] && node->cur_anim[1] == -1) { nuclear@21: return 0; /* no change, no invalidation */ nuclear@21: } nuclear@21: nuclear@21: if(aidx < 0 || aidx >= anm_get_animation_count(node)) { nuclear@21: return -1; nuclear@21: } nuclear@21: nuclear@21: node->cur_anim[0] = aidx; nuclear@21: node->cur_anim[1] = -1; nuclear@21: node->cur_mix = 0; nuclear@24: node->blend_dur = -1; nuclear@21: nuclear@21: invalidate_cache(node); nuclear@21: return 0; nuclear@21: } nuclear@21: nuclear@21: int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t) nuclear@21: { nuclear@21: int num_anim; nuclear@21: nuclear@21: if(node->cur_anim[0] == aidx && node->cur_anim[1] == bidx && nuclear@21: fabs(t - node->cur_mix) < 1e-6) { nuclear@21: return 0; /* no change, no invalidation */ nuclear@21: } nuclear@21: nuclear@21: num_anim = anm_get_animation_count(node); nuclear@21: if(aidx < 0 || aidx >= num_anim) { nuclear@21: return anm_use_animation(node, bidx); nuclear@21: } nuclear@21: if(bidx < 0 || bidx >= num_anim) { nuclear@21: return anm_use_animation(node, aidx); nuclear@21: } nuclear@21: node->cur_anim[0] = aidx; nuclear@21: node->cur_anim[1] = bidx; nuclear@21: node->cur_mix = t; nuclear@21: nuclear@21: invalidate_cache(node); nuclear@21: return 0; nuclear@21: } nuclear@21: nuclear@21: int anm_use_animation(struct anm_node *node, int aidx) nuclear@21: { nuclear@21: struct anm_node *child; nuclear@21: nuclear@21: if(anm_use_node_animation(node, aidx) == -1) { nuclear@21: return -1; nuclear@21: } nuclear@21: nuclear@21: child = node->child; nuclear@21: while(child) { nuclear@21: if(anm_use_animation(child, aidx) == -1) { nuclear@21: return -1; nuclear@21: } nuclear@21: child = child->next; nuclear@21: } nuclear@21: return 0; nuclear@21: } nuclear@21: nuclear@21: int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t) nuclear@21: { nuclear@21: struct anm_node *child; nuclear@21: nuclear@21: if(anm_use_node_animations(node, aidx, bidx, t) == -1) { nuclear@21: return -1; nuclear@21: } nuclear@21: nuclear@21: child = node->child; nuclear@21: while(child) { nuclear@21: if(anm_use_animations(child, aidx, bidx, t) == -1) { nuclear@21: return -1; nuclear@21: } nuclear@21: child = child->next; nuclear@21: } nuclear@21: return 0; nuclear@21: nuclear@21: } nuclear@21: nuclear@24: void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which) nuclear@24: { nuclear@24: if(which < 0 || which >= 2) { nuclear@24: return; nuclear@24: } nuclear@24: node->cur_anim_offset[which] = offs; nuclear@24: } nuclear@24: nuclear@24: anm_time_t anm_get_animation_offset(const struct anm_node *node, int which) nuclear@24: { nuclear@24: if(which < 0 || which >= 2) { nuclear@24: return 0; nuclear@24: } nuclear@24: return node->cur_anim_offset[which]; nuclear@24: } nuclear@24: nuclear@24: void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which) nuclear@24: { nuclear@24: struct anm_node *c = node->child; nuclear@24: while(c) { nuclear@24: anm_set_animation_offset(c, offs, which); nuclear@24: c = c->next; nuclear@24: } nuclear@24: nuclear@24: anm_set_node_animation_offset(node, offs, which); nuclear@24: } nuclear@24: nuclear@24: int anm_get_active_animation_index(const struct anm_node *node, int which) nuclear@21: { nuclear@21: if(which < 0 || which >= 2) return -1; nuclear@21: return node->cur_anim[which]; nuclear@21: } nuclear@21: nuclear@24: struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which) nuclear@21: { nuclear@21: int idx = anm_get_active_animation_index(node, which); nuclear@21: if(idx < 0 || idx >= anm_get_animation_count(node)) { nuclear@21: return 0; nuclear@21: } nuclear@21: return node->animations + idx; nuclear@21: } nuclear@21: nuclear@24: float anm_get_active_animation_mix(const struct anm_node *node) nuclear@21: { nuclear@21: return node->cur_mix; nuclear@21: } nuclear@21: nuclear@24: int anm_get_animation_count(const struct anm_node *node) nuclear@21: { nuclear@21: return dynarr_size(node->animations); nuclear@21: } nuclear@21: nuclear@21: int anm_add_node_animation(struct anm_node *node) nuclear@21: { nuclear@21: struct anm_animation newanim; nuclear@21: anm_init_animation(&newanim); nuclear@21: nuclear@21: node->animations = dynarr_push(node->animations, &newanim); nuclear@21: return 0; nuclear@21: } nuclear@21: nuclear@21: int anm_remove_node_animation(struct anm_node *node, int idx) nuclear@21: { nuclear@21: fprintf(stderr, "anm_remove_animation: unimplemented!"); nuclear@21: abort(); nuclear@21: return 0; nuclear@21: } nuclear@21: nuclear@21: int anm_add_animation(struct anm_node *node) nuclear@21: { nuclear@21: struct anm_node *child; nuclear@21: nuclear@21: if(anm_add_node_animation(node) == -1) { nuclear@21: return -1; nuclear@21: } nuclear@21: nuclear@21: child = node->child; nuclear@21: while(child) { nuclear@21: if(anm_add_animation(child)) { nuclear@21: return -1; nuclear@21: } nuclear@21: child = child->next; nuclear@21: } nuclear@21: return 0; nuclear@21: } nuclear@21: nuclear@21: int anm_remove_animation(struct anm_node *node, int idx) nuclear@21: { nuclear@21: struct anm_node *child; nuclear@21: nuclear@21: if(anm_remove_node_animation(node, idx) == -1) { nuclear@21: return -1; nuclear@21: } nuclear@21: nuclear@21: child = node->child; nuclear@21: while(child) { nuclear@21: if(anm_remove_animation(child, idx) == -1) { nuclear@21: return -1; nuclear@21: } nuclear@21: child = child->next; nuclear@21: } nuclear@21: return 0; nuclear@21: } nuclear@21: nuclear@21: struct anm_animation *anm_get_animation(struct anm_node *node, int idx) nuclear@21: { nuclear@21: if(idx < 0 || idx > anm_get_animation_count(node)) { nuclear@21: return 0; nuclear@21: } nuclear@21: return node->animations + idx; nuclear@21: } nuclear@21: nuclear@21: struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name) nuclear@21: { nuclear@21: return anm_get_animation(node, anm_find_animation(node, name)); nuclear@21: } nuclear@21: nuclear@21: int anm_find_animation(struct anm_node *node, const char *name) nuclear@21: { nuclear@21: int i, count = anm_get_animation_count(node); nuclear@21: for(i=0; ianimations[i].name, name) == 0) { nuclear@21: return i; nuclear@21: } nuclear@21: } nuclear@21: return -1; nuclear@21: } nuclear@21: nuclear@21: /* all the rest act on the current animation(s) */ nuclear@21: nuclear@21: void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in) nuclear@21: { nuclear@21: int i; nuclear@21: struct anm_animation *anim = anm_get_active_animation(node, 0); nuclear@21: if(!anim) return; nuclear@21: nuclear@21: for(i=0; itracks + i, in); nuclear@21: } nuclear@21: invalidate_cache(node); nuclear@21: } nuclear@21: nuclear@21: void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) nuclear@21: { nuclear@21: int i; nuclear@21: struct anm_animation *anim = anm_get_active_animation(node, 0); nuclear@21: if(!anim) return; nuclear@21: nuclear@21: for(i=0; itracks + i, ex); nuclear@21: } nuclear@21: invalidate_cache(node); nuclear@21: } nuclear@21: nuclear@23: void anm_set_node_active_animation_name(struct anm_node *node, const char *name) nuclear@23: { nuclear@23: struct anm_animation *anim = anm_get_active_animation(node, 0); nuclear@23: if(!anim) return; nuclear@23: nuclear@23: anm_set_animation_name(anim, name); nuclear@23: } nuclear@23: nuclear@23: void anm_set_active_animation_name(struct anm_node *node, const char *name) nuclear@23: { nuclear@23: struct anm_node *child; nuclear@23: nuclear@23: anm_set_node_active_animation_name(node, name); nuclear@23: nuclear@23: child = node->child; nuclear@23: while(child) { nuclear@23: anm_set_active_animation_name(child, name); nuclear@23: child = child->next; nuclear@23: } nuclear@23: } nuclear@23: nuclear@23: const char *anm_get_active_animation_name(struct anm_node *node) nuclear@23: { nuclear@23: struct anm_animation *anim = anm_get_active_animation(node, 0); nuclear@23: if(anim) { nuclear@23: return anim->name; nuclear@23: } nuclear@23: return 0; nuclear@23: } nuclear@23: nuclear@24: /* ---- high level animation blending ---- */ nuclear@24: void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur) nuclear@24: { nuclear@24: struct anm_node *c = node->child; nuclear@25: nuclear@25: if(anmidx == node->cur_anim[0]) { nuclear@25: return; nuclear@25: } nuclear@25: nuclear@24: while(c) { nuclear@24: anm_transition(c, anmidx, start, dur); nuclear@24: c = c->next; nuclear@24: } nuclear@24: nuclear@24: anm_node_transition(node, anmidx, start, dur); nuclear@24: } nuclear@24: nuclear@24: void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur) nuclear@24: { nuclear@25: if(anmidx == node->cur_anim[0]) { nuclear@25: return; nuclear@25: } nuclear@25: nuclear@24: node->cur_anim[1] = anmidx; nuclear@24: node->cur_anim_offset[1] = start; nuclear@24: node->blend_dur = dur; nuclear@24: } nuclear@24: nuclear@24: nuclear@24: #define BLEND_START_TM node->cur_anim_offset[1] nuclear@24: nuclear@24: static anm_time_t animation_time(struct anm_node *node, anm_time_t tm, int which) nuclear@24: { nuclear@24: float t; nuclear@24: nuclear@24: if(node->blend_dur >= 0) { nuclear@24: /* we're in transition... */ nuclear@24: t = (float)(tm - BLEND_START_TM) / (float)node->blend_dur; nuclear@24: if(t < 0.0) t = 0.0; nuclear@24: nuclear@24: node->cur_mix = t; nuclear@24: nuclear@24: if(t > 1.0) { nuclear@24: /* switch completely over to the target animation and stop blending */ nuclear@24: anm_use_node_animation(node, node->cur_anim[1]); nuclear@24: node->cur_anim_offset[0] = node->cur_anim_offset[1]; nuclear@24: } nuclear@24: } nuclear@24: nuclear@24: return tm - node->cur_anim_offset[which]; nuclear@24: } nuclear@24: nuclear@24: nuclear@0: void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm) nuclear@0: { nuclear@21: struct anm_animation *anim = anm_get_active_animation(node, 0); nuclear@21: if(!anim) return; nuclear@21: nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_POS_X, tm, pos.x); nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_POS_Y, tm, pos.y); nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_POS_Z, tm, pos.z); nuclear@0: invalidate_cache(node); nuclear@0: } nuclear@0: nuclear@24: nuclear@0: vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm) nuclear@0: { nuclear@0: vec3_t v; nuclear@24: anm_time_t tm0 = animation_time(node, tm, 0); nuclear@21: struct anm_animation *anim0 = anm_get_active_animation(node, 0); nuclear@21: struct anm_animation *anim1 = anm_get_active_animation(node, 1); nuclear@21: nuclear@21: if(!anim0) { nuclear@21: return v3_cons(0, 0, 0); nuclear@21: } nuclear@21: nuclear@24: v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm0); nuclear@24: v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm0); nuclear@24: v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm0); nuclear@21: nuclear@21: if(anim1) { nuclear@21: vec3_t v1; nuclear@24: anm_time_t tm1 = animation_time(node, tm, 1); nuclear@24: v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm1); nuclear@24: v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm1); nuclear@24: v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm1); nuclear@21: nuclear@21: v.x = v.x + (v1.x - v.x) * node->cur_mix; nuclear@21: v.y = v.y + (v1.y - v.y) * node->cur_mix; nuclear@21: v.z = v.z + (v1.z - v.z) * node->cur_mix; nuclear@21: } nuclear@21: nuclear@0: return v; nuclear@0: } nuclear@0: nuclear@0: void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm) nuclear@0: { nuclear@21: struct anm_animation *anim = anm_get_active_animation(node, 0); nuclear@21: if(!anim) return; nuclear@21: nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_ROT_X, tm, rot.x); nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_ROT_Y, tm, rot.y); nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_ROT_Z, tm, rot.z); nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_ROT_W, tm, rot.w); nuclear@0: invalidate_cache(node); nuclear@0: } nuclear@0: nuclear@21: static quat_t get_node_rotation(struct anm_node *node, anm_time_t tm, struct anm_animation *anim) nuclear@0: { nuclear@7: #ifndef ROT_USE_SLERP nuclear@7: quat_t q; nuclear@21: q.x = anm_get_value(anim->tracks + ANM_TRACK_ROT_X, tm); nuclear@21: q.y = anm_get_value(anim->tracks + ANM_TRACK_ROT_Y, tm); nuclear@21: q.z = anm_get_value(anim->tracks + ANM_TRACK_ROT_Z, tm); nuclear@21: q.w = anm_get_value(anim->tracks + ANM_TRACK_ROT_W, tm); nuclear@7: return q; nuclear@7: #else nuclear@0: int idx0, idx1, last_idx; nuclear@0: anm_time_t tstart, tend; nuclear@0: float t, dt; nuclear@0: struct anm_track *track_x, *track_y, *track_z, *track_w; nuclear@0: quat_t q, q1, q2; nuclear@0: nuclear@21: track_x = anim->tracks + ANM_TRACK_ROT_X; nuclear@21: track_y = anim->tracks + ANM_TRACK_ROT_Y; nuclear@21: track_z = anim->tracks + ANM_TRACK_ROT_Z; nuclear@21: track_w = anim->tracks + ANM_TRACK_ROT_W; nuclear@0: nuclear@0: if(!track_x->count) { nuclear@0: q.x = track_x->def_val; nuclear@0: q.y = track_y->def_val; nuclear@0: q.z = track_z->def_val; nuclear@0: q.w = track_w->def_val; nuclear@0: return q; nuclear@0: } nuclear@0: nuclear@0: last_idx = track_x->count - 1; nuclear@0: nuclear@0: tstart = track_x->keys[0].time; nuclear@0: tend = track_x->keys[last_idx].time; nuclear@6: nuclear@6: if(tstart == tend) { nuclear@6: q.x = track_x->keys[0].val; nuclear@6: q.y = track_y->keys[0].val; nuclear@6: q.z = track_z->keys[0].val; nuclear@6: q.w = track_w->keys[0].val; nuclear@6: return q; nuclear@6: } nuclear@6: nuclear@0: tm = anm_remap_time(track_x, tm, tstart, tend); nuclear@0: nuclear@0: idx0 = anm_get_key_interval(track_x, tm); nuclear@0: assert(idx0 >= 0 && idx0 < track_x->count); nuclear@0: idx1 = idx0 + 1; nuclear@0: nuclear@6: if(idx0 == last_idx) { nuclear@6: q.x = track_x->keys[idx0].val; nuclear@6: q.y = track_y->keys[idx0].val; nuclear@6: q.z = track_z->keys[idx0].val; nuclear@6: q.w = track_w->keys[idx0].val; nuclear@6: return q; nuclear@6: } nuclear@6: nuclear@0: dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time); nuclear@0: t = (float)(tm - track_x->keys[idx0].time) / dt; nuclear@0: nuclear@0: q1.x = track_x->keys[idx0].val; nuclear@0: q1.y = track_y->keys[idx0].val; nuclear@0: q1.z = track_z->keys[idx0].val; nuclear@0: q1.w = track_w->keys[idx0].val; nuclear@0: nuclear@0: q2.x = track_x->keys[idx1].val; nuclear@0: q2.y = track_y->keys[idx1].val; nuclear@0: q2.z = track_z->keys[idx1].val; nuclear@0: q2.w = track_w->keys[idx1].val; nuclear@0: nuclear@9: /*q1 = quat_normalize(q1); nuclear@9: q2 = quat_normalize(q2);*/ nuclear@9: nuclear@0: return quat_slerp(q1, q2, t); nuclear@7: #endif nuclear@0: } nuclear@0: nuclear@21: quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) nuclear@21: { nuclear@21: quat_t q; nuclear@24: anm_time_t tm0 = animation_time(node, tm, 0); nuclear@21: struct anm_animation *anim0 = anm_get_active_animation(node, 0); nuclear@21: struct anm_animation *anim1 = anm_get_active_animation(node, 1); nuclear@21: nuclear@21: if(!anim0) { nuclear@21: return quat_identity(); nuclear@21: } nuclear@21: nuclear@24: q = get_node_rotation(node, tm0, anim0); nuclear@21: nuclear@21: if(anim1) { nuclear@24: anm_time_t tm1 = animation_time(node, tm, 1); nuclear@24: quat_t q1 = get_node_rotation(node, tm1, anim1); nuclear@21: nuclear@21: q = quat_slerp(q, q1, node->cur_mix); nuclear@21: } nuclear@21: return q; nuclear@21: } nuclear@21: nuclear@0: void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm) nuclear@0: { nuclear@21: struct anm_animation *anim = anm_get_active_animation(node, 0); nuclear@21: if(!anim) return; nuclear@21: nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_SCL_X, tm, scl.x); nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_SCL_Y, tm, scl.y); nuclear@21: anm_set_value(anim->tracks + ANM_TRACK_SCL_Z, tm, scl.z); nuclear@0: invalidate_cache(node); nuclear@0: } nuclear@0: nuclear@0: vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm) nuclear@0: { nuclear@0: vec3_t v; nuclear@24: anm_time_t tm0 = animation_time(node, tm, 0); nuclear@21: struct anm_animation *anim0 = anm_get_active_animation(node, 0); nuclear@21: struct anm_animation *anim1 = anm_get_active_animation(node, 1); nuclear@21: nuclear@21: if(!anim0) { nuclear@21: return v3_cons(1, 1, 1); nuclear@21: } nuclear@21: nuclear@24: v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm0); nuclear@24: v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm0); nuclear@24: v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm0); nuclear@21: nuclear@21: if(anim1) { nuclear@21: vec3_t v1; nuclear@24: anm_time_t tm1 = animation_time(node, tm, 1); nuclear@24: v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm1); nuclear@24: v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm1); nuclear@24: v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm1); nuclear@21: nuclear@21: v.x = v.x + (v1.x - v.x) * node->cur_mix; nuclear@21: v.y = v.y + (v1.y - v.y) * node->cur_mix; nuclear@21: v.z = v.z + (v1.z - v.z) * node->cur_mix; nuclear@21: } nuclear@21: nuclear@0: return v; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: vec3_t anm_get_position(struct anm_node *node, anm_time_t tm) nuclear@0: { nuclear@0: mat4_t xform; nuclear@0: vec3_t pos = {0.0, 0.0, 0.0}; nuclear@0: nuclear@0: if(!node->parent) { nuclear@0: return anm_get_node_position(node, tm); nuclear@0: } nuclear@0: nuclear@0: anm_get_matrix(node, xform, tm); nuclear@0: return v3_transform(pos, xform); nuclear@0: } nuclear@0: nuclear@0: quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm) nuclear@0: { nuclear@0: quat_t rot, prot; nuclear@0: rot = anm_get_node_rotation(node, tm); nuclear@0: nuclear@0: if(!node->parent) { nuclear@0: return rot; nuclear@0: } nuclear@0: nuclear@0: prot = anm_get_rotation(node->parent, tm); nuclear@0: return quat_mul(prot, rot); nuclear@0: } nuclear@0: nuclear@0: vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm) nuclear@0: { nuclear@0: vec3_t s, ps; nuclear@0: s = anm_get_node_scaling(node, tm); nuclear@0: nuclear@0: if(!node->parent) { nuclear@0: return s; nuclear@0: } nuclear@0: nuclear@0: ps = anm_get_scaling(node->parent, tm); nuclear@0: return v3_mul(s, ps); nuclear@0: } nuclear@0: nuclear@5: void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) nuclear@5: { nuclear@9: int i; nuclear@9: mat4_t rmat; nuclear@5: vec3_t pos, scale; nuclear@5: quat_t rot; nuclear@5: nuclear@5: pos = anm_get_node_position(node, tm); nuclear@5: rot = anm_get_node_rotation(node, tm); nuclear@5: scale = anm_get_node_scaling(node, tm); nuclear@5: nuclear@9: m4_set_translation(mat, node->pivot.x, node->pivot.y, node->pivot.z); nuclear@5: nuclear@5: quat_to_mat4(rmat, rot); nuclear@9: for(i=0; i<3; i++) { nuclear@9: mat[i][0] = rmat[i][0]; nuclear@9: mat[i][1] = rmat[i][1]; nuclear@9: mat[i][2] = rmat[i][2]; nuclear@9: } nuclear@9: /* this loop is equivalent to: m4_mult(mat, mat, rmat); */ nuclear@5: nuclear@9: mat[0][0] *= scale.x; mat[0][1] *= scale.y; mat[0][2] *= scale.z; mat[0][3] += pos.x; nuclear@9: mat[1][0] *= scale.x; mat[1][1] *= scale.y; mat[1][2] *= scale.z; mat[1][3] += pos.y; nuclear@9: mat[2][0] *= scale.x; mat[2][1] *= scale.y; mat[2][2] *= scale.z; mat[2][3] += pos.z; nuclear@9: nuclear@9: m4_translate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z); nuclear@9: nuclear@9: /* that's basically: pivot * rotation * translation * scaling * -pivot */ nuclear@5: } nuclear@5: nuclear@5: void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) nuclear@5: { nuclear@5: mat4_t tmp; nuclear@5: anm_get_node_matrix(node, tmp, tm); nuclear@5: m4_inverse(mat, tmp); nuclear@5: } nuclear@5: nuclear@20: void anm_eval_node(struct anm_node *node, anm_time_t tm) nuclear@20: { nuclear@20: anm_get_node_matrix(node, node->matrix, tm); nuclear@20: } nuclear@20: nuclear@20: void anm_eval(struct anm_node *node, anm_time_t tm) nuclear@20: { nuclear@20: struct anm_node *c; nuclear@20: nuclear@20: anm_eval_node(node, tm); nuclear@20: nuclear@20: if(node->parent) { nuclear@20: /* due to post-order traversal, the parent matrix is already evaluated */ nuclear@20: m4_mult(node->matrix, node->parent->matrix, node->matrix); nuclear@20: } nuclear@20: nuclear@20: /* recersively evaluate all children */ nuclear@20: c = node->child; nuclear@20: while(c) { nuclear@20: anm_eval(c, tm); nuclear@20: c = c->next; nuclear@20: } nuclear@20: } nuclear@20: nuclear@0: void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) nuclear@0: { nuclear@0: struct mat_cache *cache = pthread_getspecific(node->cache_key); nuclear@0: if(!cache) { nuclear@0: cache = malloc(sizeof *cache); nuclear@0: assert(cache); nuclear@0: nuclear@0: pthread_mutex_lock(&node->cache_list_lock); nuclear@0: cache->next = node->cache_list; nuclear@0: node->cache_list = cache; nuclear@0: pthread_mutex_unlock(&node->cache_list_lock); nuclear@0: nuclear@0: cache->time = ANM_TIME_INVAL; nuclear@2: cache->inv_time = ANM_TIME_INVAL; nuclear@0: pthread_setspecific(node->cache_key, cache); nuclear@0: } nuclear@0: nuclear@0: if(cache->time != tm) { nuclear@5: anm_get_node_matrix(node, cache->matrix, tm); nuclear@0: nuclear@0: if(node->parent) { nuclear@0: mat4_t parent_mat; nuclear@0: nuclear@4: anm_get_matrix(node->parent, parent_mat, tm); nuclear@0: m4_mult(cache->matrix, parent_mat, cache->matrix); nuclear@0: } nuclear@0: cache->time = tm; nuclear@0: } nuclear@0: m4_copy(mat, cache->matrix); nuclear@0: } nuclear@0: nuclear@0: void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) nuclear@0: { nuclear@0: struct mat_cache *cache = pthread_getspecific(node->cache_key); nuclear@0: if(!cache) { nuclear@0: cache = malloc(sizeof *cache); nuclear@0: assert(cache); nuclear@0: nuclear@0: pthread_mutex_lock(&node->cache_list_lock); nuclear@0: cache->next = node->cache_list; nuclear@0: node->cache_list = cache; nuclear@0: pthread_mutex_unlock(&node->cache_list_lock); nuclear@0: nuclear@0: cache->inv_time = ANM_TIME_INVAL; nuclear@2: cache->inv_time = ANM_TIME_INVAL; nuclear@0: pthread_setspecific(node->cache_key, cache); nuclear@0: } nuclear@0: nuclear@0: if(cache->inv_time != tm) { nuclear@0: anm_get_matrix(node, mat, tm); nuclear@0: m4_inverse(cache->inv_matrix, mat); nuclear@0: cache->inv_time = tm; nuclear@0: } nuclear@0: m4_copy(mat, cache->inv_matrix); nuclear@0: } nuclear@0: nuclear@0: anm_time_t anm_get_start_time(struct anm_node *node) nuclear@0: { nuclear@21: int i, j; nuclear@0: struct anm_node *c; nuclear@0: anm_time_t res = LONG_MAX; nuclear@0: nuclear@21: for(j=0; j<2; j++) { nuclear@21: struct anm_animation *anim = anm_get_active_animation(node, j); nuclear@21: if(!anim) break; nuclear@21: nuclear@21: for(i=0; itracks[i].count) { nuclear@21: anm_time_t tm = anim->tracks[i].keys[0].time; nuclear@21: if(tm < res) { nuclear@21: res = tm; nuclear@21: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: c = node->child; nuclear@0: while(c) { nuclear@0: anm_time_t tm = anm_get_start_time(c); nuclear@0: if(tm < res) { nuclear@0: res = tm; nuclear@0: } nuclear@0: c = c->next; nuclear@0: } nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: anm_time_t anm_get_end_time(struct anm_node *node) nuclear@0: { nuclear@21: int i, j; nuclear@0: struct anm_node *c; nuclear@0: anm_time_t res = LONG_MIN; nuclear@0: nuclear@21: for(j=0; j<2; j++) { nuclear@21: struct anm_animation *anim = anm_get_active_animation(node, j); nuclear@21: if(!anim) break; nuclear@21: nuclear@21: for(i=0; itracks[i].count) { nuclear@21: anm_time_t tm = anim->tracks[i].keys[anim->tracks[i].count - 1].time; nuclear@21: if(tm > res) { nuclear@21: res = tm; nuclear@21: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: c = node->child; nuclear@0: while(c) { nuclear@0: anm_time_t tm = anm_get_end_time(c); nuclear@0: if(tm > res) { nuclear@0: res = tm; nuclear@0: } nuclear@0: c = c->next; nuclear@0: } nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: static void invalidate_cache(struct anm_node *node) nuclear@0: { nuclear@0: struct mat_cache *cache = pthread_getspecific(node->cache_key); nuclear@0: if(cache) { nuclear@13: cache->time = cache->inv_time = ANM_TIME_INVAL; nuclear@0: } nuclear@0: }