nuclear@67: #include nuclear@67: #include nuclear@67: #include "anim.h" nuclear@67: #include "dynarr.h" nuclear@67: nuclear@67: int anm_init_node(struct anm_node *node) nuclear@67: { nuclear@67: int i, j; nuclear@67: static const float defaults[] = { nuclear@67: 0.0f, 0.0f, 0.0f, /* default position */ nuclear@67: 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */ nuclear@67: 1.0f, 1.0f, 1.0f /* default scale factor */ nuclear@67: }; nuclear@67: nuclear@67: memset(node, 0, sizeof *node); nuclear@67: nuclear@67: for(i=0; itracks + i) == -1) { nuclear@67: for(j=0; jtracks + i); nuclear@67: } nuclear@67: } nuclear@67: anm_set_track_default(node->tracks + i, defaults[i]); nuclear@67: } nuclear@67: return 0; nuclear@67: } nuclear@67: nuclear@67: void anm_destroy_node(struct anm_node *node) nuclear@67: { nuclear@67: int i; nuclear@67: free(node->name); nuclear@67: nuclear@67: for(i=0; itracks + i); nuclear@67: } nuclear@67: } nuclear@67: nuclear@67: void anm_destroy_node_tree(struct anm_node *tree) nuclear@67: { nuclear@67: struct anm_node *c, *tmp; nuclear@67: nuclear@67: if(!tree) return; nuclear@67: nuclear@67: c = tree->child; nuclear@67: while(c) { nuclear@67: tmp = c; nuclear@67: c = c->next; nuclear@67: nuclear@67: anm_destroy_node_tree(tmp); nuclear@67: } nuclear@67: anm_destroy_node(tree); nuclear@67: } nuclear@67: nuclear@67: struct anm_node *anm_create_node(void) nuclear@67: { nuclear@67: struct anm_node *n; nuclear@67: nuclear@67: if((n = malloc(sizeof *n))) { nuclear@67: if(anm_init_node(n) == -1) { nuclear@67: free(n); nuclear@67: return 0; nuclear@67: } nuclear@67: } nuclear@67: return n; nuclear@67: } nuclear@67: nuclear@67: void anm_free_node(struct anm_node *node) nuclear@67: { nuclear@67: anm_destroy_node(node); nuclear@67: free(node); nuclear@67: } nuclear@67: nuclear@67: void anm_free_node_tree(struct anm_node *tree) nuclear@67: { nuclear@67: struct anm_node *c, *tmp; nuclear@67: nuclear@67: if(!tree) return; nuclear@67: nuclear@67: c = tree->child; nuclear@67: while(c) { nuclear@67: tmp = c; nuclear@67: c = c->next; nuclear@67: nuclear@67: anm_free_node_tree(tmp); nuclear@67: } nuclear@67: nuclear@67: anm_free_node(tree); nuclear@67: } nuclear@67: nuclear@67: int anm_set_node_name(struct anm_node *node, const char *name) nuclear@67: { nuclear@67: char *str; nuclear@67: nuclear@67: if(!(str = malloc(strlen(name) + 1))) { nuclear@67: return -1; nuclear@67: } nuclear@67: strcpy(str, name); nuclear@67: free(node->name); nuclear@67: node->name = str; nuclear@67: return 0; nuclear@67: } nuclear@67: nuclear@67: const char *anm_get_node_name(struct anm_node *node) nuclear@67: { nuclear@67: return node->name ? node->name : ""; nuclear@67: } nuclear@67: nuclear@67: void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in) nuclear@67: { nuclear@67: int i; nuclear@67: nuclear@67: for(i=0; itracks + i, in); nuclear@67: } nuclear@67: } nuclear@67: nuclear@67: void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) nuclear@67: { nuclear@67: int i; nuclear@67: nuclear@67: for(i=0; itracks + i, ex); nuclear@67: } nuclear@67: } nuclear@67: nuclear@67: void anm_link_node(struct anm_node *p, struct anm_node *c) nuclear@67: { nuclear@67: c->next = p->child; nuclear@67: p->child = c; nuclear@67: nuclear@67: c->parent = p; nuclear@67: } nuclear@67: nuclear@67: int anm_unlink_node(struct anm_node *p, struct anm_node *c) nuclear@67: { nuclear@67: struct anm_node *iter; nuclear@67: nuclear@67: if(p->child == c) { nuclear@67: p->child = c->next; nuclear@67: c->next = 0; nuclear@67: return 0; nuclear@67: } nuclear@67: nuclear@67: iter = p->child; nuclear@67: while(iter->next) { nuclear@67: if(iter->next == c) { nuclear@67: iter->next = c->next; nuclear@67: c->next = 0; nuclear@67: return 0; nuclear@67: } nuclear@67: } nuclear@67: return -1; nuclear@67: } nuclear@67: nuclear@67: void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm) nuclear@67: { nuclear@67: anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x); nuclear@67: anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y); nuclear@67: anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z); nuclear@67: } nuclear@67: nuclear@67: vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm) nuclear@67: { nuclear@67: vec3_t v; nuclear@67: v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm); nuclear@67: v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm); nuclear@67: v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm); nuclear@67: return v; nuclear@67: } nuclear@67: nuclear@67: void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm) nuclear@67: { nuclear@67: anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x); nuclear@67: anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y); nuclear@67: anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z); nuclear@67: anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w); nuclear@67: } nuclear@67: nuclear@67: quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) nuclear@67: { nuclear@67: int idx0, idx1, last_idx; nuclear@67: anm_time_t tstart, tend; nuclear@67: float t, dt; nuclear@67: struct anm_track *track_x, *track_y, *track_z, *track_w; nuclear@67: quat_t q, q1, q2; nuclear@67: nuclear@67: track_x = node->tracks + ANM_TRACK_ROT_X; nuclear@67: track_y = node->tracks + ANM_TRACK_ROT_Y; nuclear@67: track_z = node->tracks + ANM_TRACK_ROT_Z; nuclear@67: track_w = node->tracks + ANM_TRACK_ROT_W; nuclear@67: nuclear@67: if(!track_x->count) { nuclear@67: q.x = track_x->def_val; nuclear@67: q.y = track_y->def_val; nuclear@67: q.z = track_z->def_val; nuclear@67: q.w = track_w->def_val; nuclear@67: return q; nuclear@67: } nuclear@67: nuclear@67: last_idx = track_x->count - 1; nuclear@67: nuclear@67: tstart = track_x->keys[0].time; nuclear@67: tend = track_x->keys[last_idx].time; nuclear@67: tm = anm_remap_time(track_x, tm, tstart, tend); nuclear@67: nuclear@67: idx0 = anm_get_key_interval(track_x, tm); nuclear@67: assert(idx0 >= 0 && idx0 < track_x->count); nuclear@67: idx1 = idx0 + 1; nuclear@67: nuclear@67: dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time); nuclear@67: t = (float)(tm - track_x->keys[idx0].time) / dt; nuclear@67: nuclear@67: q1.x = track_x->keys[idx0].val; nuclear@67: q1.y = track_y->keys[idx0].val; nuclear@67: q1.z = track_z->keys[idx0].val; nuclear@67: q1.w = track_w->keys[idx0].val; nuclear@67: nuclear@67: q2.x = track_x->keys[idx1].val; nuclear@67: q2.y = track_y->keys[idx1].val; nuclear@67: q2.z = track_z->keys[idx1].val; nuclear@67: q2.w = track_w->keys[idx1].val; nuclear@67: nuclear@67: return quat_slerp(q1, q2, t); nuclear@67: } nuclear@67: nuclear@67: void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm) nuclear@67: { nuclear@67: anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x); nuclear@67: anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y); nuclear@67: anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z); nuclear@67: } nuclear@67: nuclear@67: vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm) nuclear@67: { nuclear@67: vec3_t v; nuclear@67: v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm); nuclear@67: v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm); nuclear@67: v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm); nuclear@67: return v; nuclear@67: } nuclear@67: nuclear@67: nuclear@67: vec3_t anm_get_position(struct anm_node *node, anm_time_t tm) nuclear@67: { nuclear@67: mat4_t xform; nuclear@67: vec3_t pos = {0.0, 0.0, 0.0}; nuclear@67: nuclear@67: if(!node->parent) { nuclear@67: return anm_get_node_position(node, tm); nuclear@67: } nuclear@67: nuclear@67: anm_get_matrix(node, xform, tm); nuclear@67: return v3_transform(pos, xform); nuclear@67: } nuclear@67: nuclear@67: quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm) nuclear@67: { nuclear@67: quat_t rot, prot; nuclear@67: rot = anm_get_node_rotation(node, tm); nuclear@67: nuclear@67: if(!node->parent) { nuclear@67: return rot; nuclear@67: } nuclear@67: nuclear@67: prot = anm_get_rotation(node->parent, tm); nuclear@67: return quat_mul(prot, rot); nuclear@67: } nuclear@67: nuclear@67: vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm) nuclear@67: { nuclear@67: vec3_t s, ps; nuclear@67: s = anm_get_node_scaling(node, tm); nuclear@67: nuclear@67: if(!node->parent) { nuclear@67: return s; nuclear@67: } nuclear@67: nuclear@67: ps = anm_get_scaling(node->parent, tm); nuclear@67: return v3_mul(s, ps); nuclear@67: } nuclear@67: nuclear@67: void anm_set_pivot(struct anm_node *node, vec3_t piv) nuclear@67: { nuclear@67: node->pivot = piv; nuclear@67: } nuclear@67: nuclear@67: vec3_t anm_get_pivot(struct anm_node *node) nuclear@67: { nuclear@67: return node->pivot; nuclear@67: } nuclear@67: nuclear@67: void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) nuclear@67: { nuclear@67: #ifdef ANIM_THREAD_SAFE nuclear@67: /* XXX we're holding the mutex for way too anm_time_t... but it looks like the nuclear@67: * alternative would be to lock/unlock twice which might be worse. nuclear@67: */ nuclear@67: pthread_mutex_lock(node->cache_mutex); nuclear@67: #endif nuclear@67: nuclear@67: if(node->cache_time != tm) { nuclear@67: mat4_t tmat, rmat, smat, pivmat, neg_pivmat; nuclear@67: vec3_t pos, scale; nuclear@67: quat_t rot; nuclear@67: nuclear@67: m4_identity(tmat); nuclear@67: /*no need to m4_identity(rmat); quat_to_mat4 sets this properly */ nuclear@67: m4_identity(smat); nuclear@67: m4_identity(pivmat); nuclear@67: m4_identity(neg_pivmat); nuclear@67: nuclear@67: pos = anm_get_node_position(node, tm); nuclear@67: rot = anm_get_node_rotation(node, tm); nuclear@67: scale = anm_get_node_scaling(node, tm); nuclear@67: nuclear@67: m4_translate(pivmat, node->pivot.x, node->pivot.y, node->pivot.z); nuclear@67: m4_translate(neg_pivmat, -node->pivot.x, -node->pivot.y, -node->pivot.z); nuclear@67: nuclear@67: m4_translate(tmat, pos.x, pos.y, pos.z); nuclear@67: quat_to_mat4(rmat, rot); nuclear@67: m4_translate(smat, scale.x, scale.y, scale.z); nuclear@67: nuclear@67: /* ok this would look nicer in C++ */ nuclear@67: m4_mult(node->cache_matrix, pivmat, tmat); nuclear@67: m4_mult(node->cache_matrix, node->cache_matrix, rmat); nuclear@67: m4_mult(node->cache_matrix, node->cache_matrix, smat); nuclear@67: m4_mult(node->cache_matrix, node->cache_matrix, neg_pivmat); nuclear@67: nuclear@67: if(node->parent) { nuclear@67: mat4_t parent_mat; nuclear@67: nuclear@67: anm_get_matrix(node->parent, mat, tm); nuclear@67: m4_mult(node->cache_matrix, parent_mat, node->cache_matrix); nuclear@67: } nuclear@67: node->cache_time = tm; nuclear@67: } nuclear@67: m4_copy(mat, node->cache_matrix); nuclear@67: nuclear@67: #ifdef ANIM_THREAD_SAFE nuclear@67: pthread_mutex_unlock(node->cache_mutex); nuclear@67: #endif nuclear@67: } nuclear@67: nuclear@67: anm_time_t anm_get_start_time(struct anm_node *node) nuclear@67: { nuclear@67: int i; nuclear@67: struct anm_node *c; nuclear@67: anm_time_t res = LONG_MAX; nuclear@67: nuclear@67: for(i=0; itracks[i].count) { nuclear@67: anm_time_t tm = node->tracks[i].keys[0].time; nuclear@67: if(tm < res) { nuclear@67: res = tm; nuclear@67: } nuclear@67: } nuclear@67: } nuclear@67: nuclear@67: c = node->child; nuclear@67: while(c) { nuclear@67: anm_time_t tm = anm_get_start_time(c); nuclear@67: if(tm < res) { nuclear@67: res = tm; nuclear@67: } nuclear@67: c = c->next; nuclear@67: } nuclear@67: return res; nuclear@67: } nuclear@67: nuclear@67: anm_time_t anm_get_end_time(struct anm_node *node) nuclear@67: { nuclear@67: int i; nuclear@67: struct anm_node *c; nuclear@67: anm_time_t res = LONG_MIN; nuclear@67: nuclear@67: for(i=0; itracks[i].count) { nuclear@67: anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time; nuclear@67: if(tm > res) { nuclear@67: res = tm; nuclear@67: } nuclear@67: } nuclear@67: } nuclear@67: nuclear@67: c = node->child; nuclear@67: while(c) { nuclear@67: anm_time_t tm = anm_get_end_time(c); nuclear@67: if(tm > res) { nuclear@67: res = tm; nuclear@67: } nuclear@67: c = c->next; nuclear@67: } nuclear@67: return res; nuclear@67: }