mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-30 19:08:12 +03:00
Merge pull request #201 from kichikuou/outline
TapirEngine: Outline rendering
This commit is contained in:
+7
-1
@@ -63,10 +63,16 @@ enum RE_draw_type {
|
||||
};
|
||||
|
||||
enum RE_draw_options {
|
||||
RE_DRAW_OPTION_UNKNOWN,
|
||||
RE_DRAW_OPTION_EDGE = 0,
|
||||
RE_DRAW_OPTION_MAX
|
||||
};
|
||||
|
||||
enum RE_draw_edge_mode {
|
||||
RE_DRAW_EDGE_NONE = 0,
|
||||
RE_DRAW_EDGE_CHARACTERS_ONLY = 1,
|
||||
RE_DRAW_EDGE_ALL = 2,
|
||||
};
|
||||
|
||||
struct RE_options {
|
||||
int anti_aliasing;
|
||||
int wait_vsync;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Copyright (C) 2024 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* 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 <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform vec3 outline_color;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = vec4(outline_color, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Copyright (C) 2024 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* 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 <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform mat3 normal_transform;
|
||||
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
layout(std140) uniform BoneTransforms {
|
||||
mat4 bone_matrices[MAX_BONES];
|
||||
};
|
||||
uniform float outline_thickness;
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in vec3 vertex_normal;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
void main() {
|
||||
mat4 local_bone_transform = local_transform;
|
||||
mat3 normal_bone_transform = normal_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];
|
||||
}
|
||||
}
|
||||
local_bone_transform *= bone_transform;
|
||||
normal_bone_transform *= mat3(bone_transform);
|
||||
}
|
||||
|
||||
vec4 pos = local_bone_transform * vec4(vertex_pos, 1.0);
|
||||
vec3 normal = normalize(normal_bone_transform * vertex_normal);
|
||||
|
||||
gl_Position = proj_transform * view_transform * (pos + vec4(normal, 0.0) * outline_thickness);
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 world_transform;
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
@@ -29,7 +29,7 @@ in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
void main() {
|
||||
mat4 world_bone_transform = world_transform;
|
||||
mat4 local_bone_transform = local_transform;
|
||||
if (has_bones) {
|
||||
mat4 bone_transform = mat4(0.f);
|
||||
for (int i = 0; i < NR_WEIGHTS; i++) {
|
||||
@@ -37,8 +37,8 @@ void main() {
|
||||
bone_transform += bone_matrices[vertex_bone_index[i]] * vertex_bone_weight[i];
|
||||
}
|
||||
}
|
||||
world_bone_transform *= bone_transform;
|
||||
local_bone_transform *= bone_transform;
|
||||
}
|
||||
|
||||
gl_Position = view_transform * world_bone_transform * vec4(vertex_pos, 1.0);
|
||||
gl_Position = view_transform * local_bone_transform * vec4(vertex_pos, 1.0);
|
||||
}
|
||||
|
||||
+33
-8
@@ -57,6 +57,9 @@ struct mesh {
|
||||
int nr_vertices;
|
||||
int nr_indices;
|
||||
int material;
|
||||
vec3 outline_color;
|
||||
float outline_thickness;
|
||||
vec2 uv_scroll;
|
||||
};
|
||||
|
||||
struct material {
|
||||
@@ -108,18 +111,31 @@ struct shadow_renderer {
|
||||
GLuint texture;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint world_transform;
|
||||
GLint local_transform;
|
||||
GLint view_transform;
|
||||
GLint has_bones;
|
||||
};
|
||||
|
||||
struct outline_renderer {
|
||||
GLuint program;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint local_transform;
|
||||
GLint view_transform;
|
||||
GLint proj_transform;
|
||||
GLint normal_transform;
|
||||
GLint has_bones;
|
||||
GLint outline_color;
|
||||
GLint outline_thickness;
|
||||
};
|
||||
|
||||
struct RE_renderer {
|
||||
GLuint program;
|
||||
GLuint depth_buffer;
|
||||
struct shadow_renderer shadow;
|
||||
struct outline_renderer outline;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint world_transform;
|
||||
GLint view_transform;
|
||||
GLint texture;
|
||||
GLint local_transform;
|
||||
@@ -190,7 +206,7 @@ struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, cons
|
||||
|
||||
// renderer.c
|
||||
|
||||
struct RE_renderer *RE_renderer_new(void);
|
||||
struct RE_renderer *RE_renderer_new(enum RE_plugin_version version);
|
||||
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);
|
||||
@@ -407,11 +423,14 @@ struct pol_material_group {
|
||||
};
|
||||
|
||||
enum mesh_flags {
|
||||
MESH_NOLIGHTING = 1 << 0,
|
||||
MESH_NOMAKESHADOW = 1 << 1,
|
||||
MESH_ENVMAP = 1 << 2,
|
||||
MESH_BOTH = 1 << 3,
|
||||
MESH_SPRITE = 1 << 4,
|
||||
MESH_NOLIGHTING = 1 << 0,
|
||||
MESH_NOMAKESHADOW = 1 << 1,
|
||||
MESH_ENVMAP = 1 << 2,
|
||||
MESH_BOTH = 1 << 3,
|
||||
MESH_SPRITE = 1 << 4,
|
||||
MESH_BLEND_ADDITIVE = 1 << 5,
|
||||
MESH_NO_EDGE = 1 << 6,
|
||||
MESH_NO_HEIGHT_DETECTION = 1 << 7,
|
||||
};
|
||||
|
||||
struct pol_mesh {
|
||||
@@ -428,6 +447,10 @@ struct pol_mesh {
|
||||
vec3 *colors;
|
||||
uint32_t nr_triangles;
|
||||
struct pol_triangle *triangles;
|
||||
// Parameters in .opr file.
|
||||
Color edge_color;
|
||||
float edge_size;
|
||||
vec2 uv_scroll;
|
||||
};
|
||||
|
||||
struct pol_vertex {
|
||||
@@ -512,6 +535,8 @@ struct amt *amt_parse(uint8_t *data, size_t size);
|
||||
void amt_free(struct amt *amt);
|
||||
struct amt_material *amt_find_material(struct amt *amt, const char *name);
|
||||
|
||||
void opr_load(uint8_t *data, size_t size, struct pol *pol);
|
||||
|
||||
// collision.c
|
||||
|
||||
struct collider {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#define FP16_MIN 6.103516e-5f
|
||||
#define NR_WEIGHTS 4
|
||||
#define DEFAULT_OUTLINE_THICKNESS 0.003
|
||||
|
||||
struct vertex_common {
|
||||
GLfloat pos[3];
|
||||
@@ -298,6 +299,11 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
struct mesh *mesh = &model->meshes[model->nr_meshes++];
|
||||
mesh->flags = m->flags;
|
||||
mesh->material = material;
|
||||
mesh->outline_color[0] = m->edge_color.r / 255.f;
|
||||
mesh->outline_color[1] = m->edge_color.g / 255.f;
|
||||
mesh->outline_color[2] = m->edge_color.b / 255.f;
|
||||
mesh->outline_thickness = m->edge_size ? m->edge_size : DEFAULT_OUTLINE_THICKNESS;
|
||||
glm_vec2_copy(m->uv_scroll, mesh->uv_scroll);
|
||||
mesh->nr_vertices = nr_vertices;
|
||||
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
@@ -440,6 +446,13 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
struct model *model = xcalloc(1, sizeof(struct model));
|
||||
model->path = strdup(path);
|
||||
|
||||
// Load .opr file, if any
|
||||
struct archive_data *opr_file = RE_get_aar_entry(aar, path, basename, ".opr");
|
||||
if (opr_file) {
|
||||
opr_load(opr_file->data, opr_file->size, pol);
|
||||
archive_free_data(opr_file);
|
||||
}
|
||||
|
||||
// Bones
|
||||
if (pol->nr_bones > 0) {
|
||||
if (pol->nr_bones > MAX_BONES)
|
||||
|
||||
@@ -473,3 +473,64 @@ struct amt_material *amt_find_material(struct amt *amt, const char *name)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void opr_load(uint8_t *data, size_t size, struct pol *pol)
|
||||
{
|
||||
struct pol_mesh *current_mesh = NULL;
|
||||
while (size > 0) {
|
||||
char line[200];
|
||||
uint8_t *nl = memchr(data, '\n', size);
|
||||
if (nl)
|
||||
nl++;
|
||||
else
|
||||
nl = data + size;
|
||||
int copylen = min(nl - data, sizeof(line) - 1);
|
||||
memcpy(line, (char *)data, copylen);
|
||||
line[copylen] = '\0';
|
||||
size -= nl - data;
|
||||
data = nl;
|
||||
|
||||
char s[200];
|
||||
if (sscanf(line, "Mesh = \"%[^\"]\"", s) == 1 ||
|
||||
sscanf(line, "MeshPart = \"%[^\"]\"", s) == 1) {
|
||||
current_mesh = NULL;
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (pol->meshes[i] && !strcmp(pol->meshes[i]->name, s)) {
|
||||
current_mesh = pol->meshes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!current_mesh)
|
||||
continue;
|
||||
|
||||
int i1, i2, i3;
|
||||
float f1, f2;
|
||||
if (sscanf(line, "BlendMode = %s", s) == 1) {
|
||||
if (!strcmp(s, "Add"))
|
||||
current_mesh->flags |= MESH_BLEND_ADDITIVE;
|
||||
else
|
||||
WARNING("unknown BlendMode: %s", s);
|
||||
} else if (sscanf(line, "Edge = %d", &i1) == 1) {
|
||||
if (i1 == 0)
|
||||
current_mesh->flags |= MESH_NO_EDGE;
|
||||
else
|
||||
WARNING("invalid Edge value: %d", i1);
|
||||
} else if (sscanf(line, "EdgeColor = ( %d , %d , %d )", &i1, &i2, &i3) == 3) {
|
||||
current_mesh->edge_color = COLOR(i1, i2, i3, 255);
|
||||
} else if (sscanf(line, "EdgeSize = %f", &f1) == 1) {
|
||||
current_mesh->edge_size = f1;
|
||||
} else if (sscanf(line, "HeightDetection = %s", s) == 1) {
|
||||
if (!strcmp(s, "false"))
|
||||
current_mesh->flags |= MESH_NO_HEIGHT_DETECTION;
|
||||
else
|
||||
WARNING("invalid HeightDetection: %s", s);
|
||||
} else if (sscanf(line, "UVScroll = ( %f , %f )", &f1, &f2) == 2) {
|
||||
current_mesh->uv_scroll[0] = f1;
|
||||
current_mesh->uv_scroll[1] = f2;
|
||||
} else if (strchr(line, '=')) {
|
||||
WARNING("unknown field: %s", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -260,7 +260,7 @@ bool RE_plugin_bind(struct RE_plugin *plugin, int sprite)
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sprite);
|
||||
if (!sp)
|
||||
return false;
|
||||
plugin->renderer = RE_renderer_new();
|
||||
plugin->renderer = RE_renderer_new(plugin->version);
|
||||
plugin->sprite = sprite;
|
||||
sprite_bind_plugin(sp, &plugin->plugin);
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
|
||||
+77
-5
@@ -99,7 +99,7 @@ static void init_shadow_renderer(struct shadow_renderer *sr)
|
||||
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->local_transform = glGetUniformLocation(sr->program, "local_transform");
|
||||
sr->view_transform = glGetUniformLocation(sr->program, "view_transform");
|
||||
sr->has_bones = glGetUniformLocation(sr->program, "has_bones");
|
||||
GLuint bone_transforms = glGetUniformBlockIndex(sr->program, "BoneTransforms");
|
||||
@@ -128,6 +128,30 @@ static void destroy_shadow_renderer(struct shadow_renderer *sr)
|
||||
glDeleteProgram(sr->program);
|
||||
}
|
||||
|
||||
static void init_outline_renderer(struct outline_renderer *or, enum RE_plugin_version version)
|
||||
{
|
||||
if (version < RE_TAPIR_PLUGIN) {
|
||||
or->program = 0;
|
||||
return;
|
||||
}
|
||||
or->program = load_shader("shaders/reign_outline.v.glsl", "shaders/reign_outline.f.glsl");
|
||||
or->local_transform = glGetUniformLocation(or->program, "local_transform");
|
||||
or->view_transform = glGetUniformLocation(or->program, "view_transform");
|
||||
or->proj_transform = glGetUniformLocation(or->program, "proj_transform");
|
||||
or->normal_transform = glGetUniformLocation(or->program, "normal_transform");
|
||||
or->has_bones = glGetUniformLocation(or->program, "has_bones");
|
||||
GLuint bone_transforms = glGetUniformBlockIndex(or->program, "BoneTransforms");
|
||||
glUniformBlockBinding(or->program, bone_transforms, BONE_TRANSFORMS_BINDING);
|
||||
or->outline_color = glGetUniformLocation(or->program, "outline_color");
|
||||
or->outline_thickness = glGetUniformLocation(or->program, "outline_thickness");
|
||||
}
|
||||
|
||||
static void destroy_outline_renderer(struct outline_renderer *or)
|
||||
{
|
||||
if (or->program)
|
||||
glDeleteProgram(or->program);
|
||||
}
|
||||
|
||||
static void init_billboard_mesh(struct RE_renderer *r)
|
||||
{
|
||||
static const GLfloat vertices[] = {
|
||||
@@ -171,12 +195,11 @@ static void destroy_billboard_mesh(struct RE_renderer *r)
|
||||
glDeleteBuffers(1, &r->billboard_attr_buffer);
|
||||
}
|
||||
|
||||
struct RE_renderer *RE_renderer_new(void)
|
||||
struct RE_renderer *RE_renderer_new(enum RE_plugin_version version)
|
||||
{
|
||||
struct RE_renderer *r = xcalloc(1, sizeof(struct RE_renderer));
|
||||
|
||||
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");
|
||||
@@ -226,6 +249,7 @@ struct RE_renderer *RE_renderer_new(void)
|
||||
glGenRenderbuffers(1, &r->depth_buffer);
|
||||
|
||||
init_shadow_renderer(&r->shadow);
|
||||
init_outline_renderer(&r->outline, version);
|
||||
init_billboard_mesh(r);
|
||||
r->billboard_textures = ht_create(256);
|
||||
r->last_frame_timestamp = SDL_GetTicks();
|
||||
@@ -280,6 +304,7 @@ void RE_renderer_free(struct RE_renderer *r)
|
||||
ht_free_int(r->billboard_textures);
|
||||
destroy_billboard_mesh(r);
|
||||
destroy_shadow_renderer(&r->shadow);
|
||||
destroy_outline_renderer(&r->outline);
|
||||
free(r);
|
||||
}
|
||||
|
||||
@@ -729,7 +754,7 @@ static void render_shadow_map(struct RE_plugin *plugin, mat4 light_space_transfo
|
||||
} else {
|
||||
glUniform1i(r->shadow.has_bones, GL_FALSE);
|
||||
}
|
||||
glUniformMatrix4fv(r->shadow.world_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
glUniformMatrix4fv(r->shadow.local_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
for (int j = 0; j < model->nr_meshes; j++) {
|
||||
struct mesh *mesh = &model->meshes[j];
|
||||
if (mesh->flags & MESH_NOMAKESHADOW)
|
||||
@@ -745,6 +770,49 @@ static void render_shadow_map(struct RE_plugin *plugin, mat4 light_space_transfo
|
||||
glViewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]);
|
||||
}
|
||||
|
||||
static void render_outlines(struct RE_plugin *plugin, mat4 view_transform)
|
||||
{
|
||||
enum RE_draw_edge_mode mode = plugin->draw_options[RE_DRAW_OPTION_EDGE];
|
||||
if (mode == RE_DRAW_EDGE_NONE)
|
||||
return;
|
||||
struct outline_renderer *or = &plugin->renderer->outline;
|
||||
if (!or->program)
|
||||
return;
|
||||
glUseProgram(or->program);
|
||||
glUniformMatrix4fv(or->view_transform, 1, GL_FALSE, view_transform[0]);
|
||||
glUniformMatrix4fv(or->proj_transform, 1, GL_FALSE, plugin->proj_transform[0]);
|
||||
|
||||
glCullFace(GL_FRONT);
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (!inst || !inst->draw)
|
||||
continue;
|
||||
if (mode == RE_DRAW_EDGE_CHARACTERS_ONLY && inst->type != RE_ITYPE_SKINNED)
|
||||
continue;
|
||||
struct model *model = inst->model;
|
||||
if (inst->model && model->nr_bones > 0) {
|
||||
glUniform1i(or->has_bones, GL_TRUE);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, BONE_TRANSFORMS_BINDING, inst->bone_transforms_ubo);
|
||||
} else {
|
||||
glUniform1i(or->has_bones, GL_FALSE);
|
||||
}
|
||||
glUniformMatrix4fv(or->local_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
glUniformMatrix3fv(or->normal_transform, 1, GL_FALSE, inst->normal_transform[0]);
|
||||
for (int j = 0; j < model->nr_meshes; j++) {
|
||||
struct mesh *mesh = &model->meshes[j];
|
||||
if (mesh->flags & MESH_NO_EDGE)
|
||||
continue;
|
||||
glUniform3fv(or->outline_color, 1, mesh->outline_color);
|
||||
glUniform1f(or->outline_thickness, mesh->outline_thickness);
|
||||
glBindVertexArray(mesh->vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
glCullFace(GL_BACK);
|
||||
glUseProgram(plugin->renderer->program);
|
||||
}
|
||||
|
||||
static void render_back_cg(struct texture *dst, struct RE_back_cg *bcg, struct RE_renderer *r)
|
||||
{
|
||||
if (!bcg->show || !bcg->texture.handle)
|
||||
@@ -912,6 +980,10 @@ void RE_render(struct sact_sprite *sp)
|
||||
continue;
|
||||
render_instance(inst, r, view_transform, DRAW_OPAQUE);
|
||||
}
|
||||
|
||||
// Render outlines.
|
||||
render_outlines(plugin, view_transform);
|
||||
|
||||
// Render transparent instances, from farthest to nearest.
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = sorted_instances[i];
|
||||
@@ -975,7 +1047,7 @@ struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r
|
||||
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
glUseProgram(r->shadow.program);
|
||||
glUniformMatrix4fv(r->shadow.world_transform, 1, GL_FALSE, view_matrix[0]);
|
||||
glUniformMatrix4fv(r->shadow.local_transform, 1, GL_FALSE, view_matrix[0]);
|
||||
glUniformMatrix4fv(r->shadow.view_transform, 1, GL_FALSE, proj_matrix[0]);
|
||||
glUniform1i(r->shadow.has_bones, GL_FALSE);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user