libanim

annotate src/anim.c @ 44:e9db1d0c09b3

made the kind of interpolation for quaternion tracks into a conditional block for testing (lerp/slerp), obviously defaults to slerp...
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 01 Mar 2013 09:44:20 +0200
parents b3312cf87715
children 710658962108
rev   line source
nuclear@1 1 #include <stdlib.h>
nuclear@0 2 #include <limits.h>
nuclear@0 3 #include <assert.h>
nuclear@0 4 #include "anim.h"
nuclear@0 5 #include "dynarr.h"
nuclear@0 6
nuclear@7 7 #define ROT_USE_SLERP
nuclear@7 8
nuclear@0 9 static void invalidate_cache(struct anm_node *node);
nuclear@0 10
nuclear@0 11 int anm_init_node(struct anm_node *node)
nuclear@0 12 {
nuclear@0 13 int i, j;
nuclear@0 14 static const float defaults[] = {
nuclear@0 15 0.0f, 0.0f, 0.0f, /* default position */
nuclear@0 16 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */
nuclear@0 17 1.0f, 1.0f, 1.0f /* default scale factor */
nuclear@0 18 };
nuclear@0 19
nuclear@0 20 memset(node, 0, sizeof *node);
nuclear@0 21
nuclear@0 22 /* initialize thread-local matrix cache */
nuclear@0 23 pthread_key_create(&node->cache_key, 0);
nuclear@0 24
nuclear@0 25 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@0 26 if(anm_init_track(node->tracks + i) == -1) {
nuclear@0 27 for(j=0; j<i; j++) {
nuclear@0 28 anm_destroy_track(node->tracks + i);
nuclear@0 29 }
nuclear@0 30 }
nuclear@0 31 anm_set_track_default(node->tracks + i, defaults[i]);
nuclear@0 32 }
nuclear@0 33 return 0;
nuclear@0 34 }
nuclear@0 35
nuclear@0 36 void anm_destroy_node(struct anm_node *node)
nuclear@0 37 {
nuclear@0 38 int i;
nuclear@0 39 free(node->name);
nuclear@0 40
nuclear@0 41 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@0 42 anm_destroy_track(node->tracks + i);
nuclear@0 43 }
nuclear@0 44
nuclear@0 45 /* destroy thread-specific cache */
nuclear@0 46 pthread_key_delete(node->cache_key);
nuclear@0 47
nuclear@0 48 while(node->cache_list) {
nuclear@0 49 struct mat_cache *tmp = node->cache_list;
nuclear@0 50 node->cache_list = tmp->next;
nuclear@0 51 free(tmp);
nuclear@0 52 }
nuclear@0 53 }
nuclear@0 54
nuclear@0 55 void anm_destroy_node_tree(struct anm_node *tree)
nuclear@0 56 {
nuclear@0 57 struct anm_node *c, *tmp;
nuclear@0 58
nuclear@0 59 if(!tree) return;
nuclear@0 60
nuclear@0 61 c = tree->child;
nuclear@0 62 while(c) {
nuclear@0 63 tmp = c;
nuclear@0 64 c = c->next;
nuclear@0 65
nuclear@0 66 anm_destroy_node_tree(tmp);
nuclear@0 67 }
nuclear@0 68 anm_destroy_node(tree);
nuclear@0 69 }
nuclear@0 70
nuclear@0 71 struct anm_node *anm_create_node(void)
nuclear@0 72 {
nuclear@0 73 struct anm_node *n;
nuclear@0 74
nuclear@0 75 if((n = malloc(sizeof *n))) {
nuclear@0 76 if(anm_init_node(n) == -1) {
nuclear@0 77 free(n);
nuclear@0 78 return 0;
nuclear@0 79 }
nuclear@0 80 }
nuclear@0 81 return n;
nuclear@0 82 }
nuclear@0 83
nuclear@0 84 void anm_free_node(struct anm_node *node)
nuclear@0 85 {
nuclear@0 86 anm_destroy_node(node);
nuclear@0 87 free(node);
nuclear@0 88 }
nuclear@0 89
nuclear@0 90 void anm_free_node_tree(struct anm_node *tree)
nuclear@0 91 {
nuclear@0 92 struct anm_node *c, *tmp;
nuclear@0 93
nuclear@0 94 if(!tree) return;
nuclear@0 95
nuclear@0 96 c = tree->child;
nuclear@0 97 while(c) {
nuclear@0 98 tmp = c;
nuclear@0 99 c = c->next;
nuclear@0 100
nuclear@0 101 anm_free_node_tree(tmp);
nuclear@0 102 }
nuclear@0 103
nuclear@0 104 anm_free_node(tree);
nuclear@0 105 }
nuclear@0 106
nuclear@0 107 int anm_set_node_name(struct anm_node *node, const char *name)
nuclear@0 108 {
nuclear@0 109 char *str;
nuclear@0 110
nuclear@0 111 if(!(str = malloc(strlen(name) + 1))) {
nuclear@0 112 return -1;
nuclear@0 113 }
nuclear@0 114 strcpy(str, name);
nuclear@0 115 free(node->name);
nuclear@0 116 node->name = str;
nuclear@0 117 return 0;
nuclear@0 118 }
nuclear@0 119
nuclear@0 120 const char *anm_get_node_name(struct anm_node *node)
nuclear@0 121 {
nuclear@0 122 return node->name ? node->name : "";
nuclear@0 123 }
nuclear@0 124
nuclear@0 125 void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in)
nuclear@0 126 {
nuclear@0 127 int i;
nuclear@0 128
nuclear@0 129 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@0 130 anm_set_track_interpolator(node->tracks + i, in);
nuclear@0 131 }
nuclear@0 132 invalidate_cache(node);
nuclear@0 133 }
nuclear@0 134
nuclear@0 135 void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex)
nuclear@0 136 {
nuclear@0 137 int i;
nuclear@0 138
nuclear@0 139 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@0 140 anm_set_track_extrapolator(node->tracks + i, ex);
nuclear@0 141 }
nuclear@0 142 invalidate_cache(node);
nuclear@0 143 }
nuclear@0 144
nuclear@0 145 void anm_link_node(struct anm_node *p, struct anm_node *c)
nuclear@0 146 {
nuclear@0 147 c->next = p->child;
nuclear@0 148 p->child = c;
nuclear@0 149
nuclear@0 150 c->parent = p;
nuclear@0 151 invalidate_cache(c);
nuclear@0 152 }
nuclear@0 153
nuclear@0 154 int anm_unlink_node(struct anm_node *p, struct anm_node *c)
nuclear@0 155 {
nuclear@0 156 struct anm_node *iter;
nuclear@0 157
nuclear@0 158 if(p->child == c) {
nuclear@0 159 p->child = c->next;
nuclear@0 160 c->next = 0;
nuclear@0 161 invalidate_cache(c);
nuclear@0 162 return 0;
nuclear@0 163 }
nuclear@0 164
nuclear@0 165 iter = p->child;
nuclear@0 166 while(iter->next) {
nuclear@0 167 if(iter->next == c) {
nuclear@0 168 iter->next = c->next;
nuclear@0 169 c->next = 0;
nuclear@0 170 invalidate_cache(c);
nuclear@0 171 return 0;
nuclear@0 172 }
nuclear@0 173 }
nuclear@0 174 return -1;
nuclear@0 175 }
nuclear@0 176
nuclear@0 177 void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm)
nuclear@0 178 {
nuclear@0 179 anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x);
nuclear@0 180 anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y);
nuclear@0 181 anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z);
nuclear@0 182 invalidate_cache(node);
nuclear@0 183 }
nuclear@0 184
nuclear@0 185 vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm)
nuclear@0 186 {
nuclear@0 187 vec3_t v;
nuclear@0 188 v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm);
nuclear@0 189 v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm);
nuclear@0 190 v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm);
nuclear@0 191 return v;
nuclear@0 192 }
nuclear@0 193
nuclear@0 194 void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm)
nuclear@0 195 {
nuclear@0 196 anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x);
nuclear@0 197 anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y);
nuclear@0 198 anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z);
nuclear@0 199 anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w);
nuclear@0 200 invalidate_cache(node);
nuclear@0 201 }
nuclear@0 202
nuclear@0 203 quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm)
nuclear@0 204 {
nuclear@7 205 #ifndef ROT_USE_SLERP
nuclear@7 206 quat_t q;
nuclear@6 207 q.x = anm_get_value(node->tracks + ANM_TRACK_ROT_X, tm);
nuclear@6 208 q.y = anm_get_value(node->tracks + ANM_TRACK_ROT_Y, tm);
nuclear@6 209 q.z = anm_get_value(node->tracks + ANM_TRACK_ROT_Z, tm);
nuclear@6 210 q.w = anm_get_value(node->tracks + ANM_TRACK_ROT_W, tm);
nuclear@7 211 return q;
nuclear@7 212 #else
nuclear@0 213 int idx0, idx1, last_idx;
nuclear@0 214 anm_time_t tstart, tend;
nuclear@0 215 float t, dt;
nuclear@0 216 struct anm_track *track_x, *track_y, *track_z, *track_w;
nuclear@0 217 quat_t q, q1, q2;
nuclear@0 218
nuclear@0 219 track_x = node->tracks + ANM_TRACK_ROT_X;
nuclear@0 220 track_y = node->tracks + ANM_TRACK_ROT_Y;
nuclear@0 221 track_z = node->tracks + ANM_TRACK_ROT_Z;
nuclear@0 222 track_w = node->tracks + ANM_TRACK_ROT_W;
nuclear@0 223
nuclear@0 224 if(!track_x->count) {
nuclear@0 225 q.x = track_x->def_val;
nuclear@0 226 q.y = track_y->def_val;
nuclear@0 227 q.z = track_z->def_val;
nuclear@0 228 q.w = track_w->def_val;
nuclear@0 229 return q;
nuclear@0 230 }
nuclear@0 231
nuclear@0 232 last_idx = track_x->count - 1;
nuclear@0 233
nuclear@0 234 tstart = track_x->keys[0].time;
nuclear@0 235 tend = track_x->keys[last_idx].time;
nuclear@6 236
nuclear@6 237 if(tstart == tend) {
nuclear@6 238 q.x = track_x->keys[0].val;
nuclear@6 239 q.y = track_y->keys[0].val;
nuclear@6 240 q.z = track_z->keys[0].val;
nuclear@6 241 q.w = track_w->keys[0].val;
nuclear@6 242 return q;
nuclear@6 243 }
nuclear@6 244
nuclear@0 245 tm = anm_remap_time(track_x, tm, tstart, tend);
nuclear@0 246
nuclear@0 247 idx0 = anm_get_key_interval(track_x, tm);
nuclear@0 248 assert(idx0 >= 0 && idx0 < track_x->count);
nuclear@0 249 idx1 = idx0 + 1;
nuclear@0 250
nuclear@6 251 if(idx0 == last_idx) {
nuclear@6 252 q.x = track_x->keys[idx0].val;
nuclear@6 253 q.y = track_y->keys[idx0].val;
nuclear@6 254 q.z = track_z->keys[idx0].val;
nuclear@6 255 q.w = track_w->keys[idx0].val;
nuclear@6 256 return q;
nuclear@6 257 }
nuclear@6 258
nuclear@0 259 dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time);
nuclear@0 260 t = (float)(tm - track_x->keys[idx0].time) / dt;
nuclear@0 261
nuclear@0 262 q1.x = track_x->keys[idx0].val;
nuclear@0 263 q1.y = track_y->keys[idx0].val;
nuclear@0 264 q1.z = track_z->keys[idx0].val;
nuclear@0 265 q1.w = track_w->keys[idx0].val;
nuclear@0 266
nuclear@0 267 q2.x = track_x->keys[idx1].val;
nuclear@0 268 q2.y = track_y->keys[idx1].val;
nuclear@0 269 q2.z = track_z->keys[idx1].val;
nuclear@0 270 q2.w = track_w->keys[idx1].val;
nuclear@0 271
nuclear@0 272 return quat_slerp(q1, q2, t);
nuclear@7 273 #endif
nuclear@0 274 }
nuclear@0 275
nuclear@0 276 void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm)
nuclear@0 277 {
nuclear@0 278 anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x);
nuclear@0 279 anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y);
nuclear@0 280 anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z);
nuclear@0 281 invalidate_cache(node);
nuclear@0 282 }
nuclear@0 283
nuclear@0 284 vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm)
nuclear@0 285 {
nuclear@0 286 vec3_t v;
nuclear@0 287 v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm);
nuclear@0 288 v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm);
nuclear@0 289 v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm);
nuclear@0 290 return v;
nuclear@0 291 }
nuclear@0 292
nuclear@0 293
nuclear@0 294 vec3_t anm_get_position(struct anm_node *node, anm_time_t tm)
nuclear@0 295 {
nuclear@0 296 mat4_t xform;
nuclear@0 297 vec3_t pos = {0.0, 0.0, 0.0};
nuclear@0 298
nuclear@0 299 if(!node->parent) {
nuclear@0 300 return anm_get_node_position(node, tm);
nuclear@0 301 }
nuclear@0 302
nuclear@0 303 anm_get_matrix(node, xform, tm);
nuclear@0 304 return v3_transform(pos, xform);
nuclear@0 305 }
nuclear@0 306
nuclear@0 307 quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm)
nuclear@0 308 {
nuclear@0 309 quat_t rot, prot;
nuclear@0 310 rot = anm_get_node_rotation(node, tm);
nuclear@0 311
nuclear@0 312 if(!node->parent) {
nuclear@0 313 return rot;
nuclear@0 314 }
nuclear@0 315
nuclear@0 316 prot = anm_get_rotation(node->parent, tm);
nuclear@0 317 return quat_mul(prot, rot);
nuclear@0 318 }
nuclear@0 319
nuclear@0 320 vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm)
nuclear@0 321 {
nuclear@0 322 vec3_t s, ps;
nuclear@0 323 s = anm_get_node_scaling(node, tm);
nuclear@0 324
nuclear@0 325 if(!node->parent) {
nuclear@0 326 return s;
nuclear@0 327 }
nuclear@0 328
nuclear@0 329 ps = anm_get_scaling(node->parent, tm);
nuclear@0 330 return v3_mul(s, ps);
nuclear@0 331 }
nuclear@0 332
nuclear@0 333 void anm_set_pivot(struct anm_node *node, vec3_t piv)
nuclear@0 334 {
nuclear@0 335 node->pivot = piv;
nuclear@0 336 }
nuclear@0 337
nuclear@0 338 vec3_t anm_get_pivot(struct anm_node *node)
nuclear@0 339 {
nuclear@0 340 return node->pivot;
nuclear@0 341 }
nuclear@0 342
nuclear@5 343 void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
nuclear@5 344 {
nuclear@5 345 mat4_t tmat, rmat, smat, pivmat, neg_pivmat;
nuclear@5 346 vec3_t pos, scale;
nuclear@5 347 quat_t rot;
nuclear@5 348
nuclear@5 349 m4_identity(tmat);
nuclear@5 350 /*no need to m4_identity(rmat); quat_to_mat4 sets this properly */
nuclear@5 351 m4_identity(smat);
nuclear@5 352 m4_identity(pivmat);
nuclear@5 353 m4_identity(neg_pivmat);
nuclear@5 354
nuclear@5 355 pos = anm_get_node_position(node, tm);
nuclear@5 356 rot = anm_get_node_rotation(node, tm);
nuclear@5 357 scale = anm_get_node_scaling(node, tm);
nuclear@5 358
nuclear@5 359 m4_translate(pivmat, node->pivot.x, node->pivot.y, node->pivot.z);
nuclear@5 360 m4_translate(neg_pivmat, -node->pivot.x, -node->pivot.y, -node->pivot.z);
nuclear@5 361
nuclear@5 362 m4_translate(tmat, pos.x, pos.y, pos.z);
nuclear@5 363 quat_to_mat4(rmat, rot);
nuclear@5 364 m4_scale(smat, scale.x, scale.y, scale.z);
nuclear@5 365
nuclear@5 366 /* ok this would look nicer in C++ */
nuclear@5 367 m4_mult(mat, pivmat, tmat);
nuclear@5 368 m4_mult(mat, mat, rmat);
nuclear@5 369 m4_mult(mat, mat, smat);
nuclear@5 370 m4_mult(mat, mat, neg_pivmat);
nuclear@5 371 }
nuclear@5 372
nuclear@5 373 void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
nuclear@5 374 {
nuclear@5 375 mat4_t tmp;
nuclear@5 376 anm_get_node_matrix(node, tmp, tm);
nuclear@5 377 m4_inverse(mat, tmp);
nuclear@5 378 }
nuclear@5 379
nuclear@0 380 void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
nuclear@0 381 {
nuclear@0 382 struct mat_cache *cache = pthread_getspecific(node->cache_key);
nuclear@0 383 if(!cache) {
nuclear@0 384 cache = malloc(sizeof *cache);
nuclear@0 385 assert(cache);
nuclear@0 386
nuclear@0 387 pthread_mutex_lock(&node->cache_list_lock);
nuclear@0 388 cache->next = node->cache_list;
nuclear@0 389 node->cache_list = cache;
nuclear@0 390 pthread_mutex_unlock(&node->cache_list_lock);
nuclear@0 391
nuclear@0 392 cache->time = ANM_TIME_INVAL;
nuclear@2 393 cache->inv_time = ANM_TIME_INVAL;
nuclear@0 394 pthread_setspecific(node->cache_key, cache);
nuclear@0 395 }
nuclear@0 396
nuclear@0 397 if(cache->time != tm) {
nuclear@5 398 anm_get_node_matrix(node, cache->matrix, tm);
nuclear@0 399
nuclear@0 400 if(node->parent) {
nuclear@0 401 mat4_t parent_mat;
nuclear@0 402
nuclear@4 403 anm_get_matrix(node->parent, parent_mat, tm);
nuclear@0 404 m4_mult(cache->matrix, parent_mat, cache->matrix);
nuclear@0 405 }
nuclear@0 406 cache->time = tm;
nuclear@0 407 }
nuclear@0 408 m4_copy(mat, cache->matrix);
nuclear@0 409 }
nuclear@0 410
nuclear@0 411 void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
nuclear@0 412 {
nuclear@0 413 struct mat_cache *cache = pthread_getspecific(node->cache_key);
nuclear@0 414 if(!cache) {
nuclear@0 415 cache = malloc(sizeof *cache);
nuclear@0 416 assert(cache);
nuclear@0 417
nuclear@0 418 pthread_mutex_lock(&node->cache_list_lock);
nuclear@0 419 cache->next = node->cache_list;
nuclear@0 420 node->cache_list = cache;
nuclear@0 421 pthread_mutex_unlock(&node->cache_list_lock);
nuclear@0 422
nuclear@0 423 cache->inv_time = ANM_TIME_INVAL;
nuclear@2 424 cache->inv_time = ANM_TIME_INVAL;
nuclear@0 425 pthread_setspecific(node->cache_key, cache);
nuclear@0 426 }
nuclear@0 427
nuclear@0 428 if(cache->inv_time != tm) {
nuclear@0 429 anm_get_matrix(node, mat, tm);
nuclear@0 430 m4_inverse(cache->inv_matrix, mat);
nuclear@0 431 cache->inv_time = tm;
nuclear@0 432 }
nuclear@0 433 m4_copy(mat, cache->inv_matrix);
nuclear@0 434 }
nuclear@0 435
nuclear@0 436 anm_time_t anm_get_start_time(struct anm_node *node)
nuclear@0 437 {
nuclear@0 438 int i;
nuclear@0 439 struct anm_node *c;
nuclear@0 440 anm_time_t res = LONG_MAX;
nuclear@0 441
nuclear@0 442 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@0 443 if(node->tracks[i].count) {
nuclear@0 444 anm_time_t tm = node->tracks[i].keys[0].time;
nuclear@0 445 if(tm < res) {
nuclear@0 446 res = tm;
nuclear@0 447 }
nuclear@0 448 }
nuclear@0 449 }
nuclear@0 450
nuclear@0 451 c = node->child;
nuclear@0 452 while(c) {
nuclear@0 453 anm_time_t tm = anm_get_start_time(c);
nuclear@0 454 if(tm < res) {
nuclear@0 455 res = tm;
nuclear@0 456 }
nuclear@0 457 c = c->next;
nuclear@0 458 }
nuclear@0 459 return res;
nuclear@0 460 }
nuclear@0 461
nuclear@0 462 anm_time_t anm_get_end_time(struct anm_node *node)
nuclear@0 463 {
nuclear@0 464 int i;
nuclear@0 465 struct anm_node *c;
nuclear@0 466 anm_time_t res = LONG_MIN;
nuclear@0 467
nuclear@0 468 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@0 469 if(node->tracks[i].count) {
nuclear@0 470 anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time;
nuclear@0 471 if(tm > res) {
nuclear@0 472 res = tm;
nuclear@0 473 }
nuclear@0 474 }
nuclear@0 475 }
nuclear@0 476
nuclear@0 477 c = node->child;
nuclear@0 478 while(c) {
nuclear@0 479 anm_time_t tm = anm_get_end_time(c);
nuclear@0 480 if(tm > res) {
nuclear@0 481 res = tm;
nuclear@0 482 }
nuclear@0 483 c = c->next;
nuclear@0 484 }
nuclear@0 485 return res;
nuclear@0 486 }
nuclear@0 487
nuclear@0 488 static void invalidate_cache(struct anm_node *node)
nuclear@0 489 {
nuclear@0 490 struct mat_cache *cache = pthread_getspecific(node->cache_key);
nuclear@0 491 if(cache) {
nuclear@0 492 cache->time = ANM_TIME_INVAL;
nuclear@0 493 }
nuclear@0 494 }