From a301ca13112e56a08471c759008a2b7b271b414e Mon Sep 17 00:00:00 2001 From: kichikuou Date: Thu, 31 Aug 2023 14:11:32 +0900 Subject: [PATCH 1/2] DrawGraph, Gpx2Plus: Implement CopyReverseUD() --- include/gfx/gfx.h | 1 + src/draw.c | 17 +++++++++++++++++ src/hll/DrawGraph.c | 7 +++++-- src/hll/Gpx2Plus.c | 11 +++++++++-- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/include/gfx/gfx.h b/include/gfx/gfx.h index 893bac1..e897d18 100644 --- a/include/gfx/gfx.h +++ b/include/gfx/gfx.h @@ -154,6 +154,7 @@ void gfx_copy_rot_zoom_amap(Texture *dst, Texture *src, int sx, int sy, int w, i void gfx_copy_rot_zoom_use_amap(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag); void gfx_copy_root_zoom2(Texture *dst, float cx, float cy, Texture *src, float scx, float scy, float rot, float mag); void gfx_copy_reverse_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h); +void gfx_copy_reverse_UD(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h); void gfx_copy_reverse_amap_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h); void gfx_copy_rotate_y(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag); void gfx_copy_rotate_y_use_amap(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag); diff --git a/src/draw.c b/src/draw.c index 165380e..ecfbf45 100644 --- a/src/draw.c +++ b/src/draw.c @@ -891,6 +891,23 @@ void gfx_copy_reverse_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int restore_blend_mode(); } +void gfx_copy_reverse_UD(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h) +{ + mat4 mw_transform = MAT4( + src->w, 0, 0, -sx, + 0, -src->h, 0, sy + h, + 0, 0, 1, 0, + 0, 0, 0, 1); + mat4 wv_transform = WV_TRANSFORM(w, h); + + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE); + + struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h); + run_draw_shader(©_shader.s, dst, src, mw_transform, wv_transform, &data); + + restore_blend_mode(); +} + void gfx_copy_reverse_amap_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h) { mat4 mw_transform = MAT4( diff --git a/src/hll/DrawGraph.c b/src/hll/DrawGraph.c index e4ba504..d8de2b5 100644 --- a/src/hll/DrawGraph.c +++ b/src/hll/DrawGraph.c @@ -338,7 +338,10 @@ static void DrawGraph_CopyReverseLR(int dst, int dx, int dy, int src, int sx, in gfx_copy_reverse_LR(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h); } -//void DrawGraph_CopyReverseUD(int dst, int dx, int dy, int src, int sx, int sy, int w, int h); +static void DrawGraph_CopyReverseUD(int dst, int dx, int dy, int src, int sx, int sy, int w, int h) +{ + gfx_copy_reverse_UD(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h); +} static void DrawGraph_CopyReverseAMapLR(int dst, int dx, int dy, int src, int sx, int sy, int w, int h) { @@ -495,7 +498,7 @@ HLL_LIBRARY(DrawGraph, //HLL_EXPORT(CopyRotateXFixUUseAMap, DrawGraph_CopyRotateXFixUUseAMap), //HLL_EXPORT(CopyRotateXFixDUseAMap, DrawGraph_CopyRotateXFixDUseAMap), HLL_EXPORT(CopyReverseLR, DrawGraph_CopyReverseLR), - //HLL_EXPORT(CopyReverseUD, DrawGraph_CopyReverseUD), + HLL_EXPORT(CopyReverseUD, DrawGraph_CopyReverseUD), HLL_EXPORT(CopyReverseAMapLR, DrawGraph_CopyReverseAMapLR), //HLL_EXPORT(CopyReverseAMapUD, DrawGraph_CopyReverseAMapUD), HLL_EXPORT(CopyWidthBlur, DrawGraph_CopyWidthBlur), diff --git a/src/hll/Gpx2Plus.c b/src/hll/Gpx2Plus.c index e59105b..2a3458c 100644 --- a/src/hll/Gpx2Plus.c +++ b/src/hll/Gpx2Plus.c @@ -372,7 +372,14 @@ static void Gpx2Plus_CopyReverseLR(int destSurface, int dx, int dy, int srcSurfa gfx_copy_reverse_LR(dst, dx, dy, src, sx, sy, width, height); } -// void CopyReverseUD(int nDestSurface, int nDx, int nDy, int nSrcSurface, int nSx, int nSy, int nWidth, int nHeight); +static void Gpx2Plus_CopyReverseUD(int destSurface, int dx, int dy, int srcSurface, int sx, int sy, int width, int height) +{ + struct texture *dst = get_texture(destSurface); + struct texture *src = get_texture(srcSurface); + if (!dst || !src) + return; + gfx_copy_reverse_UD(dst, dx, dy, src, sx, sy, width, height); +} static void Gpx2Plus_CopyReverseAMapLR(int destSurface, int dx, int dy, int srcSurface, int sx, int sy, int width, int height) { @@ -745,7 +752,7 @@ HLL_LIBRARY(Gpx2Plus, HLL_EXPORT(CopyStretchReduce, Gpx2Plus_CopyStretchReduce), HLL_EXPORT(CopyStretchReduceAMap, Gpx2Plus_CopyStretchReduceAMap), HLL_EXPORT(CopyReverseLR, Gpx2Plus_CopyReverseLR), - // HLL_EXPORT(CopyReverseUD, Gpx2Plus_CopyReverseUD), + HLL_EXPORT(CopyReverseUD, Gpx2Plus_CopyReverseUD), HLL_EXPORT(CopyReverseAMapLR, Gpx2Plus_CopyReverseAMapLR), // HLL_EXPORT(CopyReverseAMapUD, Gpx2Plus_CopyReverseAMapUD), // HLL_EXPORT(BlendScreenWDS, Gpx2Plus_BlendScreenWDS), From afae3fc9a65d4c58a77be9ea04ccd1d0a9315cf8 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Fri, 1 Sep 2023 21:39:02 +0900 Subject: [PATCH 2/2] DrawDungeon14: Implement raster effect It is implemented as a post-processing effect. The hack for the projection transform matrix has been removed, because now the image is flipped upside down in the post-processing stage. --- include/dungeon/dungeon.h | 3 +- include/dungeon/renderer.h | 4 ++ shaders/dungeon_raster.f.glsl | 39 +++++++++++++++++++ src/dungeon/dungeon.c | 23 ++++------- src/dungeon/renderer.c | 72 +++++++++++++++++++++++++++++++++++ src/hll/DrawDungeon.c | 26 ++++++++++--- 6 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 shaders/dungeon_raster.f.glsl diff --git a/include/dungeon/dungeon.h b/include/dungeon/dungeon.h index c3d4389..2b92c96 100644 --- a/include/dungeon/dungeon.h +++ b/include/dungeon/dungeon.h @@ -20,7 +20,7 @@ #include #include #include -#include "gfx/gl.h" +#include "gfx/gfx.h" #include "plugin.h" struct page; @@ -63,6 +63,7 @@ struct dungeon_context { struct dtx *dtx; struct tes *tes; struct dungeon_renderer *renderer; + struct texture texture; GLuint depth_buffer; }; diff --git a/include/dungeon/renderer.h b/include/dungeon/renderer.h index 8970020..21377c3 100644 --- a/include/dungeon/renderer.h +++ b/include/dungeon/renderer.h @@ -20,6 +20,7 @@ #include #include "gfx/gl.h" +struct texture; struct dgn_cell; struct dtx; struct polyobj; @@ -31,5 +32,8 @@ void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells void dungeon_renderer_enable_event_markers(struct dungeon_renderer *r, bool enable); bool dungeon_renderer_event_markers_enabled(struct dungeon_renderer *r); bool dungeon_renderer_is_floor_opaque(struct dungeon_renderer *r, struct dgn_cell *cell); +void dungeon_renderer_run_post_processing(struct dungeon_renderer *r, struct texture *src, struct texture *dst); +void dungeon_renderer_set_raster_scroll(struct dungeon_renderer *r, int type); +void dungeon_renderer_set_raster_amp(struct dungeon_renderer *r, float amp); #endif /* SYSTEM4_DUNGEON_RENDERER_H */ diff --git a/shaders/dungeon_raster.f.glsl b/shaders/dungeon_raster.f.glsl new file mode 100644 index 0000000..2588eea --- /dev/null +++ b/shaders/dungeon_raster.f.glsl @@ -0,0 +1,39 @@ +/* Copyright (C) 2023 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 sampler2D tex; + +uniform float amp; +uniform float t; + +const float PI = 3.14159265; + +in vec2 tex_coord; +out vec4 frag_color; + +vec4 sample(float offset_x) { + return texture(tex, vec2(tex_coord.x + offset_x, tex_coord.y)); +} + +void main() { + float a = amp * cos((tex_coord.y + fract(t / 8.0)) * 16.0 * PI); + float p1 = 6.0 * tex_coord.y + 1.5; + float p2 = 4.0 * tex_coord.y + 2.0; + float offset1 = cos(p1 * 2.0 * PI) * a; + float offset2 = cos(p2 * 2.0 * PI) * a; + + frag_color = mix(sample(offset1), sample(offset2), 0.5); +} diff --git a/src/dungeon/dungeon.c b/src/dungeon/dungeon.c index 2652c6a..b9124f3 100644 --- a/src/dungeon/dungeon.c +++ b/src/dungeon/dungeon.c @@ -62,13 +62,12 @@ struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version if (!sp) VM_ERROR("DrawDungeon.Init: invalid surface %d", surface); - // Dungeon scene will be rendered to this texture. - struct texture *texture = sprite_get_texture(sp); sprite_bind_plugin(sp, &ctx->plugin); + gfx_init_texture_blank(&ctx->texture, sp->rect.w, sp->rect.h); glGenRenderbuffers(1, &ctx->depth_buffer); glBindRenderbuffer(GL_RENDERBUFFER, ctx->depth_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, sp->rect.w, sp->rect.h); glBindRenderbuffer(GL_RENDERBUFFER, 0); ctx->map = dungeon_map_create(version); @@ -91,6 +90,7 @@ void dungeon_context_free(struct dungeon_context *ctx) if (ctx->renderer) dungeon_renderer_free(ctx->renderer); + gfx_delete_texture(&ctx->texture); glDeleteRenderbuffers(1, &ctx->depth_buffer); if (ctx->map) dungeon_map_free(ctx->map); @@ -265,10 +265,8 @@ static void dungeon_render(struct sact_sprite *sp) struct dungeon_context *ctx = (struct dungeon_context *)sp->plugin; if (!ctx->loaded || !ctx->draw_enabled) return; - sprite_dirty(sp); - struct texture *texture = sprite_get_texture(sp); - GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, texture, 0, 0, texture->w, texture->h); + GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, &ctx->texture, 0, 0, ctx->texture.w, ctx->texture.h); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ctx->depth_buffer); if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) ERROR("Incomplete framebuffer"); @@ -281,14 +279,7 @@ static void dungeon_render(struct sact_sprite *sp) mat4 local_transform, view_transform, proj_transform; glm_mat4_identity(local_transform); model_view_matrix(&ctx->camera, eye, view_transform); - glm_perspective(M_PI / 3.0, (float)texture->w / texture->h, 0.5, 100.0, proj_transform); - - // Tweak the projection transform so that the rendering result is vertically - // flipped. If we render the scene normally, the resulting image will be - // bottom-up (the first pixel is at the bottom-left), but we want a top-down - // image (the first pixel is at the top-left). - proj_transform[1][1] *= -1; - glFrontFace(GL_CW); + glm_perspective(M_PI / 3.0, (float)ctx->texture.w / ctx->texture.h, 0.5, 100.0, proj_transform); int dgn_x = round(eye[0] / 2.0); int dgn_y = round(eye[1] / 2.0); @@ -297,11 +288,13 @@ static void dungeon_render(struct sact_sprite *sp) struct dgn_cell **cells = dgn_get_visible_cells(ctx->dgn, dgn_x, dgn_y, dgn_z, &nr_cells); dungeon_renderer_render(ctx->renderer, cells, nr_cells, view_transform, proj_transform); - glFrontFace(GL_CCW); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo); + + dungeon_renderer_run_post_processing(ctx->renderer, &ctx->texture, sprite_get_texture(sp)); + sprite_dirty(sp); } static void neighbor_reveal(struct dungeon_context *ctx, int x, int y, int z) diff --git a/src/dungeon/renderer.c b/src/dungeon/renderer.c index 5a33b3a..233d841 100644 --- a/src/dungeon/renderer.c +++ b/src/dungeon/renderer.c @@ -77,6 +77,12 @@ struct geometry; struct material; struct object3d; +struct raster_shader { + struct shader s; + GLint amp; + GLint t; +}; + struct dungeon_renderer { struct shader shader; // Uniform variable locations @@ -108,6 +114,10 @@ struct dungeon_renderer { struct material *polyobj_materials; struct skybox *skybox; + + struct raster_shader raster_shader; + bool raster_scroll; + float raster_amp; }; static const struct marker_info *get_marker_info(struct dungeon_renderer *r, int event_type) @@ -402,6 +412,8 @@ void dungeon_renderer_free(struct dungeon_renderer *r) free(r->polyobj_materials); skybox_free(r->skybox); + if (r->raster_shader.s.program) + glDeleteProgram(r->raster_shader.s.program); free(r); } @@ -719,3 +731,63 @@ bool dungeon_renderer_is_floor_opaque(struct dungeon_renderer *r, struct dgn_cel struct material *material = get_material(r, DTX_FLOOR, cell->floor); return material && material->opaque; } + +static void prepare_raster_shader(struct gfx_render_job *job, void *data) +{ + struct dungeon_renderer *r = data; + float width = job->world_transform[0]; + glUniform1f(r->raster_shader.amp, r->raster_amp / width); + glUniform1f(r->raster_shader.t, SDL_GetTicks() / 1000.f); +} + +static void load_raster_shader(struct raster_shader *s) +{ + gfx_load_shader(&s->s, "shaders/render.v.glsl", "shaders/dungeon_raster.f.glsl"); + s->amp = glGetUniformLocation(s->s.program, "amp"); + s->t = glGetUniformLocation(s->s.program, "t"); + s->s.prepare = prepare_raster_shader; +} + +void dungeon_renderer_run_post_processing(struct dungeon_renderer *r, struct texture *src, struct texture *dst) +{ + // This function flips the image upside down, because the image in `src` + // (rendered by dungeon_renderer_render) is bottom-up (the first pixel is at + // the bottom-left), but sprite textures are top-down (the first pixel is at + // the top-left). + + if (r->raster_scroll) { + mat4 mw_transform = MAT4( + src->w, 0, 0, 0, + 0, -src->h, 0, src->h, // flip vertically + 0, 0, 1, 0, + 0, 0, 0, 1); + mat4 wv_transform = WV_TRANSFORM(src->w, src->h); + + struct gfx_render_job job = { + .shader = &r->raster_shader.s, + .shape = GFX_RECTANGLE, + .texture = src->handle, + .world_transform = mw_transform[0], + .view_transform = wv_transform[0], + .data = r + }; + GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0, dst->w, dst->h); + gfx_render(&job); + gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo); + } else { + gfx_copy_reverse_UD(dst, 0, 0, src, 0, 0, src->w, src->h); + } +} + +void dungeon_renderer_set_raster_scroll(struct dungeon_renderer *r, int type) +{ + r->raster_scroll = !!type; + if (r->raster_scroll && !r->raster_shader.s.program) { + load_raster_shader(&r->raster_shader); + } +} + +void dungeon_renderer_set_raster_amp(struct dungeon_renderer *r, float amp) +{ + r->raster_amp = amp; +} diff --git a/src/hll/DrawDungeon.c b/src/hll/DrawDungeon.c index db6a27f..20c3a4c 100644 --- a/src/hll/DrawDungeon.c +++ b/src/hll/DrawDungeon.c @@ -267,7 +267,8 @@ HLL_QUIET_UNIMPLEMENTED(1, bool, DrawDungeon, IsDrawSettingFile, void); HLL_QUIET_UNIMPLEMENTED( , void, DrawDungeon, DeleteDrawSettingFile, void); HLL_QUIET_UNIMPLEMENTED(1, bool, DrawDungeon, IsSSEFlag, void); HLL_QUIET_UNIMPLEMENTED( , void, DrawDungeon, SetSSEFlag, bool flag); -bool DrawDungeon_IsPVSData(int surface) + +static bool DrawDungeon_IsPVSData(int surface) { return false; } @@ -408,11 +409,26 @@ HLL_LIBRARY(DrawDungeon, DRAW_DUNGEON_EXPORTS ); -HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon14, SetRasterScroll, int surface, int type); -HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon14, SetRasterAmp, int nSurface, float fAmp); +static void DrawDungeon14_SetRasterScroll(int surface, int type) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->renderer) + return true; + return dungeon_renderer_set_raster_scroll(ctx->renderer, type); +} + +static void DrawDungeon14_SetRasterAmp(int surface, float amp) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->renderer) + return true; + return dungeon_renderer_set_raster_amp(ctx->renderer, amp); +} + +// unused HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon14, SetLapFilter, int surface, int type, int r, int g, int b); -bool DrawDungeon14_GetDrawMapObjectFlag(int surface) +static bool DrawDungeon14_GetDrawMapObjectFlag(int surface) { struct dungeon_context *ctx = dungeon_get_context(surface); if (!ctx || !ctx->renderer) @@ -420,7 +436,7 @@ bool DrawDungeon14_GetDrawMapObjectFlag(int surface) return dungeon_renderer_event_markers_enabled(ctx->renderer); } -void DrawDungeon14_SetDrawMapObjectFlag(int surface, bool flag) +static void DrawDungeon14_SetDrawMapObjectFlag(int surface, bool flag) { struct dungeon_context *ctx = dungeon_get_context(surface); if (!ctx || !ctx->renderer)