diff --git a/include/reign.h b/include/reign.h index dedeedb..9c700da 100644 --- a/include/reign.h +++ b/include/reign.h @@ -28,6 +28,7 @@ typedef struct cJSON cJSON; struct hash_table; +struct billboard_texture; enum RE_plugin_version { RE_REIGN_PLUGIN, // Toushin Toshi 3 @@ -89,6 +90,7 @@ enum RE_draw_type { enum RE_draw_options { RE_DRAW_OPTION_EDGE = 0, + RE_DRAW_OPTION_LIGHTING = 1, RE_DRAW_OPTION_MAX }; @@ -179,6 +181,7 @@ struct RE_plugin { int mag_speed; float light_params[RE_NR_LIGHT_PARAMS]; float edge_length; + float edge_reduction_rate; vec3 edge_color; }; @@ -197,6 +200,7 @@ struct RE_instance { float pitch, roll, yaw; // in degrees vec3 scale; float alpha; + float grayscale_rate; bool draw; bool draw_edge; bool draw_shadow; @@ -216,6 +220,10 @@ struct RE_instance { // Billboards enum RE_draw_type draw_type; + vec3 vertex_pos[4]; + vec2 vertex_uv[4]; + struct billboard_texture **billboard_frames; + int nr_billboard_frames; // Lights vec3 vec; @@ -259,6 +267,7 @@ 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); +bool RE_instance_set_vertex_uv(struct RE_instance *instance, int index, float u, float v); 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); diff --git a/shaders/reign.f.glsl b/shaders/reign.f.glsl index e0f2fad..5f26117 100644 --- a/shaders/reign.f.glsl +++ b/shaders/reign.f.glsl @@ -108,6 +108,7 @@ in vec3 specular_dir; uniform vec4 tonemap_param; // (ExposureBias, WhitePoint, A, B) uniform vec4 tonemap_param2; // (C, D, E, F) uniform bool nolighting; +uniform float grayscale_rate; vec3 hable(vec3 v) { @@ -239,6 +240,9 @@ void main() { #if ENGINE == SEAL_ENGINE if (!nolighting) frag_rgb = tone_map(frag_rgb); + + if (grayscale_rate != 0.0) + frag_rgb = mix(frag_rgb, vec3(dot(frag_rgb, vec3(0.299, 0.587, 0.114))), grayscale_rate); #endif // Alpha mapping diff --git a/shaders/reign.v.glsl b/shaders/reign.v.glsl index 60fd555..c0a4064 100644 --- a/shaders/reign.v.glsl +++ b/shaders/reign.v.glsl @@ -42,6 +42,8 @@ uniform vec2 uv_scroll; #endif // ENGINE == REIGN_ENGINE +uniform vec4 uv_tiling; + #if ENABLE_LIGHT_SCATTERING uniform int fog_type; uniform vec4 ls_params; // (beta_r, beta_m, g, distance) @@ -128,11 +130,11 @@ void main() { vec4 view_pos = view_transform * world_pos; gl_Position = proj_transform * view_pos; - tex_coord = vertex_uv + uv_scroll; + tex_coord = (vertex_uv + uv_scroll) * uv_tiling.xy; light_tex_coord = vertex_light_uv + uv_scroll; color_mod = vertex_color; blend_weight = vertex_blend_weight; - blend_tex_coord = vertex_blend_uv + uv_scroll; + blend_tex_coord = (vertex_blend_uv + uv_scroll) * uv_tiling.zw; dist = -view_pos.z; shadow_frag_pos = shadow_transform * world_pos; diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 7cd80cc..cc76f09 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -70,6 +70,8 @@ struct mesh { struct material { uint32_t flags; bool is_transparent; + vec2 uv_tiling; + vec2 blend_uv_tiling; int nr_color_maps; GLuint *color_maps; GLuint specular_map; @@ -195,15 +197,20 @@ struct RE_renderer { GLint tonemap_param; GLint tonemap_param2; GLint nolighting; + GLint grayscale_rate; GLint alpha_mode; GLint alpha_texture; GLint uv_scroll; + GLint uv_tiling; GLint blend_tex; GLint use_blend_texture; - GLuint billboard_vao; - GLuint billboard_attr_buffer; - struct hash_table *billboard_textures; // cg_no -> struct billboard_texture* + GLuint billboard_particle_vao; + GLuint billboard_particle_attr_buffer; + GLuint billboard_instance_vao; + GLuint billboard_instance_attr_buffer; + struct hash_table *billboard_textures_by_no; // cg_no -> struct billboard_texture* + struct hash_table *billboard_textures_by_path; // path -> struct billboard_texture* uint32_t last_frame_timestamp; }; @@ -235,7 +242,8 @@ struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, cons struct RE_renderer *RE_renderer_new(void); void RE_renderer_free(struct RE_renderer *r); void RE_renderer_set_viewport_size(struct RE_renderer *r, int width, int height); -bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no); +bool RE_renderer_load_billboard_texture_by_no(struct RE_renderer *r, int cg_no); +struct billboard_texture *RE_renderer_load_billboard_texture_by_path(struct RE_renderer *r, struct archive *aar, const char *path); struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r, struct model *model); void RE_renderer_free_height_detector(struct height_detector *hd); bool RE_renderer_detect_height(struct height_detector *hd, float x, float z, float *y_out); @@ -653,6 +661,7 @@ struct pol_material { char *name; uint32_t flags; char *textures[MAX_TEXTURE_TYPE]; + vec2 uv_tiling; }; struct pol_material_group { diff --git a/src/3d/model.c b/src/3d/model.c index 05de49c..c7d9859 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -143,9 +143,11 @@ static GLuint *load_texture_list(struct archive *aar, const char *path, const ch return textures; } -static bool init_material(struct material *material, const struct pol_material *m, struct amt *amt, struct archive *aar, const char *path) +static bool init_material(struct material *material, struct pol_material *m, struct amt *amt, struct archive *aar, const char *path) { material->flags = m->flags; + glm_vec2_copy(m->uv_tiling, material->uv_tiling); + glm_vec2_one(material->blend_uv_tiling); if (!m->textures[COLOR_MAP]) { WARNING("No color texture"); return false; @@ -575,6 +577,8 @@ struct model *model_load(struct archive *aar, const char *path) if (child->children[1].m.textures[COLOR_MAP]) { model->materials[material_offsets[i] + j].blend_texture = load_texture(aar, path, child->children[1].m.textures[COLOR_MAP], NULL); + glm_vec2_copy(child->children[1].m.uv_tiling, + model->materials[material_offsets[i] + j].blend_uv_tiling); } } else { init_material(&model->materials[material_offsets[i] + j], @@ -735,6 +739,8 @@ 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; + glm_vec2_one(material->uv_tiling); + glm_vec2_one(material->blend_uv_tiling); material->color_maps = xmalloc(sizeof(GLuint)); material->nr_color_maps = 1; glGenTextures(1, material->color_maps); diff --git a/src/3d/parser.c b/src/3d/parser.c index 2d23959..e37d04b 100644 --- a/src/3d/parser.c +++ b/src/3d/parser.c @@ -72,14 +72,20 @@ static uint32_t parse_material_attributes(const char *name) static void parse_textures(struct buffer *r, int pol_version, struct pol_material *m) { + glm_vec2_one(m->uv_tiling); int nr_textures = buffer_read_int32(r); for (int i = 0; i < nr_textures; i++) { char *filename = read_cstring(r); int type = buffer_read_int32(r); - if (pol_version >= 3) - buffer_skip(r, 8); // 2 unknown float params + vec2 uv_tiling = { 1.0f, 1.0f }; + if (pol_version >= 3) { + uv_tiling[0] = buffer_read_float(r); + uv_tiling[1] = buffer_read_float(r); + } if ((unsigned)type < MAX_TEXTURE_TYPE) { m->textures[type] = filename; + if (type == COLOR_MAP) + glm_vec2_copy(uv_tiling, m->uv_tiling); } else { WARNING("invalid texture type %d", type); free(filename); diff --git a/src/3d/reign.c b/src/3d/reign.c index 4220526..9f88619 100644 --- a/src/3d/reign.c +++ b/src/3d/reign.c @@ -53,6 +53,14 @@ static struct RE_instance *create_instance(struct RE_plugin *plugin) instance->use_mag_speed = true; instance->column_height = 1.0f; instance->column_radius = 1.0f; + glm_vec3_copy((vec3){-1.0f, 2.0f, 0.0f}, instance->vertex_pos[0]); + glm_vec3_copy((vec3){-1.0f, 0.0f, 0.0f}, instance->vertex_pos[1]); + glm_vec3_copy((vec3){ 1.0f, 2.0f, 0.0f}, instance->vertex_pos[2]); + glm_vec3_copy((vec3){ 1.0f, 0.0f, 0.0f}, instance->vertex_pos[3]); + glm_vec2_copy((vec2){0.0f, 0.0f}, instance->vertex_uv[0]); + glm_vec2_copy((vec2){0.0f, 1.0f}, instance->vertex_uv[1]); + glm_vec2_copy((vec2){1.0f, 0.0f}, instance->vertex_uv[2]); + glm_vec2_copy((vec2){1.0f, 1.0f}, instance->vertex_uv[3]); glm_mat4_identity(instance->local_transform); glm_mat3_identity(instance->normal_transform); return instance; @@ -62,7 +70,9 @@ static void free_instance(struct RE_instance *instance); static void unload_instance(struct RE_instance *instance) { - if (instance->motion) { + // A billboard's motion holds frame animation state that the game can set + // before loading the instance data, so it must survive unloads. + if (instance->motion && instance->type != RE_ITYPE_BILLBOARD) { motion_free(instance->motion); instance->motion = NULL; } @@ -87,6 +97,12 @@ static void unload_instance(struct RE_instance *instance) free(instance->bone_transforms); instance->bone_transforms = NULL; } + if (instance->billboard_frames) { + // No need to free the textures, they are owned by the renderer. + free(instance->billboard_frames); + instance->billboard_frames = NULL; + instance->nr_billboard_frames = 0; + } if (instance->height_detector) { RE_renderer_free_height_detector(instance->height_detector); instance->height_detector = NULL; @@ -100,6 +116,8 @@ static void unload_instance(struct RE_instance *instance) static void free_instance(struct RE_instance *instance) { unload_instance(instance); + if (instance->motion) + motion_free(instance->motion); free(instance->light_params); xfree_aligned(instance); } @@ -252,6 +270,7 @@ struct RE_plugin *RE_plugin_new(enum RE_plugin_version version) plugin->mag_speed = 1; if (version == RE_TAPIR_PLUGIN) plugin->draw_options[RE_DRAW_OPTION_EDGE] = 1; + plugin->draw_options[RE_DRAW_OPTION_LIGHTING] = 1; plugin->edge_length = 0.02f; plugin->fog_type = RE_FOG_NONE; plugin->fog_near = 1.0f; @@ -483,6 +502,37 @@ bool RE_instance_data_exists(struct RE_instance *instance, const char *name) return exists; } +// Loads the frame images of a billboard instance. They are stored in the +// archive as "\.png", "\[1].png", ... +static bool load_billboard_frames(struct RE_instance *instance, const char *name) +{ + const char *basename = strrchr(name, '\\'); + basename = basename ? basename + 1 : name; + char *path = xmalloc(strlen(name) + strlen(basename) + 22); + + int capacity = 8; + instance->billboard_frames = xcalloc(capacity, sizeof(struct billboard_texture *)); + for (int i = 0;; i++) { + if (i == 0) + sprintf(path, "%s\\%s.png", name, basename); + else + sprintf(path, "%s\\%s[%d].png", name, basename, i); + struct billboard_texture *bt = RE_renderer_load_billboard_texture_by_path( + instance->plugin->renderer, instance->plugin->aar, path); + if (!bt) + break; + if (i == capacity) { + instance->billboard_frames = xrealloc_array( + instance->billboard_frames, capacity, capacity * 2, sizeof(struct billboard_texture *)); + capacity *= 2; + } + instance->billboard_frames[i] = bt; + instance->nr_billboard_frames = i + 1; + } + free(path); + return instance->nr_billboard_frames > 0; +} + bool RE_instance_load(struct RE_instance *instance, const char *name) { if (!instance) @@ -539,6 +589,9 @@ bool RE_instance_load(struct RE_instance *instance, const char *name) } } return true; + case RE_ITYPE_BILLBOARD: + if (re_plugin_version >= RE_SEAL_PLUGIN) + return load_billboard_frames(instance, name); case RE_ITYPE_DIRECTIONAL_LIGHT: if (re_plugin_version >= RE_SEAL_PLUGIN) { struct archive_data *dfile = archive_get_by_name(instance->plugin->aar, name); @@ -610,19 +663,28 @@ bool RE_instance_set_mesh_show(struct RE_instance *instance, const char *mesh_na return false; } +// Converts from the (top-left, top-right, bottom-left, bottom-right) order used +// by the API to the vertex order of the billboard mesh. +static const int billboard_vertex_index[4] = {0, 2, 1, 3}; + bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z) { - if (!instance) + if (!instance || (unsigned)index >= 4) 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; + glm_vec3_copy((vec3){x, y, z}, instance->vertex_pos[billboard_vertex_index[index]]); + return true; +} + +bool RE_instance_set_vertex_uv(struct RE_instance *instance, int index, float u, float v) +{ + if (!instance || (unsigned)index >= 4) + return false; + if (instance->type != RE_ITYPE_BILLBOARD) + ERROR("not implemented"); + glm_vec2_copy((vec2){u, v}, instance->vertex_uv[billboard_vertex_index[index]]); + return true; } int RE_instance_get_bone_index(struct RE_instance *instance, const char *name) @@ -907,9 +969,9 @@ 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) { + if (motion->instance->type == RE_ITYPE_BILLBOARD && re_plugin_version < RE_SEAL_PLUGIN) { for (int i = begin; i < end; i++) { - if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i)) + if (!RE_renderer_load_billboard_texture_by_no(motion->instance->plugin->renderer, i)) return false; } } @@ -922,9 +984,9 @@ 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) { + if (motion->instance->type == RE_ITYPE_BILLBOARD && re_plugin_version < RE_SEAL_PLUGIN) { for (int i = begin; i < end; i++) { - if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i)) + if (!RE_renderer_load_billboard_texture_by_no(motion->instance->plugin->renderer, i)) return false; } } diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 4a79b31..78fd729 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -166,24 +166,8 @@ static void destroy_outline_renderer(struct outline_renderer *or) glDeleteProgram(or->program); } -static void init_billboard_mesh(struct RE_renderer *r) +static void init_billboard_unused_vertex_attrs(void) { - static const GLfloat vertices[] = { - // x, y, z, u, v - -1.0, 1.0, 0.0, 0.0, 0.0, - -1.0, -1.0, 0.0, 0.0, 1.0, - 1.0, 1.0, 0.0, 1.0, 0.0, - 1.0, -1.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(VATTR_POS); - glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, 20, (const void *)0); - glEnableVertexAttribArray(VATTR_UV); - glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, 20, (const void *)12); glDisableVertexAttribArray(VATTR_LIGHT_UV); glVertexAttrib2f(VATTR_LIGHT_UV, 0.0, 0.0); glDisableVertexAttribArray(VATTR_COLOR); @@ -196,8 +180,39 @@ static void init_billboard_mesh(struct RE_renderer *r) glVertexAttribI4i(VATTR_BONE_INDEX, 0, 0, 0, 0); glDisableVertexAttribArray(VATTR_BONE_WEIGHT); glVertexAttrib4f(VATTR_BONE_WEIGHT, 0.0, 0.0, 0.0, 0.0); +} +static void init_billboard_mesh(struct RE_renderer *r) +{ + static const GLfloat vertices[] = { + // x, y, z, u, v + -1.0, 1.0, 0.0, 0.0, 0.0, + -1.0, -1.0, 0.0, 0.0, 1.0, + 1.0, 1.0, 0.0, 1.0, 0.0, + 1.0, -1.0, 0.0, 1.0, 1.0, + }; + glGenBuffers(1, &r->billboard_particle_attr_buffer); + glBindBuffer(GL_ARRAY_BUFFER, r->billboard_particle_attr_buffer); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glGenBuffers(1, &r->billboard_instance_attr_buffer); + + glGenVertexArrays(1, &r->billboard_particle_vao); + glBindVertexArray(r->billboard_particle_vao); + glBindBuffer(GL_ARRAY_BUFFER, r->billboard_particle_attr_buffer); + glEnableVertexAttribArray(VATTR_POS); + glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, 20, (const void *)0); + glEnableVertexAttribArray(VATTR_UV); + glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, 20, (const void *)12); + init_billboard_unused_vertex_attrs(); + + glGenVertexArrays(1, &r->billboard_instance_vao); + glBindVertexArray(r->billboard_instance_vao); + glBindBuffer(GL_ARRAY_BUFFER, r->billboard_instance_attr_buffer); + glEnableVertexAttribArray(VATTR_POS); + glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, 20, (const void *)0); + glEnableVertexAttribArray(VATTR_UV); + glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, 20, (const void *)12); + init_billboard_unused_vertex_attrs(); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -205,8 +220,10 @@ static void init_billboard_mesh(struct RE_renderer *r) static void destroy_billboard_mesh(struct RE_renderer *r) { - glDeleteVertexArrays(1, &r->billboard_vao); - glDeleteBuffers(1, &r->billboard_attr_buffer); + glDeleteVertexArrays(1, &r->billboard_particle_vao); + glDeleteVertexArrays(1, &r->billboard_instance_vao); + glDeleteBuffers(1, &r->billboard_particle_attr_buffer); + glDeleteBuffers(1, &r->billboard_instance_attr_buffer); } struct RE_renderer *RE_renderer_new(void) @@ -266,9 +283,11 @@ struct RE_renderer *RE_renderer_new(void) r->tonemap_param = glGetUniformLocation(r->program, "tonemap_param"); r->tonemap_param2 = glGetUniformLocation(r->program, "tonemap_param2"); r->nolighting = glGetUniformLocation(r->program, "nolighting"); + r->grayscale_rate = glGetUniformLocation(r->program, "grayscale_rate"); r->alpha_mode = glGetUniformLocation(r->program, "alpha_mode"); r->alpha_texture = glGetUniformLocation(r->program, "alpha_texture"); r->uv_scroll = glGetUniformLocation(r->program, "uv_scroll"); + r->uv_tiling = glGetUniformLocation(r->program, "uv_tiling"); r->blend_tex = glGetUniformLocation(r->program, "blend_tex"); r->use_blend_texture = glGetUniformLocation(r->program, "use_blend_texture"); @@ -277,7 +296,8 @@ struct RE_renderer *RE_renderer_new(void) init_shadow_renderer(&r->shadow); init_outline_renderer(&r->outline); init_billboard_mesh(r); - r->billboard_textures = ht_create(256); + r->billboard_textures_by_no = ht_create(256); + r->billboard_textures_by_path = ht_create(256); r->last_frame_timestamp = SDL_GetTicks(); return r; } @@ -291,15 +311,8 @@ void RE_renderer_set_viewport_size(struct RE_renderer *r, int width, int height) glBindRenderbuffer(GL_RENDERBUFFER, 0); } -bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no) +static struct billboard_texture *create_billboard_texture(struct cg *cg) { - 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); @@ -311,12 +324,45 @@ bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no) glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); bt->has_alpha = cg->metrics.has_alpha; + return bt; +} +bool RE_renderer_load_billboard_texture_by_no(struct RE_renderer *r, int cg_no) +{ + if (ht_get_int(r->billboard_textures_by_no, cg_no, NULL)) + return true; + + struct cg *cg = asset_cg_load(cg_no); + if (!cg) + return false; + + ht_put_int(r->billboard_textures_by_no, cg_no, create_billboard_texture(cg)); cg_free(cg); - ht_put_int(r->billboard_textures, cg_no, bt); return true; } +struct billboard_texture *RE_renderer_load_billboard_texture_by_path(struct RE_renderer *r, struct archive *aar, const char *path) +{ + struct billboard_texture *bt = ht_get(r->billboard_textures_by_path, path, NULL); + if (bt) + return bt; + + struct archive_data *dfile = archive_get_by_name(aar, path); + if (!dfile) + return NULL; + struct cg *cg = cg_load_data(dfile); + archive_free_data(dfile); + if (!cg) { + WARNING("cg_load_data failed: %s", path); + return NULL; + } + + bt = create_billboard_texture(cg); + cg_free(cg); + ht_put(r->billboard_textures_by_path, path, bt); + return bt; +} + static void free_billboard_texture(void *value) { struct billboard_texture *bt = value; @@ -328,8 +374,10 @@ void RE_renderer_free(struct RE_renderer *r) { glDeleteProgram(r->program); glDeleteRenderbuffers(1, &r->depth_buffer); - ht_foreach_value(r->billboard_textures, free_billboard_texture); - ht_free_int(r->billboard_textures); + ht_foreach_value(r->billboard_textures_by_no, free_billboard_texture); + ht_free_int(r->billboard_textures_by_no); + ht_foreach_value(r->billboard_textures_by_path, free_billboard_texture); + ht_free(r->billboard_textures_by_path); destroy_billboard_mesh(r); destroy_shadow_renderer(&r->shadow); destroy_outline_renderer(&r->outline); @@ -371,6 +419,16 @@ static bool should_draw_shadow(struct mesh *mesh, struct material *material) && !(material->flags & (MATERIAL_ALPHA | MATERIAL_SPRITE)); } +static bool lighting_disabled(struct RE_plugin *plugin) +{ + return plugin->draw_options[RE_DRAW_OPTION_LIGHTING] <= 0; +} + +static bool is_nolighting(struct RE_plugin *plugin, struct mesh *mesh) +{ + return lighting_disabled(plugin) || (mesh->flags & MESH_NOLIGHTING); +} + static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase) { struct model *model = inst->model; @@ -431,16 +489,17 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); } + bool nolighting = is_nolighting(inst->plugin, mesh); int fog_type = inst->plugin->fog_type; - if (!inst->plugin->fog_mode || (re_plugin_version == RE_REIGN_PLUGIN && (mesh->flags & MESH_NOLIGHTING))) { + if (!inst->plugin->fog_mode || (re_plugin_version == RE_REIGN_PLUGIN && nolighting)) { fog_type = RE_FOG_NONE; } glUniform1i(r->fog_type, fog_type); - glUniform1i(r->nolighting, !!(mesh->flags & MESH_NOLIGHTING)); + glUniform1i(r->nolighting, nolighting); GLboolean use_specular_map = GL_FALSE; float shininess = (mesh->flags & MESH_HAS_SPECULAR_POWER) ? mesh->specular_power : material->specular_shininess; - if (inst->plugin->specular_mode && !(mesh->flags & MESH_NOLIGHTING) && shininess > 0.0f) { + if (inst->plugin->specular_mode && !nolighting && shininess > 0.0f) { if (mesh->flags & MESH_HAS_SPECULAR_COLOR) { glUniform3fv(r->specular_color, 1, mesh->specular_color); } else { @@ -475,7 +534,7 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d if (mesh->flags & MESH_ENVMAP) { glUniform1i(r->diffuse_type, DIFFUSE_ENV_MAP); - } else if (mesh->flags & MESH_NOLIGHTING) { + } else if (nolighting) { glUniform1i(r->diffuse_type, DIFFUSE_EMISSIVE); } else if (material->light_map && mesh->flags & MESH_HAS_LIGHT_UV && inst->plugin->light_map_mode) { glUniform1i(r->diffuse_type, DIFFUSE_LIGHT_MAP); @@ -516,6 +575,9 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d vec2 uv_scroll; glm_vec2_scale(mesh->uv_scroll, r->last_frame_timestamp / 1000.f, uv_scroll); glUniform2fv(r->uv_scroll, 1, uv_scroll); + glUniform4f(r->uv_tiling, + material->uv_tiling[0], material->uv_tiling[1], + material->blend_uv_tiling[0], material->blend_uv_tiling[1]); glBindVertexArray(mesh->vao); @@ -566,11 +628,13 @@ static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r render_static_model(inst->shadow_volume_instance, r, phase); } -static void reset_draw_uniforms(struct RE_renderer *r) +static void reset_draw_uniforms(struct RE_renderer *r, struct RE_plugin *plugin) { + bool nolighting = lighting_disabled(plugin); glUniform1f(r->alpha_mod, 1.0f); glUniform3f(r->diffuse_mod, 1.0f, 1.0f, 1.0f); glUniform2f(r->uv_scroll, 0.0f, 0.0f); + glUniform4f(r->uv_tiling, 1.0f, 1.0f, 1.0f, 1.0f); glUniform1i(r->has_bones, GL_FALSE); glUniform3f(r->specular_color, 0.0f, 0.0f, 0.0f); glUniform1f(r->specular_shininess, 0.0f); @@ -582,15 +646,23 @@ static void reset_draw_uniforms(struct RE_renderer *r) glUniform1f(r->shadow_darkness, 0.0f); glUniform1i(r->alpha_mode, ALPHA_BLEND); glUniform1i(r->fog_type, 0); - glUniform1i(r->diffuse_type, DIFFUSE_NORMAL); + glUniform1i(r->nolighting, nolighting); + glUniform1i(r->diffuse_type, nolighting ? DIFFUSE_EMISSIVE : DIFFUSE_NORMAL); } static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat, enum draw_phase phase) { if (!inst->draw) return; - int cg_no = inst->motion->current_frame; - struct billboard_texture *bt = ht_get_int(r->billboard_textures, cg_no, NULL); + int frame = inst->motion->current_frame; + struct billboard_texture *bt; + if (inst->nr_billboard_frames > 0) { + if (frame < 0 || frame >= inst->nr_billboard_frames) + return; + bt = inst->billboard_frames[frame]; + } else { + bt = ht_get_int(r->billboard_textures_by_no, frame, NULL); + } if (!bt) return; bool is_transparent = bt->has_alpha || inst->alpha < 1.0f; @@ -603,8 +675,6 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma glm_mat4_pick3t(view_mat, rot); glm_mat4_ins3(rot, local_transform); glm_scale(local_transform, inst->scale); - // Billboard instances are bottomed at y=0. - glm_translate_y(local_transform, 1.0); mat3 normal_transform; // This should be safe because billboards do not have non-uniform scaling. glm_mat4_pick3(local_transform, normal_transform); @@ -613,7 +683,7 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]); glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, normal_transform[0]); - reset_draw_uniforms(r); + reset_draw_uniforms(r, inst->plugin); glUniform1f(r->alpha_mod, inst->alpha); glUniform1i(r->fog_type, inst->plugin->fog_mode ? inst->plugin->fog_type : 0); switch (inst->draw_type) { @@ -629,7 +699,15 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, bt->texture); glUniform1i(r->texture, 0); - glBindVertexArray(r->billboard_vao); + GLfloat vertices[4][5]; + for (int i = 0; i < 4; i++) { + glm_vec3_copy(inst->vertex_pos[i], vertices[i]); + glm_vec2_copy(inst->vertex_uv[i], vertices[i] + 3); + } + glBindVertexArray(r->billboard_instance_vao); + glBindBuffer(GL_ARRAY_BUFFER, r->billboard_instance_attr_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -649,7 +727,7 @@ static void render_billboard_particles(struct RE_renderer *r, struct RE_instance glActiveTexture(GL_TEXTURE0); glUniform1i(r->texture, 0); - glBindVertexArray(r->billboard_vao); + glBindVertexArray(r->billboard_particle_vao); for (int index = 0; index < pae_obj->nr_particles; index++) { struct particle_instance *pi = &po->instances[index]; @@ -686,7 +764,8 @@ static void render_polygon_particles(struct RE_renderer *r, struct RE_instance * if (!model) return; - glUniform1i(r->diffuse_type, DIFFUSE_NORMAL); + glUniform1i(r->diffuse_type, + lighting_disabled(inst->plugin) ? DIFFUSE_EMISSIVE : DIFFUSE_NORMAL); glUniform1i(r->fog_type, inst->plugin->fog_mode ? inst->plugin->fog_type : 0); for (int index = 0; index < pae_obj->nr_particles; index++) { @@ -705,6 +784,9 @@ static void render_polygon_particles(struct RE_renderer *r, struct RE_instance * for (int i = 0; i < model->nr_meshes; i++) { struct mesh *mesh = &model->meshes[i]; struct material *material = &model->materials[mesh->material]; + glUniform4f(r->uv_tiling, + material->uv_tiling[0], material->uv_tiling[1], + material->blend_uv_tiling[0], material->blend_uv_tiling[1]); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, material->color_maps[0]); @@ -742,7 +824,7 @@ static void render_s3de_billboard_particles(struct RE_renderer *r, struct RE_ins } glActiveTexture(GL_TEXTURE0); glUniform1i(r->texture, 0); - glBindVertexArray(r->billboard_vao); + glBindVertexArray(r->billboard_particle_vao); glDisable(GL_CULL_FACE); glBindTexture(GL_TEXTURE_2D, bt->texture); @@ -780,7 +862,8 @@ static void render_s3de_polygon_particles(struct RE_renderer *r, struct RE_insta if (phase == DRAW_OPAQUE && st->emitter_alpha < 1.0f) return; - glUniform1i(r->diffuse_type, DIFFUSE_NORMAL); + glUniform1i(r->diffuse_type, + lighting_disabled(inst->plugin) ? DIFFUSE_EMISSIVE : DIFFUSE_NORMAL); // The .3de blend_type is ignored for polygon objects. Per-mesh additive // blend modes are not yet handled. glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); @@ -807,6 +890,9 @@ static void render_s3de_polygon_particles(struct RE_renderer *r, struct RE_insta if (phase != (is_transparent ? DRAW_TRANSPARENT : DRAW_OPAQUE)) continue; struct material *material = &model->materials[mesh->material]; + glUniform4f(r->uv_tiling, + material->uv_tiling[0], material->uv_tiling[1], + material->blend_uv_tiling[0], material->blend_uv_tiling[1]); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, material->color_maps[0]); @@ -850,7 +936,7 @@ static void render_s3de_effect(struct RE_instance *inst, struct RE_renderer *r, RE_instance_update_local_transform(inst); glUniform3fv(r->instance_ambient, 1, inst->ambient); - reset_draw_uniforms(r); + reset_draw_uniforms(r, inst->plugin); if (phase == DRAW_TRANSPARENT) glDepthMask(GL_FALSE); @@ -920,7 +1006,7 @@ static void render_particle_effect(struct RE_instance *inst, struct RE_renderer return; glUniform3fv(r->instance_ambient, 1, inst->ambient); - reset_draw_uniforms(r); + reset_draw_uniforms(r, inst->plugin); glDepthMask(GL_FALSE); @@ -953,6 +1039,8 @@ static void render_particle_effect(struct RE_instance *inst, struct RE_renderer static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat, enum draw_phase phase) { + glUniform1f(r->grayscale_rate, inst->grayscale_rate); + switch (inst->type) { case RE_ITYPE_STATIC: render_static_model(inst, r, phase); @@ -1086,7 +1174,8 @@ static void render_outlines(struct RE_plugin *plugin, mat4 view_transform) bool seal_edge = re_plugin_version >= RE_SEAL_PLUGIN; if (seal_edge) { glUniform3fv(or->outline_color, 1, plugin->edge_color); - glUniform1f(or->outline_thickness, plugin->edge_length); + float thickness = plugin->edge_length * (1.f - plugin->edge_reduction_rate); + glUniform1f(or->outline_thickness, thickness); } glCullFace(GL_FRONT); diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c index 48ca102..c0cd1db 100644 --- a/src/hll/ReignEngine.c +++ b/src/hll/ReignEngine.c @@ -2405,7 +2405,11 @@ static bool SealEngine_GetInstanceAngleB(int plugin, int instance, float *angle_ return true; } -//bool SealEngine_SetInstanceVertexUV(int PluginNumber, int InstanceNumber, int Index, float U, float V); +static bool SealEngine_SetInstanceVertexUV(int plugin, int instance, int index, float u, float v) +{ + return RE_instance_set_vertex_uv(get_instance(plugin, instance), index, u, v); +} + //bool SealEngine_GetInstanceDiffuse(int PluginNumber, int InstanceNumber, float *pR, float *pG, float *pB); //bool SealEngine_GetInstanceAmbient(int PluginNumber, int InstanceNumber, float *pR, float *pG, float *pB); @@ -2418,8 +2422,23 @@ static bool SealEngine_GetInstanceAlpha(int plugin, int instance, float *alpha) return true; } -//bool SealEngine_SetInstanceGrayscaleRate(int PluginNumber, int InstanceNumber, float GrayscaleRate); -//bool SealEngine_GetInstanceGrayscaleRate(int PluginNumber, int InstanceNumber, float *GrayscaleRate); +static bool SealEngine_SetInstanceGrayscaleRate(int plugin, int instance, float rate) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + ri->grayscale_rate = rate; + return true; +} + +static bool SealEngine_GetInstanceGrayscaleRate(int plugin, int instance, float *rate) +{ + struct RE_instance *ri = get_instance(plugin, instance); + if (!ri) + return false; + *rate = ri->grayscale_rate; + return true; +} static bool SealEngine_IsExistInstanceMotion(int plugin, int instance, struct string *motion_name) { @@ -2550,8 +2569,20 @@ static float SealEngine_GetEdgeLength(int PluginNumber) return p ? p->edge_length : 0.0; } -//bool SealEngine_SetEdgeReductionRate(int PluginNumber, float EdgeReductionRate); -//float SealEngine_GetEdgeReductionRate(int PluginNumber); +static bool SealEngine_SetEdgeReductionRate(int plugin, float rate) +{ + struct RE_plugin *p = RE_get_plugin(plugin); + if (!p) + return false; + p->edge_reduction_rate = rate; + return true; +} + +static float SealEngine_GetEdgeReductionRate(int plugin) +{ + struct RE_plugin *p = RE_get_plugin(plugin); + return p ? p->edge_reduction_rate : 0.0; +} static bool SealEngine_SetEdgeColor(int PluginNumber, float ColorR, float ColorG, float ColorB) { @@ -2624,12 +2655,12 @@ HLL_QUIET_UNIMPLEMENTED(false, bool, SealEngine, IsThreadLoadingMode, int Plugin HLL_EXPORT(GetInstanceAngle, SealEngine_GetInstanceAngle), \ HLL_EXPORT(GetInstanceAngleP, SealEngine_GetInstanceAngleP), \ HLL_EXPORT(GetInstanceAngleB, SealEngine_GetInstanceAngleB), \ - HLL_TODO_EXPORT(SetInstanceVertexUV, SealEngine_SetInstanceVertexUV), \ + HLL_EXPORT(SetInstanceVertexUV, SealEngine_SetInstanceVertexUV), \ HLL_TODO_EXPORT(GetInstanceDiffuse, SealEngine_GetInstanceDiffuse), \ HLL_TODO_EXPORT(GetInstanceAmbient, SealEngine_GetInstanceAmbient), \ HLL_EXPORT(GetInstanceAlpha, SealEngine_GetInstanceAlpha), \ - HLL_TODO_EXPORT(SetInstanceGrayscaleRate, SealEngine_SetInstanceGrayscaleRate), \ - HLL_TODO_EXPORT(GetInstanceGrayscaleRate, SealEngine_GetInstanceGrayscaleRate), \ + HLL_EXPORT(SetInstanceGrayscaleRate, SealEngine_SetInstanceGrayscaleRate), \ + HLL_EXPORT(GetInstanceGrayscaleRate, SealEngine_GetInstanceGrayscaleRate), \ HLL_EXPORT(IsExistInstanceMotion, SealEngine_IsExistInstanceMotion), \ HLL_TODO_EXPORT(GetInstanceNumofBone, SealEngine_GetInstanceNumofBone), \ HLL_TODO_EXPORT(GetInstanceBoneName, SealEngine_GetInstanceBoneName), \ @@ -2702,8 +2733,8 @@ HLL_QUIET_UNIMPLEMENTED(false, bool, SealEngine, IsThreadLoadingMode, int Plugin HLL_TODO_EXPORT(GetSoftFogEdgeLength, SealEngine_GetSoftFogEdgeLength), \ HLL_EXPORT(SetEdgeLength, SealEngine_SetEdgeLength), \ HLL_EXPORT(GetEdgeLength, SealEngine_GetEdgeLength), \ - HLL_TODO_EXPORT(SetEdgeReductionRate, SealEngine_SetEdgeReductionRate), \ - HLL_TODO_EXPORT(GetEdgeReductionRate, SealEngine_GetEdgeReductionRate), \ + HLL_EXPORT(SetEdgeReductionRate, SealEngine_SetEdgeReductionRate), \ + HLL_EXPORT(GetEdgeReductionRate, SealEngine_GetEdgeReductionRate), \ HLL_EXPORT(SetEdgeColor, SealEngine_SetEdgeColor), \ HLL_EXPORT(GetEdgeColor, SealEngine_GetEdgeColor), \ HLL_TODO_EXPORT(Calc2DDetectionHeight, SealEngine_Calc2DDetectionHeight), \