From fdd0af8dcf6fdc276c54038d8e87a58eb63acc79 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 27 Nov 2022 21:27:29 +0900 Subject: [PATCH 1/6] ReignEngine: Improve shadow view-frustum calculation This fixes broken shadow rendering e.g. when Nacht climbs a ladder. Now update_bones() calculates the instance's bounding sphere which is the minimum sphere containing the AABB of current bone coordinates. This corresponds to the sphere that is displayed when calling SetInstanceDebugDrawShadowVolume on the original ReignEngine.dll. --- include/reign.h | 1 + src/3d/reign.c | 30 ++++++++++++++++++++------- src/3d/renderer.c | 53 +++++++++++++---------------------------------- 3 files changed, 38 insertions(+), 46 deletions(-) diff --git a/include/reign.h b/include/reign.h index f6c4888..56bee5e 100644 --- a/include/reign.h +++ b/include/reign.h @@ -165,6 +165,7 @@ struct RE_instance { mat4 local_transform; mat3 normal_transform; mat4 *bone_transforms; // model->nr_bones elements + vec4 bounding_sphere; float z_from_camera; }; diff --git a/src/3d/reign.c b/src/3d/reign.c index d432488..e32cc71 100644 --- a/src/3d/reign.c +++ b/src/3d/reign.c @@ -39,14 +39,15 @@ static struct RE_instance *create_instance(struct RE_plugin *plugin) instance->plugin = plugin; for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++) instance->target[i] = -1; - instance->scale[0] = 1.0; - instance->scale[1] = 1.0; - instance->scale[2] = 1.0; - instance->alpha = 1.0; + instance->scale[0] = 1.0f; + instance->scale[1] = 1.0f; + instance->scale[2] = 1.0f; + instance->alpha = 1.0f; + instance->shadow_volume_bone_radius = 0.1f; instance->draw_bump = true; - instance->fps = 30.0; - instance->column_height = 1.0; - instance->column_radius = 1.0; + instance->fps = 30.0f; + instance->column_height = 1.0f; + instance->column_radius = 1.0f; glm_mat4_identity(instance->local_transform); glm_mat3_identity(instance->normal_transform); return instance; @@ -143,6 +144,10 @@ static void update_bones(struct RE_instance *inst) return; mat4 parent_transforms[MAX_BONES]; + vec3 aabb[2]; + glm_aabb_invalidate(aabb); + + // Update inst->bone_transforms. for (int i = 0; i < inst->model->nr_bones; i++) { struct mot_frame mf; calc_motion_frame(inst->motion, i, &mf); @@ -162,7 +167,18 @@ static void update_bones(struct RE_instance *inst) glm_mat4_copy(bone_transform, parent_transforms[i]); glm_mat4_mul(bone_transform, inst->model->bones[i].inverse_bind_matrix, inst->bone_transforms[i]); + + glm_vec3_minv(aabb[0], bone_transform[3], aabb[0]); + glm_vec3_maxv(aabb[1], bone_transform[3], aabb[1]); } + + // Update inst->bounding_sphere. + vec3 center; + glm_aabb_center(aabb, center); + if (inst->local_transform_needs_update) + RE_instance_update_local_transform(inst); + glm_mat4_mulv3(inst->local_transform, center, 1.0f, inst->bounding_sphere); + inst->bounding_sphere[3] = glm_aabb_radius(aabb) * inst->scale[0] + inst->shadow_volume_bone_radius; } struct RE_plugin *RE_plugin_new(void) diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 7fcfd9c..0300150 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -27,8 +27,8 @@ #include "sact.h" // TODO: Respect RE_plugin.shadow_map_resolution_level -#define SHADOW_WIDTH 1024 -#define SHADOW_HEIGHT 1024 +#define SHADOW_WIDTH 512 +#define SHADOW_HEIGHT 512 enum { COLOR_TEXTURE_UNIT, @@ -626,52 +626,27 @@ static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat static bool calc_shadow_light_transform(struct RE_plugin *plugin, mat4 dest) { - // Compute AABB of shadow casters. - vec3 aabb[2]; - glm_aabb_invalidate(aabb); + // Compute a bounding sphere of shadow casters. + vec4 bounding_sphere = {}; for (int i = 0; i < plugin->nr_instances; i++) { struct RE_instance *inst = plugin->instances[i]; if (!inst || !inst->draw || !inst->make_shadow || !inst->model) continue; - // Start with the model's AABB, inflated with shadow_volume_bone_radius. - vec3 inst_aabb[2]; - glm_vec3_subs(inst->model->aabb[0], inst->shadow_volume_bone_radius, inst_aabb[0]); - glm_vec3_adds(inst->model->aabb[1], inst->shadow_volume_bone_radius, inst_aabb[1]); - // To save CPU cycles, consider only the translation components of bone transforms. - vec3 bone_translation_aabb[2]; - glm_aabb_invalidate(bone_translation_aabb); - for (int j = 0; j < inst->model->nr_bones; j++) { - glm_vec3_minv(inst->bone_transforms[j][3], bone_translation_aabb[0], bone_translation_aabb[0]); - glm_vec3_maxv(inst->bone_transforms[j][3], bone_translation_aabb[1], bone_translation_aabb[1]); - } - if (glm_aabb_isvalid(bone_translation_aabb)) { - glm_vec3_add(inst_aabb[0], bone_translation_aabb[0], inst_aabb[0]); - glm_vec3_add(inst_aabb[1], bone_translation_aabb[1], inst_aabb[1]); - } - // Apply local transform. - if (inst->local_transform_needs_update) - RE_instance_update_local_transform(inst); - glm_aabb_transform(inst_aabb, inst->local_transform, inst_aabb); - - glm_aabb_merge(inst_aabb, aabb, aabb); + if (bounding_sphere[3] > 0.0f) + glm_sphere_merge(inst->bounding_sphere, bounding_sphere, inst->bounding_sphere); + else + glm_vec4_copy(inst->bounding_sphere, bounding_sphere); } - if (!glm_aabb_isvalid(aabb)) + + float radius = bounding_sphere[3] * 1.2f; // Add some padding. + if (radius <= 0.0f) return false; - // Create a orthographic frustum that contains the AABB. - vec3 center; - glm_aabb_center(aabb, center); - vec3 light_pos; - glm_vec3_scale(plugin->shadow_map_light_dir, -glm_aabb_radius(aabb), light_pos); - glm_vec3_add(light_pos, center, light_pos); + // Create a orthographic frustum that contains the bounding sphere. mat4 view_matrix; - vec3 up = {0.0, 1.0, 0.0}; - glm_lookat(light_pos, center, up, view_matrix); - + glm_look(bounding_sphere, plugin->shadow_map_light_dir, GLM_YUP, view_matrix); mat4 proj_matrix; - glm_aabb_transform(aabb, view_matrix, aabb); - glm_ortho_aabb(aabb, proj_matrix); - + glm_ortho(-radius, radius, -radius, radius, -radius, radius, proj_matrix); glm_mat4_mul(proj_matrix, view_matrix, dest); return true; } From 06b9da800c657fc19790ec20bcbc1917ec948dfb Mon Sep 17 00:00:00 2001 From: kichikuou Date: Thu, 1 Dec 2022 13:46:51 +0900 Subject: [PATCH 2/6] Implement ReignEngine.SetInstanceDebugDrawShadowVolume It visualizes the bounding sphere of the instance. Although it is only called from dead code in Toushin Toshi 3, it's useful for debugging shadow mapping. --- include/reign.h | 2 + src/3d/3d_internal.h | 3 ++ src/3d/model.c | 97 +++++++++++++++++++++++++++++++++++++++++++ src/3d/reign.c | 26 ++++++++++++ src/3d/renderer.c | 10 ++++- src/hll/ReignEngine.c | 17 ++++++-- 6 files changed, 150 insertions(+), 5 deletions(-) diff --git a/include/reign.h b/include/reign.h index 56bee5e..b5424dc 100644 --- a/include/reign.h +++ b/include/reign.h @@ -166,6 +166,7 @@ struct RE_instance { mat3 normal_transform; mat4 *bone_transforms; // model->nr_bones elements vec4 bounding_sphere; + struct RE_instance *shadow_volume_instance; float z_from_camera; }; @@ -187,6 +188,7 @@ bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x 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); float RE_instance_calc_height(struct RE_instance *instance, float x, float z); +bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *instance, bool draw); 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 3ef56e1..6325583 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -51,7 +51,9 @@ struct mesh { uint32_t flags; GLuint vao; GLuint attr_buffer; + GLuint index_buffer; int nr_vertices; + int nr_indices; int material; }; @@ -176,6 +178,7 @@ void RE_instance_update_local_transform(struct RE_instance *inst); struct model *model_load(struct archive *aar, const char *path); void model_free(struct model *model); +struct model *model_create_sphere(int r, int g, int b, int a); struct motion *motion_load(const char *name, struct RE_instance *instance, struct archive *aar); void motion_free(struct motion *motion); diff --git a/src/3d/model.c b/src/3d/model.c index 15ac432..0806f13 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -336,6 +336,8 @@ static void destroy_mesh(struct mesh *mesh) { glDeleteVertexArrays(1, &mesh->vao); glDeleteBuffers(1, &mesh->attr_buffer); + if (mesh->index_buffer) + glDeleteBuffers(1, &mesh->index_buffer); } static struct bone *add_bone(struct model *model, struct pol *pol, struct pol_bone *pol_bone) @@ -499,6 +501,101 @@ void model_free(struct model *model) free(model); } +static void init_sphere_mesh(struct mesh *mesh) +{ + const int w_segments = 16; + const int h_segments = 16; + const int nr_vertices = (w_segments + 1) * (h_segments + 1); + struct vertex_common *vertices = xcalloc(nr_vertices, sizeof(struct vertex_common)); + struct vertex_common *v = vertices; + for (int y = 0; y <= h_segments; y++) { + float theta = GLM_PIf * y / h_segments; + for (int x = 0; x <= w_segments; x++, v++) { + float phi = 2.0f * GLM_PIf * x / w_segments; + v->pos[0] = -cosf(phi) * sinf(theta); + v->pos[1] = cosf(theta); + v->pos[2] = sinf(phi) * sinf(theta); + glm_vec3_copy(v->pos, v->normal); + v->uv[0] = v->uv[1] = 0.0f; + } + } + assert(v == vertices + nr_vertices); + + const int nr_indices = 3 * 2 * w_segments * (h_segments - 1); + GLushort *indices = xcalloc(nr_indices, sizeof(GLushort)); + GLushort *pi = indices; + for (int y = 0; y < h_segments; y++) { + for (int x = 0; x < w_segments; x++) { + GLushort a = y * w_segments + x + 1; + GLushort b = y * w_segments + x; + GLushort c = (y + 1) * w_segments + x; + GLushort d = (y + 1) * w_segments + x + 1; + if (y > 0) { + *pi++ = a; + *pi++ = b; + *pi++ = d; + } + if (y < h_segments - 1) { + *pi++ = b; + *pi++ = c; + *pi++ = d; + } + } + } + assert(pi == indices + nr_indices); + + mesh->flags = MESH_NOLIGHTING; + mesh->nr_vertices = nr_vertices; + mesh->nr_indices = nr_indices; + glGenVertexArrays(1, &mesh->vao); + glBindVertexArray(mesh->vao); + glGenBuffers(1, &mesh->attr_buffer); + glBindBuffer(GL_ARRAY_BUFFER, mesh->attr_buffer); + glBufferData(GL_ARRAY_BUFFER, nr_vertices * sizeof(struct vertex_common), vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(VATTR_POS); + glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex_common), (void*)offsetof(struct vertex_common, pos)); + glEnableVertexAttribArray(VATTR_NORMAL); + glVertexAttribPointer(VATTR_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex_common), (void*)offsetof(struct vertex_common, normal)); + glEnableVertexAttribArray(VATTR_UV); + glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex_common), (void*)offsetof(struct vertex_common, uv)); + + glGenBuffers(1, &mesh->index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, nr_indices * sizeof(GLushort), indices, GL_STATIC_DRAW); + glBindVertexArray(0); + + free(vertices); + free(indices); +} + +struct model *model_create_sphere(int r, int g, int b, int a) +{ + struct model *model = xcalloc(1, sizeof(struct model)); + + model->nr_meshes = 1; + model->meshes = xcalloc(1, sizeof(struct mesh)); + init_sphere_mesh(&model->meshes[0]); + model->aabb[0][0] = -1.0f; + model->aabb[0][1] = -1.0f; + model->aabb[0][2] = -1.0f; + model->aabb[1][0] = 1.0f; + model->aabb[1][1] = 1.0f; + model->aabb[1][2] = 1.0f; + + model->nr_materials = 1; + 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); + 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); + model->has_transparent_material = true; + + return 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; diff --git a/src/3d/reign.c b/src/3d/reign.c index e32cc71..a1e546f 100644 --- a/src/3d/reign.c +++ b/src/3d/reign.c @@ -53,6 +53,8 @@ static struct RE_instance *create_instance(struct RE_plugin *plugin) return instance; } +static void free_instance(struct RE_instance *instance); + static void unload_instance(struct RE_instance *instance) { if (instance->motion) { @@ -79,6 +81,10 @@ static void unload_instance(struct RE_instance *instance) RE_renderer_free_height_detector(instance->height_detector); instance->height_detector = NULL; } + if (instance->shadow_volume_instance) { + free_instance(instance->shadow_volume_instance); + instance->shadow_volume_instance = NULL; + } } static void free_instance(struct RE_instance *instance) @@ -179,6 +185,13 @@ static void update_bones(struct RE_instance *inst) RE_instance_update_local_transform(inst); glm_mat4_mulv3(inst->local_transform, center, 1.0f, inst->bounding_sphere); inst->bounding_sphere[3] = glm_aabb_radius(aabb) * inst->scale[0] + inst->shadow_volume_bone_radius; + + struct RE_instance *shvol = inst->shadow_volume_instance; + if (shvol) { + glm_vec3_copy(inst->bounding_sphere, shvol->pos); + glm_vec3_broadcast(inst->bounding_sphere[3], shvol->scale); + RE_instance_update_local_transform(shvol); + } } struct RE_plugin *RE_plugin_new(void) @@ -459,6 +472,19 @@ float RE_instance_calc_height(struct RE_instance *instance, float x, float z) return y; } +bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *inst, bool draw) +{ + if (!inst) + return false; + if (draw && !inst->shadow_volume_instance) { + inst->shadow_volume_instance = create_instance(inst->plugin); + inst->shadow_volume_instance->model = model_create_sphere(255, 0, 0, 64); + } + if (inst->shadow_volume_instance) + inst->shadow_volume_instance->draw = draw; + return true; +} + void RE_instance_update_local_transform(struct RE_instance *inst) { vec3 euler = { diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 0300150..9bd97fe 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -383,7 +383,12 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d } glBindVertexArray(mesh->vao); - glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices); + + if (mesh->nr_indices) + glDrawElements(GL_TRIANGLES, mesh->nr_indices, GL_UNSIGNED_SHORT, NULL); + else + glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices); + glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); } @@ -412,6 +417,9 @@ static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r glUniform1i(r->has_bones, GL_FALSE); } render_model(inst, r, phase); + + if (inst->shadow_volume_instance && phase == DRAW_TRANSPARENT) + render_static_model(inst->shadow_volume_instance, r, phase); } static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat, enum draw_phase phase) diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c index bc16071..cc52541 100644 --- a/src/hll/ReignEngine.c +++ b/src/hll/ReignEngine.c @@ -651,8 +651,17 @@ static bool ReignEngine_SetInstanceShadowVolumeBoneRadius(int plugin, int instan return true; } -//bool ReignEngine_GetInstanceDebugDrawShadowVolume(int plugin, int instance); -//bool ReignEngine_SetInstanceDebugDrawShadowVolume(int plugin, int instance, bool flag); +static bool ReignEngine_GetInstanceDebugDrawShadowVolume(int plugin, int instance) +{ + struct RE_instance *ri = get_instance(plugin, instance); + return ri && ri->shadow_volume_instance && ri->shadow_volume_instance->draw; +} + +static bool ReignEngine_SetInstanceDebugDrawShadowVolume(int plugin, int instance, bool flag) +{ + return RE_instance_set_debug_draw_shadow_volume(get_instance(plugin, instance), flag); +} + //bool ReignEngine_SaveEffect(int plugin, int instance); static bool ReignEngine_GetEffectFrameRange(int plugin, int instance, int *begin_frame, int *end_frame) @@ -1899,8 +1908,8 @@ HLL_LIBRARY(ReignEngine, HLL_EXPORT(SetInstanceSoftFogEdgeLength, ReignEngine_SetInstanceSoftFogEdgeLength), HLL_EXPORT(GetInstanceShadowVolumeBoneRadius, ReignEngine_GetInstanceShadowVolumeBoneRadius), HLL_EXPORT(SetInstanceShadowVolumeBoneRadius, ReignEngine_SetInstanceShadowVolumeBoneRadius), - HLL_TODO_EXPORT(GetInstanceDebugDrawShadowVolume, ReignEngine_GetInstanceDebugDrawShadowVolume), - HLL_TODO_EXPORT(SetInstanceDebugDrawShadowVolume, ReignEngine_SetInstanceDebugDrawShadowVolume), + HLL_EXPORT(GetInstanceDebugDrawShadowVolume, ReignEngine_GetInstanceDebugDrawShadowVolume), + HLL_EXPORT(SetInstanceDebugDrawShadowVolume, ReignEngine_SetInstanceDebugDrawShadowVolume), HLL_TODO_EXPORT(SaveEffect, ReignEngine_SaveEffect), HLL_EXPORT(GetEffectFrameRange, ReignEngine_GetEffectFrameRange), HLL_EXPORT(GetEffectObjectType, ReignEngine_GetEffectObjectType), From eb13ae0cc1f53e691e178ff685ec8626245a0fc0 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 17 Dec 2022 15:24:34 +0900 Subject: [PATCH 3/6] ReignEngine: Fix rendering issue of skinned models without motion This fixes a bug where skinned instances with no motion loaded were not drawn. --- src/3d/renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 9bd97fe..7db9b67 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -410,7 +410,7 @@ static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r if (!model) return; - if (model->nr_bones > 0) { + if (model->nr_bones > 0 && inst->motion) { glUniform1i(r->has_bones, GL_TRUE); glUniformMatrix4fv(r->bone_matrices, model->nr_bones, GL_FALSE, inst->bone_transforms[0][0]); } else { From 70b40713500d9fdb8ec8ef28e59c2c3245541069 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Fri, 23 Dec 2022 22:34:07 +0900 Subject: [PATCH 4/6] ReignEngine: Support "shadow darkness" material parameter It is a value between 0.0 and 1.0, where 0.0 means no shadows are drawn on the material. --- shaders/reign.f.glsl | 10 +++++----- src/3d/3d_internal.h | 5 ++++- src/3d/model.c | 3 +++ src/3d/renderer.c | 11 +++++++---- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/shaders/reign.f.glsl b/shaders/reign.f.glsl index 6517383..a2fbd90 100644 --- a/shaders/reign.f.glsl +++ b/shaders/reign.f.glsl @@ -52,7 +52,7 @@ uniform bool use_specular_map; uniform float rim_exponent; uniform vec3 rim_color; uniform int diffuse_type; -uniform bool use_shadow_map; +uniform float shadow_darkness; uniform float shadow_bias; uniform int fog_type; uniform float fog_near; @@ -133,20 +133,20 @@ void main() { } // Shadow mapping - if (use_shadow_map) { + if (shadow_darkness > 0.0) { vec3 shadow_coords = shadow_frag_pos.xyz / shadow_frag_pos.w * 0.5 + 0.5; shadow_coords.z = min(1.0, shadow_coords.z - shadow_bias); // PCF with 9 samples. - float shadow_factor = 1.0; + float brightness = 1.0; vec2 texel_size = 1.0 / vec2(textureSize(shadow_texture, 0)); for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { float depth = texture(shadow_texture, shadow_coords.xy + vec2(x, y) * texel_size).r; if (depth < shadow_coords.z) - shadow_factor -= 0.05; + brightness -= 0.05 * shadow_darkness; } } - frag_rgb *= shadow_factor; + frag_rgb *= brightness; } // Alpha mapping diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 6325583..9ee61f6 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -67,6 +67,7 @@ struct material { GLuint normal_map; float specular_strength; float specular_shininess; + float shadow_darkness; float rim_exponent; vec3 rim_color; }; @@ -145,7 +146,7 @@ struct RE_renderer { GLint light_texture; GLint use_normal_map; GLint normal_texture; - GLint use_shadow_map; + GLint shadow_darkness; GLint shadow_transform; GLint shadow_texture; GLint shadow_bias; @@ -471,6 +472,8 @@ struct amt_material { enum amt_field_index { AMT_SPECULAR_STRENGTH = 0, AMT_SPECULAR_SHININESS = 2, + // amt v4+ + AMT_SHADOW_DARKNESS = 6, // amt v5+ AMT_RIM_EXPONENT = 7, AMT_RIM_R = 8, diff --git a/src/3d/model.c b/src/3d/model.c index 0806f13..f24b12f 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -111,11 +111,14 @@ static bool init_material(struct material *material, const struct pol_material * material->normal_map = load_texture(aar, path, m->textures[NORMAL_MAP], NULL); material->is_transparent = has_alpha || material->alpha_map; + material->shadow_darkness = 1.0f; struct amt_material *amt_m = amt ? amt_find_material(amt, m->name) : NULL; if (amt_m) { material->specular_strength = amt_m->fields[AMT_SPECULAR_STRENGTH]; material->specular_shininess = amt_m->fields[AMT_SPECULAR_SHININESS]; + if (amt->version >= 4) + material->shadow_darkness = amt_m->fields[AMT_SHADOW_DARKNESS]; if (amt->version >= 5) { material->rim_exponent = amt_m->fields[AMT_RIM_EXPONENT]; material->rim_color[0] = amt_m->fields[AMT_RIM_R]; diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 7db9b67..40b0781 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -201,7 +201,7 @@ struct RE_renderer *RE_renderer_new(struct texture *texture) r->light_texture = glGetUniformLocation(r->program, "light_texture"); r->use_normal_map = glGetUniformLocation(r->program, "use_normal_map"); r->normal_texture = glGetUniformLocation(r->program, "normal_texture"); - r->use_shadow_map = glGetUniformLocation(r->program, "use_shadow_map"); + r->shadow_darkness = glGetUniformLocation(r->program, "shadow_darkness"); r->shadow_transform = glGetUniformLocation(r->program, "shadow_transform"); r->shadow_texture = glGetUniformLocation(r->program, "shadow_texture"); r->shadow_bias = glGetUniformLocation(r->program, "shadow_bias"); @@ -347,7 +347,10 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d glBindTexture(GL_TEXTURE_2D, material->color_map); glUniform1i(r->texture, COLOR_TEXTURE_UNIT); - glUniform1i(r->use_shadow_map, draw_shadow && !(mesh->flags & MESH_BOTH)); + if (draw_shadow && !(mesh->flags & MESH_BOTH)) + glUniform1f(r->shadow_darkness, material->shadow_darkness); + else + glUniform1f(r->shadow_darkness, 0.0f); if (mesh->flags & MESH_ENVMAP) { glUniform1i(r->diffuse_type, DIFFUSE_ENV_MAP); @@ -460,7 +463,7 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma glUniform1f(r->rim_exponent, 0.0); glUniform1i(r->diffuse_type, DIFFUSE_NORMAL); glUniform1i(r->use_normal_map, GL_FALSE); - glUniform1i(r->use_shadow_map, GL_FALSE); + glUniform1f(r->shadow_darkness, 0.0f); glUniform1i(r->alpha_mode, ALPHA_BLEND); glUniform1i(r->fog_type, inst->plugin->fog_mode ? inst->plugin->fog_type : 0); switch (inst->draw_type) { @@ -580,7 +583,7 @@ static void render_particle_effect(struct RE_instance *inst, struct RE_renderer glUniform1i(r->use_specular_map, GL_FALSE); glUniform1f(r->rim_exponent, 0.0); glUniform1i(r->use_normal_map, GL_FALSE); - glUniform1i(r->use_shadow_map, GL_FALSE); + glUniform1f(r->shadow_darkness, 0.0f); glUniform1i(r->alpha_mode, ALPHA_BLEND); glDepthMask(GL_FALSE); From 2de51328032614a5b006e854d5f1bfc1cccb3a09 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 24 Dec 2022 10:37:59 +0900 Subject: [PATCH 5/6] ReignEngine: Support (sprite) mesh attribute It has the same effect as the (sprite) material attribute. Also, now sprite objects are rendered in the DRAW_OPAQUE phase. --- shaders/reign.f.glsl | 2 +- src/3d/3d_internal.h | 1 + src/3d/model.c | 2 +- src/3d/parser.c | 2 ++ src/3d/renderer.c | 4 ++-- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/shaders/reign.f.glsl b/shaders/reign.f.glsl index a2fbd90..fd29a6b 100644 --- a/shaders/reign.f.glsl +++ b/shaders/reign.f.glsl @@ -152,7 +152,7 @@ void main() { // Alpha mapping float alpha = texel.a * alpha_mod; if (alpha_mode == ALPHA_TEST) { - if (alpha < 1.0) + if (alpha < 0.8) discard; } else if (alpha_mode == ALPHA_MAP_BLEND) { alpha *= texture(alpha_texture, tex_coord).r; diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 9ee61f6..2a8aaf7 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -397,6 +397,7 @@ enum mesh_flags { MESH_NOMAKESHADOW = 1 << 1, MESH_ENVMAP = 1 << 2, MESH_BOTH = 1 << 3, + MESH_SPRITE = 1 << 4, }; struct pol_mesh { diff --git a/src/3d/model.c b/src/3d/model.c index f24b12f..0396fcd 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -110,7 +110,7 @@ static bool init_material(struct material *material, const struct pol_material * if (m->textures[NORMAL_MAP]) material->normal_map = load_texture(aar, path, m->textures[NORMAL_MAP], NULL); - material->is_transparent = has_alpha || material->alpha_map; + material->is_transparent = (has_alpha || material->alpha_map) && !(material->flags & MATERIAL_SPRITE); material->shadow_darkness = 1.0f; struct amt_material *amt_m = amt ? amt_find_material(amt, m->name) : NULL; diff --git a/src/3d/parser.c b/src/3d/parser.c index 38d975a..c71e78d 100644 --- a/src/3d/parser.c +++ b/src/3d/parser.c @@ -168,6 +168,8 @@ static uint32_t parse_mesh_attributes(const char *name) flags |= MESH_ENVMAP; if (strstr(name, "(both)")) flags |= MESH_BOTH; + if (strstr(name, "(sprite)")) + flags |= MESH_SPRITE; return flags; } diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 40b0781..ef8974e 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -317,7 +317,7 @@ 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]; struct material *material = &model->materials[mesh->material]; - bool is_transparent = material->is_transparent || inst->alpha < 1.0f; + bool is_transparent = (material->is_transparent && !(mesh->flags & MESH_SPRITE)) || inst->alpha < 1.0f; if (phase != (is_transparent ? DRAW_TRANSPARENT : DRAW_OPAQUE)) continue; @@ -379,7 +379,7 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d glActiveTexture(GL_TEXTURE0 + ALPHA_TEXTURE_UNIT); glBindTexture(GL_TEXTURE_2D, material->alpha_map); glUniform1i(r->alpha_texture, ALPHA_TEXTURE_UNIT); - } else if (material->flags & MATERIAL_SPRITE) { + } else if (material->flags & MATERIAL_SPRITE || mesh->flags & MESH_SPRITE) { glUniform1i(r->alpha_mode, ALPHA_TEST); } else { glUniform1i(r->alpha_mode, ALPHA_BLEND); From c7cebb9ea9429f121bcdf9f1564d76d1505f2edc Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 24 Dec 2022 15:12:31 +0900 Subject: [PATCH 6/6] ReignEngine: Fix out-of-range material index in .POL Without this, some maps (e.g. map059) and battle backgrounds (e.g. bg101) in Toushin Toshi 3 are not rendered correctly. --- src/3d/3d_internal.h | 2 +- src/3d/model.c | 6 +++--- src/3d/parser.c | 22 ++++++++++++++-------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 2a8aaf7..ff078f0 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -430,7 +430,7 @@ struct pol_triangle { uint32_t uv_index[3]; uint32_t light_uv_index[3]; vec3 normals[3]; - uint32_t material; // index in the material group + uint32_t material_group_index; }; struct pol_bone { diff --git a/src/3d/model.c b/src/3d/model.c index 0396fcd..3936284 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -214,7 +214,7 @@ static void *buf_alloc(uint8_t **ptr, int size) return p; } -static void add_mesh(struct model *model, struct pol_mesh *m, int material_index, int material) +static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_group_index, int material) { bool has_light_map = m->light_uvs && model->materials[material].light_map; bool has_normal_map = model->materials[material].normal_map != 0; @@ -234,7 +234,7 @@ static void add_mesh(struct model *model, struct pol_mesh *m, int material_index int nr_vertices = 0; for (uint32_t i = 0; i < m->nr_triangles; i++) { struct pol_triangle *t = &m->triangles[i]; - if (material_index >= 0 && t->material != (uint32_t)material_index) + if (t->material_group_index != material_group_index) continue; vec4 tangent[3]; if (has_normal_map) @@ -465,7 +465,7 @@ struct model *model_load(struct archive *aar, const char *path) struct pol_material_group *mg = &pol->materials[pol->meshes[i]->material]; int m_off = material_offsets[pol->meshes[i]->material]; if (mg->nr_children == 0) { - add_mesh(model, pol->meshes[i], -1, m_off); + add_mesh(model, pol->meshes[i], 0, m_off); continue; } for (uint32_t j = 0; j < mg->nr_children; j++) { diff --git a/src/3d/parser.c b/src/3d/parser.c index c71e78d..228853f 100644 --- a/src/3d/parser.c +++ b/src/3d/parser.c @@ -134,7 +134,7 @@ static void destroy_vertex(struct pol_vertex *v) free(v->weights); } -static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle_index, int unknowns_length) +static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle_index, int unknowns_length, const struct pol_material_group *mg) { struct pol_triangle *t = &mesh->triangles[triangle_index]; t->vert_index[0] = buffer_read_int32(r); @@ -154,7 +154,9 @@ static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle read_direction(r, t->normals[0]); read_direction(r, t->normals[1]); read_direction(r, t->normals[2]); - t->material = buffer_read_int32(r); + t->material_group_index = buffer_read_int32(r); + if (t->material_group_index >= mg->nr_children) + t->material_group_index = 0; } static uint32_t parse_mesh_attributes(const char *name) @@ -173,7 +175,7 @@ static uint32_t parse_mesh_attributes(const char *name) return flags; } -static struct pol_mesh *parse_mesh(struct buffer *r, int pol_version) +static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol) { int type = buffer_read_int32(r); if (type != 0) { @@ -185,11 +187,15 @@ static struct pol_mesh *parse_mesh(struct buffer *r, int pol_version) mesh->name = read_cstring(r); mesh->flags = parse_mesh_attributes(mesh->name); mesh->material = buffer_read_int32(r); + if (mesh->material >= pol->nr_materials) { + WARNING("material index out of range (%d >= %d)", mesh->material, pol->nr_materials); + mesh->material = 0; + } mesh->nr_vertices = buffer_read_int32(r); mesh->vertices = xcalloc(mesh->nr_vertices, sizeof(struct pol_vertex)); for (uint32_t i = 0; i < mesh->nr_vertices; i++) { - parse_vertex(r, pol_version, &mesh->vertices[i]); + parse_vertex(r, pol->version, &mesh->vertices[i]); } mesh->nr_uvs = buffer_read_int32(r); @@ -209,7 +215,7 @@ static struct pol_mesh *parse_mesh(struct buffer *r, int pol_version) } int triangle_unknowns_length = 12; - if (pol_version == 1) { + if (pol->version == 1) { int nr_unknown_vecs = buffer_read_int32(r); buffer_skip(r, nr_unknown_vecs * 12); } else { @@ -225,10 +231,10 @@ static struct pol_mesh *parse_mesh(struct buffer *r, int pol_version) mesh->nr_triangles = buffer_read_int32(r); mesh->triangles = xcalloc(mesh->nr_triangles, sizeof(struct pol_triangle)); for (uint32_t i = 0; i < mesh->nr_triangles; i++) { - parse_triangle(r, mesh, i, triangle_unknowns_length); + parse_triangle(r, mesh, i, triangle_unknowns_length, &pol->materials[mesh->material]); } - if (pol_version == 1) { + if (pol->version == 1) { if (buffer_read_int32(r) != 1) WARNING("unexpected mesh footer"); if (buffer_read_int32(r) != 0) @@ -290,7 +296,7 @@ struct pol *pol_parse(uint8_t *data, size_t size) pol->nr_meshes = buffer_read_int32(&r); pol->meshes = xcalloc(pol->nr_meshes, sizeof(struct pol_mesh *)); for (uint32_t i = 0; i < pol->nr_meshes; i++) { - pol->meshes[i] = parse_mesh(&r, pol->version); + pol->meshes[i] = parse_mesh(&r, pol); } pol->nr_bones = buffer_read_int32(&r);