Animation trees
This commit is contained in:
parent
cc37e17046
commit
295c882100
169
src/k3.c
169
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);
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t b = 0; b < anim->base->boneCount; b++) {
|
||||
glm_mat4_mul(anim->inter[b], anim->base->invBind[b], anim->inter[b]);
|
||||
}
|
||||
}
|
||||
void k3AnimatorSet(struct k3Animator *anim, float time) {
|
||||
if(!anim->inter) anim->inter = _mm_malloc(sizeof(mat4) * anim->base->boneCount * anim->base->frameCount, 16);
|
||||
void k3MdlQueryWeights(struct k3Mdl *mdl, size_t boneIdx, float *output) {
|
||||
memset(output, 0, sizeof(*output) * mdl->boneCount);
|
||||
|
||||
if(!anim->loop) {
|
||||
time = fmaxf(time, 0);
|
||||
time = fminf(time, (float) (anim->base->frameCount - 1) / anim->base->fps);
|
||||
if(boneIdx < mdl->boneCount) {
|
||||
qwfun(mdl, boneIdx, output);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
39
src/k3.h
39
src/k3.h
@ -5,6 +5,7 @@
|
||||
#include<cglm/vec3.h>
|
||||
#include<cglm/mat4.h>
|
||||
#include<cglm/quat.h>
|
||||
#include"k3anim.h"
|
||||
|
||||
#ifdef k3_MULTITHREAD
|
||||
#include<pthread.h>
|
||||
@ -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);
|
||||
|
||||
|
@ -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];
|
||||
|
||||
|
185
src/k3anim.c
Normal file
185
src/k3anim.c
Normal file
@ -0,0 +1,185 @@
|
||||
#include"k3anim.h"
|
||||
|
||||
#include<cglm/mat4.h>
|
||||
#include<cglm/quat.h>
|
||||
#include<stdint.h>
|
||||
#include<stddef.h>
|
||||
|
||||
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);
|
||||
}
|
70
src/k3anim.h
Normal file
70
src/k3anim.h
Normal file
@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include<cglm/mat4.h>
|
||||
#include<cglm/quat.h>
|
||||
#include<stdint.h>
|
||||
#include<stddef.h>
|
||||
|
||||
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);
|
Loading…
Reference in New Issue
Block a user