libanim

annotate src/anim.c @ 24:09e267e7ed4a

implemented high-level animation blending interface
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 30 Dec 2013 15:20:31 +0200
parents 203c11299586
children ebead35c9eb1
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@21 11 int anm_init_animation(struct anm_animation *anim)
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@21 20 anim->name = 0;
nuclear@21 21
nuclear@21 22 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@21 23 if(anm_init_track(anim->tracks + i) == -1) {
nuclear@21 24 for(j=0; j<i; j++) {
nuclear@21 25 anm_destroy_track(anim->tracks + i);
nuclear@21 26 }
nuclear@21 27 }
nuclear@21 28 anm_set_track_default(anim->tracks + i, defaults[i]);
nuclear@21 29 }
nuclear@21 30 return 0;
nuclear@21 31 }
nuclear@21 32
nuclear@21 33 void anm_destroy_animation(struct anm_animation *anim)
nuclear@21 34 {
nuclear@21 35 int i;
nuclear@21 36 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@21 37 anm_destroy_track(anim->tracks + i);
nuclear@21 38 }
nuclear@21 39 free(anim->name);
nuclear@21 40 }
nuclear@21 41
nuclear@22 42 void anm_set_animation_name(struct anm_animation *anim, const char *name)
nuclear@22 43 {
nuclear@22 44 char *newname = malloc(strlen(name) + 1);
nuclear@22 45 if(!newname) return;
nuclear@22 46
nuclear@23 47 strcpy(newname, name);
nuclear@23 48
nuclear@22 49 free(anim->name);
nuclear@22 50 anim->name = newname;
nuclear@22 51 }
nuclear@22 52
nuclear@21 53 /* ---- node implementation ----- */
nuclear@21 54
nuclear@21 55 int anm_init_node(struct anm_node *node)
nuclear@21 56 {
nuclear@0 57 memset(node, 0, sizeof *node);
nuclear@0 58
nuclear@21 59 node->cur_anim[1] = -1;
nuclear@21 60
nuclear@21 61 if(!(node->animations = dynarr_alloc(1, sizeof *node->animations))) {
nuclear@21 62 return -1;
nuclear@21 63 }
nuclear@21 64 if(anm_init_animation(node->animations) == -1) {
nuclear@21 65 dynarr_free(node->animations);
nuclear@21 66 return -1;
nuclear@21 67 }
nuclear@21 68
nuclear@0 69 /* initialize thread-local matrix cache */
nuclear@0 70 pthread_key_create(&node->cache_key, 0);
nuclear@10 71 pthread_mutex_init(&node->cache_list_lock, 0);
nuclear@0 72
nuclear@0 73 return 0;
nuclear@0 74 }
nuclear@0 75
nuclear@0 76 void anm_destroy_node(struct anm_node *node)
nuclear@0 77 {
nuclear@0 78 int i;
nuclear@0 79 free(node->name);
nuclear@0 80
nuclear@0 81 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@21 82 anm_destroy_animation(node->animations + i);
nuclear@0 83 }
nuclear@21 84 dynarr_free(node->animations);
nuclear@0 85
nuclear@0 86 /* destroy thread-specific cache */
nuclear@0 87 pthread_key_delete(node->cache_key);
nuclear@0 88
nuclear@0 89 while(node->cache_list) {
nuclear@0 90 struct mat_cache *tmp = node->cache_list;
nuclear@0 91 node->cache_list = tmp->next;
nuclear@0 92 free(tmp);
nuclear@0 93 }
nuclear@0 94 }
nuclear@0 95
nuclear@0 96 void anm_destroy_node_tree(struct anm_node *tree)
nuclear@0 97 {
nuclear@0 98 struct anm_node *c, *tmp;
nuclear@0 99
nuclear@0 100 if(!tree) return;
nuclear@0 101
nuclear@0 102 c = tree->child;
nuclear@0 103 while(c) {
nuclear@0 104 tmp = c;
nuclear@0 105 c = c->next;
nuclear@0 106
nuclear@0 107 anm_destroy_node_tree(tmp);
nuclear@0 108 }
nuclear@0 109 anm_destroy_node(tree);
nuclear@0 110 }
nuclear@0 111
nuclear@0 112 struct anm_node *anm_create_node(void)
nuclear@0 113 {
nuclear@0 114 struct anm_node *n;
nuclear@0 115
nuclear@0 116 if((n = malloc(sizeof *n))) {
nuclear@0 117 if(anm_init_node(n) == -1) {
nuclear@0 118 free(n);
nuclear@0 119 return 0;
nuclear@0 120 }
nuclear@0 121 }
nuclear@0 122 return n;
nuclear@0 123 }
nuclear@0 124
nuclear@0 125 void anm_free_node(struct anm_node *node)
nuclear@0 126 {
nuclear@0 127 anm_destroy_node(node);
nuclear@0 128 free(node);
nuclear@0 129 }
nuclear@0 130
nuclear@0 131 void anm_free_node_tree(struct anm_node *tree)
nuclear@0 132 {
nuclear@0 133 struct anm_node *c, *tmp;
nuclear@0 134
nuclear@0 135 if(!tree) return;
nuclear@0 136
nuclear@0 137 c = tree->child;
nuclear@0 138 while(c) {
nuclear@0 139 tmp = c;
nuclear@0 140 c = c->next;
nuclear@0 141
nuclear@0 142 anm_free_node_tree(tmp);
nuclear@0 143 }
nuclear@0 144
nuclear@0 145 anm_free_node(tree);
nuclear@0 146 }
nuclear@0 147
nuclear@0 148 int anm_set_node_name(struct anm_node *node, const char *name)
nuclear@0 149 {
nuclear@0 150 char *str;
nuclear@0 151
nuclear@0 152 if(!(str = malloc(strlen(name) + 1))) {
nuclear@0 153 return -1;
nuclear@0 154 }
nuclear@0 155 strcpy(str, name);
nuclear@0 156 free(node->name);
nuclear@0 157 node->name = str;
nuclear@0 158 return 0;
nuclear@0 159 }
nuclear@0 160
nuclear@0 161 const char *anm_get_node_name(struct anm_node *node)
nuclear@0 162 {
nuclear@0 163 return node->name ? node->name : "";
nuclear@0 164 }
nuclear@0 165
nuclear@0 166 void anm_link_node(struct anm_node *p, struct anm_node *c)
nuclear@0 167 {
nuclear@0 168 c->next = p->child;
nuclear@0 169 p->child = c;
nuclear@0 170
nuclear@0 171 c->parent = p;
nuclear@0 172 invalidate_cache(c);
nuclear@0 173 }
nuclear@0 174
nuclear@0 175 int anm_unlink_node(struct anm_node *p, struct anm_node *c)
nuclear@0 176 {
nuclear@0 177 struct anm_node *iter;
nuclear@0 178
nuclear@0 179 if(p->child == c) {
nuclear@0 180 p->child = c->next;
nuclear@0 181 c->next = 0;
nuclear@0 182 invalidate_cache(c);
nuclear@0 183 return 0;
nuclear@0 184 }
nuclear@0 185
nuclear@0 186 iter = p->child;
nuclear@0 187 while(iter->next) {
nuclear@0 188 if(iter->next == c) {
nuclear@0 189 iter->next = c->next;
nuclear@0 190 c->next = 0;
nuclear@0 191 invalidate_cache(c);
nuclear@0 192 return 0;
nuclear@0 193 }
nuclear@0 194 }
nuclear@0 195 return -1;
nuclear@0 196 }
nuclear@0 197
nuclear@21 198 void anm_set_pivot(struct anm_node *node, vec3_t piv)
nuclear@21 199 {
nuclear@21 200 node->pivot = piv;
nuclear@21 201 }
nuclear@21 202
nuclear@21 203 vec3_t anm_get_pivot(struct anm_node *node)
nuclear@21 204 {
nuclear@21 205 return node->pivot;
nuclear@21 206 }
nuclear@21 207
nuclear@21 208
nuclear@21 209 /* animation management */
nuclear@21 210
nuclear@21 211 int anm_use_node_animation(struct anm_node *node, int aidx)
nuclear@21 212 {
nuclear@21 213 if(aidx == node->cur_anim[0] && node->cur_anim[1] == -1) {
nuclear@21 214 return 0; /* no change, no invalidation */
nuclear@21 215 }
nuclear@21 216
nuclear@21 217 if(aidx < 0 || aidx >= anm_get_animation_count(node)) {
nuclear@21 218 return -1;
nuclear@21 219 }
nuclear@21 220
nuclear@21 221 node->cur_anim[0] = aidx;
nuclear@21 222 node->cur_anim[1] = -1;
nuclear@21 223 node->cur_mix = 0;
nuclear@24 224 node->blend_dur = -1;
nuclear@21 225
nuclear@21 226 invalidate_cache(node);
nuclear@21 227 return 0;
nuclear@21 228 }
nuclear@21 229
nuclear@21 230 int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t)
nuclear@21 231 {
nuclear@21 232 int num_anim;
nuclear@21 233
nuclear@21 234 if(node->cur_anim[0] == aidx && node->cur_anim[1] == bidx &&
nuclear@21 235 fabs(t - node->cur_mix) < 1e-6) {
nuclear@21 236 return 0; /* no change, no invalidation */
nuclear@21 237 }
nuclear@21 238
nuclear@21 239 num_anim = anm_get_animation_count(node);
nuclear@21 240 if(aidx < 0 || aidx >= num_anim) {
nuclear@21 241 return anm_use_animation(node, bidx);
nuclear@21 242 }
nuclear@21 243 if(bidx < 0 || bidx >= num_anim) {
nuclear@21 244 return anm_use_animation(node, aidx);
nuclear@21 245 }
nuclear@21 246 node->cur_anim[0] = aidx;
nuclear@21 247 node->cur_anim[1] = bidx;
nuclear@21 248 node->cur_mix = t;
nuclear@21 249
nuclear@21 250 invalidate_cache(node);
nuclear@21 251 return 0;
nuclear@21 252 }
nuclear@21 253
nuclear@21 254 int anm_use_animation(struct anm_node *node, int aidx)
nuclear@21 255 {
nuclear@21 256 struct anm_node *child;
nuclear@21 257
nuclear@21 258 if(anm_use_node_animation(node, aidx) == -1) {
nuclear@21 259 return -1;
nuclear@21 260 }
nuclear@21 261
nuclear@21 262 child = node->child;
nuclear@21 263 while(child) {
nuclear@21 264 if(anm_use_animation(child, aidx) == -1) {
nuclear@21 265 return -1;
nuclear@21 266 }
nuclear@21 267 child = child->next;
nuclear@21 268 }
nuclear@21 269 return 0;
nuclear@21 270 }
nuclear@21 271
nuclear@21 272 int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t)
nuclear@21 273 {
nuclear@21 274 struct anm_node *child;
nuclear@21 275
nuclear@21 276 if(anm_use_node_animations(node, aidx, bidx, t) == -1) {
nuclear@21 277 return -1;
nuclear@21 278 }
nuclear@21 279
nuclear@21 280 child = node->child;
nuclear@21 281 while(child) {
nuclear@21 282 if(anm_use_animations(child, aidx, bidx, t) == -1) {
nuclear@21 283 return -1;
nuclear@21 284 }
nuclear@21 285 child = child->next;
nuclear@21 286 }
nuclear@21 287 return 0;
nuclear@21 288
nuclear@21 289 }
nuclear@21 290
nuclear@24 291 void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which)
nuclear@24 292 {
nuclear@24 293 if(which < 0 || which >= 2) {
nuclear@24 294 return;
nuclear@24 295 }
nuclear@24 296 node->cur_anim_offset[which] = offs;
nuclear@24 297 }
nuclear@24 298
nuclear@24 299 anm_time_t anm_get_animation_offset(const struct anm_node *node, int which)
nuclear@24 300 {
nuclear@24 301 if(which < 0 || which >= 2) {
nuclear@24 302 return 0;
nuclear@24 303 }
nuclear@24 304 return node->cur_anim_offset[which];
nuclear@24 305 }
nuclear@24 306
nuclear@24 307 void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which)
nuclear@24 308 {
nuclear@24 309 struct anm_node *c = node->child;
nuclear@24 310 while(c) {
nuclear@24 311 anm_set_animation_offset(c, offs, which);
nuclear@24 312 c = c->next;
nuclear@24 313 }
nuclear@24 314
nuclear@24 315 anm_set_node_animation_offset(node, offs, which);
nuclear@24 316 }
nuclear@24 317
nuclear@24 318 int anm_get_active_animation_index(const struct anm_node *node, int which)
nuclear@21 319 {
nuclear@21 320 if(which < 0 || which >= 2) return -1;
nuclear@21 321 return node->cur_anim[which];
nuclear@21 322 }
nuclear@21 323
nuclear@24 324 struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which)
nuclear@21 325 {
nuclear@21 326 int idx = anm_get_active_animation_index(node, which);
nuclear@21 327 if(idx < 0 || idx >= anm_get_animation_count(node)) {
nuclear@21 328 return 0;
nuclear@21 329 }
nuclear@21 330 return node->animations + idx;
nuclear@21 331 }
nuclear@21 332
nuclear@24 333 float anm_get_active_animation_mix(const struct anm_node *node)
nuclear@21 334 {
nuclear@21 335 return node->cur_mix;
nuclear@21 336 }
nuclear@21 337
nuclear@24 338 int anm_get_animation_count(const struct anm_node *node)
nuclear@21 339 {
nuclear@21 340 return dynarr_size(node->animations);
nuclear@21 341 }
nuclear@21 342
nuclear@21 343 int anm_add_node_animation(struct anm_node *node)
nuclear@21 344 {
nuclear@21 345 struct anm_animation newanim;
nuclear@21 346 anm_init_animation(&newanim);
nuclear@21 347
nuclear@21 348 node->animations = dynarr_push(node->animations, &newanim);
nuclear@21 349 return 0;
nuclear@21 350 }
nuclear@21 351
nuclear@21 352 int anm_remove_node_animation(struct anm_node *node, int idx)
nuclear@21 353 {
nuclear@21 354 fprintf(stderr, "anm_remove_animation: unimplemented!");
nuclear@21 355 abort();
nuclear@21 356 return 0;
nuclear@21 357 }
nuclear@21 358
nuclear@21 359 int anm_add_animation(struct anm_node *node)
nuclear@21 360 {
nuclear@21 361 struct anm_node *child;
nuclear@21 362
nuclear@21 363 if(anm_add_node_animation(node) == -1) {
nuclear@21 364 return -1;
nuclear@21 365 }
nuclear@21 366
nuclear@21 367 child = node->child;
nuclear@21 368 while(child) {
nuclear@21 369 if(anm_add_animation(child)) {
nuclear@21 370 return -1;
nuclear@21 371 }
nuclear@21 372 child = child->next;
nuclear@21 373 }
nuclear@21 374 return 0;
nuclear@21 375 }
nuclear@21 376
nuclear@21 377 int anm_remove_animation(struct anm_node *node, int idx)
nuclear@21 378 {
nuclear@21 379 struct anm_node *child;
nuclear@21 380
nuclear@21 381 if(anm_remove_node_animation(node, idx) == -1) {
nuclear@21 382 return -1;
nuclear@21 383 }
nuclear@21 384
nuclear@21 385 child = node->child;
nuclear@21 386 while(child) {
nuclear@21 387 if(anm_remove_animation(child, idx) == -1) {
nuclear@21 388 return -1;
nuclear@21 389 }
nuclear@21 390 child = child->next;
nuclear@21 391 }
nuclear@21 392 return 0;
nuclear@21 393 }
nuclear@21 394
nuclear@21 395 struct anm_animation *anm_get_animation(struct anm_node *node, int idx)
nuclear@21 396 {
nuclear@21 397 if(idx < 0 || idx > anm_get_animation_count(node)) {
nuclear@21 398 return 0;
nuclear@21 399 }
nuclear@21 400 return node->animations + idx;
nuclear@21 401 }
nuclear@21 402
nuclear@21 403 struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name)
nuclear@21 404 {
nuclear@21 405 return anm_get_animation(node, anm_find_animation(node, name));
nuclear@21 406 }
nuclear@21 407
nuclear@21 408 int anm_find_animation(struct anm_node *node, const char *name)
nuclear@21 409 {
nuclear@21 410 int i, count = anm_get_animation_count(node);
nuclear@21 411 for(i=0; i<count; i++) {
nuclear@21 412 if(strcmp(node->animations[i].name, name) == 0) {
nuclear@21 413 return i;
nuclear@21 414 }
nuclear@21 415 }
nuclear@21 416 return -1;
nuclear@21 417 }
nuclear@21 418
nuclear@21 419 /* all the rest act on the current animation(s) */
nuclear@21 420
nuclear@21 421 void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in)
nuclear@21 422 {
nuclear@21 423 int i;
nuclear@21 424 struct anm_animation *anim = anm_get_active_animation(node, 0);
nuclear@21 425 if(!anim) return;
nuclear@21 426
nuclear@21 427 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@21 428 anm_set_track_interpolator(anim->tracks + i, in);
nuclear@21 429 }
nuclear@21 430 invalidate_cache(node);
nuclear@21 431 }
nuclear@21 432
nuclear@21 433 void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex)
nuclear@21 434 {
nuclear@21 435 int i;
nuclear@21 436 struct anm_animation *anim = anm_get_active_animation(node, 0);
nuclear@21 437 if(!anim) return;
nuclear@21 438
nuclear@21 439 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@21 440 anm_set_track_extrapolator(anim->tracks + i, ex);
nuclear@21 441 }
nuclear@21 442 invalidate_cache(node);
nuclear@21 443 }
nuclear@21 444
nuclear@23 445 void anm_set_node_active_animation_name(struct anm_node *node, const char *name)
nuclear@23 446 {
nuclear@23 447 struct anm_animation *anim = anm_get_active_animation(node, 0);
nuclear@23 448 if(!anim) return;
nuclear@23 449
nuclear@23 450 anm_set_animation_name(anim, name);
nuclear@23 451 }
nuclear@23 452
nuclear@23 453 void anm_set_active_animation_name(struct anm_node *node, const char *name)
nuclear@23 454 {
nuclear@23 455 struct anm_node *child;
nuclear@23 456
nuclear@23 457 anm_set_node_active_animation_name(node, name);
nuclear@23 458
nuclear@23 459 child = node->child;
nuclear@23 460 while(child) {
nuclear@23 461 anm_set_active_animation_name(child, name);
nuclear@23 462 child = child->next;
nuclear@23 463 }
nuclear@23 464 }
nuclear@23 465
nuclear@23 466 const char *anm_get_active_animation_name(struct anm_node *node)
nuclear@23 467 {
nuclear@23 468 struct anm_animation *anim = anm_get_active_animation(node, 0);
nuclear@23 469 if(anim) {
nuclear@23 470 return anim->name;
nuclear@23 471 }
nuclear@23 472 return 0;
nuclear@23 473 }
nuclear@23 474
nuclear@24 475 /* ---- high level animation blending ---- */
nuclear@24 476 void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur)
nuclear@24 477 {
nuclear@24 478 struct anm_node *c = node->child;
nuclear@24 479 while(c) {
nuclear@24 480 anm_transition(c, anmidx, start, dur);
nuclear@24 481 c = c->next;
nuclear@24 482 }
nuclear@24 483
nuclear@24 484 anm_node_transition(node, anmidx, start, dur);
nuclear@24 485 }
nuclear@24 486
nuclear@24 487 void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur)
nuclear@24 488 {
nuclear@24 489 node->cur_anim[1] = anmidx;
nuclear@24 490 node->cur_anim_offset[1] = start;
nuclear@24 491 node->blend_dur = dur;
nuclear@24 492 }
nuclear@24 493
nuclear@24 494
nuclear@24 495 #define BLEND_START_TM node->cur_anim_offset[1]
nuclear@24 496
nuclear@24 497 static anm_time_t animation_time(struct anm_node *node, anm_time_t tm, int which)
nuclear@24 498 {
nuclear@24 499 float t;
nuclear@24 500
nuclear@24 501 if(node->blend_dur >= 0) {
nuclear@24 502 /* we're in transition... */
nuclear@24 503 t = (float)(tm - BLEND_START_TM) / (float)node->blend_dur;
nuclear@24 504 if(t < 0.0) t = 0.0;
nuclear@24 505
nuclear@24 506 node->cur_mix = t;
nuclear@24 507
nuclear@24 508 if(t > 1.0) {
nuclear@24 509 /* switch completely over to the target animation and stop blending */
nuclear@24 510 anm_use_node_animation(node, node->cur_anim[1]);
nuclear@24 511 node->cur_anim_offset[0] = node->cur_anim_offset[1];
nuclear@24 512 }
nuclear@24 513 }
nuclear@24 514
nuclear@24 515 return tm - node->cur_anim_offset[which];
nuclear@24 516 }
nuclear@24 517
nuclear@24 518
nuclear@0 519 void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm)
nuclear@0 520 {
nuclear@21 521 struct anm_animation *anim = anm_get_active_animation(node, 0);
nuclear@21 522 if(!anim) return;
nuclear@21 523
nuclear@21 524 anm_set_value(anim->tracks + ANM_TRACK_POS_X, tm, pos.x);
nuclear@21 525 anm_set_value(anim->tracks + ANM_TRACK_POS_Y, tm, pos.y);
nuclear@21 526 anm_set_value(anim->tracks + ANM_TRACK_POS_Z, tm, pos.z);
nuclear@0 527 invalidate_cache(node);
nuclear@0 528 }
nuclear@0 529
nuclear@24 530
nuclear@0 531 vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm)
nuclear@0 532 {
nuclear@0 533 vec3_t v;
nuclear@24 534 anm_time_t tm0 = animation_time(node, tm, 0);
nuclear@21 535 struct anm_animation *anim0 = anm_get_active_animation(node, 0);
nuclear@21 536 struct anm_animation *anim1 = anm_get_active_animation(node, 1);
nuclear@21 537
nuclear@21 538 if(!anim0) {
nuclear@21 539 return v3_cons(0, 0, 0);
nuclear@21 540 }
nuclear@21 541
nuclear@24 542 v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm0);
nuclear@24 543 v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm0);
nuclear@24 544 v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm0);
nuclear@21 545
nuclear@21 546 if(anim1) {
nuclear@21 547 vec3_t v1;
nuclear@24 548 anm_time_t tm1 = animation_time(node, tm, 1);
nuclear@24 549 v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm1);
nuclear@24 550 v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm1);
nuclear@24 551 v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm1);
nuclear@21 552
nuclear@21 553 v.x = v.x + (v1.x - v.x) * node->cur_mix;
nuclear@21 554 v.y = v.y + (v1.y - v.y) * node->cur_mix;
nuclear@21 555 v.z = v.z + (v1.z - v.z) * node->cur_mix;
nuclear@21 556 }
nuclear@21 557
nuclear@0 558 return v;
nuclear@0 559 }
nuclear@0 560
nuclear@0 561 void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm)
nuclear@0 562 {
nuclear@21 563 struct anm_animation *anim = anm_get_active_animation(node, 0);
nuclear@21 564 if(!anim) return;
nuclear@21 565
nuclear@21 566 anm_set_value(anim->tracks + ANM_TRACK_ROT_X, tm, rot.x);
nuclear@21 567 anm_set_value(anim->tracks + ANM_TRACK_ROT_Y, tm, rot.y);
nuclear@21 568 anm_set_value(anim->tracks + ANM_TRACK_ROT_Z, tm, rot.z);
nuclear@21 569 anm_set_value(anim->tracks + ANM_TRACK_ROT_W, tm, rot.w);
nuclear@0 570 invalidate_cache(node);
nuclear@0 571 }
nuclear@0 572
nuclear@21 573 static quat_t get_node_rotation(struct anm_node *node, anm_time_t tm, struct anm_animation *anim)
nuclear@0 574 {
nuclear@7 575 #ifndef ROT_USE_SLERP
nuclear@7 576 quat_t q;
nuclear@21 577 q.x = anm_get_value(anim->tracks + ANM_TRACK_ROT_X, tm);
nuclear@21 578 q.y = anm_get_value(anim->tracks + ANM_TRACK_ROT_Y, tm);
nuclear@21 579 q.z = anm_get_value(anim->tracks + ANM_TRACK_ROT_Z, tm);
nuclear@21 580 q.w = anm_get_value(anim->tracks + ANM_TRACK_ROT_W, tm);
nuclear@7 581 return q;
nuclear@7 582 #else
nuclear@0 583 int idx0, idx1, last_idx;
nuclear@0 584 anm_time_t tstart, tend;
nuclear@0 585 float t, dt;
nuclear@0 586 struct anm_track *track_x, *track_y, *track_z, *track_w;
nuclear@0 587 quat_t q, q1, q2;
nuclear@0 588
nuclear@21 589 track_x = anim->tracks + ANM_TRACK_ROT_X;
nuclear@21 590 track_y = anim->tracks + ANM_TRACK_ROT_Y;
nuclear@21 591 track_z = anim->tracks + ANM_TRACK_ROT_Z;
nuclear@21 592 track_w = anim->tracks + ANM_TRACK_ROT_W;
nuclear@0 593
nuclear@0 594 if(!track_x->count) {
nuclear@0 595 q.x = track_x->def_val;
nuclear@0 596 q.y = track_y->def_val;
nuclear@0 597 q.z = track_z->def_val;
nuclear@0 598 q.w = track_w->def_val;
nuclear@0 599 return q;
nuclear@0 600 }
nuclear@0 601
nuclear@0 602 last_idx = track_x->count - 1;
nuclear@0 603
nuclear@0 604 tstart = track_x->keys[0].time;
nuclear@0 605 tend = track_x->keys[last_idx].time;
nuclear@6 606
nuclear@6 607 if(tstart == tend) {
nuclear@6 608 q.x = track_x->keys[0].val;
nuclear@6 609 q.y = track_y->keys[0].val;
nuclear@6 610 q.z = track_z->keys[0].val;
nuclear@6 611 q.w = track_w->keys[0].val;
nuclear@6 612 return q;
nuclear@6 613 }
nuclear@6 614
nuclear@0 615 tm = anm_remap_time(track_x, tm, tstart, tend);
nuclear@0 616
nuclear@0 617 idx0 = anm_get_key_interval(track_x, tm);
nuclear@0 618 assert(idx0 >= 0 && idx0 < track_x->count);
nuclear@0 619 idx1 = idx0 + 1;
nuclear@0 620
nuclear@6 621 if(idx0 == last_idx) {
nuclear@6 622 q.x = track_x->keys[idx0].val;
nuclear@6 623 q.y = track_y->keys[idx0].val;
nuclear@6 624 q.z = track_z->keys[idx0].val;
nuclear@6 625 q.w = track_w->keys[idx0].val;
nuclear@6 626 return q;
nuclear@6 627 }
nuclear@6 628
nuclear@0 629 dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time);
nuclear@0 630 t = (float)(tm - track_x->keys[idx0].time) / dt;
nuclear@0 631
nuclear@0 632 q1.x = track_x->keys[idx0].val;
nuclear@0 633 q1.y = track_y->keys[idx0].val;
nuclear@0 634 q1.z = track_z->keys[idx0].val;
nuclear@0 635 q1.w = track_w->keys[idx0].val;
nuclear@0 636
nuclear@0 637 q2.x = track_x->keys[idx1].val;
nuclear@0 638 q2.y = track_y->keys[idx1].val;
nuclear@0 639 q2.z = track_z->keys[idx1].val;
nuclear@0 640 q2.w = track_w->keys[idx1].val;
nuclear@0 641
nuclear@9 642 /*q1 = quat_normalize(q1);
nuclear@9 643 q2 = quat_normalize(q2);*/
nuclear@9 644
nuclear@0 645 return quat_slerp(q1, q2, t);
nuclear@7 646 #endif
nuclear@0 647 }
nuclear@0 648
nuclear@21 649 quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm)
nuclear@21 650 {
nuclear@21 651 quat_t q;
nuclear@24 652 anm_time_t tm0 = animation_time(node, tm, 0);
nuclear@21 653 struct anm_animation *anim0 = anm_get_active_animation(node, 0);
nuclear@21 654 struct anm_animation *anim1 = anm_get_active_animation(node, 1);
nuclear@21 655
nuclear@21 656 if(!anim0) {
nuclear@21 657 return quat_identity();
nuclear@21 658 }
nuclear@21 659
nuclear@24 660 q = get_node_rotation(node, tm0, anim0);
nuclear@21 661
nuclear@21 662 if(anim1) {
nuclear@24 663 anm_time_t tm1 = animation_time(node, tm, 1);
nuclear@24 664 quat_t q1 = get_node_rotation(node, tm1, anim1);
nuclear@21 665
nuclear@21 666 q = quat_slerp(q, q1, node->cur_mix);
nuclear@21 667 }
nuclear@21 668 return q;
nuclear@21 669 }
nuclear@21 670
nuclear@0 671 void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm)
nuclear@0 672 {
nuclear@21 673 struct anm_animation *anim = anm_get_active_animation(node, 0);
nuclear@21 674 if(!anim) return;
nuclear@21 675
nuclear@21 676 anm_set_value(anim->tracks + ANM_TRACK_SCL_X, tm, scl.x);
nuclear@21 677 anm_set_value(anim->tracks + ANM_TRACK_SCL_Y, tm, scl.y);
nuclear@21 678 anm_set_value(anim->tracks + ANM_TRACK_SCL_Z, tm, scl.z);
nuclear@0 679 invalidate_cache(node);
nuclear@0 680 }
nuclear@0 681
nuclear@0 682 vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm)
nuclear@0 683 {
nuclear@0 684 vec3_t v;
nuclear@24 685 anm_time_t tm0 = animation_time(node, tm, 0);
nuclear@21 686 struct anm_animation *anim0 = anm_get_active_animation(node, 0);
nuclear@21 687 struct anm_animation *anim1 = anm_get_active_animation(node, 1);
nuclear@21 688
nuclear@21 689 if(!anim0) {
nuclear@21 690 return v3_cons(1, 1, 1);
nuclear@21 691 }
nuclear@21 692
nuclear@24 693 v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm0);
nuclear@24 694 v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm0);
nuclear@24 695 v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm0);
nuclear@21 696
nuclear@21 697 if(anim1) {
nuclear@21 698 vec3_t v1;
nuclear@24 699 anm_time_t tm1 = animation_time(node, tm, 1);
nuclear@24 700 v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm1);
nuclear@24 701 v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm1);
nuclear@24 702 v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm1);
nuclear@21 703
nuclear@21 704 v.x = v.x + (v1.x - v.x) * node->cur_mix;
nuclear@21 705 v.y = v.y + (v1.y - v.y) * node->cur_mix;
nuclear@21 706 v.z = v.z + (v1.z - v.z) * node->cur_mix;
nuclear@21 707 }
nuclear@21 708
nuclear@0 709 return v;
nuclear@0 710 }
nuclear@0 711
nuclear@0 712
nuclear@0 713 vec3_t anm_get_position(struct anm_node *node, anm_time_t tm)
nuclear@0 714 {
nuclear@0 715 mat4_t xform;
nuclear@0 716 vec3_t pos = {0.0, 0.0, 0.0};
nuclear@0 717
nuclear@0 718 if(!node->parent) {
nuclear@0 719 return anm_get_node_position(node, tm);
nuclear@0 720 }
nuclear@0 721
nuclear@0 722 anm_get_matrix(node, xform, tm);
nuclear@0 723 return v3_transform(pos, xform);
nuclear@0 724 }
nuclear@0 725
nuclear@0 726 quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm)
nuclear@0 727 {
nuclear@0 728 quat_t rot, prot;
nuclear@0 729 rot = anm_get_node_rotation(node, tm);
nuclear@0 730
nuclear@0 731 if(!node->parent) {
nuclear@0 732 return rot;
nuclear@0 733 }
nuclear@0 734
nuclear@0 735 prot = anm_get_rotation(node->parent, tm);
nuclear@0 736 return quat_mul(prot, rot);
nuclear@0 737 }
nuclear@0 738
nuclear@0 739 vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm)
nuclear@0 740 {
nuclear@0 741 vec3_t s, ps;
nuclear@0 742 s = anm_get_node_scaling(node, tm);
nuclear@0 743
nuclear@0 744 if(!node->parent) {
nuclear@0 745 return s;
nuclear@0 746 }
nuclear@0 747
nuclear@0 748 ps = anm_get_scaling(node->parent, tm);
nuclear@0 749 return v3_mul(s, ps);
nuclear@0 750 }
nuclear@0 751
nuclear@5 752 void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
nuclear@5 753 {
nuclear@9 754 int i;
nuclear@9 755 mat4_t rmat;
nuclear@5 756 vec3_t pos, scale;
nuclear@5 757 quat_t rot;
nuclear@5 758
nuclear@5 759 pos = anm_get_node_position(node, tm);
nuclear@5 760 rot = anm_get_node_rotation(node, tm);
nuclear@5 761 scale = anm_get_node_scaling(node, tm);
nuclear@5 762
nuclear@9 763 m4_set_translation(mat, node->pivot.x, node->pivot.y, node->pivot.z);
nuclear@5 764
nuclear@5 765 quat_to_mat4(rmat, rot);
nuclear@9 766 for(i=0; i<3; i++) {
nuclear@9 767 mat[i][0] = rmat[i][0];
nuclear@9 768 mat[i][1] = rmat[i][1];
nuclear@9 769 mat[i][2] = rmat[i][2];
nuclear@9 770 }
nuclear@9 771 /* this loop is equivalent to: m4_mult(mat, mat, rmat); */
nuclear@5 772
nuclear@9 773 mat[0][0] *= scale.x; mat[0][1] *= scale.y; mat[0][2] *= scale.z; mat[0][3] += pos.x;
nuclear@9 774 mat[1][0] *= scale.x; mat[1][1] *= scale.y; mat[1][2] *= scale.z; mat[1][3] += pos.y;
nuclear@9 775 mat[2][0] *= scale.x; mat[2][1] *= scale.y; mat[2][2] *= scale.z; mat[2][3] += pos.z;
nuclear@9 776
nuclear@9 777 m4_translate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z);
nuclear@9 778
nuclear@9 779 /* that's basically: pivot * rotation * translation * scaling * -pivot */
nuclear@5 780 }
nuclear@5 781
nuclear@5 782 void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
nuclear@5 783 {
nuclear@5 784 mat4_t tmp;
nuclear@5 785 anm_get_node_matrix(node, tmp, tm);
nuclear@5 786 m4_inverse(mat, tmp);
nuclear@5 787 }
nuclear@5 788
nuclear@20 789 void anm_eval_node(struct anm_node *node, anm_time_t tm)
nuclear@20 790 {
nuclear@20 791 anm_get_node_matrix(node, node->matrix, tm);
nuclear@20 792 }
nuclear@20 793
nuclear@20 794 void anm_eval(struct anm_node *node, anm_time_t tm)
nuclear@20 795 {
nuclear@20 796 struct anm_node *c;
nuclear@20 797
nuclear@20 798 anm_eval_node(node, tm);
nuclear@20 799
nuclear@20 800 if(node->parent) {
nuclear@20 801 /* due to post-order traversal, the parent matrix is already evaluated */
nuclear@20 802 m4_mult(node->matrix, node->parent->matrix, node->matrix);
nuclear@20 803 }
nuclear@20 804
nuclear@20 805 /* recersively evaluate all children */
nuclear@20 806 c = node->child;
nuclear@20 807 while(c) {
nuclear@20 808 anm_eval(c, tm);
nuclear@20 809 c = c->next;
nuclear@20 810 }
nuclear@20 811 }
nuclear@20 812
nuclear@0 813 void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
nuclear@0 814 {
nuclear@0 815 struct mat_cache *cache = pthread_getspecific(node->cache_key);
nuclear@0 816 if(!cache) {
nuclear@0 817 cache = malloc(sizeof *cache);
nuclear@0 818 assert(cache);
nuclear@0 819
nuclear@0 820 pthread_mutex_lock(&node->cache_list_lock);
nuclear@0 821 cache->next = node->cache_list;
nuclear@0 822 node->cache_list = cache;
nuclear@0 823 pthread_mutex_unlock(&node->cache_list_lock);
nuclear@0 824
nuclear@0 825 cache->time = ANM_TIME_INVAL;
nuclear@2 826 cache->inv_time = ANM_TIME_INVAL;
nuclear@0 827 pthread_setspecific(node->cache_key, cache);
nuclear@0 828 }
nuclear@0 829
nuclear@0 830 if(cache->time != tm) {
nuclear@5 831 anm_get_node_matrix(node, cache->matrix, tm);
nuclear@0 832
nuclear@0 833 if(node->parent) {
nuclear@0 834 mat4_t parent_mat;
nuclear@0 835
nuclear@4 836 anm_get_matrix(node->parent, parent_mat, tm);
nuclear@0 837 m4_mult(cache->matrix, parent_mat, cache->matrix);
nuclear@0 838 }
nuclear@0 839 cache->time = tm;
nuclear@0 840 }
nuclear@0 841 m4_copy(mat, cache->matrix);
nuclear@0 842 }
nuclear@0 843
nuclear@0 844 void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
nuclear@0 845 {
nuclear@0 846 struct mat_cache *cache = pthread_getspecific(node->cache_key);
nuclear@0 847 if(!cache) {
nuclear@0 848 cache = malloc(sizeof *cache);
nuclear@0 849 assert(cache);
nuclear@0 850
nuclear@0 851 pthread_mutex_lock(&node->cache_list_lock);
nuclear@0 852 cache->next = node->cache_list;
nuclear@0 853 node->cache_list = cache;
nuclear@0 854 pthread_mutex_unlock(&node->cache_list_lock);
nuclear@0 855
nuclear@0 856 cache->inv_time = ANM_TIME_INVAL;
nuclear@2 857 cache->inv_time = ANM_TIME_INVAL;
nuclear@0 858 pthread_setspecific(node->cache_key, cache);
nuclear@0 859 }
nuclear@0 860
nuclear@0 861 if(cache->inv_time != tm) {
nuclear@0 862 anm_get_matrix(node, mat, tm);
nuclear@0 863 m4_inverse(cache->inv_matrix, mat);
nuclear@0 864 cache->inv_time = tm;
nuclear@0 865 }
nuclear@0 866 m4_copy(mat, cache->inv_matrix);
nuclear@0 867 }
nuclear@0 868
nuclear@0 869 anm_time_t anm_get_start_time(struct anm_node *node)
nuclear@0 870 {
nuclear@21 871 int i, j;
nuclear@0 872 struct anm_node *c;
nuclear@0 873 anm_time_t res = LONG_MAX;
nuclear@0 874
nuclear@21 875 for(j=0; j<2; j++) {
nuclear@21 876 struct anm_animation *anim = anm_get_active_animation(node, j);
nuclear@21 877 if(!anim) break;
nuclear@21 878
nuclear@21 879 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@21 880 if(anim->tracks[i].count) {
nuclear@21 881 anm_time_t tm = anim->tracks[i].keys[0].time;
nuclear@21 882 if(tm < res) {
nuclear@21 883 res = tm;
nuclear@21 884 }
nuclear@0 885 }
nuclear@0 886 }
nuclear@0 887 }
nuclear@0 888
nuclear@0 889 c = node->child;
nuclear@0 890 while(c) {
nuclear@0 891 anm_time_t tm = anm_get_start_time(c);
nuclear@0 892 if(tm < res) {
nuclear@0 893 res = tm;
nuclear@0 894 }
nuclear@0 895 c = c->next;
nuclear@0 896 }
nuclear@0 897 return res;
nuclear@0 898 }
nuclear@0 899
nuclear@0 900 anm_time_t anm_get_end_time(struct anm_node *node)
nuclear@0 901 {
nuclear@21 902 int i, j;
nuclear@0 903 struct anm_node *c;
nuclear@0 904 anm_time_t res = LONG_MIN;
nuclear@0 905
nuclear@21 906 for(j=0; j<2; j++) {
nuclear@21 907 struct anm_animation *anim = anm_get_active_animation(node, j);
nuclear@21 908 if(!anim) break;
nuclear@21 909
nuclear@21 910 for(i=0; i<ANM_NUM_TRACKS; i++) {
nuclear@21 911 if(anim->tracks[i].count) {
nuclear@21 912 anm_time_t tm = anim->tracks[i].keys[anim->tracks[i].count - 1].time;
nuclear@21 913 if(tm > res) {
nuclear@21 914 res = tm;
nuclear@21 915 }
nuclear@0 916 }
nuclear@0 917 }
nuclear@0 918 }
nuclear@0 919
nuclear@0 920 c = node->child;
nuclear@0 921 while(c) {
nuclear@0 922 anm_time_t tm = anm_get_end_time(c);
nuclear@0 923 if(tm > res) {
nuclear@0 924 res = tm;
nuclear@0 925 }
nuclear@0 926 c = c->next;
nuclear@0 927 }
nuclear@0 928 return res;
nuclear@0 929 }
nuclear@0 930
nuclear@0 931 static void invalidate_cache(struct anm_node *node)
nuclear@0 932 {
nuclear@0 933 struct mat_cache *cache = pthread_getspecific(node->cache_key);
nuclear@0 934 if(cache) {
nuclear@13 935 cache->time = cache->inv_time = ANM_TIME_INVAL;
nuclear@0 936 }
nuclear@0 937 }