diff --git a/src/k3.c b/src/k3.c index 9164acf..1efaab3 100644 --- a/src/k3.c +++ b/src/k3.c @@ -294,12 +294,12 @@ struct k3Mesh *k3MdlGetMeshes(struct k3Mdl *mdl, size_t *count) { return mdl->meshes; } -void k3MdlAddAnim(struct k3Mdl *mdl, struct k3Animation *anim) { +void k3MdlAddAnim(struct k3Mdl *mdl, struct k3AnimationFountain *anim) { mdl->anims = realloc(mdl->anims, sizeof(*mdl->anims) * (mdl->animCount + 1)); mdl->anims[mdl->animCount++] = anim; } -struct k3Animation *k3MdlGetAnim(struct k3Mdl *mdl, uint16_t id) { +struct k3AnimationFountain *k3MdlGetAnim(struct k3Mdl *mdl, uint16_t id) { for(size_t i = 0; i < mdl->animCount; i++) { if(mdl->anims[i]->id == id) { return mdl->anims[i]; @@ -311,6 +311,16 @@ size_t k3MdlGetBoneCount(struct k3Mdl *mdl) { return mdl->boneCount; } +static size_t bonenames_len(size_t boneCount, const char *names) { + const char *bn = names; + size_t sz = 0; + for(int b = 0; b < boneCount; b++) { + sz += strlen(bn) + 1; + bn += strlen(bn) + 1; + } + return sz; +} + struct k3Mdl *k3MdlCopySubs(struct k3Mdl *src) { struct k3Mdl *dst = calloc(1, sizeof(*dst)); @@ -331,9 +341,14 @@ struct k3Mdl *k3MdlCopySubs(struct k3Mdl *src) { dst->boneCount = src->boneCount; dst->invBind = malloc(sizeof(*dst->invBind) * dst->boneCount); - memcpy(dst->invBind, src->invBind, sizeof(dst->invBind) * dst->boneCount); + memcpy(dst->invBind, src->invBind, sizeof(*dst->invBind) * dst->boneCount); dst->boneParents = malloc(sizeof(*dst->boneParents) * dst->boneCount); memcpy(dst->boneParents, src->boneParents, sizeof(*dst->boneParents) * dst->boneCount); + if(src->boneNames) { + size_t sz = bonenames_len(src->boneCount, src->boneNames); + dst->boneNames = calloc(1, sz); + memcpy(dst->boneNames, src->boneNames, sz); + } dst->animCount = src->animCount; dst->anims = malloc(sizeof(*dst->anims) * dst->animCount); @@ -360,65 +375,113 @@ void k3MdlSetDebugName(struct k3Mdl *mdl, const char *name) { k3Log(k3_DEBUG, "Model name: %s", name); } -static void anim_update(struct k3Animator *anim) { - float frame = anim->time * anim->base->fps; - - size_t f0 = floorf(frame); - size_t f1 = (f0 + 1); - - float alpha = frame - f0; - - f0 %= anim->base->frameCount; - f1 %= anim->base->frameCount; - - for(size_t b = 0; b < anim->base->boneCount; b++) { - vec4 t0, t1; - glm_vec4_copy(anim->base->frames[anim->base->boneCount * f0 + b].translation, t0); - glm_vec4_copy(anim->base->frames[anim->base->boneCount * f1 + b].translation, t1); - - versor r0, r1; - glm_quat_copy(anim->base->frames[anim->base->boneCount * f0 + b].rotation, r0); - glm_quat_copy(anim->base->frames[anim->base->boneCount * f1 + b].rotation, r1); - - vec4 t; - glm_vec4_lerp(t0, t1, alpha, t); - - versor r; - glm_quat_slerp(r0, r1, alpha, r); - - mat4 m = GLM_MAT4_IDENTITY_INIT; - glm_quat_mat4(r, m); - glm_vec4_copy(t, m[3]); - - uint8_t parent = anim->base->boneParents[b]; - if(parent != 0xFF) { - glm_mat4_mul(anim->inter[parent], m, anim->inter[b]); - } else { - glm_mat4_copy(m, anim->inter[b]); +static void qwfun(struct k3Mdl *mdl, size_t b, float *output) { + for(size_t c = b + 1; c < mdl->boneCount; c++) { + if(mdl->boneParents[c] == b) { + output[c] = 1; + qwfun(mdl, c, output); } } +} +void k3MdlQueryWeights(struct k3Mdl *mdl, size_t boneIdx, float *output) { + memset(output, 0, sizeof(*output) * mdl->boneCount); - for(size_t b = 0; b < anim->base->boneCount; b++) { - glm_mat4_mul(anim->inter[b], anim->base->invBind[b], anim->inter[b]); + if(boneIdx < mdl->boneCount) { + qwfun(mdl, boneIdx, output); } } -void k3AnimatorSet(struct k3Animator *anim, float time) { - if(!anim->inter) anim->inter = _mm_malloc(sizeof(mat4) * anim->base->boneCount * anim->base->frameCount, 16); - - if(!anim->loop) { - time = fmaxf(time, 0); - time = fminf(time, (float) (anim->base->frameCount - 1) / anim->base->fps); + +intmax_t k3MdlGetBoneFromName(struct k3Mdl *mdl, const char *targetName) { + if(!mdl->boneNames) { + return -1; } - anim->time = time; + const char *name = mdl->boneNames; - anim_update(anim); + for(size_t b = 0; b < mdl->boneCount; b++) { + if(!strcmp(name, targetName)) { + return b; + } + + name = name + strlen(name) + 1; + } + + return -1; +} + +void k3MdlSetBoneNames(struct k3Mdl *mdl, const char *names) { + size_t sz = bonenames_len(mdl->boneCount, names); + mdl->boneNames = malloc(sz); + memcpy(mdl->boneNames, names, sz); +} + +void k3AnimatorInit(struct k3Animator *this, struct k3Mdl *mdl) { + this->mdl = mdl; + + size_t boneCount = k3MdlGetBoneCount(this->mdl); + this->bones = _mm_malloc(boneCount * sizeof(*this->bones), 16); + for(size_t b = 0; b < boneCount; b++) { + this->bones[b].translation[0] = 0; + this->bones[b].translation[1] = 0; + this->bones[b].translation[2] = 0; + this->bones[b].translation[3] = 1; + + this->bones[b].rotation[0] = 0; + this->bones[b].rotation[1] = 0; + this->bones[b].rotation[2] = 0; + this->bones[b].rotation[3] = 1; + } +} + +void k3AnimatorSet(struct k3Animator *anim, float time) { + anim->time = time; + k3AnimationUpdate(anim->anim, anim->time); + + if(!anim->bones) { + anim->bones = _mm_malloc(sizeof(*anim->bones) * anim->anim->bones, 16); + memset(anim->bones, 0, sizeof(*anim->bones) * anim->anim->bones); + } + + if(!anim->anim->cache) { + return; + } + + mat4 *mats = alloca(sizeof(*mats) * anim->anim->bones); + memset(mats, 0, sizeof(*mats) * anim->anim->bones); + + for(size_t b = 0; b < anim->anim->bones; b++) { + mat4 m = GLM_MAT4_IDENTITY_INIT; + glm_quat_mat4(anim->anim->cache[b].rotation, m); + glm_vec4_copy(anim->anim->cache[b].translation, m[3]); + + uint8_t parent = anim->mdl->boneParents[b]; + if(parent != 0xFF) { + glm_mat4_mul(mats[parent], m, mats[b]); + } else { + glm_mat4_copy(m, mats[b]); + } + } + for(size_t b = 0; b < anim->anim->bones; b++) { + glm_mat4_mul(mats[b], anim->mdl->invBind[b], mats[b]); + } + for(size_t b = 0; b < anim->anim->bones; b++) { + vec4 t; + mat4 r; + vec3 s; + glm_decompose(mats[b], t, r, s); + + t[3] = 1; + glm_vec4_copy(t, anim->bones[b].translation); + + versor q; + glm_mat4_quat(r, q); + glm_quat_copy(q, anim->bones[b].rotation); + } } void k3AnimatorStep(struct k3Animator *anim, float dt) { k3AnimatorSet(anim, anim->time + dt); } - struct k3Tex *k3TexCreate(enum k3TexType type) { struct k3Tex *ret = calloc(1, sizeof(*ret)); @@ -1499,7 +1562,7 @@ static void forward_subpass(mat4 projection, mat4 view, int transparent, int lig enable_glsl_tangents(glsl, mdl); } - if(k3IsSoftSkinning) { + if(k3IsSoftSkinning && bones) { apply_cpu_skinning(mdl, bones); } @@ -1694,7 +1757,7 @@ void k3PassDepthOnly(mat4 projection, mat4 cam, int clear, int cull) { enable_glsl_bones(glsl, mdl, bones); } - if(k3IsSoftSkinning) { + if(k3IsSoftSkinning && bones) { apply_cpu_skinning(mdl, bones); } @@ -1940,7 +2003,7 @@ static void pass_irregular(int passnum, mat4 mainproj, mat4 maincam, mat4 lightp enable_glsl_bones(glsl, mdl, bones); } - if(k3IsSoftSkinning) { + if(k3IsSoftSkinning && bones) { apply_cpu_skinning(mdl, bones); } diff --git a/src/k3.h b/src/k3.h index 381a835..327ded6 100644 --- a/src/k3.h +++ b/src/k3.h @@ -5,6 +5,7 @@ #include #include #include +#include"k3anim.h" #ifdef k3_MULTITHREAD #include @@ -101,27 +102,17 @@ struct k3Mat { } passes[1]; }; -struct k3AnimationBone { - vec4 translation; - versor rotation; -}; -struct k3Animation { - struct k3AnimationBone *frames; - mat4 *invBind; - uint8_t *boneParents; - uint16_t fps; - uint16_t frameCount; - uint16_t boneCount; - uint16_t id; -}; - struct k3Animator { - float time; - int loop; + struct k3Mdl *mdl; - mat4 *inter; + struct k3AnimationBone *bones; - struct k3Animation *base; + struct k3Animation *anim; + + double time; + + bool playing; + double playStartTime; }; struct k3Offscreen; @@ -170,14 +161,16 @@ struct k3Mdl *k3MdlCreate(size_t verts, size_t indices, size_t boneCount, vec3 * void k3MdlUpdatePos(struct k3Mdl *mdl, vec3 *pos); void k3MdlAddMesh(struct k3Mdl*, struct k3Mat*, uint16_t idxStart, uint16_t idxNumber); struct k3Mesh *k3MdlGetMeshes(struct k3Mdl*, size_t *count); -void k3MdlAddAnim(struct k3Mdl*, struct k3Animation*); -struct k3Animation *k3MdlGetAnim(struct k3Mdl*, uint16_t id); +void k3MdlAddAnim(struct k3Mdl*, struct k3AnimationFountain*); +struct k3AnimationFountain *k3MdlGetAnim(struct k3Mdl*, uint16_t id); size_t k3MdlGetBoneCount(struct k3Mdl*); - -void k3MdlSetDebugName(struct k3Mdl*, const char*); - struct k3Mdl *k3MdlCopySubs(struct k3Mdl*); +void k3MdlSetDebugName(struct k3Mdl*, const char*); +void k3MdlQueryWeights(struct k3Mdl*, size_t bone, float *output); +intmax_t k3MdlGetBoneFromName(struct k3Mdl*, const char*); +void k3MdlSetBoneNames(struct k3Mdl*, const char*); +void k3AnimatorInit(struct k3Animator*, struct k3Mdl*); void k3AnimatorSet(struct k3Animator*, float); void k3AnimatorStep(struct k3Animator*, float); diff --git a/src/k3_internal.h b/src/k3_internal.h index 9948e8f..da34da4 100644 --- a/src/k3_internal.h +++ b/src/k3_internal.h @@ -71,9 +71,10 @@ struct k3Mdl { size_t boneCount; mat4 *invBind; uint8_t *boneParents; + char *boneNames; uint16_t animCount; - struct k3Animation **anims; + struct k3AnimationFountain **anims; vec3 aabb[2]; diff --git a/src/k3anim.c b/src/k3anim.c new file mode 100644 index 0000000..5ae941e --- /dev/null +++ b/src/k3anim.c @@ -0,0 +1,185 @@ +#include"k3anim.h" + +#include +#include +#include +#include + +static uint32_t lcg() { + static uint32_t asdf = 0; + asdf = asdf * 1664525 + 1013904223; + return asdf; +} + +static void power_iter(mat4 M, int iterations, float eps, vec4 ev) { + // Random starting vector + uint32_t rng = lcg(); + ev[0] = (rng & 0xFF) / 255.f; + rng >>= 8; + ev[1] = (rng & 0xFF) / 255.f; + rng >>= 8; + ev[2] = (rng & 0xFF) / 255.f; + rng >>= 8; + ev[3] = rng / 255.f; + + for(int i = 0; i < iterations; i++) { + vec4 k; + glm_mat4_mulv(M, ev, k); + glm_vec4_normalize_to(k, ev); + } +} + +static void quat_to_mat(versor v, mat4 m) { + for(int y = 0; y < 4; y++) { + for(int x = 0; x < 4; x++) { + m[y][x] = v[y] * v[x]; + } + } +} + +bool k3AnimationUpdate(struct k3Animation *anim, float time) { + if(anim->op == k3_ANIM_BASIC) { + struct k3AnimationBasic *basic = (void*) anim; + struct k3AnimationFountain *fountain = basic->fountain; + + if(!anim->cache) { + anim->bones = fountain->bones; + anim->cache = _mm_malloc(sizeof(*anim->cache) * anim->bones, 16); + } + + float frame = time * fountain->fps; + + if(!basic->loop) { + frame = fminf(frame, fountain->frameCount - 1); + } + + size_t f0 = floorf(frame); + size_t f1 = (f0 + 1); + + float alpha = frame - f0; + + f0 %= fountain->frameCount; + f1 %= fountain->frameCount; + + for(size_t b = 0; b < fountain->bones; b++) { + vec4 t0, t1; + glm_vec4_copy(fountain->frames[fountain->bones * f0 + b].translation, t0); + glm_vec4_copy(fountain->frames[fountain->bones * f1 + b].translation, t1); + + versor r0, r1; + glm_quat_copy(fountain->frames[fountain->bones * f0 + b].rotation, r0); + glm_quat_copy(fountain->frames[fountain->bones * f1 + b].rotation, r1); + + glm_vec4_lerp(t0, t1, alpha, basic->cache[b].translation); + glm_quat_slerp(r0, r1, alpha, basic->cache[b].rotation); + } + + return true; + } else if(anim->op == k3_ANIM_BLEND) { + struct k3AnimationBlend *blend = (void*) anim; + + if(blend->children == 0) return false; + + if(!blend->cache || blend->bones != blend->subs[0]->bones) { + if(blend->cache) _mm_free(blend->cache); + + blend->bones = blend->subs[0]->bones; + blend->cache = _mm_malloc(blend->bones * sizeof(*blend->cache), 16); + } + + for(size_t c = 0; c < blend->children; c++) { + if(!k3AnimationUpdate(blend->subs[c], blend->offsets[c] + time * blend->speeds[c])) return false; + if(blend->subs[c]->bones != blend->bones) return false; + } + + for(size_t b = 0; b < blend->bones; b++) { + float weightSum = 0; + vec4 translationSum = GLM_VEC4_ZERO_INIT; + mat4 rotationSum = GLM_MAT4_ZERO_INIT; + + for(size_t c = 0; c < blend->children; c++) { + float w = blend->weights[c * blend->bones + b]; + + weightSum += w; + + glm_vec4_muladds(blend->subs[c]->cache[b].translation, w, translationSum); + + mat4 rm; + quat_to_mat(blend->subs[c]->cache[b].rotation, rm); + glm_mat4_scale(rm, w); + + glm_vec4_add(rotationSum[0], rm[0], rotationSum[0]); + glm_vec4_add(rotationSum[1], rm[1], rotationSum[1]); + glm_vec4_add(rotationSum[2], rm[2], rotationSum[2]); + glm_vec4_add(rotationSum[3], rm[3], rotationSum[3]); + } + + glm_vec4_scale(translationSum, 1.0f / weightSum, blend->cache[b].translation); + + vec4 dominantEigenVector; + power_iter(rotationSum, 64, 1e-5f, dominantEigenVector); + glm_vec4_normalize(dominantEigenVector); + glm_quat_copy((float*) dominantEigenVector, blend->cache[b].rotation); + } + + return true; + } else if(anim->op == k3_ANIM_ADD) { + struct k3AnimationAdd *add = (void*) anim; + + if(!k3AnimationUpdate(add->sub1, time)) return false; + if(!k3AnimationUpdate(add->sub2, time)) return false; + + if(!add->cache || add->bones != add->sub1->bones) { + if(add->cache) _mm_free(add->cache); + + add->bones = add->sub1->bones; + add->cache = _mm_malloc(add->bones * sizeof(*add->cache), 16); + } + + versor norot = {0, 0, 0, 1}; + + for(size_t b = 0; b < add->bones; b++) { + glm_vec4_copy(add->sub1->cache[b].translation, add->cache[b].translation); + glm_vec4_muladds(add->sub2->cache[b].translation, add->scale, add->cache[b].translation); + + versor o; + glm_quat_nlerp(norot, add->sub2->cache[b].rotation, add->scale, o); + glm_quat_mul(o, add->sub1->cache[b].rotation, add->cache[b].rotation); + } + + return true; + } + + return false; +} + +void k3AnimationFree(struct k3Animation *anim) { + if(!anim) return; + + if(anim->ref != 0) { + anim->ref--; + return; + } + + if(anim->op == k3_ANIM_BLEND) { + struct k3AnimationBlend *blend = (void*) anim; + + for(size_t c = 0; c < blend->children; c++) { + k3AnimationFree(blend->subs[c]); + } + free(blend->subs); + + free(blend->weights); + free(blend->offsets); + free(blend->speeds); + } else if(anim->op == k3_ANIM_ADD) { + struct k3AnimationAdd *add = (void*) anim; + + k3AnimationFree(add->sub1); + k3AnimationFree(add->sub2); + } + + _mm_free(anim->cache); + + free(anim); +} diff --git a/src/k3anim.h b/src/k3anim.h new file mode 100644 index 0000000..ff1eada --- /dev/null +++ b/src/k3anim.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +struct k3AnimationBone { + vec4 translation; + versor rotation; +}; + +enum k3AnimationOp { + k3_ANIM_BASIC, + k3_ANIM_BLEND, + k3_ANIM_ADD, +}; + +struct k3AnimationFountain { + float fps; + size_t frameCount; + size_t bones; + uint16_t id; + _Alignas(16) struct k3AnimationBone frames[]; +}; + +struct k3Animation { + enum k3AnimationOp op; + size_t bones; + struct k3AnimationBone *cache; + size_t ref; +}; + +struct k3AnimationBasic { + enum k3AnimationOp op; + size_t bones; + struct k3AnimationBone *cache; + size_t ref; + + struct k3AnimationFountain *fountain; + bool loop; +}; + +struct k3AnimationBlend { + enum k3AnimationOp op; + size_t bones; + struct k3AnimationBone *cache; + size_t ref; + + size_t children; + float *offsets; + float *speeds; + float *weights; + struct k3Animation **subs; +}; + +struct k3AnimationAdd { + enum k3AnimationOp op; + size_t bones; + struct k3AnimationBone *cache; + size_t ref; + + struct k3Animation *sub1; + struct k3Animation *sub2; + + float scale; +}; + +bool k3AnimationUpdate(struct k3Animation *anim, float time); +void k3AnimationFree(struct k3Animation *anim);