libanim
diff src/anim.c @ 0:fad4701f484e
libanim mercurial repo
author | John Tsiombikas <nuclear@mutantstargoat.com> |
---|---|
date | Sun, 08 Jan 2012 05:13:13 +0200 |
parents | |
children | 69654793abc3 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/anim.c Sun Jan 08 05:13:13 2012 +0200 1.3 @@ -0,0 +1,451 @@ 1.4 +#include <limits.h> 1.5 +#include <assert.h> 1.6 +#include "anim.h" 1.7 +#include "dynarr.h" 1.8 + 1.9 +static void invalidate_cache(struct anm_node *node); 1.10 + 1.11 +int anm_init_node(struct anm_node *node) 1.12 +{ 1.13 + int i, j; 1.14 + static const float defaults[] = { 1.15 + 0.0f, 0.0f, 0.0f, /* default position */ 1.16 + 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */ 1.17 + 1.0f, 1.0f, 1.0f /* default scale factor */ 1.18 + }; 1.19 + 1.20 + memset(node, 0, sizeof *node); 1.21 + 1.22 + /* initialize thread-local matrix cache */ 1.23 + pthread_key_create(&node->cache_key, 0); 1.24 + 1.25 + for(i=0; i<ANM_NUM_TRACKS; i++) { 1.26 + if(anm_init_track(node->tracks + i) == -1) { 1.27 + for(j=0; j<i; j++) { 1.28 + anm_destroy_track(node->tracks + i); 1.29 + } 1.30 + } 1.31 + anm_set_track_default(node->tracks + i, defaults[i]); 1.32 + } 1.33 + return 0; 1.34 +} 1.35 + 1.36 +void anm_destroy_node(struct anm_node *node) 1.37 +{ 1.38 + int i; 1.39 + free(node->name); 1.40 + 1.41 + for(i=0; i<ANM_NUM_TRACKS; i++) { 1.42 + anm_destroy_track(node->tracks + i); 1.43 + } 1.44 + 1.45 + /* destroy thread-specific cache */ 1.46 + pthread_key_delete(node->cache_key); 1.47 + 1.48 + while(node->cache_list) { 1.49 + struct mat_cache *tmp = node->cache_list; 1.50 + node->cache_list = tmp->next; 1.51 + free(tmp); 1.52 + } 1.53 +} 1.54 + 1.55 +void anm_destroy_node_tree(struct anm_node *tree) 1.56 +{ 1.57 + struct anm_node *c, *tmp; 1.58 + 1.59 + if(!tree) return; 1.60 + 1.61 + c = tree->child; 1.62 + while(c) { 1.63 + tmp = c; 1.64 + c = c->next; 1.65 + 1.66 + anm_destroy_node_tree(tmp); 1.67 + } 1.68 + anm_destroy_node(tree); 1.69 +} 1.70 + 1.71 +struct anm_node *anm_create_node(void) 1.72 +{ 1.73 + struct anm_node *n; 1.74 + 1.75 + if((n = malloc(sizeof *n))) { 1.76 + if(anm_init_node(n) == -1) { 1.77 + free(n); 1.78 + return 0; 1.79 + } 1.80 + } 1.81 + return n; 1.82 +} 1.83 + 1.84 +void anm_free_node(struct anm_node *node) 1.85 +{ 1.86 + anm_destroy_node(node); 1.87 + free(node); 1.88 +} 1.89 + 1.90 +void anm_free_node_tree(struct anm_node *tree) 1.91 +{ 1.92 + struct anm_node *c, *tmp; 1.93 + 1.94 + if(!tree) return; 1.95 + 1.96 + c = tree->child; 1.97 + while(c) { 1.98 + tmp = c; 1.99 + c = c->next; 1.100 + 1.101 + anm_free_node_tree(tmp); 1.102 + } 1.103 + 1.104 + anm_free_node(tree); 1.105 +} 1.106 + 1.107 +int anm_set_node_name(struct anm_node *node, const char *name) 1.108 +{ 1.109 + char *str; 1.110 + 1.111 + if(!(str = malloc(strlen(name) + 1))) { 1.112 + return -1; 1.113 + } 1.114 + strcpy(str, name); 1.115 + free(node->name); 1.116 + node->name = str; 1.117 + return 0; 1.118 +} 1.119 + 1.120 +const char *anm_get_node_name(struct anm_node *node) 1.121 +{ 1.122 + return node->name ? node->name : ""; 1.123 +} 1.124 + 1.125 +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in) 1.126 +{ 1.127 + int i; 1.128 + 1.129 + for(i=0; i<ANM_NUM_TRACKS; i++) { 1.130 + anm_set_track_interpolator(node->tracks + i, in); 1.131 + } 1.132 + invalidate_cache(node); 1.133 +} 1.134 + 1.135 +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) 1.136 +{ 1.137 + int i; 1.138 + 1.139 + for(i=0; i<ANM_NUM_TRACKS; i++) { 1.140 + anm_set_track_extrapolator(node->tracks + i, ex); 1.141 + } 1.142 + invalidate_cache(node); 1.143 +} 1.144 + 1.145 +void anm_link_node(struct anm_node *p, struct anm_node *c) 1.146 +{ 1.147 + c->next = p->child; 1.148 + p->child = c; 1.149 + 1.150 + c->parent = p; 1.151 + invalidate_cache(c); 1.152 +} 1.153 + 1.154 +int anm_unlink_node(struct anm_node *p, struct anm_node *c) 1.155 +{ 1.156 + struct anm_node *iter; 1.157 + 1.158 + if(p->child == c) { 1.159 + p->child = c->next; 1.160 + c->next = 0; 1.161 + invalidate_cache(c); 1.162 + return 0; 1.163 + } 1.164 + 1.165 + iter = p->child; 1.166 + while(iter->next) { 1.167 + if(iter->next == c) { 1.168 + iter->next = c->next; 1.169 + c->next = 0; 1.170 + invalidate_cache(c); 1.171 + return 0; 1.172 + } 1.173 + } 1.174 + return -1; 1.175 +} 1.176 + 1.177 +void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm) 1.178 +{ 1.179 + anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x); 1.180 + anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y); 1.181 + anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z); 1.182 + invalidate_cache(node); 1.183 +} 1.184 + 1.185 +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm) 1.186 +{ 1.187 + vec3_t v; 1.188 + v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm); 1.189 + v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm); 1.190 + v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm); 1.191 + return v; 1.192 +} 1.193 + 1.194 +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm) 1.195 +{ 1.196 + anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x); 1.197 + anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y); 1.198 + anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z); 1.199 + anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w); 1.200 + invalidate_cache(node); 1.201 +} 1.202 + 1.203 +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) 1.204 +{ 1.205 + int idx0, idx1, last_idx; 1.206 + anm_time_t tstart, tend; 1.207 + float t, dt; 1.208 + struct anm_track *track_x, *track_y, *track_z, *track_w; 1.209 + quat_t q, q1, q2; 1.210 + 1.211 + track_x = node->tracks + ANM_TRACK_ROT_X; 1.212 + track_y = node->tracks + ANM_TRACK_ROT_Y; 1.213 + track_z = node->tracks + ANM_TRACK_ROT_Z; 1.214 + track_w = node->tracks + ANM_TRACK_ROT_W; 1.215 + 1.216 + if(!track_x->count) { 1.217 + q.x = track_x->def_val; 1.218 + q.y = track_y->def_val; 1.219 + q.z = track_z->def_val; 1.220 + q.w = track_w->def_val; 1.221 + return q; 1.222 + } 1.223 + 1.224 + last_idx = track_x->count - 1; 1.225 + 1.226 + tstart = track_x->keys[0].time; 1.227 + tend = track_x->keys[last_idx].time; 1.228 + tm = anm_remap_time(track_x, tm, tstart, tend); 1.229 + 1.230 + idx0 = anm_get_key_interval(track_x, tm); 1.231 + assert(idx0 >= 0 && idx0 < track_x->count); 1.232 + idx1 = idx0 + 1; 1.233 + 1.234 + dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time); 1.235 + t = (float)(tm - track_x->keys[idx0].time) / dt; 1.236 + 1.237 + q1.x = track_x->keys[idx0].val; 1.238 + q1.y = track_y->keys[idx0].val; 1.239 + q1.z = track_z->keys[idx0].val; 1.240 + q1.w = track_w->keys[idx0].val; 1.241 + 1.242 + q2.x = track_x->keys[idx1].val; 1.243 + q2.y = track_y->keys[idx1].val; 1.244 + q2.z = track_z->keys[idx1].val; 1.245 + q2.w = track_w->keys[idx1].val; 1.246 + 1.247 + return quat_slerp(q1, q2, t); 1.248 +} 1.249 + 1.250 +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm) 1.251 +{ 1.252 + anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x); 1.253 + anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y); 1.254 + anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z); 1.255 + invalidate_cache(node); 1.256 +} 1.257 + 1.258 +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm) 1.259 +{ 1.260 + vec3_t v; 1.261 + v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm); 1.262 + v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm); 1.263 + v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm); 1.264 + return v; 1.265 +} 1.266 + 1.267 + 1.268 +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm) 1.269 +{ 1.270 + mat4_t xform; 1.271 + vec3_t pos = {0.0, 0.0, 0.0}; 1.272 + 1.273 + if(!node->parent) { 1.274 + return anm_get_node_position(node, tm); 1.275 + } 1.276 + 1.277 + anm_get_matrix(node, xform, tm); 1.278 + return v3_transform(pos, xform); 1.279 +} 1.280 + 1.281 +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm) 1.282 +{ 1.283 + quat_t rot, prot; 1.284 + rot = anm_get_node_rotation(node, tm); 1.285 + 1.286 + if(!node->parent) { 1.287 + return rot; 1.288 + } 1.289 + 1.290 + prot = anm_get_rotation(node->parent, tm); 1.291 + return quat_mul(prot, rot); 1.292 +} 1.293 + 1.294 +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm) 1.295 +{ 1.296 + vec3_t s, ps; 1.297 + s = anm_get_node_scaling(node, tm); 1.298 + 1.299 + if(!node->parent) { 1.300 + return s; 1.301 + } 1.302 + 1.303 + ps = anm_get_scaling(node->parent, tm); 1.304 + return v3_mul(s, ps); 1.305 +} 1.306 + 1.307 +void anm_set_pivot(struct anm_node *node, vec3_t piv) 1.308 +{ 1.309 + node->pivot = piv; 1.310 +} 1.311 + 1.312 +vec3_t anm_get_pivot(struct anm_node *node) 1.313 +{ 1.314 + return node->pivot; 1.315 +} 1.316 + 1.317 +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) 1.318 +{ 1.319 + struct mat_cache *cache = pthread_getspecific(node->cache_key); 1.320 + if(!cache) { 1.321 + cache = malloc(sizeof *cache); 1.322 + assert(cache); 1.323 + 1.324 + pthread_mutex_lock(&node->cache_list_lock); 1.325 + cache->next = node->cache_list; 1.326 + node->cache_list = cache; 1.327 + pthread_mutex_unlock(&node->cache_list_lock); 1.328 + 1.329 + cache->time = ANM_TIME_INVAL; 1.330 + pthread_setspecific(node->cache_key, cache); 1.331 + } 1.332 + 1.333 + if(cache->time != tm) { 1.334 + mat4_t tmat, rmat, smat, pivmat, neg_pivmat; 1.335 + vec3_t pos, scale; 1.336 + quat_t rot; 1.337 + 1.338 + m4_identity(tmat); 1.339 + /*no need to m4_identity(rmat); quat_to_mat4 sets this properly */ 1.340 + m4_identity(smat); 1.341 + m4_identity(pivmat); 1.342 + m4_identity(neg_pivmat); 1.343 + 1.344 + pos = anm_get_node_position(node, tm); 1.345 + rot = anm_get_node_rotation(node, tm); 1.346 + scale = anm_get_node_scaling(node, tm); 1.347 + 1.348 + m4_translate(pivmat, node->pivot.x, node->pivot.y, node->pivot.z); 1.349 + m4_translate(neg_pivmat, -node->pivot.x, -node->pivot.y, -node->pivot.z); 1.350 + 1.351 + m4_translate(tmat, pos.x, pos.y, pos.z); 1.352 + quat_to_mat4(rmat, rot); 1.353 + m4_translate(smat, scale.x, scale.y, scale.z); 1.354 + 1.355 + /* ok this would look nicer in C++ */ 1.356 + m4_mult(cache->matrix, pivmat, tmat); 1.357 + m4_mult(cache->matrix, cache->matrix, rmat); 1.358 + m4_mult(cache->matrix, cache->matrix, smat); 1.359 + m4_mult(cache->matrix, cache->matrix, neg_pivmat); 1.360 + 1.361 + if(node->parent) { 1.362 + mat4_t parent_mat; 1.363 + 1.364 + anm_get_matrix(node->parent, mat, tm); 1.365 + m4_mult(cache->matrix, parent_mat, cache->matrix); 1.366 + } 1.367 + cache->time = tm; 1.368 + } 1.369 + m4_copy(mat, cache->matrix); 1.370 +} 1.371 + 1.372 +void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) 1.373 +{ 1.374 + struct mat_cache *cache = pthread_getspecific(node->cache_key); 1.375 + if(!cache) { 1.376 + cache = malloc(sizeof *cache); 1.377 + assert(cache); 1.378 + 1.379 + pthread_mutex_lock(&node->cache_list_lock); 1.380 + cache->next = node->cache_list; 1.381 + node->cache_list = cache; 1.382 + pthread_mutex_unlock(&node->cache_list_lock); 1.383 + 1.384 + cache->inv_time = ANM_TIME_INVAL; 1.385 + pthread_setspecific(node->cache_key, cache); 1.386 + } 1.387 + 1.388 + if(cache->inv_time != tm) { 1.389 + anm_get_matrix(node, mat, tm); 1.390 + m4_inverse(cache->inv_matrix, mat); 1.391 + cache->inv_time = tm; 1.392 + } 1.393 + m4_copy(mat, cache->inv_matrix); 1.394 +} 1.395 + 1.396 +anm_time_t anm_get_start_time(struct anm_node *node) 1.397 +{ 1.398 + int i; 1.399 + struct anm_node *c; 1.400 + anm_time_t res = LONG_MAX; 1.401 + 1.402 + for(i=0; i<ANM_NUM_TRACKS; i++) { 1.403 + if(node->tracks[i].count) { 1.404 + anm_time_t tm = node->tracks[i].keys[0].time; 1.405 + if(tm < res) { 1.406 + res = tm; 1.407 + } 1.408 + } 1.409 + } 1.410 + 1.411 + c = node->child; 1.412 + while(c) { 1.413 + anm_time_t tm = anm_get_start_time(c); 1.414 + if(tm < res) { 1.415 + res = tm; 1.416 + } 1.417 + c = c->next; 1.418 + } 1.419 + return res; 1.420 +} 1.421 + 1.422 +anm_time_t anm_get_end_time(struct anm_node *node) 1.423 +{ 1.424 + int i; 1.425 + struct anm_node *c; 1.426 + anm_time_t res = LONG_MIN; 1.427 + 1.428 + for(i=0; i<ANM_NUM_TRACKS; i++) { 1.429 + if(node->tracks[i].count) { 1.430 + anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time; 1.431 + if(tm > res) { 1.432 + res = tm; 1.433 + } 1.434 + } 1.435 + } 1.436 + 1.437 + c = node->child; 1.438 + while(c) { 1.439 + anm_time_t tm = anm_get_end_time(c); 1.440 + if(tm > res) { 1.441 + res = tm; 1.442 + } 1.443 + c = c->next; 1.444 + } 1.445 + return res; 1.446 +} 1.447 + 1.448 +static void invalidate_cache(struct anm_node *node) 1.449 +{ 1.450 + struct mat_cache *cache = pthread_getspecific(node->cache_key); 1.451 + if(cache) { 1.452 + cache->time = ANM_TIME_INVAL; 1.453 + } 1.454 +}