initial work on optional compositing and variable speed logic ticks

This commit is contained in:
Sebastian Krzyszkowiak 2017-11-23 22:08:05 +01:00
parent 1f96d6f364
commit 66818031ed
7 changed files with 133 additions and 25 deletions

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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...");

View file

@ -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);

View file

@ -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);
}

View file

@ -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