From ae70770f77d0a2b49187fc3171abc38bdbaebfe4 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Sat, 3 Feb 2018 03:46:33 +0100 Subject: [PATCH] rumina hacks --- src/character.c | 4 +++ src/character.h | 1 + src/gamestate.h | 3 ++- src/internal.c | 63 ++++++++++++++++++++++++++++++++++++++------- src/internal.h | 2 ++ src/libsuperderpy.c | 19 +++++++++++--- src/libsuperderpy.h | 17 +++++++++--- src/utils.c | 33 ++++++++++++++++++++++++ src/utils.h | 9 +++++++ 9 files changed, 134 insertions(+), 17 deletions(-) diff --git a/src/character.c b/src/character.c index 7b38954..48ecaeb 100644 --- a/src/character.c +++ b/src/character.c @@ -115,6 +115,7 @@ SYMBOL_EXPORT void RegisterSpritesheet(struct Game* game, struct Character* char s->rows = strtol(al_get_config_value(config, "", "rows"), NULL, 10); s->blanks = strtol(al_get_config_value(config, "", "blanks"), NULL, 10); s->delay = strtod(al_get_config_value(config, "", "delay"), NULL); + s->flip = false; const char* val = al_get_config_value(config, "", "repeat"); if (val) { s->repeat = strtod(val, NULL); @@ -258,6 +259,9 @@ SYMBOL_EXPORT void DrawScaledCharacter(struct Game* game, struct Character* char } SYMBOL_EXPORT void DrawCharacter(struct Game* game, struct Character* character, ALLEGRO_COLOR tint, int flags) { + if (character->spritesheet->flip) { + flags = flags | ALLEGRO_FLIP_HORIZONTAL | ALLEGRO_FLIP_VERTICAL; + } DrawScaledCharacter(game, character, tint, 1, 1, flags); } diff --git a/src/character.h b/src/character.h index 6b9d150..bab61e9 100644 --- a/src/character.h +++ b/src/character.h @@ -37,6 +37,7 @@ struct Spritesheet { int delay; bool kill; int repeat; + bool flip; float scale; /*!< Scale modifier of the frame. */ char* successor; /*!< Name of animation successor. If it's not blank, then animation will be played only once. */ struct Spritesheet* next; /*!< Next spritesheet in the queue. */ diff --git a/src/gamestate.h b/src/gamestate.h index e1e2788..7d81416 100644 --- a/src/gamestate.h +++ b/src/gamestate.h @@ -49,8 +49,9 @@ struct Gamestate { bool showLoading; bool paused; struct Gamestate* next; - void* data; struct Gamestate_API* api; + ALLEGRO_BITMAP* fb; + void* data; }; void LoadGamestate(struct Game* game, const char* name); diff --git a/src/internal.c b/src/internal.c index a0af514..595d8f3 100644 --- a/src/internal.c +++ b/src/internal.c @@ -22,11 +22,24 @@ #include "libsuperderpy.h" #include #include +#include #include +SYMBOL_INTERNAL void SimpleCompositor(struct Game* game, struct Gamestate* gamestates) { + struct Gamestate* tmp = gamestates; + al_clear_to_color(al_map_rgb(0, 0, 0)); + while (tmp) { + if ((tmp->loaded) && (tmp->started)) { + al_draw_bitmap(tmp->fb, 0, 0, 0); + } + tmp = tmp->next; + } +} + SYMBOL_INTERNAL void DrawGamestates(struct Game* game) { - ClearScreen(game); - al_set_target_backbuffer(game->display); + if (!game->handlers.compositor) { + ClearScreen(game); + } struct Gamestate* tmp = game->_priv.gamestates; if (game->handlers.predraw) { (*game->handlers.predraw)(game); @@ -34,10 +47,29 @@ SYMBOL_INTERNAL void DrawGamestates(struct Game* game) { while (tmp) { if ((tmp->loaded) && (tmp->started)) { game->_priv.current_gamestate = tmp; + SetFramebufferAsTarget(game); + if (game->handlers.compositor) { // don't clear when uncomposited + al_clear_to_color(al_map_rgb(0, 0, 0)); // even if everything is going to be redrawn, it optimizes tiled rendering + } (*tmp->api->Gamestate_Draw)(game, tmp->data); + // TODO: save and restore more state for careless gamestating } tmp = tmp->next; } + + if (game->handlers.compositor) { + ALLEGRO_TRANSFORM t; + al_set_target_backbuffer(game->display); + ClearScreen(game); + al_identity_transform(&t); + /* double factor = (sin(al_get_time()) / 2.0 + 1.0) * 2; + al_translate_transform(&t, -game->_priv.clip_rect.w / factor, -game->_priv.clip_rect.h / factor); + al_scale_transform(&t, factor, factor); + al_translate_transform(&t, game->_priv.clip_rect.w / factor, game->_priv.clip_rect.h / factor);*/ + al_translate_transform(&t, game->_priv.clip_rect.x, game->_priv.clip_rect.y); + al_use_transform(&t); + game->handlers.compositor(game, game->_priv.gamestates); + } if (game->handlers.postdraw) { (*game->handlers.postdraw)(game); } @@ -104,6 +136,19 @@ SYMBOL_INTERNAL void UnfreezeGamestates(struct Game* game) { } } +SYMBOL_INTERNAL void ResizeGamestates(struct Game* game) { + struct Gamestate* tmp = game->_priv.gamestates; + while (tmp) { + al_destroy_bitmap(tmp->fb); + if (game->handlers.compositor) { + tmp->fb = CreateNotPreservedBitmap(game->_priv.clip_rect.w, game->_priv.clip_rect.h); + } else { + tmp->fb = al_create_sub_bitmap(al_get_backbuffer(game->display), game->_priv.clip_rect.x, game->_priv.clip_rect.y, game->_priv.clip_rect.w, game->_priv.clip_rect.h); + } + tmp = tmp->next; + } +} + SYMBOL_INTERNAL void DrawConsole(struct Game* game) { if (game->_priv.showconsole) { al_set_target_backbuffer(game->display); @@ -264,6 +309,11 @@ SYMBOL_INTERNAL struct Gamestate* AllocateGamestate(struct Game* game, const cha tmp->pending_unload = false; tmp->next = NULL; tmp->api = NULL; + if (game->handlers.compositor) { + tmp->fb = CreateNotPreservedBitmap(game->_priv.clip_rect.w, game->_priv.clip_rect.h); + } else { + tmp->fb = al_create_sub_bitmap(al_get_backbuffer(game->display), game->_priv.clip_rect.x, game->_priv.clip_rect.y, game->_priv.clip_rect.w, game->_priv.clip_rect.h); + } return tmp; } @@ -278,6 +328,7 @@ SYMBOL_INTERNAL void CloseGamestate(struct Game* game, struct Gamestate* gamesta if (gamestate->api) { free(gamestate->api); } + al_destroy_bitmap(gamestate->fb); } SYMBOL_INTERNAL struct libsuperderpy_list* AddToList(struct libsuperderpy_list* list, void* data) { @@ -356,16 +407,10 @@ SYMBOL_INTERNAL void RemoveTimeline(struct Game* game, struct Timeline* timeline } SYMBOL_INTERNAL void ClearScreen(struct Game* game) { - ALLEGRO_TRANSFORM identity; - int clipX, clipY, clipWidth, clipHeight; al_set_target_backbuffer(game->display); - al_get_clipping_rectangle(&clipX, &clipY, &clipWidth, &clipHeight); al_set_clipping_rectangle(0, 0, al_get_display_width(game->display), al_get_display_height(game->display)); - al_identity_transform(&identity); - al_use_transform(&identity); al_clear_to_color(al_map_rgb(0, 0, 0)); - al_use_transform(&game->projection); - al_set_clipping_rectangle(clipX, clipY, clipWidth, clipHeight); + al_set_clipping_rectangle(game->_priv.clip_rect.x, game->_priv.clip_rect.y, game->_priv.clip_rect.w, game->_priv.clip_rect.h); } static void DrawQueue(struct Game* game, struct TM_Action* queue, int clipX, int clipY) { diff --git a/src/internal.h b/src/internal.h index 5e2a1a0..1938547 100644 --- a/src/internal.h +++ b/src/internal.h @@ -62,12 +62,14 @@ struct ScreenshotThreadData { ALLEGRO_BITMAP* bitmap; }; +void SimpleCompositor(struct Game* game, struct Gamestate* gamestates); void DrawGamestates(struct Game* game); void LogicGamestates(struct Game* game, double delta); void EventGamestates(struct Game* game, ALLEGRO_EVENT* ev); void ReloadGamestates(struct Game* game); void FreezeGamestates(struct Game* game); void UnfreezeGamestates(struct Game* game); +void ResizeGamestates(struct Game* game); void DrawConsole(struct Game* game); void Console_Load(struct Game* game); void Console_Unload(struct Game* game); diff --git a/src/libsuperderpy.c b/src/libsuperderpy.c index e25a6bb..55f808d 100644 --- a/src/libsuperderpy.c +++ b/src/libsuperderpy.c @@ -76,6 +76,7 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char* game->handlers.event = NULL; game->handlers.destroy = NULL; + game->handlers.compositor = NULL; game->handlers.prelogic = NULL; game->handlers.postlogic = NULL; game->handlers.predraw = NULL; @@ -159,6 +160,8 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char* al_set_new_display_option(ALLEGRO_SUPPORTED_ORIENTATIONS, ALLEGRO_DISPLAY_ORIENTATION_PORTRAIT, ALLEGRO_SUGGEST); #endif + al_set_new_display_option(ALLEGRO_DEPTH_SIZE, 24, ALLEGRO_SUGGEST); + #ifdef ALLEGRO_WINDOWS al_set_new_window_position(20, 40); // workaround nasty Windows bug with window being created off-screen #endif @@ -321,6 +324,7 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop_exit(struct Game* game) { SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) { struct Game* game = (struct Game*)g; + redraw = true; while (!al_is_event_queue_empty(game->_priv.event_queue) || redraw) { #else bool redraw = false; @@ -330,7 +334,7 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) { // TODO: split mainloop to functions to make it readable ALLEGRO_EVENT ev; - if (game->_priv.draw && ((redraw && al_is_event_queue_empty(game->_priv.event_queue)) || (game->_priv.gamestate_scheduled))) { + if (game->_priv.draw && (((redraw || true) && al_is_event_queue_empty(game->_priv.event_queue)) || (game->_priv.gamestate_scheduled))) { game->_priv.gamestate_scheduled = false; struct Gamestate* tmp = game->_priv.gamestates; @@ -392,6 +396,7 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) { al_run_detached_thread(GamestateLoadingThread, &data); while (game->_priv.loading.inProgress) { DrawGamestates(game); + al_set_target_backbuffer(game->display); if (tmp->showLoading) { (*game->_priv.loading.gamestate->api->Gamestate_Draw)(game, game->_priv.loading.gamestate->data); } @@ -450,8 +455,15 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) { al_convert_memory_bitmaps(); + double delta = al_get_time() - game->_priv.timestamp; + game->_priv.timestamp += delta; + delta /= ALLEGRO_BPS_TO_SECS(al_get_timer_speed(game->_priv.timer) / (1 / 60.f)); + LogicGamestates(game, delta); + redraw = true; + DrawGamestates(game); DrawConsole(game); + //al_wait_for_vsync(); al_flip_display(); redraw = false; @@ -471,10 +483,10 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) { } if ((ev.type == ALLEGRO_EVENT_TIMER) && (ev.timer.source == game->_priv.timer)) { - double delta = al_get_time() - game->_priv.timestamp; + /*double delta = al_get_time() - game->_priv.timestamp; game->_priv.timestamp += delta; LogicGamestates(game, delta); - redraw = true; + redraw = true;*/ } else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { #ifdef __EMSCRIPTEN__ libsuperderpy_mainloop_exit(game); @@ -498,6 +510,7 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) { } else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) { al_acknowledge_resize(game->display); SetupViewport(game, game->viewport_config); + ResizeGamestates(game); } #ifdef ALLEGRO_ANDROID else if ((ev.type == ALLEGRO_EVENT_KEY_CHAR) && ((ev.keyboard.keycode == ALLEGRO_KEY_MENU) || (ev.keyboard.keycode == ALLEGRO_KEY_TILDE) || (ev.keyboard.keycode == ALLEGRO_KEY_BACKQUOTE))) { diff --git a/src/libsuperderpy.h b/src/libsuperderpy.h index 3cabfc0..cdebf1c 100644 --- a/src/libsuperderpy.h +++ b/src/libsuperderpy.h @@ -53,10 +53,11 @@ struct Game; struct Gamestate; struct Viewport { - int width; /*!< Actual available width of the drawing canvas. */ - int height; /*!< Actual available height of the drawing canvas. */ - float aspect; - bool integer_scaling; + int width; /*!< Width of the drawing canvas. */ + int height; /*!< Height of the drawing canvas. */ + float aspect; /*!< When set instead of width/height pair, makes the viewport side fluid; when non-zero, locks its aspect ratio. */ + bool integer_scaling; /*!< Ensure that the viewport is zoomed only with integer factors. */ + bool pixel_perfect; /*!< Ensure that the resulting image is really viewport-sized and (potentially) rescaled afterwards, as opposed to default transformation-based scaling. */ }; /*! \brief Main struct of the game. */ @@ -97,6 +98,8 @@ struct Game { bool showconsole; /*!< If true, game console is rendered on screen. */ bool showtimeline; + ALLEGRO_BITMAP* fb; /*!< Default framebuffer. */ + struct { double old_time, fps; int frames_done; @@ -123,6 +126,11 @@ struct Game { double timestamp; + struct { + int x, y; + int w, h; + } clip_rect; + #ifdef ALLEGRO_MACOSX char cwd[MAXPATHLEN]; #endif @@ -144,6 +152,7 @@ struct Game { struct { bool (*event)(struct Game* game, ALLEGRO_EVENT* ev); void (*destroy)(struct Game* game); + void (*compositor)(struct Game* game, struct Gamestate* gamestates); void (*prelogic)(struct Game* game, double delta); void (*postlogic)(struct Game* game, double delta); void (*predraw)(struct Game* game); diff --git a/src/utils.c b/src/utils.c index 9df9f96..709bb1e 100644 --- a/src/utils.c +++ b/src/utils.c @@ -46,6 +46,7 @@ SYMBOL_EXPORT void DrawHorizontalGradientRect(float x, float y, float w, float h } SYMBOL_EXPORT void DrawTextWithShadow(ALLEGRO_FONT* font, ALLEGRO_COLOR color, float x, float y, int flags, char const* text) { + // TODO: consider using a set of shaders al_draw_text(font, al_map_rgba(0, 0, 0, 128), x + 1, y + 1, flags, text); al_draw_text(font, color, x, y, flags, text); } @@ -454,6 +455,10 @@ SYMBOL_EXPORT void SetupViewport(struct Game* game, struct Viewport config) { int clipY = (al_get_display_height(game->display) - clipHeight) / 2; al_build_transform(&game->projection, clipX, clipY, resolution, resolution, 0.0f); al_set_clipping_rectangle(clipX, clipY, clipWidth, clipHeight); + game->_priv.clip_rect.x = clipX; + game->_priv.clip_rect.y = clipY; + game->_priv.clip_rect.w = clipWidth; + game->_priv.clip_rect.h = clipHeight; } else if (strtol(GetConfigOptionDefault(game, "SuperDerpy", "scaling", "1"), NULL, 10)) { al_build_transform(&game->projection, 0, 0, al_get_display_width(game->display) / (float)game->viewport.width, al_get_display_height(game->display) / (float)game->viewport.height, 0.0f); } @@ -471,10 +476,38 @@ SYMBOL_EXPORT void WindowCoordsToViewport(struct Game* game, int* x, int* y) { *y /= clipHeight / (float)game->viewport.height; } +SYMBOL_EXPORT ALLEGRO_BITMAP* GetFramebuffer(struct Game* game) { + return game->_priv.current_gamestate->fb; +} + +SYMBOL_EXPORT void SetFramebufferAsTarget(struct Game* game) { + al_set_target_bitmap(GetFramebuffer(game)); + double x = al_get_bitmap_width(GetFramebuffer(game)) / (double)game->viewport.width; + double y = al_get_bitmap_height(GetFramebuffer(game)) / (double)game->viewport.height; + ALLEGRO_TRANSFORM t; + al_identity_transform(&t); + al_scale_transform(&t, x, y); + al_use_transform(&t); +} + SYMBOL_EXPORT ALLEGRO_BITMAP* CreateNotPreservedBitmap(int width, int height) { int flags = al_get_new_bitmap_flags(); + //al_set_new_bitmap_depth(24); al_add_new_bitmap_flag(ALLEGRO_NO_PRESERVE_TEXTURE); ALLEGRO_BITMAP* bitmap = al_create_bitmap(width, height); al_set_new_bitmap_flags(flags); + //al_set_new_bitmap_depth(0); return bitmap; } + +SYMBOL_EXPORT void EnableCompositor(struct Game* game, void compositor(struct Game* game, struct Gamestate* gamestates)) { + PrintConsole(game, "Compositor enabled."); + game->handlers.compositor = compositor ? compositor : SimpleCompositor; + ResizeGamestates(game); +} + +SYMBOL_EXPORT void DisableCompositor(struct Game* game) { + PrintConsole(game, "Compositor disabled."); + game->handlers.compositor = NULL; + ResizeGamestates(game); +} diff --git a/src/utils.h b/src/utils.h index 69814d3..988f4de 100644 --- a/src/utils.h +++ b/src/utils.h @@ -36,6 +36,7 @@ #endif struct Viewport; +struct Gamestate; /*! \brief Draws rectangle filled with vertical gradient. */ void DrawVerticalGradientRect(float x, float y, float w, float h, ALLEGRO_COLOR top, ALLEGRO_COLOR bottom); @@ -76,5 +77,13 @@ void SetupViewport(struct Game* game, struct Viewport config); void WindowCoordsToViewport(struct Game* game, int* x, int* y); +ALLEGRO_BITMAP* GetFramebuffer(struct Game* game); + +void SetFramebufferAsTarget(struct Game* game); + ALLEGRO_BITMAP* CreateNotPreservedBitmap(int width, int height); + +void EnableCompositor(struct Game* game, void compositor(struct Game* game, struct Gamestate* gamestates)); +void DisableCompositor(struct Game* game); + #endif