diff --git a/include/reign.h b/include/reign.h index 029bdaa..e0ceb7a 100644 --- a/include/reign.h +++ b/include/reign.h @@ -195,6 +195,7 @@ struct RE_instance { vec4 bounding_sphere; struct RE_instance *shadow_volume_instance; float z_from_camera; + int texture_animation_index; }; struct RE_plugin *RE_plugin_new(enum RE_plugin_version version); @@ -214,6 +215,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_mesh_show(struct RE_instance *instance, const char *mesh_name, bool show); bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z); int RE_instance_get_bone_index(struct RE_instance *instance, const char *name); bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instance, int bone, vec3 offset, vec3 out); diff --git a/shaders/reign.f.glsl b/shaders/reign.f.glsl index ce603c8..067ce09 100644 --- a/shaders/reign.f.glsl +++ b/shaders/reign.f.glsl @@ -177,12 +177,12 @@ void main() { // Alpha mapping float alpha = texel.a * color_mod.a * alpha_mod; if (alpha_mode == ALPHA_TEST) { - if (alpha < 0.8) + if (alpha < 0.75) discard; } else if (alpha_mode == ALPHA_MAP_BLEND) { alpha *= texture(alpha_texture, tex_coord).r; } else if (alpha_mode == ALPHA_MAP_TEST) { - if (texture(alpha_texture, tex_coord).r < 0.8) + if (texture(alpha_texture, tex_coord).r < 0.1) discard; } diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index a5287e7..7494e2f 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -50,8 +50,10 @@ struct model { }; struct mesh { + char *name; uint32_t flags; bool is_transparent; + bool hidden; GLuint vao; GLuint attr_buffer; GLuint index_buffer; @@ -66,7 +68,8 @@ struct mesh { struct material { uint32_t flags; bool is_transparent; - GLuint color_map; + int nr_color_maps; + GLuint *color_maps; GLuint specular_map; GLuint alpha_map; GLuint light_map; @@ -92,6 +95,7 @@ struct motion { float current_frame; float frame_begin, frame_end; float loop_frame_begin, loop_frame_end; + int texture_index; }; // Attribute variable locations @@ -498,6 +502,8 @@ struct mot { char *name; uint32_t nr_frames; uint32_t nr_bones; + uint32_t nr_texture_indices; + uint32_t *texture_indices; // texture index for each frame struct mot_bone *motions[]; }; @@ -549,6 +555,7 @@ void amt_free(struct amt *amt); struct amt_material *amt_find_material(struct amt *amt, const char *name); void opr_load(uint8_t *data, size_t size, struct pol *pol); +void txa_load(uint8_t *data, size_t size, struct mot *mot); // collision.c diff --git a/src/3d/model.c b/src/3d/model.c index c53c57a..cb105fb 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -83,7 +83,6 @@ static GLuint load_texture(struct archive *aar, const char *path, const char *na { struct archive_data *dfile = RE_get_aar_entry(aar, path, name, ""); if (!dfile) { - WARNING("cannot load texture %s\\%s", path, name); return 0; } struct cg *cg = cg_load_data(dfile); @@ -110,6 +109,35 @@ static GLuint load_texture(struct archive *aar, const char *path, const char *na return texture; } +static GLuint *load_texture_list(struct archive *aar, const char *path, const char *name, int *nr_textures_out, bool *has_alpha_out) +{ + // Load the base texture (e.g. face.png) + GLuint tex = load_texture(aar, path, name, has_alpha_out); + if (!tex) + return NULL; + GLuint *textures = xmalloc(10 * sizeof(GLuint)); + int nr_textures = 0; + textures[nr_textures++] = tex; + + // Try to load additional textures (e.g. face[1].png, face[2].png, etc.) + const char *ext = strrchr(name, '.'); + if (ext) { + for (;;) { + char next_name[100]; + snprintf(next_name, sizeof(next_name), "%.*s[%d]%s", (int)(ext - name), name, nr_textures, ext); + tex = load_texture(aar, path, next_name, NULL); + if (!tex) + break; + textures[nr_textures++] = tex; + if (nr_textures % 10 == 0) { + textures = xrealloc(textures, (nr_textures + 10) * sizeof(GLuint)); + } + } + } + *nr_textures_out = nr_textures; + return textures; +} + static bool init_material(struct material *material, const struct pol_material *m, struct amt *amt, struct archive *aar, const char *path) { material->flags = m->flags; @@ -118,8 +146,8 @@ static bool init_material(struct material *material, const struct pol_material * return false; } bool has_alpha; - material->color_map = load_texture(aar, path, m->textures[COLOR_MAP], &has_alpha); - if (!material->color_map) + material->color_maps = load_texture_list(aar, path, m->textures[COLOR_MAP], &material->nr_color_maps, &has_alpha); + if (!material->color_maps) return false; if (m->textures[SPECULAR_MAP]) @@ -162,8 +190,10 @@ static bool init_material(struct material *material, const struct pol_material * static void destroy_material(struct material *material) { - if (material->color_map) - glDeleteTextures(1, &material->color_map); + if (material->color_maps) { + glDeleteTextures(material->nr_color_maps, material->color_maps); + free(material->color_maps); + } if (material->specular_map) glDeleteTextures(1, &material->specular_map); if (material->alpha_map) @@ -317,6 +347,7 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_ } model->meshes = xrealloc_array(model->meshes, model->nr_meshes, model->nr_meshes + 1, sizeof(struct mesh)); struct mesh *mesh = &model->meshes[model->nr_meshes++]; + mesh->name = xstrdup(m->name); mesh->flags = m->flags; mesh->material = material; if (re_plugin_version == RE_REIGN_PLUGIN) @@ -393,6 +424,7 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_ static void destroy_mesh(struct mesh *mesh) { + free(mesh->name); glDeleteVertexArrays(1, &mesh->vao); glDeleteBuffers(1, &mesh->attr_buffer); if (mesh->index_buffer) @@ -661,8 +693,10 @@ struct model *model_create_sphere(int r, int g, int b, int a) model->materials = xcalloc(1, sizeof(struct material)); struct material *material = &model->materials[0]; material->is_transparent = true; - glGenTextures(1, &material->color_map); - glBindTexture(GL_TEXTURE_2D, material->color_map); + material->color_maps = xmalloc(sizeof(GLuint)); + material->nr_color_maps = 1; + glGenTextures(1, material->color_maps); + glBindTexture(GL_TEXTURE_2D, material->color_maps[0]); uint8_t pixel[4] = {r, g, b, a}; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel); glBindTexture(GL_TEXTURE_2D, 0); @@ -678,18 +712,18 @@ static int cmp_motions_by_bone_id(const void *lhs, const void *rhs) static struct mot *mot_load(const char *name, struct model *model, struct archive *aar) { - struct archive_data *dfile = RE_get_aar_entry(aar, model->path, name, ".MOT"); - if (!dfile) { + struct archive_data *mot_file = RE_get_aar_entry(aar, model->path, name, ".MOT"); + if (!mot_file) { WARNING("%s\\%s.MOT: not found", model->path, name); return NULL; } - struct mot *mot = mot_parse(dfile->data, dfile->size, name); + struct mot *mot = mot_parse(mot_file->data, mot_file->size, name); if (!mot) { - WARNING("%s: parse error", dfile->name); - archive_free_data(dfile); + WARNING("%s: parse error", mot_file->name); + archive_free_data(mot_file); return NULL; } - archive_free_data(dfile); + archive_free_data(mot_file); 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); @@ -708,6 +742,13 @@ static struct mot *mot_load(const char *name, struct model *model, struct archiv } qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id); + // Load optional .txa file. + struct archive_data *txa_file = RE_get_aar_entry(aar, model->path, name, ".txa"); + if (txa_file) { + txa_load(txa_file->data, txa_file->size, mot); + archive_free_data(txa_file); + } + return mot; } diff --git a/src/3d/parser.c b/src/3d/parser.c index 862a884..b03b7f2 100644 --- a/src/3d/parser.c +++ b/src/3d/parser.c @@ -14,6 +14,7 @@ * along with this program; if not, see . */ +#include #include #include #include @@ -424,6 +425,9 @@ void mot_free(struct mot *mot) free(mot->motions[i]->name); free(mot->motions[i]); } + if (mot->texture_indices) { + free(mot->texture_indices); + } free(mot); } @@ -487,7 +491,10 @@ struct amt_material *amt_find_material(struct amt *amt, const char *name) void opr_load(uint8_t *data, size_t size, struct pol *pol) { - bool *selected = xcalloc(pol->nr_meshes, sizeof(bool)); + bool *selected = xmalloc(pol->nr_meshes * sizeof(bool)); + for (int i = 0; i < pol->nr_meshes; i++) { + selected[i] = !!pol->meshes[i]; + } while (size > 0) { char line[200]; uint8_t *nl = memchr(data, '\n', size); @@ -561,3 +568,30 @@ void opr_load(uint8_t *data, size_t size, struct pol *pol) } free(selected); } + +void txa_load(uint8_t *data, size_t size, struct mot *mot) +{ + uint32_t count = 0; + uint32_t *indices = xmalloc(100 * sizeof(uint32_t)); + const char *p = (const char *)data; + while (p < (const char *)data + size) { + if (isdigit(*p)) { + uint32_t n = 0; + while (p < (const char *)data + size && isdigit(*p)) { + n = n * 10 + (*p - '0'); + p++; + } + indices[count++] = n; + if (count % 100 == 0) { + indices = xrealloc(indices, (count + 100) * sizeof(uint32_t)); + } + } else if (isspace(*p)) { + p++; + } else { + WARNING("invalid character in txa file: '%c'", *p); + p++; + } + } + mot->nr_texture_indices = count; + mot->texture_indices = indices; +} diff --git a/src/3d/reign.c b/src/3d/reign.c index 1c415be..472ba71 100644 --- a/src/3d/reign.c +++ b/src/3d/reign.c @@ -131,12 +131,18 @@ static void update_motion(struct motion *m, float delta_frame) } break; case RE_MOTION_STATE_LOOP: - if (m->current_frame > m->loop_frame_end) + if (m->loop_frame_begin == m->loop_frame_end) + m->current_frame = m->loop_frame_begin; + else 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); } + + uint32_t anim_frame = m->current_frame - m->frame_begin; + m->texture_index = (m->mot && anim_frame < m->mot->nr_texture_indices) + ? m->mot->texture_indices[anim_frame] : 0; } static void calc_motion_frame(struct motion *m, int bone, struct mot_frame *out) @@ -326,6 +332,7 @@ bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms) if (inst->type == RE_ITYPE_SKINNED) update_bones(inst); + inst->texture_animation_index = inst->motion ? inst->motion->texture_index : 0; } // Update particle effects in a second pass, because it uses the results of @@ -502,6 +509,19 @@ bool RE_instance_free_next_motion(struct RE_instance *instance) return true; } +bool RE_instance_set_mesh_show(struct RE_instance *instance, const char *mesh_name, bool show) +{ + if (!instance || !instance->model) + return false; + for (int i = 0; i < instance->model->nr_meshes; i++) { + if (!strcmp(instance->model->meshes[i].name, mesh_name)) { + instance->model->meshes[i].hidden = !show; + return true; + } + } + return false; +} + bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z) { if (!instance) diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 8abfa92..30cf0a8 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -335,6 +335,12 @@ void RE_calc_view_matrix(struct RE_camera *camera, vec3 up, mat4 out) glm_look(camera->pos, front, up, out); } +static bool should_draw_shadow(struct mesh *mesh, struct material *material) +{ + return !(mesh->flags & (MESH_ALPHA | MESH_BOTH | MESH_SPRITE)) + && !(material->flags & (MATERIAL_ALPHA | MATERIAL_SPRITE)); +} + static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase) { struct model *model = inst->model; @@ -362,6 +368,8 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d for (int i = 0; i < model->nr_meshes; i++) { struct mesh *mesh = &model->meshes[i]; + if (mesh->hidden) + continue; struct material *material = &model->materials[mesh->material]; bool is_transparent = mesh->is_transparent || inst->alpha < 1.0f; if (phase != (is_transparent ? DRAW_TRANSPARENT : DRAW_OPAQUE)) @@ -396,10 +404,12 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d glUniform3fv(r->rim_color, 1, material->rim_color); glActiveTexture(GL_TEXTURE0 + COLOR_TEXTURE_UNIT); - glBindTexture(GL_TEXTURE_2D, material->color_map); + int animation_index = inst->texture_animation_index < material->nr_color_maps + ? inst->texture_animation_index : 0; + glBindTexture(GL_TEXTURE_2D, material->color_maps[animation_index]); glUniform1i(r->texture, COLOR_TEXTURE_UNIT); - if (draw_shadow && !(mesh->flags & MESH_BOTH)) + if (draw_shadow && should_draw_shadow(mesh, material)) glUniform1f(r->shadow_darkness, material->shadow_darkness); else glUniform1f(r->shadow_darkness, 0.0f); @@ -614,7 +624,7 @@ static void render_polygon_particles(struct RE_renderer *r, struct RE_instance * struct material *material = &model->materials[mesh->material]; glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, material->color_map); + glBindTexture(GL_TEXTURE_2D, material->color_maps[0]); glUniform1i(r->texture, 0); glBindVertexArray(mesh->vao); diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c index 8ec90e7..f5a8ee4 100644 --- a/src/hll/ReignEngine.c +++ b/src/hll/ReignEngine.c @@ -1891,7 +1891,11 @@ static bool TapirEngine_GetInstancePathLine(int plugin, int instance, struct pag } //bool TapirEngine_CreateInstancePathLineList(int PluginNumber, int InstanceNumber, int PathInstanceNumber); -//bool TapirEngine_SetInstanceMeshShow(int PluginNumber, int InstanceNumber, struct string *pIMeshName, bool Show); + +static bool TapirEngine_SetInstanceMeshShow(int plugin, int instance, struct string *mesh_name, bool show) +{ + return RE_instance_set_mesh_show(get_instance(plugin, instance), mesh_name->text, show); +} static bool TapirEngine_SetDrawOption(int plugin_number, int draw_option, int param) { @@ -2303,7 +2307,7 @@ HLL_LIBRARY(ReignEngine, REIGN_EXPORTS, HLL_EXPORT(OptimizeInstancePathLine, TapirEngine_OptimizeInstancePathLine), \ HLL_EXPORT(GetInstancePathLine, TapirEngine_GetInstancePathLine), \ HLL_TODO_EXPORT(CreateInstancePathLineList, TapirEngine_CreateInstancePathLineList), \ - HLL_TODO_EXPORT(SetInstanceMeshShow, TapirEngine_SetInstanceMeshShow), \ + HLL_EXPORT(SetInstanceMeshShow, TapirEngine_SetInstanceMeshShow), \ HLL_EXPORT(SetDrawOption, TapirEngine_SetDrawOption), \ HLL_EXPORT(GetDrawOption, TapirEngine_GetDrawOption), \ HLL_TODO_EXPORT(SetDebugMode, TapirEngine_SetDebugMode), \