From 579c15847721ae68512eef4a5d1d052d720fad9f Mon Sep 17 00:00:00 2001 From: kichikuou Date: Fri, 1 Nov 2024 12:24:03 +0900 Subject: [PATCH] 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',