From dc8a9379d0a4c21f0586a939eba564e609ee94fd Mon Sep 17 00:00:00 2001 From: kichikuou Date: Thu, 31 Oct 2024 13:09:20 +0900 Subject: [PATCH 1/3] Implement DrawSnow HLL Used in Tsuma Shibori, and some indie System 4 games. This HLL is a draw plugin that uses SACT2's "custom sprite" mechanism. Custom sprites do not have a texture. Instead, the plugin's render() callback draws them directly to the main surface. --- CMakeLists.txt | 1 + include/gfx/gfx.h | 1 + include/plugin.h | 1 + include/sact.h | 1 + include/sprite.h | 1 + src/draw.c | 10 ++ src/ffi.c | 2 + src/hll/ChipmunkSpriteEngine.c | 2 +- src/hll/DrawSnow.c | 227 +++++++++++++++++++++++++++++++++ src/hll/SACT2.c | 10 +- src/hll/StoatSpriteEngine.c | 2 +- src/meson.build | 1 + src/sprite.c | 13 ++ 13 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 src/hll/DrawSnow.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c98aa4..0f50765 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,7 @@ target_sources(xsystem4 PRIVATE src/hll/DrawMovie.c src/hll/DrawPluginManager.c src/hll/DrawSimpleText.c + src/hll/DrawSnow.c src/hll/File.c src/hll/File2.c src/hll/FileOperation.c diff --git a/include/gfx/gfx.h b/include/gfx/gfx.h index 71b40be..332069b 100644 --- a/include/gfx/gfx.h +++ b/include/gfx/gfx.h @@ -151,6 +151,7 @@ void gfx_bright_dest_only(Texture *dst, int x, int y, int w, int h, int rate); void gfx_copy_stretch(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh); void gfx_copy_stretch_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh); void gfx_copy_stretch_blend(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh, int a); +void gfx_copy_stretch_blend_screen(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh); void gfx_copy_stretch_blend_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh); void gfx_copy_stretch_blend_amap_alpha(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh, int a); void gfx_copy_rot_zoom(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag); diff --git a/include/plugin.h b/include/plugin.h index 310f353..26dcecf 100644 --- a/include/plugin.h +++ b/include/plugin.h @@ -25,6 +25,7 @@ struct sact_sprite; struct draw_plugin { const char *name; void (*update)(struct sact_sprite *); + void (*render)(struct sact_sprite *); // for custom sprites cJSON *(*to_json)(struct sact_sprite *, bool); }; diff --git a/include/sact.h b/include/sact.h index 9756e80..2bc8b42 100644 --- a/include/sact.h +++ b/include/sact.h @@ -42,6 +42,7 @@ int sact_SP_SetCGFromFile(int sp, struct string *filename); int sact_SP_SaveCG(int sp, struct string *filename); int sact_SP_Create(int sp, int width, int height, int r, int g, int b, int a); int sact_SP_CreatePixelOnly(int sp, int width, int height); +int sact_SP_CreateCustom(int sp); int sact_SP_Delete(int sp); int sact_SP_DeleteAll(void); int sact_SP_SetPos(int sp_no, int x, int y); diff --git a/include/sprite.h b/include/sprite.h index efe4236..20cfa39 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -91,6 +91,7 @@ int sprite_set_cg_from_file(struct sact_sprite *sp, const char *path); int sprite_save_cg(struct sact_sprite *sp, const char *path); void sprite_init(struct sact_sprite *sp); void sprite_init_color(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a); +void sprite_init_custom(struct sact_sprite *sp); void sprite_set_pos(struct sact_sprite *sp, int x, int y); void sprite_set_x(struct sact_sprite *sp, int x); void sprite_set_y(struct sact_sprite *sp, int y); diff --git a/src/draw.c b/src/draw.c index 79f3624..48c8b4e 100644 --- a/src/draw.c +++ b/src/draw.c @@ -707,6 +707,16 @@ void gfx_copy_stretch_blend(struct texture *dst, int dx, int dy, int dw, int dh, restore_blend_mode(); } +void gfx_copy_stretch_blend_screen(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh) +{ + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE); + + struct copy_data data = STRETCH_DATA(dx, dy, dw, dh, sx, sy, sw, sh); + run_copy_shader(©_shader.s, dst, src, &data); + + restore_blend_mode(); +} + void gfx_copy_stretch_blend_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh) { glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/ffi.c b/src/ffi.c index b185c4d..a813d52 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -427,6 +427,7 @@ extern struct static_library lib_DrawMovie2; extern struct static_library lib_DrawMovie3; extern struct static_library lib_DrawPluginManager; extern struct static_library lib_DrawSimpleText; +extern struct static_library lib_DrawSnow; extern struct static_library lib_File; extern struct static_library lib_File2; extern struct static_library lib_FileOperation; @@ -530,6 +531,7 @@ static struct static_library *static_libraries[] = { &lib_DrawMovie3, &lib_DrawPluginManager, &lib_DrawSimpleText, + &lib_DrawSnow, &lib_File, &lib_File2, &lib_FileOperation, diff --git a/src/hll/ChipmunkSpriteEngine.c b/src/hll/ChipmunkSpriteEngine.c index b49535c..82ede6b 100644 --- a/src/hll/ChipmunkSpriteEngine.c +++ b/src/hll/ChipmunkSpriteEngine.c @@ -223,7 +223,7 @@ HLL_LIBRARY(ChipmunkSpriteEngine, HLL_EXPORT(SP_SaveCG, sact_SP_SaveCG), HLL_EXPORT(SP_Create, sact_SP_Create), HLL_EXPORT(SP_CreatePixelOnly, sact_SP_CreatePixelOnly), - HLL_TODO_EXPORT(SP_CreateCustom, SACT2_SP_CreateCustom), + HLL_EXPORT(SP_CreateCustom, sact_SP_CreateCustom), HLL_EXPORT(SP_Delete, sact_SP_Delete), HLL_EXPORT(SP_DeleteAll, sact_SP_DeleteAll), HLL_EXPORT(SP_SetPos, sact_SP_SetPos), diff --git a/src/hll/DrawSnow.c b/src/hll/DrawSnow.c new file mode 100644 index 0000000..7ae3f17 --- /dev/null +++ b/src/hll/DrawSnow.c @@ -0,0 +1,227 @@ +/* Copyright (C) 2024 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 . + */ + +#include +#include +#include +#include + +#include "cJSON.h" +#include "gfx/gfx.h" +#include "hll.h" +#include "json.h" +#include "plugin.h" +#include "sact.h" +#include "sprite.h" +#include "xsystem4.h" + +static const char plugin_name[] = "DrawSnow"; + +enum draw_snow_type { + DRAW_SNOW_SCREEN_BLEND = 0, + DRAW_SNOW_ALPHA_BLEND = 1, +}; + +struct snowflake { + int start_x; + int start_y; + int end_x; + int end_y; + int total_time; + float scale; +}; + +struct draw_snow_plugin { + struct draw_plugin p; + enum draw_snow_type type; + int snow_sprite; + int max_time; + int max_amp; + int nr_particles; + struct snowflake *particles; + uint32_t timestamp; +}; + +static inline float frand(void) +{ + return ((float)rand() / RAND_MAX); +} + +static struct draw_snow_plugin *get_draw_snow_plugin(int sprite) +{ + struct sact_sprite *sp = sact_try_get_sprite(sprite); + if (!sp) + return NULL; + if (!sp->plugin || sp->plugin->name != plugin_name) + return NULL; + return (struct draw_snow_plugin *)sp->plugin; +} + +static void DrawSnow_update(struct sact_sprite *sp) +{ + struct draw_snow_plugin *plugin = (struct draw_snow_plugin *)sp->plugin; + if (!plugin->particles) + return; + int elapsed = SDL_GetTicks() - plugin->timestamp; + if (elapsed >= 16) + sprite_dirty(sp); +} + +static void DrawSnow_render(struct sact_sprite *sp) +{ + struct draw_snow_plugin *plugin = (struct draw_snow_plugin *)sp->plugin; + if (!plugin->particles) + return; + + struct texture *src = sprite_get_texture(sact_get_sprite(plugin->snow_sprite)); + struct texture *dst = gfx_main_surface(); + plugin->timestamp = SDL_GetTicks(); + uint32_t timestamp = 30000 + plugin->timestamp; + for (int i = 0; i < plugin->nr_particles; i++) { + struct snowflake *p = &plugin->particles[i]; + float rate = (float)(timestamp % p->total_time) / p->total_time; + int x = p->start_x + (p->end_x - p->start_x) * rate; + int y = p->start_y + (p->end_y - p->start_y) * rate; + if (x < 0 || x >= sp->rect.w || y < 0 || y >= sp->rect.h) + continue; + int dw = src->w * p->scale; + int dh = src->h * p->scale; + int dx = sp->rect.x + x - dw / 2; + int dy = sp->rect.y + y - dh / 2; + switch (plugin->type) { + case DRAW_SNOW_SCREEN_BLEND: + gfx_copy_stretch_blend_screen(dst, dx, dy, dw, dh, src, 0, 0, src->w, src->h); + break; + case DRAW_SNOW_ALPHA_BLEND: + gfx_copy_stretch_blend_amap(dst, dx, dy, dw, dh, src, 0, 0, src->w, src->h); + break; + } + } +} + +static cJSON *DrawSnow_to_json(struct sact_sprite *sp, bool verbose) +{ + struct draw_snow_plugin *plugin = (struct draw_snow_plugin *)sp->plugin; + cJSON *obj = cJSON_CreateObject(); + cJSON_AddStringToObject(obj, "name", plugin->p.name); + cJSON_AddNumberToObject(obj, "type", plugin->type); + cJSON_AddNumberToObject(obj, "max_time", plugin->max_time); + cJSON_AddNumberToObject(obj, "max_amp", plugin->max_amp); + cJSON_AddNumberToObject(obj, "nr_particles", plugin->nr_particles); + cJSON *snow_sprite = sprite_to_json(sact_get_sprite(plugin->snow_sprite), verbose); + cJSON_AddItemToObjectCS(obj, "snow_sprite", snow_sprite); + cJSON_AddBoolToObject(obj, "started", !!plugin->particles); + return obj; +} + +static void DrawSnow_Init(int sprite, int width, int height) +{ + struct draw_snow_plugin *plugin = xcalloc(1, sizeof(struct draw_snow_plugin)); + plugin->p.name = plugin_name; + plugin->p.update = DrawSnow_update; + plugin->p.render = DrawSnow_render; + plugin->p.to_json = DrawSnow_to_json; + struct sact_sprite *sp = sact_get_sprite(sprite); + if (!sp) + VM_ERROR("DrawSnow.Init: invalid sprite %d", sprite); + sp->rect.w = width; + sp->rect.h = height; + sprite_bind_plugin(sp, &plugin->p); + plugin->max_time = 3000; + plugin->max_amp = 160; + plugin->nr_particles = 1024; +} + +static void DrawSnow_Release(int sprite) +{ + struct draw_snow_plugin *plugin = get_draw_snow_plugin(sprite); + if (!plugin) + return; + sprite_bind_plugin(sact_get_sprite(sprite), NULL); + free(plugin->particles); + free(plugin); +} + +static void DrawSnow_Start(int sprite) +{ + struct draw_snow_plugin *plugin = get_draw_snow_plugin(sprite); + if (!plugin) + return; + if (plugin->particles) + return; // already started + plugin->particles = xcalloc(plugin->nr_particles, sizeof(struct snowflake)); + struct sact_sprite *sp = sact_get_sprite(sprite); + for (int i = 0; i < plugin->nr_particles; i++) { + struct snowflake *p = &plugin->particles[i]; + p->start_x = rand() % sp->rect.w; + p->start_y = -sp->rect.h + rand() % sp->rect.h; + p->end_x = (p->start_x + rand() % plugin->max_amp) - (plugin->max_amp / 2); + p->end_y = sp->rect.h + rand() % sp->rect.h; + p->total_time = glm_lerp(0.8f, 1.2f, frand()) * plugin->max_time; + p->scale = glm_lerp(0.25f, 1.f, frand()); + } +} + +static void DrawSnow_SetType(int sprite, int type) +{ + struct draw_snow_plugin *plugin = get_draw_snow_plugin(sprite); + if (!plugin) + return; + plugin->type = type; +} + +static void DrawSnow_SetSurface(int sprite, int surface) +{ + struct draw_snow_plugin *plugin = get_draw_snow_plugin(sprite); + if (!plugin) + return; + plugin->snow_sprite = surface; +} + +static void DrawSnow_SetNumof(int sprite, int numof) +{ + struct draw_snow_plugin *plugin = get_draw_snow_plugin(sprite); + if (!plugin || plugin->particles) + return; + plugin->nr_particles = numof; +} + +static void DrawSnow_SetMaxAmp(int sprite, int max_amp) +{ + struct draw_snow_plugin *plugin = get_draw_snow_plugin(sprite); + if (!plugin) + return; + plugin->max_amp = max_amp; +} + +static void DrawSnow_SetMaxTime(int sprite, int max_time) +{ + struct draw_snow_plugin *plugin = get_draw_snow_plugin(sprite); + if (!plugin) + return; + plugin->max_time = max_time; +} + +HLL_LIBRARY(DrawSnow, + HLL_EXPORT(Init, DrawSnow_Init), + HLL_EXPORT(Release, DrawSnow_Release), + HLL_EXPORT(Start, DrawSnow_Start), + HLL_EXPORT(SetType, DrawSnow_SetType), + HLL_EXPORT(SetSurface, DrawSnow_SetSurface), + HLL_EXPORT(SetNumof, DrawSnow_SetNumof), + HLL_EXPORT(SetMaxAmp, DrawSnow_SetMaxAmp), + HLL_EXPORT(SetMaxTime, DrawSnow_SetMaxTime) + ); diff --git a/src/hll/SACT2.c b/src/hll/SACT2.c index f4d27cc..fe8ddd0 100644 --- a/src/hll/SACT2.c +++ b/src/hll/SACT2.c @@ -321,7 +321,13 @@ int sact_SP_CreatePixelOnly(int sp_no, int width, int height) return !!sact_create_sprite(sp_no, width, height, 0, 0, 0, -1); } -//int SACT2_SP_CreateCustom(int sp); +int sact_SP_CreateCustom(int sp_no) +{ + struct sact_sprite *sp = sact_get_sprite(sp_no); + if (!sp) return 0; + sprite_init_custom(sp); + return true; +} int sact_SP_Delete(int sp_no) { @@ -857,7 +863,7 @@ int SACT2_SP_GetBrightness(int sp_no) HLL_EXPORT(SP_SaveCG, sact_SP_SaveCG), \ HLL_EXPORT(SP_Create, sact_SP_Create), \ HLL_EXPORT(SP_CreatePixelOnly, sact_SP_CreatePixelOnly), \ - HLL_TODO_EXPORT(SP_CreateCustom, SACT2_SP_CreateCustom), \ + HLL_EXPORT(SP_CreateCustom, sact_SP_CreateCustom), \ HLL_EXPORT(SP_Delete, sact_SP_Delete), \ HLL_EXPORT(SP_SetPos, sact_SP_SetPos), \ HLL_EXPORT(SP_SetX, sact_SP_SetX), \ diff --git a/src/hll/StoatSpriteEngine.c b/src/hll/StoatSpriteEngine.c index 6f8dec0..4978564 100644 --- a/src/hll/StoatSpriteEngine.c +++ b/src/hll/StoatSpriteEngine.c @@ -969,7 +969,7 @@ HLL_LIBRARY(StoatSpriteEngine, HLL_EXPORT(SP_SaveCG, sact_SP_SaveCG), HLL_EXPORT(SP_Create, sact_SP_Create), HLL_EXPORT(SP_CreatePixelOnly, sact_SP_CreatePixelOnly), - HLL_TODO_EXPORT(SP_CreateCustom, SACT2_SP_CreateCustom), + HLL_EXPORT(SP_CreateCustom, sact_SP_CreateCustom), HLL_EXPORT(SP_Delete, sact_SP_Delete), HLL_EXPORT(SP_DeleteAll, sact_SP_DeleteAll), HLL_EXPORT(SP_SetPos, sact_SP_SetPos), diff --git a/src/meson.build b/src/meson.build index 88d3648..49a5ea0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -91,6 +91,7 @@ xsystem4 = [version_h, 'hll/DrawMovie.c', 'hll/DrawPluginManager.c', 'hll/DrawSimpleText.c', + 'hll/DrawSnow.c', 'hll/File.c', 'hll/File2.c', 'hll/FileOperation.c', diff --git a/src/sprite.c b/src/sprite.c index d71b598..6c98a8e 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -170,6 +170,13 @@ static void sprite_render(struct sprite *_sp) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); } +static void sprite_custom_render(struct sprite *_sp) +{ + struct sact_sprite *sp = (struct sact_sprite*)_sp; + if (sp->plugin && sp->plugin->render) + sp->plugin->render(sp); +} + struct texture *sprite_get_texture(struct sact_sprite *sp) { sprite_init_texture(sp); @@ -288,6 +295,12 @@ void sprite_init_color(struct sact_sprite *sp, int w, int h, int r, int g, int b sprite_dirty(sp); } +void sprite_init_custom(struct sact_sprite *sp) +{ + sp->sp.render = sprite_custom_render; + sp->sp.to_json = _sprite_to_json; +} + void sprite_set_pos(struct sact_sprite *sp, int x, int y) { sp->rect.x = x; From 842bac92f39ede549f2c1782a856ff59fafe5357 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Fri, 1 Nov 2024 08:46:59 +0900 Subject: [PATCH 2/3] Implement DrawRain HLL Used in Tsuma Shibori. --- CMakeLists.txt | 1 + src/ffi.c | 2 + src/hll/DrawRain.c | 165 +++++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 1 + 4 files changed, 169 insertions(+) create mode 100644 src/hll/DrawRain.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f50765..f3f590c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,7 @@ target_sources(xsystem4 PRIVATE src/hll/DrawGraph.c src/hll/DrawMovie.c src/hll/DrawPluginManager.c + src/hll/DrawRain.c src/hll/DrawSimpleText.c src/hll/DrawSnow.c src/hll/File.c diff --git a/src/ffi.c b/src/ffi.c index a813d52..0475381 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -426,6 +426,7 @@ extern struct static_library lib_DrawMovie; extern struct static_library lib_DrawMovie2; extern struct static_library lib_DrawMovie3; extern struct static_library lib_DrawPluginManager; +extern struct static_library lib_DrawRain; extern struct static_library lib_DrawSimpleText; extern struct static_library lib_DrawSnow; extern struct static_library lib_File; @@ -530,6 +531,7 @@ static struct static_library *static_libraries[] = { &lib_DrawMovie2, &lib_DrawMovie3, &lib_DrawPluginManager, + &lib_DrawRain, &lib_DrawSimpleText, &lib_DrawSnow, &lib_File, diff --git a/src/hll/DrawRain.c b/src/hll/DrawRain.c new file mode 100644 index 0000000..1f7c9e8 --- /dev/null +++ b/src/hll/DrawRain.c @@ -0,0 +1,165 @@ +/* Copyright (C) 2024 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 . + */ + +#include + +#include "cJSON.h" +#include "hll.h" +#include "plugin.h" +#include "sact.h" +#include "sprite.h" +#include "xsystem4.h" + +static const char plugin_name[] = "DrawRain"; + +struct draw_rain_plugin { + struct draw_plugin p; + int nr_lines; + int length; + float speed; + float angle; + uint32_t timestamp; + bool started; +}; + +static struct draw_rain_plugin *get_draw_rain_plugin(int surface) +{ + struct sact_sprite *sp = sact_try_get_sprite(surface); + if (!sp) + return NULL; + if (!sp->plugin || sp->plugin->name != plugin_name) + return NULL; + return (struct draw_rain_plugin *)sp->plugin; +} + +static void DrawRain_update(struct sact_sprite *sp) +{ + struct draw_rain_plugin *plugin = (struct draw_rain_plugin *)sp->plugin; + if (!plugin->started) + return; + uint32_t timestamp = SDL_GetTicks(); + if (timestamp - plugin->timestamp < 16) + return; + plugin->timestamp = timestamp; + + // Unlike the AliceSoft's DrawRain, we draw random lines every frame + // ignoring the speed parameter. The difference is not noticeable unless + // the speed is very small. + struct texture *dst = sprite_get_texture(sp); + gfx_fill_amap(dst, 0, 0, dst->w, dst->h, 0); + float rad = plugin->angle * GLM_PIf / 180.f; + for (int i = 0; i < plugin->nr_lines / 10; i++) { + int x1 = rand() % dst->w; + int y1 = rand() % dst->h; + int len = rand() % plugin->length; + int x2 = x1 + len * sinf(rad); + int y2 = y1 - len * cosf(rad); + int alpha = 128 + rand() % 128; + gfx_draw_line_to_amap(dst, x1, y1, x2, y2, alpha); + } + sprite_dirty(sp); +} + +static cJSON *DrawRain_to_json(struct sact_sprite *sp, bool verbose) +{ + struct draw_rain_plugin *plugin = (struct draw_rain_plugin *)sp->plugin; + cJSON *obj = cJSON_CreateObject(); + cJSON_AddStringToObject(obj, "name", plugin->p.name); + cJSON_AddNumberToObject(obj, "nr_lines", plugin->nr_lines); + cJSON_AddNumberToObject(obj, "length", plugin->length); + cJSON_AddNumberToObject(obj, "speed", plugin->speed); + cJSON_AddNumberToObject(obj, "angle", plugin->angle); + cJSON_AddBoolToObject(obj, "started", plugin->started); + return obj; +} + +static int DrawRain_Init(int surface) +{ + struct sact_sprite *sp = sact_get_sprite(surface); + if (!sp) { + WARNING("DrawRain.Init: invalid surface %d", surface); + return 0; + } + struct draw_rain_plugin *plugin = xcalloc(1, sizeof(struct draw_rain_plugin)); + plugin->p.name = plugin_name; + plugin->p.update = DrawRain_update; + plugin->p.to_json = DrawRain_to_json; + plugin->nr_lines = 1000; + plugin->length = 300; + plugin->speed = 0.03f; + plugin->angle = 5.f; + sprite_bind_plugin(sp, &plugin->p); + return 1; +} + +static void DrawRain_Release(int surface) +{ + struct draw_rain_plugin *plugin = get_draw_rain_plugin(surface); + if (!plugin) + return; + sprite_bind_plugin(sact_get_sprite(surface), NULL); + free(plugin); +} + +static void DrawRain_SetNumofLine(int surface, int line) +{ + struct draw_rain_plugin *plugin = get_draw_rain_plugin(surface); + if (!plugin) + return; + plugin->nr_lines = line; +} + +static void DrawRain_SetLength(int surface, int length) +{ + struct draw_rain_plugin *plugin = get_draw_rain_plugin(surface); + if (!plugin) + return; + plugin->length = length; +} + +static void DrawRain_SetSpeed(int surface, float speed) +{ + struct draw_rain_plugin *plugin = get_draw_rain_plugin(surface); + if (!plugin) + return; + plugin->speed = speed; +} + +static void DrawRain_SetAngle(int surface, float angle) +{ + struct draw_rain_plugin *plugin = get_draw_rain_plugin(surface); + if (!plugin) + return; + plugin->angle = angle; +} + +static void DrawRain_Start(int surface) +{ + struct draw_rain_plugin *plugin = get_draw_rain_plugin(surface); + if (!plugin) + return; + plugin->started = true; +} + +HLL_LIBRARY(DrawRain, + HLL_EXPORT(Init, DrawRain_Init), + HLL_EXPORT(Release, DrawRain_Release), + HLL_EXPORT(SetNumofLine, DrawRain_SetNumofLine), + HLL_EXPORT(SetLength, DrawRain_SetLength), + HLL_EXPORT(SetSpeed, DrawRain_SetSpeed), + HLL_EXPORT(SetAngle, DrawRain_SetAngle), + HLL_EXPORT(Start, DrawRain_Start) + ); diff --git a/src/meson.build b/src/meson.build index 49a5ea0..2dc5e88 100644 --- a/src/meson.build +++ b/src/meson.build @@ -90,6 +90,7 @@ xsystem4 = [version_h, 'hll/DrawGraph.c', 'hll/DrawMovie.c', 'hll/DrawPluginManager.c', + 'hll/DrawRain.c', 'hll/DrawSimpleText.c', 'hll/DrawSnow.c', 'hll/File.c', From 579c15847721ae68512eef4a5d1d052d720fad9f Mon Sep 17 00:00:00 2001 From: kichikuou Date: Fri, 1 Nov 2024 12:24:03 +0900 Subject: [PATCH 3/3] Implement DrawRipple HLL Used in Majo no Shokuzai. --- CMakeLists.txt | 1 + src/ffi.c | 2 + src/hll/DrawRipple.c | 247 +++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 1 + 4 files changed, 251 insertions(+) create mode 100644 src/hll/DrawRipple.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f3f590c..0ec2e61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,7 @@ target_sources(xsystem4 PRIVATE src/hll/DrawMovie.c src/hll/DrawPluginManager.c src/hll/DrawRain.c + src/hll/DrawRipple.c src/hll/DrawSimpleText.c src/hll/DrawSnow.c src/hll/File.c diff --git a/src/ffi.c b/src/ffi.c index 0475381..2bff3a8 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -427,6 +427,7 @@ extern struct static_library lib_DrawMovie2; extern struct static_library lib_DrawMovie3; extern struct static_library lib_DrawPluginManager; extern struct static_library lib_DrawRain; +extern struct static_library lib_DrawRipple; extern struct static_library lib_DrawSimpleText; extern struct static_library lib_DrawSnow; extern struct static_library lib_File; @@ -532,6 +533,7 @@ static struct static_library *static_libraries[] = { &lib_DrawMovie3, &lib_DrawPluginManager, &lib_DrawRain, + &lib_DrawRipple, &lib_DrawSimpleText, &lib_DrawSnow, &lib_File, diff --git a/src/hll/DrawRipple.c b/src/hll/DrawRipple.c new file mode 100644 index 0000000..7f54eae --- /dev/null +++ b/src/hll/DrawRipple.c @@ -0,0 +1,247 @@ +/* Copyright (C) 2024 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 . + */ + +#include + +#include "cJSON.h" +#include "hll.h" +#include "plugin.h" +#include "sact.h" +#include "sprite.h" +#include "xsystem4.h" + +static const char plugin_name[] = "DrawRipple"; + +struct ripple { + float x; + float y; + uint32_t start_time; +}; + +struct draw_ripple_plugin { + struct draw_plugin p; + float width; + float height; + int time; + int clip_surface; + int nr_ripples; + struct ripple *ripples; + uint32_t timestamp; +}; + +static struct draw_ripple_plugin *get_draw_ripple_plugin(int surface) +{ + struct sact_sprite *sp = sact_try_get_sprite(surface); + if (!sp) + return NULL; + if (!sp->plugin || sp->plugin->name != plugin_name) + return NULL; + return (struct draw_ripple_plugin *)sp->plugin; +} + +static void draw_point(uint32_t *pixels, int w, int h, int x, int y) +{ + if (x < 0 || x >= w || y < 0 || y >= h) + return; + pixels[y * w + x] = 0xffffffff; +} + +static void draw_ellipse(uint32_t *pixels, int dst_w, int dst_h, int cx, int cy, int rx, int ry) +{ + // Midpoint Ellipse Algorithm. + if (rx == 0 || ry == 0) + return; + const int rx2 = rx * rx; + const int ry2 = ry * ry; + int x = 0; + int y = ry; + int px = 0; + int py = 2 * rx2 * y; + + // Region 1 + int p = ry2 - (rx2 * ry) + (0.25 * rx2); + while (px < py) { + draw_point(pixels, dst_w, dst_h, cx + x, cy + y); + draw_point(pixels, dst_w, dst_h, cx - x, cy + y); + draw_point(pixels, dst_w, dst_h, cx + x, cy - y); + draw_point(pixels, dst_w, dst_h, cx - x, cy - y); + x++; + px += 2 * ry2; + if (p < 0) { + p += ry2 + px; + } else { + y--; + py -= 2 * rx2; + p += ry2 + px - py; + } + } + + // Region 2 + p = ry2 * (x + 0.5) * (x + 0.5) + rx2 * (y - 1) * (y - 1) - rx2 * ry2; + while (y > 0) { + draw_point(pixels, dst_w, dst_h, cx + x, cy + y); + draw_point(pixels, dst_w, dst_h, cx - x, cy + y); + draw_point(pixels, dst_w, dst_h, cx + x, cy - y); + draw_point(pixels, dst_w, dst_h, cx - x, cy - y); + y--; + py -= 2 * rx2; + if (p > 0) { + p += rx2 - py; + } else { + x++; + px += 2 * ry2; + p += rx2 - py + px; + } + } + draw_point(pixels, dst_w, dst_h, cx + rx, cy); + draw_point(pixels, dst_w, dst_h, cx - rx, cy); +} + +static void DrawRipple_update(struct sact_sprite *sp) +{ + struct draw_ripple_plugin *plugin = (struct draw_ripple_plugin *)sp->plugin; + if (!plugin->ripples) + return; + uint32_t timestamp = SDL_GetTicks(); + if (timestamp - plugin->timestamp < 16) + return; + plugin->timestamp = timestamp; + + struct texture *dst = sprite_get_texture(sp); + uint32_t *pixels = xcalloc(dst->w * dst->h, 4); + for (int i = 0; i < plugin->nr_ripples; i++) { + struct ripple *r = &plugin->ripples[i]; + float rate = (float)(timestamp - r->start_time) / plugin->time; + if (rate >= 1.f) { + r->x = rand() % dst->w; + r->y = rand() % dst->h; + r->start_time += plugin->time * floorf(rate); + continue; + } + draw_ellipse(pixels, dst->w, dst->h, r->x, r->y, plugin->width * rate, plugin->height * rate); + } + gfx_update_texture_with_pixels(dst, pixels); + free(pixels); + struct texture *clip = sprite_get_texture(sact_get_sprite(plugin->clip_surface)); + gfx_blend_da_daxsa(dst, 0, 0, clip, 0, 0, clip->w, clip->h); + sprite_dirty(sp); +} + +static cJSON *DrawRipple_to_json(struct sact_sprite *sp, bool verbose) +{ + struct draw_ripple_plugin *plugin = (struct draw_ripple_plugin *)sp->plugin; + cJSON *obj = cJSON_CreateObject(); + cJSON_AddStringToObject(obj, "name", plugin->p.name); + cJSON_AddNumberToObject(obj, "width", plugin->width); + cJSON_AddNumberToObject(obj, "height", plugin->height); + cJSON_AddNumberToObject(obj, "time", plugin->time); + cJSON_AddNumberToObject(obj, "clip_surface", plugin->clip_surface); + cJSON_AddNumberToObject(obj, "nr_ripples", plugin->nr_ripples); + cJSON_AddBoolToObject(obj, "started", !!plugin->ripples); + return obj; +} + +static int DrawRipple_Init(int surface) +{ + struct sact_sprite *sp = sact_get_sprite(surface); + if (!sp) { + WARNING("DrawRipple.Init: invalid surface %d", surface); + return 0; + } + struct draw_ripple_plugin *plugin = xcalloc(1, sizeof(struct draw_ripple_plugin)); + plugin->p.name = plugin_name; + plugin->p.update = DrawRipple_update; + plugin->p.to_json = DrawRipple_to_json; + plugin->width = 6; + plugin->height = 3; + plugin->time = 100; + plugin->nr_ripples = 384; + sprite_bind_plugin(sp, &plugin->p); + return 1; +} + +static void DrawRipple_Release(int surface) +{ + struct draw_ripple_plugin *plugin = get_draw_ripple_plugin(surface); + if (!plugin) + return; + sprite_bind_plugin(sact_get_sprite(surface), NULL); + free(plugin->ripples); + free(plugin); +} + +static void DrawRipple_SetNumof(int surface, int numof) +{ + struct draw_ripple_plugin *plugin = get_draw_ripple_plugin(surface); + if (!plugin || plugin->ripples) + return; + plugin->nr_ripples = numof; +} + +static void DrawRipple_SetSize(int surface, float width, float height) +{ + struct draw_ripple_plugin *plugin = get_draw_ripple_plugin(surface); + if (!plugin) + return; + plugin->width = width; + plugin->height = height; +} + +static void DrawRipple_SetTime(int surface, int time) +{ + struct draw_ripple_plugin *plugin = get_draw_ripple_plugin(surface); + if (!plugin) + return; + plugin->time = time; +} + +//void DrawRipple_SetSpeed(int nSurface, float fSpeed); + +static void DrawRipple_SetClipAMap(int surface, int clip_surface) +{ + struct draw_ripple_plugin *plugin = get_draw_ripple_plugin(surface); + if (!plugin) + return; + plugin->clip_surface = clip_surface; +} + +static void DrawRipple_Start(int surface) +{ + struct draw_ripple_plugin *plugin = get_draw_ripple_plugin(surface); + if (!plugin) + return; + if (plugin->ripples) + return; // already started + plugin->ripples = xmalloc(plugin->nr_ripples * sizeof(struct ripple)); + struct sact_sprite *sp = sact_get_sprite(surface); + uint32_t now = SDL_GetTicks(); + for (int i = 0; i < plugin->nr_ripples; i++) { + plugin->ripples[i].x = rand() % sp->rect.w; + plugin->ripples[i].y = rand() % sp->rect.h; + plugin->ripples[i].start_time = now - rand() % plugin->time; + } +} + +HLL_LIBRARY(DrawRipple, + HLL_EXPORT(Init, DrawRipple_Init), + HLL_EXPORT(Release, DrawRipple_Release), + HLL_EXPORT(SetNumof, DrawRipple_SetNumof), + HLL_EXPORT(SetSize, DrawRipple_SetSize), + HLL_EXPORT(SetTime, DrawRipple_SetTime), + HLL_TODO_EXPORT(SetSpeed, DrawRipple_SetSpeed), + HLL_EXPORT(SetClipAMap, DrawRipple_SetClipAMap), + HLL_EXPORT(Start, DrawRipple_Start) + ); diff --git a/src/meson.build b/src/meson.build index 2dc5e88..907a117 100644 --- a/src/meson.build +++ b/src/meson.build @@ -91,6 +91,7 @@ xsystem4 = [version_h, 'hll/DrawMovie.c', 'hll/DrawPluginManager.c', 'hll/DrawRain.c', + 'hll/DrawRipple.c', 'hll/DrawSimpleText.c', 'hll/DrawSnow.c', 'hll/File.c',