mirror of
https://gitlab.com/dosowisko.net/libsuperderpy.git
synced 2025-02-07 21:56:44 +01:00
character: reuse already loaded bitmaps
This commit is contained in:
parent
05c75a9437
commit
093a808dc2
6 changed files with 120 additions and 30 deletions
|
@ -105,8 +105,13 @@ SYMBOL_EXPORT void LoadSpritesheets(struct Game* game, struct Character* charact
|
|||
PrintConsole(game, "- %s", tmp->name);
|
||||
if ((!tmp->bitmap) && (tmp->file)) {
|
||||
char filename[255] = {0};
|
||||
snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->file);
|
||||
tmp->bitmap = al_load_bitmap(GetDataFilePath(game, filename));
|
||||
if (strstr(tmp->file, "../") == tmp->file) {
|
||||
snprintf(filename, 255, "sprites/%s", tmp->file + 3);
|
||||
} else {
|
||||
snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->file);
|
||||
}
|
||||
tmp->bitmap = AddBitmap(game, filename);
|
||||
tmp->filepath = strdup(filename);
|
||||
tmp->width = al_get_bitmap_width(tmp->bitmap) / tmp->cols;
|
||||
tmp->height = al_get_bitmap_height(tmp->bitmap) / tmp->rows;
|
||||
}
|
||||
|
@ -116,8 +121,13 @@ SYMBOL_EXPORT void LoadSpritesheets(struct Game* game, struct Character* charact
|
|||
PrintConsole(game, " - %s", tmp->frames[i].file);
|
||||
}
|
||||
char filename[255] = {0};
|
||||
snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->frames[i].file);
|
||||
tmp->frames[i].bitmap = al_load_bitmap(GetDataFilePath(game, filename));
|
||||
if (strstr(tmp->frames[i].file, "../") == tmp->frames[i].file) {
|
||||
snprintf(filename, 255, "sprites/%s", tmp->frames[i].file + 3);
|
||||
} else {
|
||||
snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->frames[i].file);
|
||||
}
|
||||
tmp->frames[i].bitmap = AddBitmap(game, filename);
|
||||
tmp->frames[i].filepath = strdup(filename);
|
||||
int width = al_get_bitmap_width(tmp->frames[i].bitmap) + tmp->frames[i].x;
|
||||
if (width > tmp->width) {
|
||||
tmp->width = width;
|
||||
|
@ -146,12 +156,14 @@ SYMBOL_EXPORT void UnloadSpritesheets(struct Game* game, struct Character* chara
|
|||
struct Spritesheet* tmp = character->spritesheets;
|
||||
while (tmp) {
|
||||
for (int i = 0; i < tmp->frameCount; i++) {
|
||||
if (tmp->frames[i].bitmap) {
|
||||
if (tmp->frames[i].filepath) {
|
||||
RemoveBitmap(game, tmp->frames[i].filepath);
|
||||
} else {
|
||||
al_destroy_bitmap(tmp->frames[i].bitmap);
|
||||
}
|
||||
}
|
||||
if (tmp->bitmap) {
|
||||
al_destroy_bitmap(tmp->bitmap);
|
||||
RemoveBitmap(game, tmp->filepath);
|
||||
}
|
||||
tmp->bitmap = NULL;
|
||||
tmp = tmp->next;
|
||||
|
@ -227,6 +239,8 @@ SYMBOL_EXPORT void RegisterSpritesheet(struct Game* game, struct Character* char
|
|||
strncpy(s->predecessor, predecessor, len);
|
||||
}
|
||||
|
||||
s->filepath = NULL;
|
||||
|
||||
{
|
||||
s->file = NULL;
|
||||
const char* file = al_get_config_value(config, "animation", "file");
|
||||
|
@ -263,6 +277,7 @@ SYMBOL_EXPORT void RegisterSpritesheet(struct Game* game, struct Character* char
|
|||
s->frames[i].file = malloc(len * sizeof(char));
|
||||
strncpy(s->frames[i].file, file, len);
|
||||
}
|
||||
s->frames[i].filepath = NULL;
|
||||
|
||||
if (!file) {
|
||||
s->frames[i].col = i % s->cols;
|
||||
|
@ -342,15 +357,23 @@ SYMBOL_EXPORT void DestroyCharacter(struct Game* game, struct Character* charact
|
|||
free(tmp->file);
|
||||
}
|
||||
for (int i = 0; i < tmp->frameCount; i++) {
|
||||
if (tmp->frames[i].bitmap) {
|
||||
if (tmp->frames[i].filepath) {
|
||||
RemoveBitmap(game, tmp->frames[i].filepath);
|
||||
} else {
|
||||
al_destroy_bitmap(tmp->frames[i].bitmap);
|
||||
}
|
||||
if (tmp->frames[i].file) {
|
||||
free(tmp->frames[i].file);
|
||||
}
|
||||
if (tmp->frames[i].filepath) {
|
||||
free(tmp->frames[i].filepath);
|
||||
}
|
||||
}
|
||||
if (tmp->bitmap) {
|
||||
al_destroy_bitmap(tmp->bitmap);
|
||||
RemoveBitmap(game, tmp->filepath);
|
||||
}
|
||||
if (tmp->filepath) {
|
||||
free(tmp->filepath);
|
||||
}
|
||||
free(tmp->frames);
|
||||
free(tmp->name);
|
||||
|
@ -475,6 +498,7 @@ SYMBOL_EXPORT ALLEGRO_TRANSFORM GetCharacterTransform(struct Game* game, struct
|
|||
ALLEGRO_TRANSFORM transform;
|
||||
int w = character->spritesheet->width, h = character->spritesheet->height;
|
||||
al_identity_transform(&transform);
|
||||
|
||||
al_translate_transform(&transform, -w / 2.0, -h / 2.0);
|
||||
al_scale_transform(&transform, ((character->flipX ^ character->spritesheet->flipX ^ character->frame->flipX) ? -1 : 1), ((character->flipY ^ character->spritesheet->flipY ^ character->frame->flipY) ? -1 : 1)); // flipping; FIXME: should it be here or later?
|
||||
al_translate_transform(&transform, w / 2.0, h / 2.0);
|
||||
|
@ -483,10 +507,13 @@ SYMBOL_EXPORT ALLEGRO_TRANSFORM GetCharacterTransform(struct Game* game, struct
|
|||
al_translate_transform(&transform, -w * character->spritesheet->pivotX, -h * character->spritesheet->pivotY); // pivot
|
||||
al_scale_transform(&transform, character->scaleX, character->scaleY);
|
||||
al_rotate_transform(&transform, character->angle);
|
||||
|
||||
al_translate_transform(&transform, GetCharacterX(game, character), GetCharacterY(game, character)); // position
|
||||
|
||||
if (character->parent) {
|
||||
ALLEGRO_TRANSFORM parent = GetCharacterTransform(game, character->parent);
|
||||
al_compose_transform(&transform, &parent);
|
||||
// FIXME: position should be calculated in relation to parents pivot point
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
struct SpritesheetFrame {
|
||||
char* file;
|
||||
char* filepath;
|
||||
ALLEGRO_BITMAP* bitmap;
|
||||
double duration;
|
||||
int row;
|
||||
|
@ -46,6 +47,7 @@ struct Spritesheet {
|
|||
int cols; /*!< Number of columns in the spritesheet. */
|
||||
double duration;
|
||||
char* file;
|
||||
char* filepath;
|
||||
int repeats; /*!< Number of repeats to make before the spritesheet is changed to its successor. */
|
||||
char* successor; /*!< Name of animation successor. If it's not blank, then animation will be played only once. */
|
||||
char* predecessor;
|
||||
|
|
|
@ -359,13 +359,13 @@ SYMBOL_INTERNAL void CloseGamestate(struct Game* game, struct Gamestate* gamesta
|
|||
al_destroy_bitmap(gamestate->fb);
|
||||
}
|
||||
|
||||
SYMBOL_INTERNAL struct libsuperderpy_list* AddToList(struct libsuperderpy_list* list, void* data) {
|
||||
SYMBOL_INTERNAL struct List* AddToList(struct List* list, void* data) {
|
||||
if (!list) {
|
||||
list = malloc(sizeof(struct libsuperderpy_list));
|
||||
list = malloc(sizeof(struct List));
|
||||
list->data = data;
|
||||
list->next = NULL;
|
||||
} else {
|
||||
struct libsuperderpy_list* elem = malloc(sizeof(struct libsuperderpy_list));
|
||||
struct List* elem = malloc(sizeof(struct List));
|
||||
elem->next = list;
|
||||
elem->data = data;
|
||||
list = elem;
|
||||
|
@ -373,12 +373,12 @@ SYMBOL_INTERNAL struct libsuperderpy_list* AddToList(struct libsuperderpy_list*
|
|||
return list;
|
||||
}
|
||||
|
||||
static bool Identity(struct libsuperderpy_list* elem, void* data) {
|
||||
static bool Identity(struct List* elem, void* data) {
|
||||
return elem->data == data;
|
||||
}
|
||||
|
||||
SYMBOL_INTERNAL struct libsuperderpy_list* FindInList(struct libsuperderpy_list* list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data)) {
|
||||
struct libsuperderpy_list* tmp = list;
|
||||
SYMBOL_INTERNAL struct List* FindInList(struct List* list, void* data, bool (*identity)(struct List* elem, void* data)) {
|
||||
struct List* tmp = list;
|
||||
if (!identity) {
|
||||
identity = Identity;
|
||||
}
|
||||
|
@ -391,8 +391,8 @@ SYMBOL_INTERNAL struct libsuperderpy_list* FindInList(struct libsuperderpy_list*
|
|||
return NULL;
|
||||
}
|
||||
|
||||
SYMBOL_INTERNAL void* RemoveFromList(struct libsuperderpy_list** list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data)) {
|
||||
struct libsuperderpy_list *prev = NULL, *tmp = *list, *start;
|
||||
SYMBOL_INTERNAL void* RemoveFromList(struct List** list, void* data, bool (*identity)(struct List* elem, void* data)) {
|
||||
struct List *prev = NULL, *tmp = *list, *start;
|
||||
void* d = NULL;
|
||||
if (!identity) {
|
||||
identity = Identity;
|
||||
|
@ -424,7 +424,7 @@ SYMBOL_INTERNAL void* AddGarbage(struct Game* game, void* data) {
|
|||
}
|
||||
|
||||
SYMBOL_INTERNAL void ClearGarbage(struct Game* game) {
|
||||
struct libsuperderpy_list* tmp;
|
||||
struct List* tmp;
|
||||
while (game->_priv.garbage) {
|
||||
free(game->_priv.garbage->data);
|
||||
tmp = game->_priv.garbage->next;
|
||||
|
@ -438,16 +438,16 @@ SYMBOL_INTERNAL void AddTimeline(struct Game* game, struct Timeline* timeline) {
|
|||
}
|
||||
|
||||
SYMBOL_INTERNAL void RemoveTimeline(struct Game* game, struct Timeline* timeline) {
|
||||
struct libsuperderpy_list* tmp = game->_priv.timelines;
|
||||
struct List* tmp = game->_priv.timelines;
|
||||
if (tmp->data == timeline) {
|
||||
struct libsuperderpy_list* next = tmp->next;
|
||||
struct List* next = tmp->next;
|
||||
free(tmp);
|
||||
game->_priv.timelines = next;
|
||||
return;
|
||||
}
|
||||
while (tmp->next) {
|
||||
if (tmp->next->data == timeline) {
|
||||
struct libsuperderpy_list* next = tmp->next->next;
|
||||
struct List* next = tmp->next->next;
|
||||
free(tmp->next);
|
||||
tmp->next = next;
|
||||
return;
|
||||
|
@ -500,7 +500,7 @@ SYMBOL_INTERNAL void DrawTimelines(struct Game* game) {
|
|||
if (!game->_priv.showtimeline) {
|
||||
return;
|
||||
}
|
||||
struct libsuperderpy_list* tmp = game->_priv.timelines;
|
||||
struct List* tmp = game->_priv.timelines;
|
||||
int i = 0;
|
||||
while (tmp) {
|
||||
DrawTimeline(game, tmp->data, i);
|
||||
|
@ -574,3 +574,54 @@ SYMBOL_INTERNAL char* GetGameName(struct Game* game, const char* format) {
|
|||
SUPPRESS_END
|
||||
return AddGarbage(game, result);
|
||||
}
|
||||
|
||||
static int HashString(struct Game* game, const char* str) {
|
||||
unsigned long hash = 5381;
|
||||
int c;
|
||||
|
||||
while ((c = *str++)) {
|
||||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||
}
|
||||
|
||||
//PrintConsole(game, "sum %d, bucket %d", hash, hash % LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS);
|
||||
return hash % LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS;
|
||||
}
|
||||
|
||||
static bool RefCountIdentity(struct List* elem, void* data) {
|
||||
struct RefCount* item = elem->data;
|
||||
return strcmp(data, item->id) == 0;
|
||||
}
|
||||
|
||||
SYMBOL_INTERNAL ALLEGRO_BITMAP* AddBitmap(struct Game* game, char* filename) {
|
||||
int bucket = HashString(game, filename);
|
||||
struct List* item = FindInList(game->_priv.bitmaps[bucket], filename, RefCountIdentity);
|
||||
struct RefCount* rc;
|
||||
if (item) {
|
||||
rc = item->data;
|
||||
rc->counter++;
|
||||
} else {
|
||||
rc = malloc(sizeof(struct RefCount));
|
||||
rc->counter = 1;
|
||||
rc->id = strdup(filename);
|
||||
rc->data = al_load_bitmap(GetDataFilePath(game, filename));
|
||||
game->_priv.bitmaps[bucket] = AddToList(game->_priv.bitmaps[bucket], rc);
|
||||
}
|
||||
return rc->data;
|
||||
}
|
||||
|
||||
SYMBOL_INTERNAL void RemoveBitmap(struct Game* game, char* filename) {
|
||||
int bucket = HashString(game, filename);
|
||||
struct List* item = FindInList(game->_priv.bitmaps[bucket], filename, RefCountIdentity);
|
||||
if (item) {
|
||||
struct RefCount* rc = item->data;
|
||||
rc->counter--;
|
||||
if (rc->counter == 0) {
|
||||
RemoveFromList(&game->_priv.bitmaps[bucket], filename, RefCountIdentity);
|
||||
al_destroy_bitmap(rc->data);
|
||||
free(rc->id);
|
||||
free(rc);
|
||||
}
|
||||
} else {
|
||||
PrintConsole(game, "Tried to remove non-existent bitmap %s!", filename);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,9 +46,15 @@
|
|||
#define SUPPRESS_END
|
||||
#endif
|
||||
|
||||
struct libsuperderpy_list {
|
||||
struct RefCount {
|
||||
int counter;
|
||||
char* id;
|
||||
void* data;
|
||||
struct libsuperderpy_list* next;
|
||||
};
|
||||
|
||||
struct List {
|
||||
void* data;
|
||||
struct List* next;
|
||||
};
|
||||
|
||||
struct GamestateLoadingThreadData {
|
||||
|
@ -80,9 +86,9 @@ void GamestateProgress(struct Game* game);
|
|||
void* AddGarbage(struct Game* game, void* data);
|
||||
void ClearGarbage(struct Game* game);
|
||||
void ClearScreen(struct Game* game);
|
||||
struct libsuperderpy_list* AddToList(struct libsuperderpy_list* list, void* data);
|
||||
struct libsuperderpy_list* FindInList(struct libsuperderpy_list* list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data));
|
||||
void* RemoveFromList(struct libsuperderpy_list** list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data));
|
||||
struct List* AddToList(struct List* list, void* data);
|
||||
struct List* FindInList(struct List* list, void* data, bool (*identity)(struct List* elem, void* data));
|
||||
void* RemoveFromList(struct List** list, void* data, bool (*identity)(struct List* elem, void* data));
|
||||
void AddTimeline(struct Game* game, struct Timeline* timeline);
|
||||
void RemoveTimeline(struct Game* game, struct Timeline* timeline);
|
||||
void DrawTimelines(struct Game* game);
|
||||
|
@ -96,5 +102,7 @@ void ResumeExecution(struct Game* game);
|
|||
void ReloadShaders(struct Game* game, bool force);
|
||||
void DestroyShaders(struct Game* game);
|
||||
char* GetGameName(struct Game* game, const char* format);
|
||||
ALLEGRO_BITMAP* AddBitmap(struct Game* game, char* filename);
|
||||
void RemoveBitmap(struct Game* game, char* filename);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -57,6 +57,8 @@ struct GamestateResources;
|
|||
#include <math.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#define LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS 16
|
||||
|
||||
struct Gamestate;
|
||||
|
||||
struct Viewport {
|
||||
|
@ -129,7 +131,7 @@ struct Game {
|
|||
|
||||
struct Gamestate* current_gamestate;
|
||||
|
||||
struct libsuperderpy_list *garbage, *timelines, *shaders;
|
||||
struct List *garbage, *timelines, *shaders, *bitmaps[LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS];
|
||||
|
||||
bool draw;
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ SYMBOL_EXPORT ALLEGRO_SHADER* CreateShader(struct Game* game, const char* vertex
|
|||
return shader;
|
||||
}
|
||||
|
||||
static bool ShaderIdentity(struct libsuperderpy_list* item, void* shader) {
|
||||
static bool ShaderIdentity(struct List* item, void* shader) {
|
||||
return ((struct ShaderListItem*)item->data)->shader == shader;
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ SYMBOL_EXPORT void DestroyShader(struct Game* game, ALLEGRO_SHADER* shader) {
|
|||
}
|
||||
|
||||
SYMBOL_INTERNAL void ReloadShaders(struct Game* game, bool force) {
|
||||
struct libsuperderpy_list* list = game->_priv.shaders;
|
||||
struct List* list = game->_priv.shaders;
|
||||
PrintConsole(game, "Reloading shaders...");
|
||||
while (list) {
|
||||
struct ShaderListItem* item = list->data;
|
||||
|
@ -150,7 +150,7 @@ SYMBOL_INTERNAL void DestroyShaders(struct Game* game) {
|
|||
if (item->fragment) {
|
||||
free(item->fragment);
|
||||
}
|
||||
struct libsuperderpy_list* prev = game->_priv.shaders;
|
||||
struct List* prev = game->_priv.shaders;
|
||||
game->_priv.shaders = game->_priv.shaders->next;
|
||||
free(prev);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue