From d11116f647dca8ff6c9993fc2e3170674c3fd714 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 19 Jun 2022 12:19:41 +0900 Subject: [PATCH] ReignEngine: Support billboard instance type Used for 2D objects (e.g. NPCs in dungeons). --- include/reign.h | 1 + src/3d/3d_internal.h | 11 ++- src/3d/debug.c | 10 ++- src/3d/model.c | 12 ++- src/3d/reign.c | 44 ++++++++++- src/3d/renderer.c | 169 +++++++++++++++++++++++++++++++++++++----- src/hll/ReignEngine.c | 7 +- 7 files changed, 225 insertions(+), 29 deletions(-) diff --git a/include/reign.h b/include/reign.h index 29a92dc..9018670 100644 --- a/include/reign.h +++ b/include/reign.h @@ -134,6 +134,7 @@ 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); +bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z); int RE_motion_get_state(struct motion *motion); bool RE_motion_set_state(struct motion *motion, int state); diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 5fe0004..bd7422c 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -57,6 +57,7 @@ struct bone { }; struct motion { + struct RE_instance *instance; struct mot *mot; char *name; int state; @@ -83,21 +84,29 @@ struct RE_renderer { GLint vertex_bone_weight; mat4 bone_transforms[MAX_BONES]; + GLuint billboard_vao; + GLuint billboard_attr_buffer; + struct hash_table *billboard_textures; // cg_no -> struct billboard_texture* uint32_t last_frame_timestamp; }; +struct billboard_texture { + GLuint texture; +}; + // model.c 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); +struct motion *motion_load(const char *name, struct RE_instance *instance, struct archive *aar); void motion_free(struct motion *motion); // renderer.c struct RE_renderer *RE_renderer_new(struct texture *texture); void RE_renderer_free(struct RE_renderer *r); +bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no); // parser.c diff --git a/src/3d/debug.c b/src/3d/debug.c index b1ec04c..e8c41ec 100644 --- a/src/3d/debug.c +++ b/src/3d/debug.c @@ -47,8 +47,14 @@ static const char *motion_state_name(enum RE_motion_state state) static void print_motion(const char *name, struct motion *m, int indent) { - indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n", - name, m->name, motion_state_name(m->state), m->current_frame); + if (m->instance->type == RE_ITYPE_BILLBOARD) { + indent_printf(indent, "%s = {state=%s, frame=%f, range=(%d,%d), loop_range=(%d,%d)},\n", + name, motion_state_name(m->state), m->current_frame, + (int)m->frame_begin, (int)m->frame_end, (int)m->loop_frame_begin, (int)m->loop_frame_end); + } else { + indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n", + name, m->name, motion_state_name(m->state), m->current_frame); + } } static void print_instance(struct RE_instance *inst, int index, int indent) diff --git a/src/3d/model.c b/src/3d/model.c index 939a5ac..664b1e2 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -308,8 +308,11 @@ 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) +struct motion *motion_load(const char *name, struct RE_instance *instance, struct archive *aar) { + struct model *model = instance->model; + if (!model) + return NULL; char *motname = xmalloc(strlen(model->path) + strlen(name) + 6); sprintf(motname, "%s\\%s.MOT", model->path, name); @@ -343,6 +346,7 @@ struct motion *motion_load(const char *name, struct model *model, struct archive qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id); struct motion *motion = xcalloc(1, sizeof(struct motion)); + motion->instance = instance; motion->mot = mot; motion->name = strdup(name); return motion; @@ -350,7 +354,9 @@ struct motion *motion_load(const char *name, struct model *model, struct archive void motion_free(struct motion *motion) { - mot_free(motion->mot); - free(motion->name); + if (motion->mot) + mot_free(motion->mot); + if (motion->name) + free(motion->name); free(motion); } diff --git a/src/3d/reign.c b/src/3d/reign.c index f156dc0..47918ca 100644 --- a/src/3d/reign.c +++ b/src/3d/reign.c @@ -14,6 +14,7 @@ * along with this program; if not, see . */ +#include #include #include #include @@ -184,10 +185,18 @@ bool RE_instance_set_type(struct RE_instance *instance, int type) ERROR("instance type cannot be changed"); switch (type) { case RE_ITYPE_STATIC: + break; case RE_ITYPE_SKINNED: + break; case RE_ITYPE_BILLBOARD: + assert(!instance->motion); + instance->motion = xcalloc(1, sizeof(struct motion)); + instance->motion->instance = instance; + break; case RE_ITYPE_DIRECTIONAL_LIGHT: + break; case RE_ITYPE_SPECULAR_LIGHT: + break; case RE_ITYPE_PARTICLE_EFFECT: break; default: @@ -210,21 +219,21 @@ bool RE_instance_load(struct RE_instance *instance, const char *name) bool RE_instance_load_motion(struct RE_instance *instance, const char *name) { - if (!instance || !instance->model) + if (!instance) return false; if (instance->motion) motion_free(instance->motion); - instance->motion = motion_load(name, instance->model, instance->plugin->aar); + instance->motion = motion_load(name, instance, instance->plugin->aar); return !!instance->motion; } bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name) { - if (!instance || !instance->model) + if (!instance) return false; if (instance->next_motion) motion_free(instance->next_motion); - instance->next_motion = motion_load(name, instance->model, instance->plugin->aar); + instance->next_motion = motion_load(name, instance, instance->plugin->aar); return !!instance->next_motion; } @@ -237,6 +246,21 @@ bool RE_instance_free_next_motion(struct RE_instance *instance) return true; } +bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z) +{ + if (!instance) + return false; + if (instance->type != RE_ITYPE_BILLBOARD) + ERROR("not implemented"); + // Hack: This works because SetInstanceVertexPos is only used to scale + // billboards, and SetInstanceScale is not used for billboards. + if (index == 1) { + instance->scale[0] = x; + instance->scale[1] = y / 2.0; + } + return false; +} + int RE_motion_get_state(struct motion *motion) { return motion ? motion->state : RE_MOTION_STATE_STOP; @@ -269,6 +293,12 @@ bool RE_motion_set_frame_range(struct motion *motion, float begin, float end) return false; motion->frame_begin = begin; motion->frame_end = end; + if (motion->instance->type == RE_ITYPE_BILLBOARD) { + for (int i = begin; i < end; i++) { + if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i)) + return false; + } + } return true; } @@ -278,6 +308,12 @@ bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float en return false; motion->loop_frame_begin = begin; motion->loop_frame_end = end; + if (motion->instance->type == RE_ITYPE_BILLBOARD) { + for (int i = begin; i < end; i++) { + if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i)) + return false; + } + } return true; } diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 98eb327..b2efed8 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -18,12 +18,46 @@ #include #include "system4.h" +#include "system4/cg.h" +#include "system4/hashtable.h" #include "3d_internal.h" +#include "asset_manager.h" #include "reign.h" #include "sact.h" #include "vm.h" +static void init_billboard_mesh(struct RE_renderer *r) +{ + static const GLfloat vertices[] = { + // x, y, z, u, v + -1.0, 2.0, 0.0, 0.0, 0.0, + -1.0, 0.0, 0.0, 0.0, 1.0, + 1.0, 2.0, 0.0, 1.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 1.0, + }; + glGenVertexArrays(1, &r->billboard_vao); + glBindVertexArray(r->billboard_vao); + glGenBuffers(1, &r->billboard_attr_buffer); + glBindBuffer(GL_ARRAY_BUFFER, r->billboard_attr_buffer); + + glEnableVertexAttribArray(r->shader.vertex); + glVertexAttribPointer(r->shader.vertex, 3, GL_FLOAT, GL_FALSE, 20, (const void *)0); + glEnableVertexAttribArray(r->vertex_uv); + glVertexAttribPointer(r->vertex_uv, 2, GL_FLOAT, GL_FALSE, 20, (const void *)12); + glDisableVertexAttribArray(r->vertex_normal); + glVertexAttrib3f(r->vertex_normal, 0.0, 0.0, 1.0); + glDisableVertexAttribArray(r->vertex_bone_index); + glVertexAttribI4i(r->vertex_bone_index, 0, 0, 0, 0); + glDisableVertexAttribArray(r->vertex_bone_weight); + glVertexAttrib4f(r->vertex_bone_weight, 0.0, 0.0, 0.0, 0.0); + + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + struct RE_renderer *RE_renderer_new(struct texture *texture) { struct RE_renderer *r = xcalloc(1, sizeof(struct RE_renderer)); @@ -44,14 +78,52 @@ struct RE_renderer *RE_renderer_new(struct texture *texture) glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h); glBindRenderbuffer(GL_RENDERBUFFER, 0); + init_billboard_mesh(r); + r->billboard_textures = ht_create(256); r->last_frame_timestamp = SDL_GetTicks(); return r; } +bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no) +{ + if (ht_get_int(r->billboard_textures, cg_no, NULL)) + return true; + + struct cg *cg = asset_cg_load(cg_no); + if (!cg) + return false; + + struct billboard_texture *bt = xcalloc(1, sizeof(struct billboard_texture)); + glGenTextures(1, &bt->texture); + glBindTexture(GL_TEXTURE_2D, bt->texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + cg_free(cg); + ht_put_int(r->billboard_textures, cg_no, bt); + return true; +} + +static void free_billboard_texture(void *value) +{ + struct billboard_texture *bt = value; + glDeleteTextures(1, &bt->texture); + free(bt); +} + void RE_renderer_free(struct RE_renderer *r) { glDeleteProgram(r->shader.program); glDeleteRenderbuffers(1, &r->depth_buffer); + ht_foreach_value(r->billboard_textures, free_billboard_texture); + ht_free_int(r->billboard_textures); + glDeleteVertexArrays(1, &r->billboard_vao); + glDeleteBuffers(1, &r->billboard_attr_buffer); free(r); } @@ -129,6 +201,9 @@ static void animate(struct RE_instance *inst, struct RE_renderer *r, float delta update_motion(inst->motion, delta_frame); update_motion(inst->next_motion, delta_frame); + if (inst->type == RE_ITYPE_BILLBOARD) + return; + for (int i = 0; i < inst->model->nr_bones; i++) { struct mot_frame mf; calc_motion_frame(inst->motion, i, &mf); @@ -151,30 +226,18 @@ static void animate(struct RE_instance *inst, struct RE_renderer *r, float delta } } -static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_transform, mat4 proj_transform, float delta_time) +static void render_model(struct RE_instance *inst, struct RE_renderer *r) { - if (!inst->draw || !inst->model) + struct model *model = inst->model; + if (!inst->model) return; 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]); glUniform1f(r->alpha_mod, inst->alpha); - 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]; glActiveTexture(GL_TEXTURE0); @@ -186,8 +249,59 @@ static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); } +} - glUseProgram(0); +static void render_static_model(struct RE_instance *inst, struct RE_renderer *r) +{ + glUniform1i(r->has_bones, GL_FALSE); + render_model(inst, r); +} + +static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r, float delta_time) +{ + struct model *model = inst->model; + if (!model) + return; + + animate(inst, r, delta_time); + + if (model->nr_bones > 0) { + glUniform1i(r->has_bones, GL_TRUE); + glUniformMatrix4fv(r->bone_matrices, model->nr_bones, GL_FALSE, r->bone_transforms[0][0]); + } else { + glUniform1i(r->has_bones, GL_FALSE); + } + render_model(inst, r); +} + +static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat, float delta_time) +{ + animate(inst, r, delta_time); + int cg_no = inst->motion->current_frame; + struct billboard_texture *bt = ht_get_int(r->billboard_textures, cg_no, NULL); + if (!bt) + return; + + mat4 local_transform; + glm_translate_make(local_transform, inst->pos); + mat3 rot; + glm_mat4_pick3t(view_mat, rot); + glm_mat4_ins3(rot, local_transform); + glm_scale(local_transform, inst->scale); + + glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]); + glUniform1i(r->has_bones, GL_FALSE); + glUniform1f(r->alpha_mod, inst->alpha); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, bt->texture); + glUniform1i(r->shader.texture, 0); + glBindVertexArray(r->billboard_vao); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); } static void render_back_cg(struct texture *dst, struct RE_back_cg *bcg, struct RE_renderer *r) @@ -242,18 +356,37 @@ void RE_render(struct sact_sprite *sp) plugin->proj_transform[1][1] *= -1; glFrontFace(GL_CW); + glUseProgram(r->shader.program); + glUniformMatrix4fv(r->shader.view_transform, 1, GL_FALSE, view_transform[0]); + glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, plugin->proj_transform[0]); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); for (int i = 0; i < RE_MAX_INSTANCES; i++) { - if (plugin->instances[i]) - render_instance(plugin->instances[i], r, view_transform, plugin->proj_transform, delta_time); + struct RE_instance *inst = plugin->instances[i]; + if (!inst || !inst->draw) + continue; + switch (inst->type) { + case RE_ITYPE_STATIC: + render_static_model(inst, r); + break; + case RE_ITYPE_SKINNED: + render_skinned_model(inst, r, delta_time); + break; + case RE_ITYPE_BILLBOARD: + render_billboard(inst, r, view_transform, delta_time); + break; + default: + break; + } } plugin->proj_transform[1][1] *= -1; glFrontFace(GL_CCW); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); + glUseProgram(0); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo); diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c index 5e9ab96..a73efaa 100644 --- a/src/hll/ReignEngine.c +++ b/src/hll/ReignEngine.c @@ -218,7 +218,12 @@ static bool ReignEngine_SetInstanceScaleZ(int plugin, int instance, float scale_ //bool ReignEngine_SetInstanceZBias(int plugin, int instance, float fZBias); //float ReignEngine_GetInstanceZBias(int plugin, int instance); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceVertexPos, int plugin, int instance, int index, float x, float y, float z); + +bool ReignEngine_SetInstanceVertexPos(int plugin, int instance, int index, float x, float y, float z) +{ + return RE_instance_set_vertex_pos(get_instance(plugin, instance), index, x, y, z); +} + HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceDiffuse, int plugin, int instance, float r, float g, float b); HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceGlobeDiffuse, int plugin, int instance, float r, float g, float b); HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceAmbient, int plugin, int instance, float r, float g, float b);