Replace Animator with Model:animate;

This commit is contained in:
bjorn 2019-05-24 15:14:09 -07:00
parent 4372eb293e
commit 3c07ad6b0b
13 changed files with 181 additions and 638 deletions

View File

@ -17,7 +17,6 @@ extern const luaL_Reg lovrModules[];
// Objects
extern const luaL_Reg lovrLightUserdata[];
extern const luaL_Reg lovrAnimator[];
extern const luaL_Reg lovrAudioStream[];
extern const luaL_Reg lovrBallJoint[];
extern const luaL_Reg lovrBlob[];

View File

@ -1,217 +0,0 @@
#include "api.h"
#include "graphics/animator.h"
static uint32_t luax_checkanimation(lua_State* L, int index, Animator* animator) {
switch (lua_type(L, index)) {
case LUA_TNUMBER: {
uint32_t i = lua_tointeger(L, index);
lovrAssert(i >= 1 && i <= lovrAnimatorGetAnimationCount(animator), "Invalid animation '%d'", i);
return i - 1;
}
case LUA_TSTRING: {
const char* name = lua_tostring(L, index);
uint32_t* i = lovrAnimatorGetAnimationIndex(animator, name);
lovrAssert(i, "Unknown animation '%s'", name);
return *i;
}
default:
luaL_typerror(L, index, "number or string");
return ~0u;
}
}
static int l_lovrAnimatorReset(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
lovrAnimatorReset(animator);
return 0;
}
static int l_lovrAnimatorUpdate(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
float dt = luax_checkfloat(L, 2);
lovrAnimatorUpdate(animator, dt);
return 0;
}
static int l_lovrAnimatorGetAnimationCount(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
lua_pushnumber(L, lovrAnimatorGetAnimationCount(animator));
return 1;
}
static int l_lovrAnimatorGetAnimationNames(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animationCount = lovrAnimatorGetAnimationCount(animator);
if (lua_istable(L, 2)) {
lua_settop(L, 2);
} else {
lua_settop(L, 1);
lua_createtable(L, animationCount, 0);
}
for (int i = 0; i < animationCount; i++) {
lua_pushstring(L, lovrAnimatorGetAnimationName(animator, i));
lua_rawseti(L, -2, i + 1);
}
return 1;
}
static int l_lovrAnimatorPlay(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
lovrAnimatorPlay(animator, animation);
return 0;
}
static int l_lovrAnimatorStop(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
lovrAnimatorStop(animator, animation);
return 0;
}
static int l_lovrAnimatorPause(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
lovrAnimatorPause(animator, animation);
return 0;
}
static int l_lovrAnimatorResume(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
lovrAnimatorResume(animator, animation);
return 0;
}
static int l_lovrAnimatorSeek(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
float time = luax_checkfloat(L, 3);
lovrAnimatorSeek(animator, animation, time);
return 0;
}
static int l_lovrAnimatorTell(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
float time = lovrAnimatorTell(animator, animation);
lua_pushnumber(L, time);
return 1;
}
static int l_lovrAnimatorGetAlpha(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
float alpha = lovrAnimatorGetAlpha(animator, animation);
lua_pushnumber(L, alpha);
return 1;
}
static int l_lovrAnimatorSetAlpha(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
float alpha = luax_checkfloat(L, 3);
lovrAnimatorSetAlpha(animator, animation, alpha);
return 0;
}
static int l_lovrAnimatorGetDuration(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
float duration = lovrAnimatorGetDuration(animator, animation);
lua_pushnumber(L, duration);
return 1;
}
static int l_lovrAnimatorIsPlaying(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
bool playing = lovrAnimatorIsPlaying(animator, animation);
lua_pushboolean(L, playing);
return 1;
}
static int l_lovrAnimatorIsLooping(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
bool looping = lovrAnimatorIsLooping(animator, animation);
lua_pushboolean(L, looping);
return 1;
}
static int l_lovrAnimatorSetLooping(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
bool looping = lua_toboolean(L, 3);
lovrAnimatorSetLooping(animator, animation, looping);
return 0;
}
static int l_lovrAnimatorGetPriority(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
int priority = lovrAnimatorGetPriority(animator, animation);
lua_pushinteger(L, priority);
return 1;
}
static int l_lovrAnimatorSetPriority(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
int animation = luax_checkanimation(L, 2, animator);
int priority = luaL_checkinteger(L, 3);
lovrAnimatorSetPriority(animator, animation, priority);
return 0;
}
static int l_lovrAnimatorGetSpeed(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
if (lua_isnoneornil(L, 2)) {
float speed = lovrAnimatorGetSpeed(animator, -1);
lua_pushnumber(L, speed);
} else {
int animation = luax_checkanimation(L, 2, animator);
float speed = lovrAnimatorGetSpeed(animator, animation);
lua_pushnumber(L, speed);
}
return 1;
}
static int l_lovrAnimatorSetSpeed(lua_State* L) {
Animator* animator = luax_checktype(L, 1, Animator);
if (lua_isnoneornil(L, 2)) {
float speed = luax_checkfloat(L, 2);
lovrAnimatorSetSpeed(animator, -1, speed);
} else {
int animation = luax_checkanimation(L, 2, animator);
float speed = luax_checkfloat(L, 3);
lovrAnimatorSetSpeed(animator, animation, speed);
}
return 0;
}
const luaL_Reg lovrAnimator[] = {
{ "reset", l_lovrAnimatorReset },
{ "update", l_lovrAnimatorUpdate },
{ "getAnimationCount", l_lovrAnimatorGetAnimationCount },
{ "getAnimationNames", l_lovrAnimatorGetAnimationNames },
{ "play", l_lovrAnimatorPlay },
{ "stop", l_lovrAnimatorStop },
{ "pause", l_lovrAnimatorPause },
{ "resume", l_lovrAnimatorResume },
{ "seek", l_lovrAnimatorSeek },
{ "tell", l_lovrAnimatorTell },
{ "getAlpha", l_lovrAnimatorGetAlpha },
{ "setAlpha", l_lovrAnimatorSetAlpha },
{ "getDuration", l_lovrAnimatorGetDuration },
{ "isPlaying", l_lovrAnimatorIsPlaying },
{ "isLooping", l_lovrAnimatorIsLooping },
{ "setLooping", l_lovrAnimatorSetLooping },
{ "getPriority", l_lovrAnimatorGetPriority },
{ "setPriority", l_lovrAnimatorSetPriority },
{ "getSpeed", l_lovrAnimatorGetSpeed },
{ "setSpeed", l_lovrAnimatorSetSpeed },
{ NULL, NULL }
};

View File

@ -1,6 +1,5 @@
#include "api.h"
#include "graphics/graphics.h"
#include "graphics/animator.h"
#include "graphics/buffer.h"
#include "graphics/canvas.h"
#include "graphics/material.h"
@ -973,14 +972,6 @@ static int l_lovrGraphicsCompute(lua_State* L) {
// Types
static int l_lovrGraphicsNewAnimator(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
Animator* animator = lovrAnimatorCreate(model->data);
luax_pushtype(L, Animator, animator);
lovrRelease(Animator, animator);
return 1;
}
static void luax_checkuniformtype(lua_State* L, int index, UniformType* baseType, int* components) {
size_t length;
lovrAssert(lua_type(L, index) == LUA_TSTRING, "Uniform types must be strings, got %s", lua_typename(L, index));
@ -1674,7 +1665,6 @@ static const luaL_Reg lovrGraphics[] = {
{ "compute", l_lovrGraphicsCompute },
// Types
{ "newAnimator", l_lovrGraphicsNewAnimator },
{ "newCanvas", l_lovrGraphicsNewCanvas },
{ "newFont", l_lovrGraphicsNewFont },
{ "newMaterial", l_lovrGraphicsNewMaterial },
@ -1691,7 +1681,7 @@ static const luaL_Reg lovrGraphics[] = {
int luaopen_lovr_graphics(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrGraphics);
luax_registertype(L, Animator);
luax_registertype(L, Canvas);
luax_registertype(L, Font);
luax_registertype(L, Material);
luax_registertype(L, Mesh);
@ -1699,7 +1689,6 @@ int luaopen_lovr_graphics(lua_State* L) {
luax_registertype(L, Shader);
luax_registertype(L, ShaderBlock);
luax_registertype(L, Texture);
luax_registertype(L, Canvas);
lovrGraphicsInit();
luax_pushconf(L);

View File

@ -1,7 +1,7 @@
#include "api.h"
#include "graphics/animator.h"
#include "graphics/material.h"
#include "graphics/model.h"
#include "data/modelData.h"
static int l_lovrModelDraw(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
@ -12,20 +12,29 @@ static int l_lovrModelDraw(lua_State* L) {
return 0;
}
static int l_lovrModelGetAnimator(lua_State* L) {
static int l_lovrModelAnimate(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
luax_pushtype(L, Aniamtor, lovrModelGetAnimator(model));
return 1;
}
static int l_lovrModelSetAnimator(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
if (lua_isnoneornil(L, 2)) {
lovrModelSetAnimator(model, NULL);
} else {
Animator* animator = luax_checktype(L, 2, Animator);
lovrModelSetAnimator(model, animator);
uint32_t animation = ~0u;
switch (lua_type(L, 2)) {
case LUA_TSTRING: {
const char* name = lua_tostring(L, 2);
ModelData* modelData = lovrModelGetModelData(model);
uint32_t* index = map_get(&modelData->animationMap, name);
lovrAssert(index, "Model has no animation named '%s'", name);
animation = *index;
break;
}
case LUA_TNUMBER:
animation = lua_tointeger(L, 2) - 1;
break;
default:
return luaL_typerror(L, 2, "number or string");
}
float time = luaL_checknumber(L, 3);
float alpha = luax_optfloat(L, 4, 1.f);
lovrModelAnimate(model, animation, time, alpha);
return 0;
}
@ -59,8 +68,7 @@ static int l_lovrModelGetAABB(lua_State* L) {
const luaL_Reg lovrModel[] = {
{ "draw", l_lovrModelDraw },
{ "getAnimator", l_lovrModelGetAnimator },
{ "setAnimator", l_lovrModelSetAnimator },
{ "animate", l_lovrModelAnimate },
{ "getMaterial", l_lovrModelGetMaterial },
{ "setMaterial", l_lovrModelSetMaterial },
{ "getAABB", l_lovrModelGetAABB },

View File

@ -23,6 +23,8 @@ void lovrModelDataDestroy(void* ref) {
for (uint32_t i = 0; i < model->textureCount; i++) {
lovrRelease(TextureData, model->textures[i]);
}
map_deinit(&model->animationMap);
map_deinit(&model->nodeMap);
free(model->data);
}
@ -42,7 +44,6 @@ void lovrModelDataAllocate(ModelData* model) {
totalSize += sizes[9] = model->channelCount * sizeof(ModelAnimationChannel);
totalSize += sizes[10] = model->childCount * sizeof(uint32_t);
totalSize += sizes[11] = model->jointCount * sizeof(uint32_t);
totalSize += sizes[12] = model->charCount * sizeof(char);
size_t offset = 0;
char* p = model->data = calloc(1, totalSize);
@ -59,5 +60,7 @@ void lovrModelDataAllocate(ModelData* model) {
model->channels = (ModelAnimationChannel*) (p + offset), offset += sizes[9];
model->children = (uint32_t*) (p + offset), offset += sizes[10];
model->joints = (uint32_t*) (p + offset), offset += sizes[11];
model->chars = (char*) (p + offset), offset += sizes[12];
map_init(&model->animationMap);
map_init(&model->nodeMap);
}

View File

@ -1,4 +1,5 @@
#include "util.h"
#include "lib/map/map.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
@ -133,7 +134,6 @@ typedef struct {
} ModelAnimationChannel;
typedef struct {
const char* name;
ModelAnimationChannel* channels;
uint32_t channelCount;
float duration;
@ -164,6 +164,7 @@ typedef struct {
uint32_t primitiveIndex;
uint32_t primitiveCount;
uint32_t skin;
bool matrix;
} ModelNode;
typedef struct {
@ -198,11 +199,12 @@ typedef struct ModelData {
ModelAnimationChannel* channels;
uint32_t* children;
uint32_t* joints;
char* chars;
uint32_t channelCount;
uint32_t childCount;
uint32_t jointCount;
uint32_t charCount;
map_t(uint32_t) animationMap;
map_t(uint32_t) nodeMap;
} ModelData;
ModelData* lovrModelDataInit(ModelData* model, struct Blob* blob);

View File

@ -271,7 +271,6 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
gltfString key = NOM_STR(json, t);
if (STR_EQ(key, "channels")) { model->channelCount += t->size; }
else if (STR_EQ(key, "samplers")) { samplerCount += t->size; }
else if (STR_EQ(key, "name")) { model->charCount += t->end - t->start + 1; }
t += NOM_VALUE(json, t);
}
}
@ -576,7 +575,6 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
ModelAnimation* animation = model->animations;
for (int i = (token++)->size; i > 0; i--, animation++) {
int samplerCount = 0;
animation->name = NULL;
for (int k = (token++)->size; k > 0; k--) {
gltfString key = NOM_STR(json, token);
if (STR_EQ(key, "channels")) {
@ -635,10 +633,9 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
token += NOM_VALUE(json, token);
} else if (STR_EQ(key, "name")) {
gltfString name = NOM_STR(json, token);
memcpy(model->chars, name.data, name.length);
model->chars[name.length] = '\0';
animation->name = model->chars;
model->chars += name.length;
name.data[name.length] = '\0';
map_set(&model->animationMap, name.data, model->animationCount - i);
name.data[name.length] = '"';
} else {
token += NOM_VALUE(json, token);
}
@ -802,7 +799,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
vec3 translation = vec3_set(node->translation, 0.f, 0.f, 0.f);
quat rotation = quat_set(node->rotation, 0.f, 0.f, 0.f, 1.f);
vec3 scale = vec3_set(node->scale, 1.f, 1.f, 1.f);
bool matrix = false;
node->matrix = false;
node->primitiveCount = 0;
node->skin = ~0u;
@ -822,7 +819,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
}
} else if (STR_EQ(key, "matrix")) {
lovrAssert((token++)->size == 16, "Node matrix needs 16 elements");
matrix = true;
node->matrix = true;
for (int j = 0; j < 16; j++) {
node->transform[j] = NOM_FLOAT(json, token);
}
@ -842,18 +839,15 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
scale[0] = NOM_FLOAT(json, token);
scale[1] = NOM_FLOAT(json, token);
scale[2] = NOM_FLOAT(json, token);
} else if (STR_EQ(key, "name")) {
gltfString name = NOM_STR(json, token);
name.data[name.length] = '\0';
map_set(&model->nodeMap, name.data, model->nodeCount - i);
name.data[name.length] = '"';
} else {
token += NOM_VALUE(json, token);
}
}
// Fix it in post
if (!matrix) {
mat4_identity(node->transform);
mat4_translate(node->transform, translation[0], translation[1], translation[2]);
mat4_rotateQuat(node->transform, rotation);
mat4_scale(node->transform, scale[0], scale[1], scale[2]);
}
}
}

View File

@ -304,7 +304,9 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) {
model->nodes[0] = (ModelNode) {
.transform = MAT4_IDENTITY,
.primitiveIndex = 0,
.primitiveCount = groups.length
.primitiveCount = groups.length,
.skin = ~0u,
.matrix = true
};
arr_free(&groups);

View File

@ -1,269 +0,0 @@
#include "graphics/animator.h"
#include "data/modelData.h"
#include "core/maf.h"
#include "core/ref.h"
#include <stdlib.h>
#include <math.h>
static int trackSortCallback(const void* a, const void* b) {
return ((Track*) a)->priority < ((Track*) b)->priority;
}
Animator* lovrAnimatorInit(Animator* animator, ModelData* data) {
lovrRetain(data);
animator->data = data;
map_init(&animator->animations);
arr_init(&animator->tracks);
arr_reserve(&animator->tracks, data->animationCount);
animator->speed = 1.f;
for (uint32_t i = 0; i < data->animationCount; i++) {
arr_push(&animator->tracks, ((Track) {
.time = 0.f,
.speed = 1.f,
.alpha = 1.f,
.priority = 0,
.playing = false,
.looping = false
}));
if (data->animations[i].name) {
map_set(&animator->animations, data->animations[i].name, i);
}
}
return animator;
}
void lovrAnimatorDestroy(void* ref) {
Animator* animator = ref;
lovrRelease(ModelData, animator->data);
arr_free(&animator->tracks);
}
void lovrAnimatorReset(Animator* animator) {
for (size_t i = 0; i < animator->tracks.length; i++) {
Track* track = &animator->tracks.data[i];
track->time = 0.f;
track->speed = 1.f;
track->playing = false;
track->looping = false;
}
animator->speed = 1.f;
}
void lovrAnimatorUpdate(Animator* animator, float dt) {
for (size_t i = 0; i < animator->tracks.length; i++) {
Track* track = &animator->tracks.data[i];
if (track->playing) {
track->time += dt * track->speed * animator->speed;
float duration = animator->data->animations[i].duration;
if (track->looping) {
track->time = fmodf(track->time, duration);
} else if (track->time > duration || track->time < 0) {
track->time = 0;
track->playing = false;
}
}
}
}
bool lovrAnimatorEvaluate(Animator* animator, uint32_t nodeIndex, mat4 transform) {
float properties[3][4];
ModelNode* node = &animator->data->nodes[nodeIndex];
vec3_init(properties[PROP_TRANSLATION], node->translation);
quat_init(properties[PROP_ROTATION], node->rotation);
vec3_init(properties[PROP_SCALE], node->scale);
bool touched = false;
for (uint32_t i = 0; i < animator->data->animationCount; i++) {
ModelAnimation* animation = &animator->data->animations[i];
for (uint32_t j = 0; j < animation->channelCount; j++) {
ModelAnimationChannel* channel = &animation->channels[j];
if (channel->nodeIndex != nodeIndex) {
continue;
}
Track* track = &animator->tracks.data[i];
if (!track->playing || track->alpha == 0.f) {
continue;
}
float duration = animator->data->animations[i].duration;
float time = fmodf(track->time, duration);
uint32_t k = 0;
while (k < channel->keyframeCount && channel->times[k] < time) {
k++;
}
float value[4];
bool rotate = channel->property == PROP_ROTATION;
size_t n = 3 + rotate;
float* (*lerp)(float* a, float* b, float t) = rotate ? quat_slerp : vec3_lerp;
if (k < channel->keyframeCount) {
float t1 = channel->times[k - 1];
float t2 = channel->times[k];
float z = (time - t1) / (t2 - t1);
float next[4];
memcpy(value, channel->data + (k - 1) * n, n * sizeof(float));
memcpy(next, channel->data + k * n, n * sizeof(float));
switch (channel->smoothing) {
case SMOOTH_STEP:
if (z >= .5f) {
memcpy(value, next, n * sizeof(float));
}
break;
case SMOOTH_LINEAR: lerp(value, next, z); break;
case SMOOTH_CUBIC: lovrThrow("Cubic spline interpolation is not supported yet"); break;
default: break;
}
} else {
memcpy(value, channel->data + CLAMP(k, 0, channel->keyframeCount - 1) * n, n * sizeof(float));
}
if (track->alpha == 1.f) {
memcpy(properties[channel->property], value, n * sizeof(float));
} else {
lerp(properties[channel->property], value, track->alpha);
}
touched = true;
}
}
if (touched) {
vec3 T = properties[PROP_TRANSLATION];
quat R = properties[PROP_ROTATION];
vec3 S = properties[PROP_SCALE];
mat4_translate(transform, T[0], T[1], T[2]);
mat4_rotateQuat(transform, R);
mat4_scale(transform, S[0], S[1], S[2]);
} else {
mat4_multiply(transform, node->transform);
}
return touched;
}
uint32_t lovrAnimatorGetAnimationCount(Animator* animator) {
return animator->data->animationCount;
}
uint32_t* lovrAnimatorGetAnimationIndex(Animator* animator, const char* name) {
return map_get(&animator->animations, name);
}
const char* lovrAnimatorGetAnimationName(Animator* animator, uint32_t index) {
return animator->data->animations[index].name;
}
void lovrAnimatorPlay(Animator* animator, uint32_t animation) {
Track* track = &animator->tracks.data[animation];
track->playing = true;
track->time = 0.f;
}
void lovrAnimatorStop(Animator* animator, uint32_t animation) {
Track* track = &animator->tracks.data[animation];
track->playing = false;
track->time = 0.f;
}
void lovrAnimatorPause(Animator* animator, uint32_t animation) {
Track* track = &animator->tracks.data[animation];
track->playing = false;
}
void lovrAnimatorResume(Animator* animator, uint32_t animation) {
Track* track = &animator->tracks.data[animation];
track->playing = true;
}
void lovrAnimatorSeek(Animator* animator, uint32_t animation, float time) {
Track* track = &animator->tracks.data[animation];
float duration = animator->data->animations[animation].duration;
while (time > duration) {
time -= duration;
}
while (time < 0.f) {
time += duration;
}
track->time = time;
if (!track->looping) {
track->time = MIN(track->time, duration);
track->time = MAX(track->time, 0);
}
}
float lovrAnimatorTell(Animator* animator, uint32_t animation) {
Track* track = &animator->tracks.data[animation];
return track->time;
}
float lovrAnimatorGetAlpha(Animator* animator, uint32_t animation) {
Track* track = &animator->tracks.data[animation];
return track->alpha;
}
void lovrAnimatorSetAlpha(Animator* animator, uint32_t animation, float alpha) {
Track* track = &animator->tracks.data[animation];
track->alpha = alpha;
}
float lovrAnimatorGetDuration(Animator* animator, uint32_t animation) {
return animator->data->animations[animation].duration;
}
bool lovrAnimatorIsPlaying(Animator* animator, uint32_t animation) {
Track* track = &animator->tracks.data[animation];
return track->playing;
}
bool lovrAnimatorIsLooping(Animator* animator, uint32_t animation) {
Track* track = &animator->tracks.data[animation];
return track->looping;
}
void lovrAnimatorSetLooping(Animator* animator, uint32_t animation, bool loop) {
Track* track = &animator->tracks.data[animation];
track->looping = loop;
}
int32_t lovrAnimatorGetPriority(Animator* animator, uint32_t animation) {
Track* track = &animator->tracks.data[animation];
return track->priority;
}
void lovrAnimatorSetPriority(Animator* animator, uint32_t animation, int32_t priority) {
Track* track = &animator->tracks.data[animation];
track->priority = priority;
qsort(animator->tracks.data, animator->tracks.length, sizeof(Track), trackSortCallback);
}
float lovrAnimatorGetSpeed(Animator* animator, uint32_t animation) {
if (animation == ~0u) {
return animator->speed;
}
Track* track = &animator->tracks.data[animation];
return track->speed;
}
void lovrAnimatorSetSpeed(Animator* animator, uint32_t animation, float speed) {
if (animation == ~0u) {
animator->speed = speed;
}
Track* track = &animator->tracks.data[animation];
track->speed = speed;
}

View File

@ -1,50 +0,0 @@
#include "core/arr.h"
#include "lib/map/map.h"
#include <stdbool.h>
#include <stdint.h>
#pragma once
struct ModelData;
typedef struct {
float time;
float speed;
float alpha;
int32_t priority;
bool playing;
bool looping;
} Track;
typedef struct Animator {
struct ModelData* data;
map_t(uint32_t) animations;
arr_t(Track, 1) tracks;
float speed;
} Animator;
Animator* lovrAnimatorInit(Animator* animator, struct ModelData* modelData);
#define lovrAnimatorCreate(...) lovrAnimatorInit(lovrAlloc(Animator), __VA_ARGS__)
void lovrAnimatorDestroy(void* ref);
void lovrAnimatorReset(Animator* animator);
void lovrAnimatorUpdate(Animator* animator, float dt);
bool lovrAnimatorEvaluate(Animator* animator, uint32_t nodeIndex, float* transform);
uint32_t lovrAnimatorGetAnimationCount(Animator* animator);
uint32_t* lovrAnimatorGetAnimationIndex(Animator* animator, const char* name);
const char* lovrAnimatorGetAnimationName(Animator* animator, uint32_t index);
void lovrAnimatorPlay(Animator* animator, uint32_t animation);
void lovrAnimatorStop(Animator* animator, uint32_t animation);
void lovrAnimatorPause(Animator* animator, uint32_t animation);
void lovrAnimatorResume(Animator* animator, uint32_t animation);
void lovrAnimatorSeek(Animator* animator, uint32_t animation, float time);
float lovrAnimatorTell(Animator* animator, uint32_t animation);
float lovrAnimatorGetAlpha(Animator* animator, uint32_t animation);
void lovrAnimatorSetAlpha(Animator* animator, uint32_t animation, float alpha);
float lovrAnimatorGetDuration(Animator* animator, uint32_t animation);
bool lovrAnimatorIsPlaying(Animator* animator, uint32_t animation);
bool lovrAnimatorIsLooping(Animator* animator, uint32_t animation);
void lovrAnimatorSetLooping(Animator* animator, uint32_t animation, bool loop);
int32_t lovrAnimatorGetPriority(Animator* animator, uint32_t animation);
void lovrAnimatorSetPriority(Animator* animator, uint32_t animation, int32_t priority);
float lovrAnimatorGetSpeed(Animator* animator, uint32_t animation);
void lovrAnimatorSetSpeed(Animator* animator, uint32_t animation, float speed);

View File

@ -1,45 +1,67 @@
#include "graphics/model.h"
#include "graphics/animator.h"
#include "graphics/buffer.h"
#include "graphics/graphics.h"
#include "graphics/material.h"
#include "graphics/mesh.h"
#include "graphics/texture.h"
#include "resources/shaders.h"
#include "core/maf.h"
#include "core/ref.h"
#include <stdlib.h>
#include <float.h>
#include <math.h>
static void updateGlobalNodeTransform(Model* model, uint32_t nodeIndex, mat4 transform) {
typedef struct {
float properties[3][4];
} NodeTransform;
struct Model {
struct ModelData* data;
struct Buffer** buffers;
struct Mesh** meshes;
struct Texture** textures;
struct Material** materials;
struct Material* userMaterial;
NodeTransform* localTransforms;
float* globalTransforms;
bool transformsDirty;
};
static void updateGlobalTransform(Model* model, uint32_t nodeIndex, mat4 parent) {
ModelNode* node = &model->data->nodes[nodeIndex];
mat4 global = model->globalTransforms + 16 * nodeIndex;
mat4_init(global, parent);
mat4 globalTransform = model->globalNodeTransforms + 16 * nodeIndex;
mat4_set(globalTransform, transform);
if (model->animator) {
lovrAnimatorEvaluate(model->animator, nodeIndex, globalTransform);
if (node->matrix) {
mat4_multiply(global, node->transform);
} else {
mat4_multiply(globalTransform, node->transform);
NodeTransform* local = &model->localTransforms[nodeIndex];
vec3 T = local->properties[PROP_TRANSLATION];
quat R = local->properties[PROP_ROTATION];
vec3 S = local->properties[PROP_SCALE];
mat4_translate(global, T[0], T[1], T[2]);
mat4_rotateQuat(global, R);
mat4_scale(global, S[0], S[1], S[2]);
}
for (uint32_t i = 0; i < node->childCount; i++) {
updateGlobalNodeTransform(model, node->children[i], globalTransform);
updateGlobalTransform(model, node->children[i], global);
}
}
static void renderNode(Model* model, uint32_t nodeIndex, uint32_t instances) {
ModelNode* node = &model->data->nodes[nodeIndex];
mat4 globalTransform = model->globalNodeTransforms + 16 * nodeIndex;
mat4 globalTransform = model->globalTransforms + 16 * nodeIndex;
if (node->primitiveCount > 0) {
bool animated = node->skin != ~0u && model->animator;
bool animated = node->skin != ~0u;
float pose[16 * MAX_BONES];
if (animated) {
ModelSkin* skin = &model->data->skins[node->skin];
for (uint32_t j = 0; j < skin->jointCount; j++) {
mat4 globalJointTransform = model->globalNodeTransforms + 16 * skin->joints[j];
mat4 globalJointTransform = model->globalTransforms + 16 * skin->joints[j];
mat4 inverseBindMatrix = skin->inverseBindMatrices + 16 * j;
mat4 jointPose = pose + 16 * j;
@ -65,7 +87,8 @@ static void renderNode(Model* model, uint32_t nodeIndex, uint32_t instances) {
}
}
Model* lovrModelInit(Model* model, ModelData* data) {
Model* lovrModelCreate(ModelData* data) {
Model* model = lovrAlloc(Model);
model->data = data;
lovrRetain(data);
@ -169,9 +192,20 @@ Model* lovrModelInit(Model* model, ModelData* data) {
}
}
model->globalNodeTransforms = malloc(16 * sizeof(float) * model->data->nodeCount);
for (uint32_t i = 0; i < model->data->nodeCount; i++) {
mat4_identity(model->globalNodeTransforms + 16 * i);
model->localTransforms = malloc(sizeof(NodeTransform) * data->nodeCount);
model->globalTransforms = malloc(16 * sizeof(float) * data->nodeCount);
model->transformsDirty = true;
for (uint32_t i = 0; i < data->nodeCount; i++) {
if (data->nodes[i].matrix) {
vec3_set(model->localTransforms[i].properties[PROP_TRANSLATION], 0.f, 0.f, 0.f);
quat_set(model->localTransforms[i].properties[PROP_ROTATION], 0.f, 0.f, 0.f, 1.f);
vec3_set(model->localTransforms[i].properties[PROP_SCALE], 1.f, 1.f, 1.f);
} else {
vec3_init(model->localTransforms[i].properties[PROP_TRANSLATION], data->nodes[i].translation);
quat_init(model->localTransforms[i].properties[PROP_ROTATION], data->nodes[i].rotation);
vec3_init(model->localTransforms[i].properties[PROP_SCALE], data->nodes[i].scale);
}
}
return model;
@ -192,26 +226,82 @@ void lovrModelDestroy(void* ref) {
lovrRelease(Material, model->materials[i]);
}
lovrRelease(Material, model->userMaterial);
lovrRelease(Animator, model->animator);
lovrRelease(ModelData, model->data);
free(model->globalNodeTransforms);
free(model->globalTransforms);
free(model->localTransforms);
}
ModelData* lovrModelGetModelData(Model* model) {
return model->data;
}
void lovrModelDraw(Model* model, mat4 transform, uint32_t instances) {
updateGlobalNodeTransform(model, model->data->rootNode, transform);
renderNode(model, model->data->rootNode, instances);
}
Animator* lovrModelGetAnimator(Model* model) {
return model->animator;
}
void lovrModelSetAnimator(Model* model, Animator* animator) {
if (model->animator != animator) {
lovrRetain(animator);
lovrRelease(Animator, model->animator);
model->animator = animator;
if (model->transformsDirty) {
updateGlobalTransform(model, model->data->rootNode, (float[]) MAT4_IDENTITY);
model->transformsDirty = false;
}
lovrGraphicsPush();
lovrGraphicsMatrixTransform(transform);
renderNode(model, model->data->rootNode, instances);
lovrGraphicsPop();
}
void lovrModelAnimate(Model* model, uint32_t animationIndex, float time, float alpha) {
if (alpha == 0.f) {
return;
}
lovrAssert(animationIndex < model->data->animationCount, "Invalid animation index #%d (Model only has %d animations)", animationIndex, model->data->animationCount);
ModelAnimation* animation = &model->data->animations[animationIndex];
time = fmodf(time, animation->duration);
for (uint32_t i = 0; i < animation->channelCount; i++) {
ModelAnimationChannel* channel = &animation->channels[i];
uint32_t nodeIndex = channel->nodeIndex;
NodeTransform* transform = &model->localTransforms[nodeIndex];
uint32_t keyframe = 0;
while (keyframe < channel->keyframeCount && channel->times[keyframe] < time) {
keyframe++;
}
float property[4];
bool rotate = channel->property == PROP_ROTATION;
size_t n = 3 + rotate;
float* (*lerp)(float* a, float* b, float t) = rotate ? quat_slerp : vec3_lerp;
if (keyframe >= channel->keyframeCount) {
memcpy(property, channel->data + CLAMP(keyframe, 0, channel->keyframeCount - 1) * n, n * sizeof(float));
} else {
float t1 = channel->times[keyframe - 1];
float t2 = channel->times[keyframe];
float z = (time - t1) / (t2 - t1);
float next[4];
memcpy(property, channel->data + (keyframe - 1) * n, n * sizeof(float));
memcpy(next, channel->data + keyframe * n, n * sizeof(float));
switch (channel->smoothing) {
case SMOOTH_STEP:
if (z >= .5f) {
memcpy(property, next, n * sizeof(float));
}
break;
case SMOOTH_LINEAR: lerp(property, next, z); break;
case SMOOTH_CUBIC: lovrThrow("Cubic spline interpolation is not supported yet"); break;
default: break;
}
}
if (alpha == 1.f) {
memcpy(transform->properties[channel->property], property, n * sizeof(float));
} else {
lerp(transform->properties[channel->property], property, alpha);
}
}
model->transformsDirty = true;
}
Material* lovrModelGetMaterial(Model* model) {
@ -230,7 +320,7 @@ static void applyAABB(Model* model, uint32_t nodeIndex, float aabb[6]) {
for (uint32_t i = 0; i < node->primitiveCount; i++) {
ModelAttribute* position = model->data->primitives[node->primitiveIndex + i].attributes[ATTR_POSITION];
if (position && position->hasMin && position->hasMax) {
mat4 m = model->globalNodeTransforms + 16 * nodeIndex;
mat4 m = model->globalTransforms + 16 * nodeIndex;
float xa[3] = { position->min[0] * m[0], position->min[0] * m[1], position->min[0] * m[2] };
float xb[3] = { position->max[0] * m[0], position->max[0] * m[1], position->max[0] * m[2] };
@ -268,8 +358,12 @@ static void applyAABB(Model* model, uint32_t nodeIndex, float aabb[6]) {
}
void lovrModelGetAABB(Model* model, float aabb[6]) {
if (model->transformsDirty) {
updateGlobalTransform(model, model->data->rootNode, (float[]) MAT4_IDENTITY);
model->transformsDirty = false;
}
aabb[0] = aabb[2] = aabb[4] = FLT_MAX;
aabb[1] = aabb[3] = aabb[5] = -FLT_MAX;
updateGlobalNodeTransform(model, model->data->rootNode, (float[]) MAT4_IDENTITY);
applyAABB(model, model->data->rootNode, aabb);
}

View File

@ -1,31 +1,17 @@
#include <stdint.h>
#include <stdbool.h>
#pragma once
struct Animator;
struct Buffer;
struct Material;
struct Mesh;
struct ModelData;
struct Texture;
typedef struct {
struct ModelData* data;
struct Animator* animator;
struct Buffer** buffers;
struct Mesh** meshes;
struct Texture** textures;
struct Material** materials;
struct Material* userMaterial;
float* globalNodeTransforms;
} Model;
Model* lovrModelInit(Model* model, struct ModelData* data);
#define lovrModelCreate(...) lovrModelInit(lovrAlloc(Model), __VA_ARGS__)
typedef struct Model Model;
Model* lovrModelCreate(struct ModelData* data);
void lovrModelDestroy(void* ref);
struct ModelData* lovrModelGetModelData(Model* model);
void lovrModelDraw(Model* model, float* transform, uint32_t instances);
struct Animator* lovrModelGetAnimator(Model* model);
void lovrModelSetAnimator(Model* model, struct Animator* animator);
void lovrModelAnimate(Model* model, uint32_t animationIndex, float time, float alpha);
struct Material* lovrModelGetMaterial(Model* model);
void lovrModelSetMaterial(Model* model, struct Material* material);
void lovrModelGetAABB(Model* model, float aabb[6]);

View File

@ -543,7 +543,9 @@ static ModelData* openvr_newModelData(Device device) {
model->nodes[0] = (ModelNode) {
.transform = MAT4_IDENTITY,
.primitiveIndex = 0,
.primitiveCount = 1
.primitiveCount = 1,
.skin = ~0u,
.matrix = true
};
return model;