diff --git a/include/gfx/gfx.h b/include/gfx/gfx.h index 07c80d2..e98d78c 100644 --- a/include/gfx/gfx.h +++ b/include/gfx/gfx.h @@ -73,6 +73,7 @@ void gfx_update_screen_scale(void); void gfx_set_wait_vsync(bool wait); void gfx_load_shader(struct shader *dst, const char *vertex_shader_path, const char *fragment_shader_path); +GLuint gfx_load_shader_file(const char *path, GLenum type); // rendering void gfx_set_clear_color(int r, int g, int b, int a); diff --git a/include/reign.h b/include/reign.h index a5cb8ed..801090c 100644 --- a/include/reign.h +++ b/include/reign.h @@ -74,6 +74,9 @@ struct RE_plugin { struct RE_back_cg back_cg[RE_NR_BACK_CGS]; + vec3 shadow_map_light_dir; + float shadow_bias; + // Settings. int render_mode; int shadow_mode; @@ -109,6 +112,9 @@ struct RE_instance { vec3 scale; float alpha; bool draw; + bool draw_shadow; + bool make_shadow; + float shadow_volume_bone_radius; bool draw_bump; float fps; bool motion_blend; diff --git a/shaders/reign.f.glsl b/shaders/reign.f.glsl index 0f67ba9..e2dad09 100644 --- a/shaders/reign.f.glsl +++ b/shaders/reign.f.glsl @@ -25,6 +25,7 @@ struct dir_light { uniform sampler2D tex; uniform sampler2D specular_texture; uniform sampler2D light_texture; +uniform sampler2D shadow_texture; uniform float alpha_mod; uniform bool use_normal_map; @@ -38,10 +39,13 @@ uniform bool use_specular_map; uniform float rim_exponent; uniform vec3 rim_color; uniform bool use_light_map; +uniform bool use_shadow_map; +uniform float shadow_bias; in vec2 tex_coord; in vec2 light_tex_coord; in vec3 frag_pos; +in vec4 shadow_frag_pos; in vec3 eye; in vec3 normal; in vec3 light_dir[NR_DIR_LIGHTS]; @@ -88,5 +92,22 @@ void main() { frag_rgb += pow(1.0 - max(dot(normalize(normal), view_dir), 0.0), rim_exponent) * rim_color; } + // Shadow mapping + if (use_shadow_map) { + 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; + 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; + } + } + frag_rgb *= shadow_factor; + } + frag_color = vec4(frag_rgb, texel.a * alpha_mod); } diff --git a/shaders/reign.v.glsl b/shaders/reign.v.glsl index 4d16479..be7a33d 100644 --- a/shaders/reign.v.glsl +++ b/shaders/reign.v.glsl @@ -37,6 +37,7 @@ uniform bool use_normal_map; uniform vec3 view_pos; uniform dir_light dir_lights[NR_DIR_LIGHTS]; uniform vec3 specular_light_dir; +uniform mat4 shadow_transform; in vec3 vertex_pos; in vec3 vertex_normal; @@ -49,6 +50,7 @@ in vec4 vertex_bone_weight; out vec2 tex_coord; out vec2 light_tex_coord; out vec3 frag_pos; +out vec4 shadow_frag_pos; out vec3 eye; out vec3 normal; out vec3 light_dir[NR_DIR_LIGHTS]; @@ -86,6 +88,8 @@ void main() { tex_coord = vertex_uv; light_tex_coord = vertex_light_uv; + shadow_frag_pos = shadow_transform * world_pos; + // These are in tangent-space if use_normal_map is true, in world-space // otherwise. frag_pos = TBN * vec3(world_pos); diff --git a/shaders/reign_shadow.f.glsl b/shaders/reign_shadow.f.glsl new file mode 100644 index 0000000..5a1cfa4 --- /dev/null +++ b/shaders/reign_shadow.f.glsl @@ -0,0 +1,18 @@ +/* Copyright (C) 2022 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +void main() { +} diff --git a/shaders/reign_shadow.v.glsl b/shaders/reign_shadow.v.glsl new file mode 100644 index 0000000..41d07f9 --- /dev/null +++ b/shaders/reign_shadow.v.glsl @@ -0,0 +1,42 @@ +/* Copyright (C) 2022 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +uniform mat4 world_transform; +uniform mat4 view_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 ivec4 vertex_bone_index; +in vec4 vertex_bone_weight; + +void main() { + mat4 world_bone_transform = world_transform; + if (has_bones) { + mat4 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]; + } + } + world_bone_transform *= bone_transform; + } + + gl_Position = view_transform * world_bone_transform * vec4(vertex_pos, 1.0); +} diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 55dddb0..4172887 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -36,6 +36,7 @@ struct model { int nr_bones; struct bone *bones; struct hash_table *bone_map; // bone id in POL/MOT -> struct bone * + vec3 aabb[2]; // axis-aligned bounding box }; struct mesh { @@ -73,11 +74,33 @@ struct motion { float loop_frame_begin, loop_frame_end; }; -struct RE_renderer { - struct shader shader; - GLuint depth_buffer; +enum RE_attribute_location { + ATTR_VERTEX_POS = 0, + ATTR_BONE_INDEX, + ATTR_BONE_WEIGHT, +}; + +struct shadow_renderer { + GLuint program; + GLuint fbo; + GLuint texture; // Uniform variable locations + GLint world_transform; + GLint view_transform; + GLint has_bones; + GLint bone_matrices; +}; + +struct RE_renderer { + GLuint program; + GLuint depth_buffer; + struct shadow_renderer shadow; + + // Uniform variable locations + GLint world_transform; + GLint view_transform; + GLint texture; GLint local_transform; GLint proj_transform; GLint normal_transform; @@ -103,14 +126,16 @@ struct RE_renderer { GLint light_texture; GLint use_normal_map; GLint normal_texture; + GLint use_shadow_map; + GLint shadow_transform; + GLint shadow_texture; + GLint shadow_bias; // Attribute variable locations GLint vertex_normal; GLint vertex_uv; GLint vertex_light_uv; GLint vertex_tangent; - GLint vertex_bone_index; - GLint vertex_bone_weight; GLuint billboard_vao; GLuint billboard_attr_buffer; @@ -251,6 +276,7 @@ enum amt_field_index { struct pol *pol_parse(uint8_t *data, size_t size); void pol_free(struct pol *pol); +void pol_compute_aabb(struct pol *model, vec3 dest[2]); struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id); struct mot *mot_parse(uint8_t *data, size_t size); diff --git a/src/3d/model.c b/src/3d/model.c index 4460130..68e88a1 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -271,8 +271,8 @@ static void add_mesh(struct model *model, struct pol_mesh *m, int material_index glBindBuffer(GL_ARRAY_BUFFER, mesh->attr_buffer); const uint8_t *base = (const uint8_t *)0; - glEnableVertexAttribArray(r->shader.vertex); - glVertexAttribPointer(r->shader.vertex, 3, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_common, pos)); + glEnableVertexAttribArray(ATTR_VERTEX_POS); + glVertexAttribPointer(ATTR_VERTEX_POS, 3, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_common, pos)); glEnableVertexAttribArray(r->vertex_normal); glVertexAttribPointer(r->vertex_normal, 3, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_common, normal)); glEnableVertexAttribArray(r->vertex_uv); @@ -295,16 +295,16 @@ static void add_mesh(struct model *model, struct pol_mesh *m, int material_index glVertexAttrib3f(r->vertex_tangent, 1.0, 0.0, 0.0); } if (has_bones) { - glEnableVertexAttribArray(r->vertex_bone_index); - glVertexAttribIPointer(r->vertex_bone_index, NR_WEIGHTS, GL_INT, stride, base + offsetof(struct vertex_bones, bone_id)); - glEnableVertexAttribArray(r->vertex_bone_weight); - glVertexAttribPointer(r->vertex_bone_weight, NR_WEIGHTS, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_bones, bone_weight)); + glEnableVertexAttribArray(ATTR_BONE_INDEX); + glVertexAttribIPointer(ATTR_BONE_INDEX, NR_WEIGHTS, GL_INT, stride, base + offsetof(struct vertex_bones, bone_id)); + glEnableVertexAttribArray(ATTR_BONE_WEIGHT); + glVertexAttribPointer(ATTR_BONE_WEIGHT, NR_WEIGHTS, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_bones, bone_weight)); base += sizeof(struct vertex_bones); } else { - 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); + glDisableVertexAttribArray(ATTR_BONE_INDEX); + glVertexAttribI4i(ATTR_BONE_INDEX, 0, 0, 0, 0); + glDisableVertexAttribArray(ATTR_BONE_WEIGHT); + glVertexAttrib4f(ATTR_BONE_WEIGHT, 0.0, 0.0, 0.0, 0.0); } assert((intptr_t)base == stride); @@ -440,6 +440,8 @@ struct model *model_load(struct archive *aar, const char *path, struct RE_render } } + pol_compute_aabb(pol, model->aabb); + free(material_offsets); if (amt) amt_free(amt); diff --git a/src/3d/parser.c b/src/3d/parser.c index 6df07ed..c68ca4a 100644 --- a/src/3d/parser.c +++ b/src/3d/parser.c @@ -295,6 +295,23 @@ void pol_free(struct pol *pol) free(pol); } +void pol_compute_aabb(struct pol *pol, vec3 dest[2]) +{ + vec3 aabb[2]; + glm_aabb_invalidate(aabb); + for (uint32_t i = 0; i < pol->nr_meshes; i++) { + struct pol_mesh *mesh = pol->meshes[i]; + if (!mesh) + continue; + for (uint32_t j = 0; j < mesh->nr_vertices; j++) { + glm_vec3_minv(mesh->vertices[j].pos, aabb[0], aabb[0]); + glm_vec3_maxv(mesh->vertices[j].pos, aabb[1], aabb[1]); + } + } + glm_vec3_copy(aabb[0], dest[0]); + glm_vec3_copy(aabb[1], dest[1]); +} + struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id) { for (uint32_t i = 0; i < pol->nr_bones; i++) { diff --git a/src/3d/renderer.c b/src/3d/renderer.c index ae7d479..0c6511f 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -25,13 +25,81 @@ #include "reign.h" #include "sact.h" +// TODO: Respect RE_plugin.shadow_map_resolution_level +#define SHADOW_WIDTH 1024 +#define SHADOW_HEIGHT 1024 + enum { COLOR_TEXTURE_UNIT, SPECULAR_TEXTURE_UNIT, LIGHT_TEXTURE_UNIT, NORMAL_TEXTURE_UNIT, + SHADOW_TEXTURE_UNIT, }; +static GLuint load_shader(const char *vertex_shader_path, const char *fragment_shader_path) +{ + GLuint program = glCreateProgram(); + GLuint vertex_shader = gfx_load_shader_file(vertex_shader_path, GL_VERTEX_SHADER); + GLuint fragment_shader = gfx_load_shader_file(fragment_shader_path, GL_FRAGMENT_SHADER); + + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + + // Bind some attributes to fixed locations, so that common VAO can be used + // by multiple programs. + glBindAttribLocation(program, ATTR_VERTEX_POS, "vertex_pos"); + glBindAttribLocation(program, ATTR_BONE_INDEX, "vertex_bone_index"); + glBindAttribLocation(program, ATTR_BONE_WEIGHT, "vertex_bone_weight"); + + glLinkProgram(program); + + GLint link_success; + glGetProgramiv(program, GL_LINK_STATUS, &link_success); + if (!link_success) { + GLint len; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len); + char *infolog = xmalloc(len + 1); + glGetProgramInfoLog(program, len, NULL, infolog); + ERROR("Failed to link shader %s, %s: %s", vertex_shader_path, fragment_shader_path, infolog); + } + return program; +} + +static void init_shadow_renderer(struct shadow_renderer *sr) +{ + GLint orig_fbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &orig_fbo); + + sr->program = load_shader("shaders/reign_shadow.v.glsl", "shaders/reign_shadow.f.glsl"); + sr->world_transform = glGetUniformLocation(sr->program, "world_transform"); + sr->view_transform = glGetUniformLocation(sr->program, "view_transform"); + sr->has_bones = glGetUniformLocation(sr->program, "has_bones"); + sr->bone_matrices = glGetUniformLocation(sr->program, "bone_matrices"); + + glGenFramebuffers(1, &sr->fbo); + glGenTextures(1, &sr->texture); + glBindTexture(GL_TEXTURE_2D, sr->texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindFramebuffer(GL_FRAMEBUFFER, sr->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, sr->texture, 0); + glDrawBuffers(0, NULL); + glReadBuffer(GL_NONE); + + glBindFramebuffer(GL_FRAMEBUFFER, orig_fbo); +} + +static void destroy_shadow_renderer(struct shadow_renderer *sr) +{ + glDeleteTextures(1, &sr->texture); + glDeleteFramebuffers(1, &sr->fbo); + glDeleteProgram(sr->program); +} + static void init_billboard_mesh(struct RE_renderer *r) { static const GLfloat vertices[] = { @@ -46,8 +114,8 @@ static void init_billboard_mesh(struct RE_renderer *r) 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(ATTR_VERTEX_POS); + glVertexAttribPointer(ATTR_VERTEX_POS, 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_light_uv); @@ -56,10 +124,10 @@ static void init_billboard_mesh(struct RE_renderer *r) glVertexAttrib3f(r->vertex_normal, 0.0, 0.0, 1.0); glDisableVertexAttribArray(r->vertex_tangent); glVertexAttrib3f(r->vertex_tangent, 1.0, 0.0, 0.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); + glDisableVertexAttribArray(ATTR_BONE_INDEX); + glVertexAttribI4i(ATTR_BONE_INDEX, 0, 0, 0, 0); + glDisableVertexAttribArray(ATTR_BONE_WEIGHT); + glVertexAttrib4f(ATTR_BONE_WEIGHT, 0.0, 0.0, 0.0, 0.0); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); @@ -67,51 +135,63 @@ static void init_billboard_mesh(struct RE_renderer *r) glBindBuffer(GL_ARRAY_BUFFER, 0); } +static void destroy_billboard_mesh(struct RE_renderer *r) +{ + glDeleteVertexArrays(1, &r->billboard_vao); + glDeleteBuffers(1, &r->billboard_attr_buffer); +} + struct RE_renderer *RE_renderer_new(struct texture *texture) { struct RE_renderer *r = xcalloc(1, sizeof(struct RE_renderer)); - 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->normal_transform = glGetUniformLocation(r->shader.program, "normal_transform"); - r->alpha_mod = glGetUniformLocation(r->shader.program, "alpha_mod"); - r->has_bones = glGetUniformLocation(r->shader.program, "has_bones"); - r->bone_matrices = glGetUniformLocation(r->shader.program, "bone_matrices"); - r->ambient = glGetUniformLocation(r->shader.program, "ambient"); + r->program = load_shader("shaders/reign.v.glsl", "shaders/reign.f.glsl"); + r->world_transform = glGetUniformLocation(r->program, "world_transform"); + r->view_transform = glGetUniformLocation(r->program, "view_transform"); + r->texture = glGetUniformLocation(r->program, "tex"); + r->local_transform = glGetUniformLocation(r->program, "local_transform"); + r->proj_transform = glGetUniformLocation(r->program, "proj_transform"); + r->normal_transform = glGetUniformLocation(r->program, "normal_transform"); + r->alpha_mod = glGetUniformLocation(r->program, "alpha_mod"); + r->has_bones = glGetUniformLocation(r->program, "has_bones"); + r->bone_matrices = glGetUniformLocation(r->program, "bone_matrices"); + r->ambient = glGetUniformLocation(r->program, "ambient"); for (int i = 0; i < NR_DIR_LIGHTS; i++) { char buf[64]; sprintf(buf, "dir_lights[%d].dir", i); - r->dir_lights[i].dir = glGetUniformLocation(r->shader.program, buf); + r->dir_lights[i].dir = glGetUniformLocation(r->program, buf); sprintf(buf, "dir_lights[%d].diffuse", i); - r->dir_lights[i].diffuse = glGetUniformLocation(r->shader.program, buf); + r->dir_lights[i].diffuse = glGetUniformLocation(r->program, buf); sprintf(buf, "dir_lights[%d].globe_diffuse", i); - r->dir_lights[i].globe_diffuse = glGetUniformLocation(r->shader.program, buf); + r->dir_lights[i].globe_diffuse = glGetUniformLocation(r->program, buf); } - r->specular_light_dir = glGetUniformLocation(r->shader.program, "specular_light_dir"); - r->specular_strength = glGetUniformLocation(r->shader.program, "specular_strength"); - r->specular_shininess = glGetUniformLocation(r->shader.program, "specular_shininess"); - r->use_specular_map = glGetUniformLocation(r->shader.program, "use_specular_map"); - r->specular_texture = glGetUniformLocation(r->shader.program, "specular_texture"); - r->rim_exponent = glGetUniformLocation(r->shader.program, "rim_exponent"); - r->rim_color = glGetUniformLocation(r->shader.program, "rim_color"); - r->view_pos = glGetUniformLocation(r->shader.program, "view_pos"); - r->use_light_map = glGetUniformLocation(r->shader.program, "use_light_map"); - r->light_texture = glGetUniformLocation(r->shader.program, "light_texture"); - r->use_normal_map = glGetUniformLocation(r->shader.program, "use_normal_map"); - r->normal_texture = glGetUniformLocation(r->shader.program, "normal_texture"); - r->vertex_normal = glGetAttribLocation(r->shader.program, "vertex_normal"); - r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv"); - r->vertex_light_uv = glGetAttribLocation(r->shader.program, "vertex_light_uv"); - r->vertex_tangent = glGetAttribLocation(r->shader.program, "vertex_tangent"); - r->vertex_bone_index = glGetAttribLocation(r->shader.program, "vertex_bone_index"); - r->vertex_bone_weight = glGetAttribLocation(r->shader.program, "vertex_bone_weight"); + r->specular_light_dir = glGetUniformLocation(r->program, "specular_light_dir"); + r->specular_strength = glGetUniformLocation(r->program, "specular_strength"); + r->specular_shininess = glGetUniformLocation(r->program, "specular_shininess"); + r->use_specular_map = glGetUniformLocation(r->program, "use_specular_map"); + r->specular_texture = glGetUniformLocation(r->program, "specular_texture"); + r->rim_exponent = glGetUniformLocation(r->program, "rim_exponent"); + r->rim_color = glGetUniformLocation(r->program, "rim_color"); + r->view_pos = glGetUniformLocation(r->program, "view_pos"); + r->use_light_map = glGetUniformLocation(r->program, "use_light_map"); + 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_transform = glGetUniformLocation(r->program, "shadow_transform"); + r->shadow_texture = glGetUniformLocation(r->program, "shadow_texture"); + r->shadow_bias = glGetUniformLocation(r->program, "shadow_bias"); + r->vertex_normal = glGetAttribLocation(r->program, "vertex_normal"); + r->vertex_uv = glGetAttribLocation(r->program, "vertex_uv"); + r->vertex_light_uv = glGetAttribLocation(r->program, "vertex_light_uv"); + r->vertex_tangent = glGetAttribLocation(r->program, "vertex_tangent"); 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); + init_shadow_renderer(&r->shadow); init_billboard_mesh(r); r->billboard_textures = ht_create(256); return r; @@ -151,12 +231,12 @@ static void free_billboard_texture(void *value) void RE_renderer_free(struct RE_renderer *r) { - glDeleteProgram(r->shader.program); + glDeleteProgram(r->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); + destroy_billboard_mesh(r); + destroy_shadow_renderer(&r->shadow); free(r); } @@ -191,6 +271,15 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r) glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient); glUniform3fv(r->ambient, 1, ambient); + if (inst->draw_shadow && inst->plugin->shadow_mode) { + glUniform1i(r->use_shadow_map, GL_TRUE); + glActiveTexture(GL_TEXTURE0 + SHADOW_TEXTURE_UNIT); + glBindTexture(GL_TEXTURE_2D, r->shadow.texture); + glUniform1i(r->shadow_texture, SHADOW_TEXTURE_UNIT); + } else { + glUniform1i(r->use_shadow_map, GL_FALSE); + } + for (int i = 0; i < model->nr_meshes; i++) { struct mesh *mesh = &model->meshes[i]; struct material *material = &model->materials[mesh->material]; @@ -216,7 +305,7 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r) glActiveTexture(GL_TEXTURE0 + COLOR_TEXTURE_UNIT); glBindTexture(GL_TEXTURE_2D, material->color_map); - glUniform1i(r->shader.texture, COLOR_TEXTURE_UNIT); + glUniform1i(r->texture, COLOR_TEXTURE_UNIT); if (material->light_map && inst->plugin->light_map_mode) { glUniform1i(r->use_light_map, GL_TRUE); @@ -298,7 +387,7 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, bt->texture); - glUniform1i(r->shader.texture, 0); + glUniform1i(r->texture, 0); glBindVertexArray(r->billboard_vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -307,6 +396,105 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma glBindTexture(GL_TEXTURE_2D, 0); } +static bool calc_shadow_light_transform(struct RE_plugin *plugin, mat4 dest) +{ + // Compute AABB of shadow casters. + vec3 aabb[2]; + glm_aabb_invalidate(aabb); + for (int i = 0; i < plugin->nr_instances; i++) { + struct RE_instance *inst = plugin->instances[i]; + if (!inst || !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 (!glm_aabb_isvalid(aabb)) + 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); + mat4 view_matrix; + vec3 up = {0.0, 1.0, 0.0}; + glm_lookat(light_pos, center, up, view_matrix); + + mat4 proj_matrix; + glm_aabb_transform(aabb, view_matrix, aabb); + glm_ortho_aabb(aabb, proj_matrix); + + glm_mat4_mul(proj_matrix, view_matrix, dest); + return true; +} + +static void render_shadow_map(struct RE_plugin *plugin, mat4 light_space_transform) +{ + if (!calc_shadow_light_transform(plugin, light_space_transform)) { + // No shadow casters, but proceed anyway to clear the shadow texture. + glm_mat4_identity(light_space_transform); + } + + struct RE_renderer *r = plugin->renderer; + GLint orig_fbo, orig_viewport[4]; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &orig_fbo); + glGetIntegerv(GL_VIEWPORT, orig_viewport); + + glBindFramebuffer(GL_FRAMEBUFFER, r->shadow.fbo); + glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); + glClear(GL_DEPTH_BUFFER_BIT); + glUseProgram(r->shadow.program); + + glUniformMatrix4fv(r->shadow.view_transform, 1, GL_FALSE, light_space_transform[0]); + + glEnable(GL_DEPTH_TEST); + + // Render the shadow casters. + for (int i = 0; i < plugin->nr_instances; i++) { + struct RE_instance *inst = plugin->instances[i]; + if (!inst || !inst->make_shadow || !inst->model) + continue; + struct model *model = inst->model; + if (model->nr_bones > 0) { + glUniform1i(r->shadow.has_bones, GL_TRUE); + glUniformMatrix4fv(r->shadow.bone_matrices, model->nr_bones, GL_FALSE, inst->bone_transforms[0][0]); + } else { + glUniform1i(r->shadow.has_bones, GL_FALSE); + } + glUniformMatrix4fv(r->shadow.world_transform, 1, GL_FALSE, inst->local_transform[0]); + for (int j = 0; j < model->nr_meshes; j++) { + struct mesh *mesh = &model->meshes[j]; + glBindVertexArray(mesh->vao); + glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices); + } + glBindVertexArray(0); + } + + glDisable(GL_DEPTH_TEST); + glBindFramebuffer(GL_FRAMEBUFFER, orig_fbo); + glViewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]); +} + static void render_back_cg(struct texture *dst, struct RE_back_cg *bcg, struct RE_renderer *r) { if (!bcg->texture.handle) @@ -360,6 +548,12 @@ void RE_render(struct sact_sprite *sp) sprite_dirty(sp); struct texture *texture = sprite_get_texture(sp); + mat4 shadow_transform; + if (plugin->shadow_mode) + render_shadow_map(plugin, shadow_transform); + else + glm_mat4_identity(shadow_transform); + 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) @@ -388,9 +582,11 @@ 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]); + glUseProgram(r->program); + glUniformMatrix4fv(r->view_transform, 1, GL_FALSE, view_transform[0]); glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, plugin->proj_transform[0]); + glUniformMatrix4fv(r->shadow_transform, 1, GL_FALSE, shadow_transform[0]); + glUniform1f(r->shadow_bias, plugin->shadow_bias); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c index 711724e..1ad1a89 100644 --- a/src/hll/ReignEngine.c +++ b/src/hll/ReignEngine.c @@ -285,9 +285,35 @@ static bool ReignEngine_SetInstanceDraw(int plugin, int instance, bool flag) return true; } -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceDrawShadow, int plugin, int instance, bool flag); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceDrawBackShadow, int plugin, int instance, bool flag); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceDrawMakeShadow, int plugin, int instance, bool flag); +static bool ReignEngine_SetInstanceDrawShadow(int plugin, int instance, bool flag) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + ri->draw_shadow = flag; + return true; +} + +static bool ReignEngine_SetInstanceDrawBackShadow(int plugin, int instance, bool flag) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + if (flag) { + WARNING("DrawBackShaodw is not supported"); + return false; + } + return true; +} + +static bool ReignEngine_SetInstanceDrawMakeShadow(int plugin, int instance, bool flag) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + ri->make_shadow = flag; + return true; +} static bool ReignEngine_SetInstanceDrawBump(int plugin, int instance, bool flag) { @@ -306,9 +332,23 @@ static bool ReignEngine_GetInstanceDraw(int plugin, int instance) return ri->draw; } -//bool ReignEngine_GetInstanceDrawShadow(int plugin, int instance); +static bool ReignEngine_GetInstanceDrawShadow(int plugin, int instance) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + return ri->draw_shadow; +} + //bool ReignEngine_GetInstanceDrawBackShadow(int plugin, int instance); -//bool ReignEngine_GetInstanceDrawMakeShadow(int plugin, int instance); + +static bool ReignEngine_GetInstanceDrawMakeShadow(int plugin, int instance) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + return ri->make_shadow; +} static bool ReignEngine_GetInstanceDrawBump(int plugin, int instance) { @@ -486,8 +526,22 @@ static bool ReignEngine_TransInstanceLocalPosToWorldPosByBone(int plugin, int in HLL_WARN_UNIMPLEMENTED(0.0, float, ReignEngine, CalcInstanceHeightDetection, int plugin, int instance, float x, float z); //float ReignEngine_GetInstanceSoftFogEdgeLength(int plugin, int instance); HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceSoftFogEdgeLength, int plugin, int instance, float length); -//float ReignEngine_GetInstanceShadowVolumeBoneRadius(int plugin, int instance); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceShadowVolumeBoneRadius, int plugin, int instance, float radius); + +static float ReignEngine_GetInstanceShadowVolumeBoneRadius(int plugin, int instance) +{ + struct RE_instance *ri = get_instance(plugin, instance); + return ri ? ri->shadow_volume_bone_radius : 0.0; +} + +static bool ReignEngine_SetInstanceShadowVolumeBoneRadius(int plugin, int instance, float radius) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + ri->shadow_volume_bone_radius = radius; + return true; +} + //bool ReignEngine_GetInstanceDebugDrawShadowVolume(int plugin, int instance); //bool ReignEngine_SetInstanceDebugDrawShadowVolume(int plugin, int instance, bool flag); //bool ReignEngine_SaveEffect(int plugin, int instance); @@ -662,11 +716,44 @@ static int ReignEngine_GetShadowMapResolutionLevel(int plugin) } //float ReignEngine_GetShadowSplitDepth(int plugin, int num); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetShadowMapType, int plugin, int type); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetShadowMapLightLookPos, int plugin, float x, float y, float z); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetShadowMapLightDir, int plugin, float x, float y, float z); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetShadowBox, int plugin, float x, float y, float z, float size_x, float size_y, float size_z); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetShadowBias, int plugin, int num, float shadow_bias); + +static bool ReignEngine_SetShadowMapType(int plugin, int type) +{ + // Toushin Toshi 3 uses only type 0. + if (type != 0) + WARNING("non-zero shadow map type is not supported"); + return true; +} + +HLL_QUIET_UNIMPLEMENTED(true, bool, ReignEngine, SetShadowMapLightLookPos, int plugin, float x, float y, float z); // no-op in shadow map type 0? + +static bool ReignEngine_SetShadowMapLightDir(int plugin, float x, float y, float z) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->shadow_map_light_dir[0] = x; + p->shadow_map_light_dir[1] = y; + p->shadow_map_light_dir[2] = -z; + glm_vec3_normalize(p->shadow_map_light_dir); + return true; +} + +HLL_QUIET_UNIMPLEMENTED(true, bool, ReignEngine, SetShadowBox, int plugin, float x, float y, float z, float size_x, float size_y, float size_z); // no-op in shadow map type 0? + +static bool ReignEngine_SetShadowBias(int plugin, int num, float shadow_bias) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + if (num != 0) { + WARNING("SetShadowBias(num=%d): not supported", num); + return false; + } + p->shadow_bias = shadow_bias; + return true; +} + //bool ReignEngine_SetShadowSlopeBias(int plugin, float fShadowSlopeBias); //bool ReignEngine_SetShadowFilterMag(int plugin, float fShadowFilterMag); //bool ReignEngine_SetShadowTargetDistance(int plugin, int num, float fDistance); @@ -1194,9 +1281,9 @@ HLL_LIBRARY(ReignEngine, HLL_EXPORT(SetInstanceDrawMakeShadow, ReignEngine_SetInstanceDrawMakeShadow), HLL_EXPORT(SetInstanceDrawBump, ReignEngine_SetInstanceDrawBump), HLL_EXPORT(GetInstanceDraw, ReignEngine_GetInstanceDraw), - HLL_TODO_EXPORT(GetInstanceDrawShadow, ReignEngine_GetInstanceDrawShadow), + HLL_EXPORT(GetInstanceDrawShadow, ReignEngine_GetInstanceDrawShadow), HLL_TODO_EXPORT(GetInstanceDrawBackShadow, ReignEngine_GetInstanceDrawBackShadow), - HLL_TODO_EXPORT(GetInstanceDrawMakeShadow, ReignEngine_GetInstanceDrawMakeShadow), + HLL_EXPORT(GetInstanceDrawMakeShadow, ReignEngine_GetInstanceDrawMakeShadow), HLL_EXPORT(GetInstanceDrawBump, ReignEngine_GetInstanceDrawBump), HLL_TODO_EXPORT(GetInstanceDrawType, ReignEngine_GetInstanceDrawType), HLL_EXPORT(SetInstanceDrawType, ReignEngine_SetInstanceDrawType), @@ -1253,7 +1340,7 @@ HLL_LIBRARY(ReignEngine, HLL_EXPORT(CalcInstanceHeightDetection, ReignEngine_CalcInstanceHeightDetection), HLL_TODO_EXPORT(GetInstanceSoftFogEdgeLength, ReignEngine_GetInstanceSoftFogEdgeLength), HLL_EXPORT(SetInstanceSoftFogEdgeLength, ReignEngine_SetInstanceSoftFogEdgeLength), - HLL_TODO_EXPORT(GetInstanceShadowVolumeBoneRadius, ReignEngine_GetInstanceShadowVolumeBoneRadius), + HLL_EXPORT(GetInstanceShadowVolumeBoneRadius, ReignEngine_GetInstanceShadowVolumeBoneRadius), HLL_EXPORT(SetInstanceShadowVolumeBoneRadius, ReignEngine_SetInstanceShadowVolumeBoneRadius), HLL_TODO_EXPORT(GetInstanceDebugDrawShadowVolume, ReignEngine_GetInstanceDebugDrawShadowVolume), HLL_TODO_EXPORT(SetInstanceDebugDrawShadowVolume, ReignEngine_SetInstanceDebugDrawShadowVolume), diff --git a/src/video.c b/src/video.c index b0ce270..1d2def7 100644 --- a/src/video.c +++ b/src/video.c @@ -89,7 +89,7 @@ static GLchar *read_shader_file(const char *path) return source; } -static GLuint load_shader_file(const char *path, GLenum type) +GLuint gfx_load_shader_file(const char *path, GLenum type) { GLint shader_compiled; GLuint shader; @@ -115,16 +115,11 @@ static GLuint load_shader_file(const char *path, GLenum type) void gfx_load_shader(struct shader *dst, const char *vertex_shader_path, const char *fragment_shader_path) { GLuint program = glCreateProgram(); - GLuint vertex_shader = load_shader_file(vertex_shader_path, GL_VERTEX_SHADER); - GLuint fragment_shader = load_shader_file(fragment_shader_path, GL_FRAGMENT_SHADER); + GLuint vertex_shader = gfx_load_shader_file(vertex_shader_path, GL_VERTEX_SHADER); + GLuint fragment_shader = gfx_load_shader_file(fragment_shader_path, GL_FRAGMENT_SHADER); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); - - // In OpenGL < 3.2, Attribute location 0 is special. Make sure it's assigned - // to the vertex position. - glBindAttribLocation(program, 0, "vertex_pos"); - glLinkProgram(program); GLint link_success;