diff --git a/include/reign.h b/include/reign.h index 44e7cbf..a744788 100644 --- a/include/reign.h +++ b/include/reign.h @@ -23,6 +23,12 @@ #include "plugin.h" +enum RE_motion_state { + RE_MOTION_STATE_STOP = 0, + RE_MOTION_STATE_NOLOOP = 1, + RE_MOTION_STATE_LOOP = 2, +}; + struct RE_options { int anti_aliasing; int wait_vsync; @@ -71,14 +77,22 @@ struct RE_plugin { struct RE_instance { struct RE_plugin *plugin; struct model *model; + struct motion *motion; + struct motion *next_motion; + int type; vec3 pos; float pitch, roll, yaw; // in degrees vec3 vec; vec3 scale; bool draw; + float fps; + bool motion_blend; + float motion_blend_rate; + bool local_transform_needs_update; mat4 local_transform; + mat4 *bone_transforms; // model->nr_bones elements }; struct RE_plugin *RE_plugin_new(void); @@ -90,6 +104,16 @@ int RE_create_instance(struct RE_plugin *plugin); bool RE_release_instance(struct RE_plugin *plugin, int instance); bool RE_instance_load(struct RE_instance *instance, const char *name); +bool RE_instance_load_motion(struct RE_instance *instance, const char *name); +bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name); +bool RE_instance_free_next_motion(struct RE_instance *instance); + +int RE_motion_get_state(struct motion *motion); +bool RE_motion_set_state(struct motion *motion, int state); +float RE_motion_get_frame(struct motion *motion); +bool RE_motion_set_frame(struct motion *motion, float frame); +bool RE_motion_set_frame_range(struct motion *motion, float begin, float end); +bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float end); void RE_render(struct sact_sprite *sp); void RE_debug_print(struct RE_plugin *p); diff --git a/shaders/reign.v.glsl b/shaders/reign.v.glsl index 935c4f8..363b568 100644 --- a/shaders/reign.v.glsl +++ b/shaders/reign.v.glsl @@ -18,12 +18,30 @@ uniform mat4 local_transform; uniform mat4 view_transform; uniform mat4 proj_transform; +const int MAX_BONES = 211; // see 3d_internal.h +const int NR_WEIGHTS = 4; +uniform bool has_bones; +uniform mat4 bone_matrices[MAX_BONES]; + in vec3 vertex_pos; in vec3 vertex_normal; in vec2 vertex_uv; +in ivec4 vertex_bone_index; +in vec4 vertex_bone_weight; + out vec2 tex_coord; void main() { - gl_Position = proj_transform * view_transform * local_transform * vec4(vertex_pos, 1.0); - tex_coord = vertex_uv; + mat4 bone_transform = mat4(1.0f); + if (has_bones) { + bone_transform = mat4(0.f); + for (int i = 0; i < NR_WEIGHTS; i++) { + if (vertex_bone_index[i] >= 0) { + bone_transform += bone_matrices[vertex_bone_index[i]] * vertex_bone_weight[i]; + } + } + } + + gl_Position = proj_transform * view_transform * local_transform * bone_transform * vec4(vertex_pos, 1.0); + tex_coord = vertex_uv; } diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 3b19b5d..b55297b 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -24,12 +24,17 @@ #include "gfx/gl.h" #include "gfx/gfx.h" +#define MAX_BONES 211 // Maximum in TT3 + struct model { - char *name; + char *path; int nr_meshes; struct mesh *meshes; int nr_materials; struct material *materials; + int nr_bones; + struct bone *bones; + struct hash_table *bone_map; // bone id in POL/MOT -> struct bone * }; struct mesh { @@ -44,6 +49,22 @@ struct material { bool opaque; }; +struct bone { + char *name; + int index; // index on model->bones + int parent; // index on model->bones, or -1 + mat4 inverse_bind_matrix; +}; + +struct motion { + struct mot *mot; + char *name; + int state; + float current_frame; + float frame_begin, frame_end; + float loop_frame_begin, loop_frame_end; +}; + struct RE_renderer { struct shader shader; GLuint depth_buffer; @@ -51,10 +72,17 @@ struct RE_renderer { // Uniform variable locations GLint local_transform; GLint proj_transform; + GLint has_bones; + GLint bone_matrices; // Attribute variable locations GLint vertex_normal; GLint vertex_uv; + GLint vertex_bone_index; + GLint vertex_bone_weight; + + mat4 bone_transforms[MAX_BONES]; + uint32_t last_frame_timestamp; }; // model.c @@ -62,6 +90,9 @@ struct RE_renderer { struct model *model_load(struct archive *aar, const char *path, struct RE_renderer *renderer); void model_free(struct model *model); +struct motion *motion_load(const char *name, struct model *model, struct archive *aar); +void motion_free(struct motion *motion); + // renderer.c struct RE_renderer *RE_renderer_new(struct texture *texture); @@ -132,7 +163,29 @@ struct pol_bone { versor rotq; }; +struct mot { + uint32_t nr_frames; + uint32_t nr_bones; + struct mot_bone *motions[]; +}; + +struct mot_frame { + vec3 pos; + versor rotq; +}; + +struct mot_bone { + char *name; + uint32_t id; + uint32_t parent; + struct mot_frame frames[]; +}; + struct pol *pol_parse(uint8_t *data, size_t size); void pol_free(struct pol *pol); +struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id); + +struct mot *mot_parse(uint8_t *data, size_t size); +void mot_free(struct mot *mot); #endif /* SYSTEM4_3D_3D_INTERNAL_H */ diff --git a/src/3d/model.c b/src/3d/model.c index 9706eca..939a5ac 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -21,14 +21,19 @@ #include "system4.h" #include "system4/aar.h" #include "system4/cg.h" +#include "system4/hashtable.h" #include "3d_internal.h" #include "reign.h" +#define NR_WEIGHTS 4 + struct vertex { GLfloat pos[3]; GLfloat normal[3]; GLfloat uv[2]; + GLint bone_id[NR_WEIGHTS]; + GLfloat bone_weight[NR_WEIGHTS]; }; static bool init_material(struct material *material, const struct pol_material *m, struct archive *aar, const char *path) @@ -75,6 +80,25 @@ static void destroy_material(struct material *material) glDeleteTextures(1, &material->color_map); } +static int cmp_by_bone_weight(const void *lhs, const void *rhs) +{ + float l = ((struct pol_bone_weight *)lhs)->weight; + float r = ((struct pol_bone_weight *)rhs)->weight; + return (l < r) - (l > r); // descending order. +} + +static void sort_and_normalize_bone_weights(struct pol_vertex *v) +{ + qsort(v->weights, v->nr_weights, sizeof(struct pol_bone_weight), cmp_by_bone_weight); + float total = 0.0; + for (uint32_t i = 0; i < v->nr_weights && i < NR_WEIGHTS; i++) { + total += v->weights[i].weight; + } + for (uint32_t i = 0; i < v->nr_weights && i < NR_WEIGHTS; i++) { + v->weights[i].weight /= total; + } +} + static void add_mesh(struct model *model, struct pol_mesh *m, int material_index, int material, struct RE_renderer *r) { struct vertex *buf = xmalloc(m->nr_triangles * 3 * sizeof(struct vertex)); @@ -84,9 +108,23 @@ static void add_mesh(struct model *model, struct pol_mesh *m, int material_index if (material_index >= 0 && t->material != (uint32_t)material_index) continue; for (int j = 0; j < 3; j++) { - glm_vec3_copy(m->vertices[t->vert_index[j]].pos, buf[v].pos); + struct pol_vertex *vert = &m->vertices[t->vert_index[j]]; + glm_vec3_copy(vert->pos, buf[v].pos); glm_vec3_copy(t->normals[j], buf[v].normal); glm_vec2_copy(m->uvs[t->uv_index[j]], buf[v].uv); + sort_and_normalize_bone_weights(vert); + for (uint32_t k = 0; k < NR_WEIGHTS; k++) { + if (model->bone_map && k < vert->nr_weights) { + struct bone *bone = ht_get_int(model->bone_map, vert->weights[k].bone, NULL); + if (!bone) + WARNING("%s: invalid bone id in vertex data", model->path); + buf[v].bone_id[k] = bone ? bone->index : -1; + buf[v].bone_weight[k] = vert->weights[k].weight; + } else { + buf[v].bone_id[k] = -1; + buf[v].bone_weight[k] = 0.0; + } + } v++; } } @@ -107,9 +145,13 @@ static void add_mesh(struct model *model, struct pol_mesh *m, int material_index glEnableVertexAttribArray(r->shader.vertex); glVertexAttribPointer(r->shader.vertex, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)0); glEnableVertexAttribArray(r->vertex_normal); - glVertexAttribPointer(r->vertex_normal, 3, GL_FLOAT, GL_TRUE, sizeof(struct vertex), (const void *)offsetof(struct vertex, normal)); + glVertexAttribPointer(r->vertex_normal, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)offsetof(struct vertex, normal)); glEnableVertexAttribArray(r->vertex_uv); glVertexAttribPointer(r->vertex_uv, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)offsetof(struct vertex, uv)); + glEnableVertexAttribArray(r->vertex_bone_index); + glVertexAttribIPointer(r->vertex_bone_index, NR_WEIGHTS, GL_INT, sizeof(struct vertex), (const void *)offsetof(struct vertex, bone_id)); + glEnableVertexAttribArray(r->vertex_bone_weight); + glVertexAttribPointer(r->vertex_bone_weight, NR_WEIGHTS, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)offsetof(struct vertex, bone_weight)); glBufferData(GL_ARRAY_BUFFER, mesh->nr_vertices * sizeof(struct vertex), buf, GL_STATIC_DRAW); glBindVertexArray(0); @@ -124,6 +166,39 @@ static void destroy_mesh(struct mesh *mesh) glDeleteBuffers(1, &mesh->attr_buffer); } +static struct bone *add_bone(struct model *model, struct pol *pol, struct pol_bone *pol_bone) +{ + struct bone *bone = ht_get_int(model->bone_map, pol_bone->id, NULL); + if (bone) + return bone; // already added. + + struct bone *parent = NULL; + if (pol_bone->parent >= 0) { + // Parent bone must appear before its children in model->bones. + struct pol_bone *pol_parent = pol_find_bone(pol, pol_bone->parent); + if (!pol_parent) + ERROR("Parent bone of \"%s\" is not found", pol_bone->name); + parent = add_bone(model, pol, pol_parent); + } + + bone = &model->bones[model->nr_bones]; + ht_put_int(model->bone_map, pol_bone->id, bone); + bone->name = strdup(pol_bone->name); + bone->index = model->nr_bones; + bone->parent = parent ? parent->index : -1; + + glm_quat_mat4(pol_bone->rotq, bone->inverse_bind_matrix); + glm_translate(bone->inverse_bind_matrix, pol_bone->pos); + + model->nr_bones++; + return bone; +} + +static void destroy_bone(struct bone *bone) +{ + free(bone->name); +} + struct model *model_load(struct archive *aar, const char *path, struct RE_renderer *r) { if (path[0] == '\0') @@ -148,9 +223,23 @@ struct model *model_load(struct archive *aar, const char *path, struct RE_render free(polname); return NULL; } + free(polname); struct model *model = xcalloc(1, sizeof(struct model)); - model->name = polname; + model->path = strdup(path); + + // Bones + if (pol->nr_bones > 0) { + if (pol->nr_bones > MAX_BONES) + ERROR("%s: Too many bones (%u)", model->path, pol->nr_bones); + model->bone_map = ht_create(pol->nr_bones * 3 / 2); + model->bones = xcalloc(pol->nr_bones, sizeof(struct bone)); + for (uint32_t i = 0; i < pol->nr_bones; i++) { + add_bone(model, pol, &pol->bones[i]); + } + if (model->nr_bones != (int)pol->nr_bones) + ERROR("%s: Broken bone data", model->path); + } // Materials int *material_offsets = xmalloc(pol->nr_materials * sizeof(int)); @@ -204,6 +293,64 @@ void model_free(struct model *model) destroy_material(&model->materials[i]); free(model->materials); - free(model->name); + for (int i = 0; i < model->nr_bones; i++) + destroy_bone(&model->bones[i]); + free(model->bones); + if (model->bone_map) + ht_free_int(model->bone_map); + + free(model->path); free(model); } + +static int cmp_motions_by_bone_id(const void *lhs, const void *rhs) +{ + return (*(struct mot_bone **)lhs)->id - (*(struct mot_bone **)rhs)->id; +} + +struct motion *motion_load(const char *name, struct model *model, struct archive *aar) +{ + char *motname = xmalloc(strlen(model->path) + strlen(name) + 6); + sprintf(motname, "%s\\%s.MOT", model->path, name); + + struct archive_data *dfile = archive_get_by_name(aar, motname); + if (!dfile) { + WARNING("%s: not found", motname); + free(motname); + return NULL; + } + + struct mot *mot = mot_parse(dfile->data, dfile->size); + archive_free_data(dfile); + if (!mot) { + WARNING("%s: parse error", motname); + free(motname); + return NULL; + } + free(motname); + + if (model->nr_bones != (int)mot->nr_bones) + ERROR("%s: wrong number of bones. Expected %d but got %d", name, model->nr_bones, mot->nr_bones); + + // Reorder mot->motions so that motion for model->bones[i] can be + // accessed by mot->motions[i]. + for (uint32_t i = 0; i < mot->nr_bones; i++) { + struct bone *bone = ht_get_int(model->bone_map, mot->motions[i]->id, NULL); + if (!bone) + ERROR("%s: invalid bone id %d", name, mot->motions[i]->id); + mot->motions[i]->id = bone->index; + } + qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id); + + struct motion *motion = xcalloc(1, sizeof(struct motion)); + motion->mot = mot; + motion->name = strdup(name); + return motion; +} + +void motion_free(struct motion *motion) +{ + mot_free(motion->mot); + free(motion->name); + free(motion); +} diff --git a/src/3d/parser.c b/src/3d/parser.c index 9548955..bac6d99 100644 --- a/src/3d/parser.c +++ b/src/3d/parser.c @@ -56,6 +56,7 @@ static void read_quaternion(struct buffer *r, versor q) float y = -buffer_read_float(r); float z = buffer_read_float(r); glm_quat_init(q, x, y, z, w); + glm_quat_normalize(q); } static void parse_material(struct buffer *r, struct pol_material *m) @@ -284,3 +285,57 @@ void pol_free(struct pol *pol) free(pol->bones); free(pol); } + +struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id) +{ + for (uint32_t i = 0; i < pol->nr_bones; i++) { + if (pol->bones[i].id == id) + return &pol->bones[i]; + } + return NULL; +} + +struct mot *mot_parse(uint8_t *data, size_t size) +{ + struct buffer r; + buffer_init(&r, data, size); + if (memcmp(buffer_strdata(&r), "MOT\0", 4)) + return NULL; + buffer_skip(&r, 4); + uint32_t version = buffer_read_int32(&r); + if (version != 0) { + WARNING("unknown MOT version: %d", version); + return NULL; + } + uint32_t nr_frames = buffer_read_int32(&r); + uint32_t nr_bones = buffer_read_int32(&r); + + struct mot *mot = xcalloc(1, sizeof(struct mot) + nr_bones * sizeof(struct mot_bone *)); + mot->nr_frames = nr_frames; + mot->nr_bones = nr_bones; + for (uint32_t i = 0; i < nr_bones; i++) { + struct mot_bone *m = xcalloc(1, sizeof(struct mot_bone) + sizeof(struct mot_frame) * nr_frames); + m->name = read_cstring(&r); + m->id = buffer_read_int32(&r); + m->parent = buffer_read_int32(&r); + for (uint32_t j = 0; j < nr_frames; j++) { + read_position(&r, m->frames[j].pos); + read_quaternion(&r, m->frames[j].rotq); + buffer_skip(&r, 16); // another quaternion? + } + mot->motions[i] = m; + } + if (buffer_remaining(&r) != 0) { + WARNING("extra data at end"); + } + return mot; +} + +void mot_free(struct mot *mot) +{ + for (uint32_t i = 0; i < mot->nr_bones; i++) { + free(mot->motions[i]->name); + free(mot->motions[i]); + } + free(mot); +} diff --git a/src/3d/reign.c b/src/3d/reign.c index a8f3e45..ebd4a67 100644 --- a/src/3d/reign.c +++ b/src/3d/reign.c @@ -35,14 +35,34 @@ static struct RE_instance *create_instance(struct RE_plugin *plugin) instance->scale[0] = 1.0; instance->scale[1] = 1.0; instance->scale[2] = 1.0; + instance->fps = 30.0; glm_mat4_identity(instance->local_transform); return instance; } +static void unload_instance(struct RE_instance *instance) +{ + if (instance->motion) { + motion_free(instance->motion); + instance->motion = NULL; + } + if (instance->next_motion) { + motion_free(instance->next_motion); + instance->next_motion = NULL; + } + if (instance->model) { + model_free(instance->model); + instance->model = NULL; + } + if (instance->bone_transforms) { + free(instance->bone_transforms); + instance->bone_transforms = NULL; + } +} + static void free_instance(struct RE_instance *instance) { - if (instance->model) - model_free(instance->model); + unload_instance(instance); free(instance); } @@ -138,29 +158,124 @@ bool RE_instance_load(struct RE_instance *instance, const char *name) { if (!instance) return false; - if (instance->model) - model_free(instance->model); + unload_instance(instance); instance->model = model_load(instance->plugin->aar, name, instance->plugin->renderer); + if (instance->model && instance->model->nr_bones > 0) + instance->bone_transforms = xcalloc(instance->model->nr_bones, sizeof(mat4)); return !!instance->model; } +bool RE_instance_load_motion(struct RE_instance *instance, const char *name) +{ + if (!instance || !instance->model) + return false; + instance->motion = motion_load(name, instance->model, instance->plugin->aar); + return !!instance->motion; +} + +bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name) +{ + if (!instance || !instance->model) + return false; + instance->next_motion = motion_load(name, instance->model, instance->plugin->aar); + return !!instance->next_motion; +} + +bool RE_instance_free_next_motion(struct RE_instance *instance) +{ + if (!instance || !instance->next_motion) + return false; + motion_free(instance->next_motion); + instance->next_motion = NULL; + return true; +} + +int RE_motion_get_state(struct motion *motion) +{ + return motion ? motion->state : RE_MOTION_STATE_STOP; +} + +bool RE_motion_set_state(struct motion *motion, int state) +{ + if (!motion) + return false; + motion->state = state; + return true; +} + +float RE_motion_get_frame(struct motion *motion) +{ + return motion ? motion->current_frame : 0.0; +} + +bool RE_motion_set_frame(struct motion *motion, float frame) +{ + if (!motion) + return false; + motion->current_frame = frame; + return true; +} + +bool RE_motion_set_frame_range(struct motion *motion, float begin, float end) +{ + if (!motion) + return false; + motion->frame_begin = begin; + motion->frame_end = end; + return true; +} + +bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float end) +{ + if (!motion) + return false; + motion->loop_frame_begin = begin; + motion->loop_frame_end = end; + return true; +} + +static void print_motion(const char *name, struct motion *m, int indent) +{ + const char *state; + switch (m->state) { + case RE_MOTION_STATE_STOP: state = "STOP"; break; + case RE_MOTION_STATE_NOLOOP: state = "NOLOOP"; break; + case RE_MOTION_STATE_LOOP: state = "LOOP"; break; + default: state = "?"; break; + } + indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n", + name, m->name, state, m->current_frame); +} + void RE_debug_print(struct RE_plugin *p) { - printf("camera = { x: %f, y: %f, z: %f, pitch: %f, roll: %f, yaw: %f }", - p->camera.pos[0], p->camera.pos[1], p->camera.pos[2], - p->camera.pitch, p->camera.roll, p->camera.yaw); + int indent = 0; + indent_printf(indent, "camera = {x=%f, y=%f, z=%f, pitch=%f, roll=%f, yaw=%f},\n", + p->camera.pos[0], p->camera.pos[1], p->camera.pos[2], + p->camera.pitch, p->camera.roll, p->camera.yaw); for (int i = 0; i < RE_MAX_INSTANCES; i++) { struct RE_instance *inst = p->instances[i]; if (!inst || !inst->model) continue; - printf("[%d] = {", i); - printf(" name: \"%s\",", inst->model->name); - printf(" type: %d,", inst->type); - printf(" draw: %d,", inst->draw); - printf(" pos: { x: %f, y: %f, z: %f },", inst->pos[0], inst->pos[1], inst->pos[2]); - printf(" vec: { x: %f, y: %f, z: %f },", inst->vec[0], inst->vec[1], inst->vec[2]); - printf(" scale: { x: %f, y: %f, z: %f },", inst->scale[0], inst->scale[1], inst->scale[2]); - printf("}"); + indent_printf(indent, "instance[%d] = {\n", i); + indent++; + + indent_printf(indent, "path = \"%s\",\n", inst->model->path); + indent_printf(indent, "type = %d,\n", inst->type); + indent_printf(indent, "draw = %d,\n", inst->draw); + indent_printf(indent, "pos = {x=%f, y=%f, z=%f},\n", inst->pos[0], inst->pos[1], inst->pos[2]); + indent_printf(indent, "vec = {x=%f, y=%f, z=%f},\n", inst->vec[0], inst->vec[1], inst->vec[2]); + indent_printf(indent, "scale = {x=%f, y=%f, z=%f},\n", inst->scale[0], inst->scale[1], inst->scale[2]); + if (inst->motion) + print_motion("motion", inst->motion, indent); + if (inst->next_motion) + print_motion("next_motion", inst->next_motion, indent); + indent_printf(indent, "fps = %f,\n", inst->fps); + if (inst->motion_blend) + indent_printf(indent, "motion_blend_rate = %f,\n", inst->motion_blend_rate); + + indent--; + indent_printf(indent, "},\n"); } } diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 932414f..40af51a 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -14,6 +14,7 @@ * along with this program; if not, see . */ +#include #include #include "system4.h" @@ -21,6 +22,7 @@ #include "3d_internal.h" #include "reign.h" #include "sact.h" +#include "vm.h" struct RE_renderer *RE_renderer_new(struct texture *texture) { @@ -29,14 +31,19 @@ struct RE_renderer *RE_renderer_new(struct texture *texture) gfx_load_shader(&r->shader, "shaders/reign.v.glsl", "shaders/reign.f.glsl"); r->local_transform = glGetUniformLocation(r->shader.program, "local_transform"); r->proj_transform = glGetUniformLocation(r->shader.program, "proj_transform"); + r->has_bones = glGetUniformLocation(r->shader.program, "has_bones"); + r->bone_matrices = glGetUniformLocation(r->shader.program, "bone_matrices"); r->vertex_normal = glGetAttribLocation(r->shader.program, "vertex_normal"); r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv"); + r->vertex_bone_index = glGetAttribLocation(r->shader.program, "vertex_bone_index"); + r->vertex_bone_weight = glGetAttribLocation(r->shader.program, "vertex_bone_weight"); glGenRenderbuffers(1, &r->depth_buffer); glBindRenderbuffer(GL_RENDERBUFFER, r->depth_buffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h); glBindRenderbuffer(GL_RENDERBUFFER, 0); + r->last_frame_timestamp = SDL_GetTicks(); return r; } @@ -79,7 +86,71 @@ static void update_local_transform(struct RE_instance *inst) inst->local_transform_needs_update = false; } -static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_transform, mat4 proj_transform) +static void interpolate_motion_frame(struct mot_frame *m1, struct mot_frame *m2, float t, struct mot_frame *out) +{ + glm_vec3_lerp(m1->pos, m2->pos, t, out->pos); + glm_quat_nlerp(m1->rotq, m2->rotq, t, out->rotq); +} + +static void update_motion(struct motion *m, float delta_frame) +{ + if (!m || m->state == RE_MOTION_STATE_STOP) + return; + m->current_frame += delta_frame; + switch (m->state) { + case RE_MOTION_STATE_NOLOOP: + if (m->current_frame > m->frame_end) { + m->current_frame = m->frame_end; + m->state = RE_MOTION_STATE_STOP; + } + break; + case RE_MOTION_STATE_LOOP: + if (m->current_frame > m->loop_frame_end) + m->current_frame = m->loop_frame_begin + fmodf(m->current_frame - m->loop_frame_begin, m->loop_frame_end - m->loop_frame_begin); + break; + default: + VM_ERROR("Invalid motion state %d", m->state); + } +} + +static void calc_motion_frame(struct motion *m, int bone, struct mot_frame *out) +{ + int cur_frame = m->current_frame; + int next_frame = ceilf(m->current_frame); + float t = m->current_frame - cur_frame; + struct mot_frame *frames = m->mot->motions[bone]->frames; + interpolate_motion_frame(&frames[cur_frame], &frames[next_frame], t, out); +} + +static void animate(struct RE_instance *inst, struct RE_renderer *r, float delta_time) +{ + float delta_frame = delta_time * inst->fps; + update_motion(inst->motion, delta_frame); + update_motion(inst->next_motion, delta_frame); + + for (int i = 0; i < inst->model->nr_bones; i++) { + struct mot_frame mf; + calc_motion_frame(inst->motion, i, &mf); + if (inst->motion_blend && inst->next_motion) { + struct mot_frame next_mf; + calc_motion_frame(inst->next_motion, i, &next_mf); + interpolate_motion_frame(&mf, &next_mf, inst->motion_blend_rate, &mf); + } + mat4 bone_transform; + glm_translate_make(bone_transform, mf.pos); + glm_quat_rotate(bone_transform, mf.rotq, bone_transform); + + if (inst->model->bones[i].parent >= 0) { + assert(inst->model->bones[i].parent < i); + glm_mat4_mul(inst->bone_transforms[inst->model->bones[i].parent], bone_transform, bone_transform); + } + glm_mat4_copy(bone_transform, inst->bone_transforms[i]); + + glm_mat4_mul(bone_transform, inst->model->bones[i].inverse_bind_matrix, r->bone_transforms[i]); + } +} + +static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_transform, mat4 proj_transform, float delta_time) { if (!inst->draw || !inst->model) return; @@ -87,11 +158,20 @@ static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat if (inst->local_transform_needs_update) update_local_transform(inst); + animate(inst, r, delta_time); + glUseProgram(r->shader.program); glUniformMatrix4fv(r->shader.view_transform, 1, GL_FALSE, view_transform[0]); glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, proj_transform[0]); glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, inst->local_transform[0]); + if (inst->model->nr_bones > 0) { + glUniform1i(r->has_bones, GL_TRUE); + glUniformMatrix4fv(r->bone_matrices, inst->model->nr_bones, GL_FALSE, r->bone_transforms[0][0]); + } else { + glUniform1i(r->has_bones, GL_FALSE); + } + struct model *model = inst->model; for (int i = 0; i < model->nr_meshes; i++) { struct mesh *mesh = &model->meshes[i]; @@ -110,13 +190,19 @@ static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat void RE_render(struct sact_sprite *sp) { + uint32_t timestamp = SDL_GetTicks(); + struct RE_plugin *plugin = (struct RE_plugin *)sp->plugin; struct RE_renderer *r = plugin->renderer; - if (!r) + if (!r) { + r->last_frame_timestamp = timestamp; return; + } sprite_dirty(sp); struct texture *texture = sprite_get_texture(sp); + float delta_time = (timestamp - r->last_frame_timestamp) / 1000.0; + GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, texture, 0, 0, texture->w, texture->h); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, r->depth_buffer); if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) @@ -140,7 +226,7 @@ void RE_render(struct sact_sprite *sp) for (int i = 0; i < RE_MAX_INSTANCES; i++) { if (plugin->instances[i]) - render_instance(plugin->instances[i], r, view_transform, plugin->proj_transform); + render_instance(plugin->instances[i], r, view_transform, plugin->proj_transform, delta_time); } plugin->proj_transform[1][1] *= -1; @@ -149,4 +235,6 @@ void RE_render(struct sact_sprite *sp) glDisable(GL_CULL_FACE); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo); + + r->last_frame_timestamp = timestamp; } diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c index 52dc47b..5ce8b02 100644 --- a/src/hll/ReignEngine.c +++ b/src/hll/ReignEngine.c @@ -42,6 +42,18 @@ static struct RE_instance *get_instance(unsigned plugin, unsigned instance) return instance < RE_MAX_INSTANCES ? rp->instances[instance] : NULL; } +static struct motion *get_motion(unsigned plugin, unsigned instance) +{ + struct RE_instance *inst = get_instance(plugin, instance); + return inst ? inst->motion : NULL; +} + +static struct motion *get_next_motion(unsigned plugin, unsigned instance) +{ + struct RE_instance *inst = get_instance(plugin, instance); + return inst ? inst->next_motion : NULL; +} + static int ReignEngine_CreatePlugin(void) { for (int i = 0; i < RE_MAX_PLUGINS; i++) { @@ -239,25 +251,110 @@ static bool ReignEngine_GetInstanceDraw(int plugin, int instance) //bool ReignEngine_GetInstanceDrawBump(int plugin, int instance); //int ReignEngine_GetInstanceDrawType(int plugin, int instance); HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceDrawType, int plugin, int instance, int draw_type); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, LoadInstanceMotion, int plugin, int instance, struct string *name); -HLL_WARN_UNIMPLEMENTED(0, int, ReignEngine, GetInstanceMotionState, int plugin, int instance); -HLL_WARN_UNIMPLEMENTED(0.0, float, ReignEngine, GetInstanceMotionFrame, int plugin, int instance); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionState, int plugin, int instance, int state); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionFrame, int plugin, int instance, float frame); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionFrameRange, int plugin, int instance, float begin_frame, float end_frame); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionLoopFrameRange, int plugin, int instance, float begin_frame, float end_frame); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, LoadInstanceNextMotion, int plugin, int instance, struct string *name); -//bool ReignEngine_SetInstanceNextMotionState(int plugin, int instance, int nState); -//bool ReignEngine_SetInstanceNextMotionFrame(int plugin, int instance, float fFrame); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceNextMotionFrameRange, int plugin, int instance, float begin_frame, float end_frame); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceNextMotionLoopFrameRange, int plugin, int instance, float begin_frame, float end_frame); -//bool ReignEngine_SetInstanceMotionBlendRate(int plugin, int instance, float fRate); -//bool ReignEngine_SetInstanceMotionBlend(int plugin, int instance, bool bMotionBlend); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, IsInstanceMotionBlend, int plugin, int instance); + +bool ReignEngine_LoadInstanceMotion(int plugin, int instance, struct string *name) +{ + return RE_instance_load_motion(get_instance(plugin, instance), name->text); +} + +static int ReignEngine_GetInstanceMotionState(int plugin, int instance) +{ + return RE_motion_get_state(get_motion(plugin, instance)); +} + +static float ReignEngine_GetInstanceMotionFrame(int plugin, int instance) +{ + return RE_motion_get_frame(get_motion(plugin, instance)); +} + +static bool ReignEngine_SetInstanceMotionState(int plugin, int instance, int state) +{ + return RE_motion_set_state(get_motion(plugin, instance), state); +} + +static bool ReignEngine_SetInstanceMotionFrame(int plugin, int instance, float frame) +{ + return RE_motion_set_frame(get_motion(plugin, instance), frame); +} + +static bool ReignEngine_SetInstanceMotionFrameRange(int plugin, int instance, float begin_frame, float end_frame) +{ + return RE_motion_set_frame_range(get_motion(plugin, instance), begin_frame, end_frame); +} + +static bool ReignEngine_SetInstanceMotionLoopFrameRange(int plugin, int instance, float begin_frame, float end_frame) +{ + return RE_motion_set_loop_frame_range(get_motion(plugin, instance), begin_frame, end_frame); +} + +static bool ReignEngine_LoadInstanceNextMotion(int plugin, int instance, struct string *name) +{ + return RE_instance_load_next_motion(get_instance(plugin, instance), name->text); +} + +static bool ReignEngine_SetInstanceNextMotionState(int plugin, int instance, int state) +{ + return RE_motion_set_state(get_next_motion(plugin, instance), state); +} + +static bool ReignEngine_SetInstanceNextMotionFrame(int plugin, int instance, float frame) +{ + return RE_motion_set_frame(get_next_motion(plugin, instance), frame); +} + +static bool ReignEngine_SetInstanceNextMotionFrameRange(int plugin, int instance, float begin_frame, float end_frame) +{ + return RE_motion_set_frame_range(get_next_motion(plugin, instance), begin_frame, end_frame); +} + +static bool ReignEngine_SetInstanceNextMotionLoopFrameRange(int plugin, int instance, float begin_frame, float end_frame) +{ + return RE_motion_set_loop_frame_range(get_next_motion(plugin, instance), begin_frame, end_frame); +} + +static bool ReignEngine_SetInstanceMotionBlendRate(int plugin, int instance, float rate) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + ri->motion_blend_rate = rate; + return true; +} + +static bool ReignEngine_SetInstanceMotionBlend(int plugin, int instance, bool motion_blend) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + ri->motion_blend = motion_blend; + return true; +} + +static bool ReignEngine_IsInstanceMotionBlend(int plugin, int instance) +{ + struct RE_instance *ri = get_instance(plugin, instance); + return ri ? ri->motion_blend : false; +} + //bool ReignEngine_GetInstanceMotionBlendPutTime(int plugin, int instance); HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionBlendPutTime, int plugin, int instance, bool flag); -//bool ReignEngine_SwapInstanceMotion(int plugin, int instance); -//bool ReignEngine_FreeInstanceNextMotion(int plugin, int instance); + +static bool ReignEngine_SwapInstanceMotion(int plugin, int instance) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + struct motion *tmp = ri->motion; + ri->motion = ri->next_motion; + ri->next_motion = tmp; + return true; +} + +static bool ReignEngine_FreeInstanceNextMotion(int plugin, int instance) +{ + return RE_instance_free_next_motion(get_instance(plugin, instance)); +} + //int ReignEngine_GetInstanceNumofMaterial(int plugin, int instance); //bool ReignEngine_GetInstanceMaterialName(int plugin, int instance, int num, struct string **pIName); //float ReignEngine_GetInstanceMaterialParam(int plugin, int instance, int nMaterial, int type); @@ -281,8 +378,22 @@ HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceColumnAngle, int plu //bool ReignEngine_SetInstanceDrawColumn(int plugin, int instance, bool bDraw); HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceTarget, int plugin, int instance, int index, int target); //int ReignEngine_GetInstanceTarget(int plugin, int instance, int index); -//float ReignEngine_GetInstanceFPS(int plugin, int instance); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceFPS, int plugin, int instance, float fps); + +static float ReignEngine_GetInstanceFPS(int plugin, int instance) +{ + struct RE_instance *ri = get_instance(plugin, instance); + return ri ? ri->fps : 0.0; +} + +static bool ReignEngine_SetInstanceFPS(int plugin, int instance, float fps) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + ri->fps = fps; + return true; +} + HLL_WARN_UNIMPLEMENTED(0, int, ReignEngine, GetInstanceBoneIndex, int plugin, int instance, struct string *name); HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, TransInstanceLocalPosToWorldPosByBone, int plugin, int instance, int bone, float offset_x, float offset_y, float offset_z, float *x, float *y, float *z); //int ReignEngine_GetInstanceNumofPolygon(int plugin, int instance); @@ -304,7 +415,7 @@ HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, GetEffectFrameRange, int plugin //float ReignEngine_GetEffectObjectMoveCurve(int plugin, int instance, int nObject); //void ReignEngine_GetEffectObjectFrame(int plugin, int instance, int nObject, int *pnBeginFrame, int *pnEndFrame); //int ReignEngine_GetEffectObjectStopFrame(int plugin, int instance, int nObject); -//int ReignEngine_GetEffectNumofObject(int plugin, int instance); +HLL_WARN_UNIMPLEMENTED(0, int, ReignEngine, GetEffectNumofObject, int plugin, int instance); //void ReignEngine_GetEffectObjectName(int plugin, int instance, int nObject, struct string **pIName); //int ReignEngine_GetEffectNumofObjectPos(int plugin, int instance, int nObject); //int ReignEngine_GetEffectNumofObjectPosUnit(int plugin, int instance, int nObject, int nPos); @@ -417,8 +528,8 @@ HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, GetEffectFrameRange, int plugin //float ReignEngine_GetEffectObjectOffsetY(int plugin, int instance, int nObject); //bool ReignEngine_SetEffectObjectOffsetX(int plugin, int instance, int nObject, float fOffsetX); //bool ReignEngine_SetEffectObjectOffsetY(int plugin, int instance, int nObject, float fOffsetY); -//bool ReignEngine_GetCameraQuakeEffectFlag(int plugin, int instance); -//bool ReignEngine_SetCameraQuakeEffectFlag(int plugin, int instance, bool flag); +HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, GetCameraQuakeEffectFlag, int plugin, int instance); +HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetCameraQuakeEffectFlag, int plugin, int instance, bool flag); bool ReignEngine_SetCameraPos(int plugin, float x, float y, float z) { @@ -928,17 +1039,17 @@ HLL_LIBRARY(ReignEngine, HLL_EXPORT(SetInstanceMotionFrameRange, ReignEngine_SetInstanceMotionFrameRange), HLL_EXPORT(SetInstanceMotionLoopFrameRange, ReignEngine_SetInstanceMotionLoopFrameRange), HLL_EXPORT(LoadInstanceNextMotion, ReignEngine_LoadInstanceNextMotion), - HLL_TODO_EXPORT(SetInstanceNextMotionState, ReignEngine_SetInstanceNextMotionState), - HLL_TODO_EXPORT(SetInstanceNextMotionFrame, ReignEngine_SetInstanceNextMotionFrame), + HLL_EXPORT(SetInstanceNextMotionState, ReignEngine_SetInstanceNextMotionState), + HLL_EXPORT(SetInstanceNextMotionFrame, ReignEngine_SetInstanceNextMotionFrame), HLL_EXPORT(SetInstanceNextMotionFrameRange, ReignEngine_SetInstanceNextMotionFrameRange), HLL_EXPORT(SetInstanceNextMotionLoopFrameRange, ReignEngine_SetInstanceNextMotionLoopFrameRange), - HLL_TODO_EXPORT(SetInstanceMotionBlendRate, ReignEngine_SetInstanceMotionBlendRate), - HLL_TODO_EXPORT(SetInstanceMotionBlend, ReignEngine_SetInstanceMotionBlend), + HLL_EXPORT(SetInstanceMotionBlendRate, ReignEngine_SetInstanceMotionBlendRate), + HLL_EXPORT(SetInstanceMotionBlend, ReignEngine_SetInstanceMotionBlend), HLL_EXPORT(IsInstanceMotionBlend, ReignEngine_IsInstanceMotionBlend), HLL_TODO_EXPORT(GetInstanceMotionBlendPutTime, ReignEngine_GetInstanceMotionBlendPutTime), HLL_EXPORT(SetInstanceMotionBlendPutTime, ReignEngine_SetInstanceMotionBlendPutTime), - HLL_TODO_EXPORT(SwapInstanceMotion, ReignEngine_SwapInstanceMotion), - HLL_TODO_EXPORT(FreeInstanceNextMotion, ReignEngine_FreeInstanceNextMotion), + HLL_EXPORT(SwapInstanceMotion, ReignEngine_SwapInstanceMotion), + HLL_EXPORT(FreeInstanceNextMotion, ReignEngine_FreeInstanceNextMotion), HLL_TODO_EXPORT(GetInstanceNumofMaterial, ReignEngine_GetInstanceNumofMaterial), HLL_TODO_EXPORT(GetInstanceMaterialName, ReignEngine_GetInstanceMaterialName), HLL_TODO_EXPORT(GetInstanceMaterialParam, ReignEngine_GetInstanceMaterialParam), @@ -962,7 +1073,7 @@ HLL_LIBRARY(ReignEngine, HLL_TODO_EXPORT(SetInstanceDrawColumn, ReignEngine_SetInstanceDrawColumn), HLL_EXPORT(SetInstanceTarget, ReignEngine_SetInstanceTarget), HLL_TODO_EXPORT(GetInstanceTarget, ReignEngine_GetInstanceTarget), - HLL_TODO_EXPORT(GetInstanceFPS, ReignEngine_GetInstanceFPS), + HLL_EXPORT(GetInstanceFPS, ReignEngine_GetInstanceFPS), HLL_EXPORT(SetInstanceFPS, ReignEngine_SetInstanceFPS), HLL_EXPORT(GetInstanceBoneIndex, ReignEngine_GetInstanceBoneIndex), HLL_EXPORT(TransInstanceLocalPosToWorldPosByBone, ReignEngine_TransInstanceLocalPosToWorldPosByBone), @@ -985,7 +1096,7 @@ HLL_LIBRARY(ReignEngine, HLL_TODO_EXPORT(GetEffectObjectMoveCurve, ReignEngine_GetEffectObjectMoveCurve), HLL_TODO_EXPORT(GetEffectObjectFrame, ReignEngine_GetEffectObjectFrame), HLL_TODO_EXPORT(GetEffectObjectStopFrame, ReignEngine_GetEffectObjectStopFrame), - HLL_TODO_EXPORT(GetEffectNumofObject, ReignEngine_GetEffectNumofObject), + HLL_EXPORT(GetEffectNumofObject, ReignEngine_GetEffectNumofObject), HLL_TODO_EXPORT(GetEffectObjectName, ReignEngine_GetEffectObjectName), HLL_TODO_EXPORT(GetEffectNumofObjectPos, ReignEngine_GetEffectNumofObjectPos), HLL_TODO_EXPORT(GetEffectNumofObjectPosUnit, ReignEngine_GetEffectNumofObjectPosUnit), @@ -1098,8 +1209,8 @@ HLL_LIBRARY(ReignEngine, HLL_TODO_EXPORT(GetEffectObjectOffsetY, ReignEngine_GetEffectObjectOffsetY), HLL_TODO_EXPORT(SetEffectObjectOffsetX, ReignEngine_SetEffectObjectOffsetX), HLL_TODO_EXPORT(SetEffectObjectOffsetY, ReignEngine_SetEffectObjectOffsetY), - HLL_TODO_EXPORT(GetCameraQuakeEffectFlag, ReignEngine_GetCameraQuakeEffectFlag), - HLL_TODO_EXPORT(SetCameraQuakeEffectFlag, ReignEngine_SetCameraQuakeEffectFlag), + HLL_EXPORT(GetCameraQuakeEffectFlag, ReignEngine_GetCameraQuakeEffectFlag), + HLL_EXPORT(SetCameraQuakeEffectFlag, ReignEngine_SetCameraQuakeEffectFlag), HLL_EXPORT(SetCameraPos, ReignEngine_SetCameraPos), HLL_EXPORT(SetCameraAngle, ReignEngine_SetCameraAngle), HLL_EXPORT(SetCameraAngleP, ReignEngine_SetCameraAngleP),