mirror of
https://gitlab.com/dosowisko.net/libsuperderpy.git
synced 2024-12-12 20:18:00 +01:00
initial work on optional compositing and variable speed logic ticks
This commit is contained in:
parent
1f96d6f364
commit
66818031ed
7 changed files with 133 additions and 25 deletions
|
@ -25,7 +25,7 @@
|
|||
|
||||
struct Gamestate_API {
|
||||
void (*Gamestate_Draw)(struct Game* game, void* data);
|
||||
void (*Gamestate_Logic)(struct Game* game, void* data);
|
||||
void (*Gamestate_Logic)(struct Game* game, void* data, double delta);
|
||||
|
||||
void* (*Gamestate_Load)(struct Game* game, void (*progress)(struct Game* game));
|
||||
void (*Gamestate_Start)(struct Game* game, void* data);
|
||||
|
@ -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);
|
||||
|
|
|
@ -22,27 +22,56 @@
|
|||
#include "libsuperderpy.h"
|
||||
#include <allegro5/allegro_ttf.h>
|
||||
#include <dlfcn.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
SYMBOL_INTERNAL void DrawGamestates(struct Game* game) {
|
||||
ClearScreen(game);
|
||||
al_set_target_backbuffer(game->display);
|
||||
struct Gamestate* tmp = game->_priv.gamestates;
|
||||
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)) {
|
||||
game->_priv.current_gamestate = tmp;
|
||||
(*tmp->api->Gamestate_Draw)(game, tmp->data);
|
||||
al_draw_bitmap(tmp->fb, 0, 0, 0);
|
||||
}
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
|
||||
SYMBOL_INTERNAL void LogicGamestates(struct Game* game) {
|
||||
SYMBOL_INTERNAL void DrawGamestates(struct Game* game) {
|
||||
ClearScreen(game);
|
||||
struct Gamestate* tmp = game->_priv.gamestates;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
SYMBOL_INTERNAL void LogicGamestates(struct Game* game, double delta) {
|
||||
struct Gamestate* tmp = game->_priv.gamestates;
|
||||
while (tmp) {
|
||||
if ((tmp->loaded) && (tmp->started) && (!tmp->paused)) {
|
||||
game->_priv.current_gamestate = tmp;
|
||||
(*tmp->api->Gamestate_Logic)(game, tmp->data);
|
||||
(*tmp->api->Gamestate_Logic)(game, tmp->data, delta);
|
||||
}
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
@ -92,6 +121,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);
|
||||
|
@ -250,6 +292,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;
|
||||
}
|
||||
|
||||
|
@ -264,6 +311,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) {
|
||||
|
@ -343,15 +391,14 @@ 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);
|
||||
// al_clear_to_color(al_map_rgb(0, 0, 0));
|
||||
}
|
||||
|
||||
static void DrawQueue(struct Game* game, struct TM_Action* queue, int clipX, int clipY) {
|
||||
|
|
|
@ -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);
|
||||
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);
|
||||
|
|
|
@ -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->config.fullscreen = strtol(GetConfigOptionDefault(game, "SuperDerpy", "fullscreen", "1"), NULL, 10);
|
||||
game->config.music = strtol(GetConfigOptionDefault(game, "SuperDerpy", "music", "10"), NULL, 10);
|
||||
|
@ -277,8 +278,8 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game* game) {
|
|||
game->_priv.loading.gamestate->data = (*game->_priv.loading.gamestate->api->Gamestate_Load)(game, NULL);
|
||||
PrintConsole(game, "Loading screen registered.");
|
||||
|
||||
game->_priv.timestamp = al_get_time();
|
||||
game->_priv.draw = true;
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
void libsuperderpy_mainloop(void* game);
|
||||
emscripten_set_main_loop_arg(libsuperderpy_mainloop, game, 0, true);
|
||||
|
@ -305,7 +306,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;
|
||||
|
||||
|
@ -389,6 +390,7 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) {
|
|||
(*game->_priv.loading.gamestate->api->Gamestate_Stop)(game, game->_priv.loading.gamestate->data);
|
||||
}
|
||||
al_resume_timer(game->_priv.timer);
|
||||
game->_priv.timestamp = al_get_time();
|
||||
}
|
||||
|
||||
tmp = tmp->next;
|
||||
|
@ -424,6 +426,12 @@ 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_flip_display();
|
||||
|
@ -445,8 +453,10 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) {
|
|||
}
|
||||
|
||||
if ((ev.type == ALLEGRO_EVENT_TIMER) && (ev.timer.source == game->_priv.timer)) {
|
||||
LogicGamestates(game);
|
||||
redraw = true;
|
||||
/* double delta = al_get_time() - game->_priv.timestamp;
|
||||
game->_priv.timestamp += delta;
|
||||
LogicGamestates(game, delta);
|
||||
redraw = true;*/
|
||||
} else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
libsuperderpy_mainloop_exit(game);
|
||||
|
@ -468,8 +478,9 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) {
|
|||
al_attach_mixer_to_voice(game->audio.mixer, game->audio.v);
|
||||
al_resume_timer(game->_priv.timer);
|
||||
} else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) {
|
||||
al_acknowledge_resize(game->display);
|
||||
SetupViewport(game, game->viewport_config);
|
||||
ResizeGamestates(game);
|
||||
al_acknowledge_resize(game->display);
|
||||
}
|
||||
#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))) {
|
||||
|
@ -483,7 +494,7 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) {
|
|||
} else if ((ev.type == ALLEGRO_EVENT_KEY_DOWN) && (game->config.debug) && (ev.keyboard.keycode == ALLEGRO_KEY_F1)) {
|
||||
int i;
|
||||
for (i = 0; i < 512; i++) {
|
||||
LogicGamestates(game);
|
||||
LogicGamestates(game, ALLEGRO_BPS_TO_SECS(al_get_timer_speed(game->_priv.timer)));
|
||||
}
|
||||
game->_priv.showconsole = true;
|
||||
PrintConsole(game, "DEBUG: 512 frames skipped...");
|
||||
|
|
|
@ -52,10 +52,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. */
|
||||
|
@ -96,6 +97,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;
|
||||
|
@ -120,6 +123,13 @@ struct Game {
|
|||
|
||||
bool draw;
|
||||
|
||||
double timestamp;
|
||||
|
||||
struct {
|
||||
int x, y;
|
||||
int w, h;
|
||||
} clip_rect;
|
||||
|
||||
#ifdef ALLEGRO_MACOSX
|
||||
char cwd[MAXPATHLEN];
|
||||
#endif
|
||||
|
@ -141,6 +151,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);
|
||||
} handlers;
|
||||
|
||||
LIBSUPERDERPY_DATA_TYPE* data;
|
||||
|
@ -153,7 +164,7 @@ void libsuperderpy_destroy(struct Game* game);
|
|||
struct GamestateResources;
|
||||
extern int Gamestate_ProgressCount;
|
||||
void Gamestate_ProcessEvent(struct Game* game, struct GamestateResources* data, ALLEGRO_EVENT* ev);
|
||||
void Gamestate_Logic(struct Game* game, struct GamestateResources* data);
|
||||
void Gamestate_Logic(struct Game* game, struct GamestateResources* data, double delta);
|
||||
void Gamestate_Draw(struct Game* game, struct GamestateResources* data);
|
||||
void* Gamestate_Load(struct Game* game, void (*progress)(struct Game*));
|
||||
void Gamestate_Unload(struct Game* game, struct GamestateResources* data);
|
||||
|
|
28
src/utils.c
28
src/utils.c
|
@ -428,6 +428,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);
|
||||
}
|
||||
|
@ -445,6 +449,20 @@ 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_add_new_bitmap_flag(ALLEGRO_NO_PRESERVE_TEXTURE);
|
||||
|
@ -452,3 +470,13 @@ SYMBOL_EXPORT ALLEGRO_BITMAP* CreateNotPreservedBitmap(int width, int height) {
|
|||
al_set_new_bitmap_flags(flags);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
SYMBOL_EXPORT void EnableCompositor(struct Game* game) {
|
||||
game->handlers.compositor = SimpleCompositor;
|
||||
ResizeGamestates(game);
|
||||
}
|
||||
|
||||
SYMBOL_EXPORT void DisableCompositor(struct Game* game) {
|
||||
game->handlers.compositor = NULL;
|
||||
ResizeGamestates(game);
|
||||
}
|
||||
|
|
|
@ -76,5 +76,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 DisableCompositor(struct Game* game);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue