Implemented the core functionality of the ObjectStore and started
implementing the functionality for the AssetManager
This commit is contained in:
1 parent
79f28100bd
commit
c4f1775392
14 files changed
+330
-383
No files matched your search
+6
-4
@@ -22,11 +22,12 @@ set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/../lib")
|
|||||||
|
|
||||||
# TARGETS
|
# TARGETS
|
||||||
add_library(${PROJECT_NAME} SHARED
|
add_library(${PROJECT_NAME} SHARED
|
||||||
src/GameManager.c
|
src/GameEngine.c
|
||||||
src/Window.c
|
src/Window.c
|
||||||
src/Renderer.c
|
src/Renderer.c
|
||||||
src/ObjectStore.c
|
src/ObjectStore.c
|
||||||
src/Hashmap.c
|
src/AssetManager.c
|
||||||
|
src/reusable_ids.c
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||||
@@ -47,11 +48,12 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
|
|||||||
install(FILES
|
install(FILES
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/engine_build_config.h
|
${CMAKE_CURRENT_BINARY_DIR}/engine_build_config.h
|
||||||
include/constants.h
|
include/constants.h
|
||||||
include/GameManager.h
|
include/GameEngine.h
|
||||||
include/Window.h
|
include/Window.h
|
||||||
include/Renderer.h
|
include/Renderer.h
|
||||||
include/ObjectStore.h
|
include/ObjectStore.h
|
||||||
include/Hashmap.h
|
include/AssetManager.h
|
||||||
|
include/reusable_ids.h
|
||||||
CONFIGURATIONS Release
|
CONFIGURATIONS Release
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef ASSET_MANAGER_H
|
||||||
|
#define ASSET_MANAGER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef int32_t AssetID;
|
||||||
|
typedef enum {
|
||||||
|
ASSET_TYPE_IMAGE,
|
||||||
|
ASSET_TYPE_AUDIO_FX,
|
||||||
|
ASSET_TYPE_MUSIC,
|
||||||
|
|
||||||
|
ASSET_TYPE_COUNT
|
||||||
|
} AssetType;
|
||||||
|
typedef struct asset_manager AssetManager;
|
||||||
|
|
||||||
|
AssetManager *create_asset_manager();
|
||||||
|
AssetID load_asset(AssetManager *am, const char *path, AssetType type);
|
||||||
|
void close_asset(AssetManager *am, AssetID id);
|
||||||
|
void destroy_asset_manager(AssetManager *am);
|
||||||
|
|
||||||
|
#endif // !ASSET_MANAGER_H
|
||||||
@@ -7,9 +7,9 @@
|
|||||||
bool init_game(const char *game_name, int window_width, int window_height,
|
bool init_game(const char *game_name, int window_width, int window_height,
|
||||||
bool full_screen);
|
bool full_screen);
|
||||||
bool game_running();
|
bool game_running();
|
||||||
void add_object(void *object, const char *object_tag, EventHandlerPtr eh,
|
ObjectID add_object(void *object, EventHandlerPtr eh, UpdateFuncPtr uf,
|
||||||
UpdateFuncPtr uf, RenderFuncPtr rf);
|
RenderFuncPtr rf);
|
||||||
void remove_object(const char *object_tag);
|
void remove_object(ObjectID id);
|
||||||
void handle_events();
|
void handle_events();
|
||||||
void update();
|
void update();
|
||||||
void render();
|
void render();
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#ifndef HASHMAP_H
|
|
||||||
#define HASHMAP_H
|
|
||||||
|
|
||||||
#include "engine_build_config.h"
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
|
|
||||||
typedef struct hashmap Hashmap;
|
|
||||||
|
|
||||||
Hashmap *create_hashmap();
|
|
||||||
int32_t add_object_to_map(Hashmap *map, const char *key);
|
|
||||||
int32_t get_object_index(Hashmap *map, const char *key);
|
|
||||||
int32_t delete_object_from_map(Hashmap *map, const char *key);
|
|
||||||
void destroy_hashmap(Hashmap *map);
|
|
||||||
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
void print_hashmap(Hashmap *map);
|
|
||||||
void print_non_empty(Hashmap *map);
|
|
||||||
#endif // DEBUG_ENGINE
|
|
||||||
|
|
||||||
#endif // !HASHMAP_H
|
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "SDL_events.h"
|
#include "SDL_events.h"
|
||||||
#include "engine_build_config.h"
|
#include "engine_build_config.h"
|
||||||
|
|
||||||
|
typedef int32_t ObjectID;
|
||||||
typedef void (*EventHandlerPtr)(const SDL_Event *event, void *object);
|
typedef void (*EventHandlerPtr)(const SDL_Event *event, void *object);
|
||||||
typedef void (*UpdateFuncPtr)(float delta, void *object);
|
typedef void (*UpdateFuncPtr)(float delta, void *object);
|
||||||
typedef void (*RenderFuncPtr)(Renderer *renderer, void *object);
|
typedef void (*RenderFuncPtr)(Renderer *renderer, void *object);
|
||||||
@@ -12,10 +13,9 @@ typedef void (*RenderFuncPtr)(Renderer *renderer, void *object);
|
|||||||
typedef struct object_store ObjectStore;
|
typedef struct object_store ObjectStore;
|
||||||
|
|
||||||
ObjectStore *create_object_store();
|
ObjectStore *create_object_store();
|
||||||
void add_object_to_store(ObjectStore *os, void *object, const char *object_tag,
|
ObjectID add_object_to_store(ObjectStore *os, void *object, EventHandlerPtr eh,
|
||||||
EventHandlerPtr eh, UpdateFuncPtr uf,
|
UpdateFuncPtr uf, RenderFuncPtr rf);
|
||||||
RenderFuncPtr rf);
|
void remove_object_from_store(ObjectStore *os, ObjectID id);
|
||||||
void remove_object_from_store(ObjectStore *os, const char *object_tag);
|
|
||||||
void handle_all_objects_events(ObjectStore *os, const SDL_Event *event);
|
void handle_all_objects_events(ObjectStore *os, const SDL_Event *event);
|
||||||
void update_all_objects(ObjectStore *os, float delta);
|
void update_all_objects(ObjectStore *os, float delta);
|
||||||
void render_all_objects(ObjectStore *os, Renderer *renderer);
|
void render_all_objects(ObjectStore *os, Renderer *renderer);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#define INTERNAL static
|
#define INTERNAL static
|
||||||
|
|
||||||
#define MAX_OBJ_COUNT 10000
|
#define MAX_OBJ_COUNT 10000
|
||||||
|
#define MAX_ASSET_COUNT 1000
|
||||||
#define HASH_PRIME_01 1987
|
#define HASH_PRIME_01 1987
|
||||||
#define HASH_PRIME_02 7919
|
#define HASH_PRIME_02 7919
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef REUSABLE_IDS_H
|
||||||
|
#define REUSABLE_IDS_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int32_t reuse_id(int32_t *ids, size_t count);
|
||||||
|
|
||||||
|
#endif // !REUSABLE_IDS_H
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
#include "AssetManager.h"
|
||||||
|
#include "SDL_image.h"
|
||||||
|
#include "SDL_mixer.h"
|
||||||
|
#include "SDL_surface.h"
|
||||||
|
#include "constants.h"
|
||||||
|
#include "reusable_ids.h"
|
||||||
|
#include <SDL2/SDL_surface.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct asset {
|
||||||
|
AssetType type;
|
||||||
|
char *path;
|
||||||
|
union {
|
||||||
|
SDL_Surface *surface;
|
||||||
|
Mix_Chunk *chunk;
|
||||||
|
Mix_Music *music;
|
||||||
|
} data;
|
||||||
|
} Asset;
|
||||||
|
|
||||||
|
struct asset_manager {
|
||||||
|
size_t count;
|
||||||
|
size_t deleted_count;
|
||||||
|
AssetID reusable_ids[MAX_ASSET_COUNT];
|
||||||
|
Asset assets[MAX_ASSET_COUNT];
|
||||||
|
};
|
||||||
|
|
||||||
|
AssetManager *create_asset_manager() {
|
||||||
|
AssetManager *am = (AssetManager *)malloc(sizeof(AssetManager));
|
||||||
|
if (!am) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
am->count = 0;
|
||||||
|
am->deleted_count = 0;
|
||||||
|
memset(am->reusable_ids, 0, sizeof(am->reusable_ids));
|
||||||
|
memset(am->assets, 0, sizeof(am->assets));
|
||||||
|
|
||||||
|
return am;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetID load_asset(AssetManager *am, const char *path, AssetType type) {
|
||||||
|
if (!am || type == ASSET_TYPE_COUNT) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Asset asset = {};
|
||||||
|
bool loaded = true;
|
||||||
|
|
||||||
|
// TODO (Abdelrahman): Implement a way to make sure that an asset with the
|
||||||
|
// same path is not already loaded. If the user attempts to load an asset
|
||||||
|
// that already exists, the function should return -1 to indicate an error
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ASSET_TYPE_IMAGE:
|
||||||
|
asset.data.surface = IMG_Load(path);
|
||||||
|
|
||||||
|
if (!(asset.data.surface)) {
|
||||||
|
loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ASSET_TYPE_AUDIO_FX:
|
||||||
|
break;
|
||||||
|
case ASSET_TYPE_MUSIC:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
loaded = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loaded) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetID id;
|
||||||
|
|
||||||
|
if (am->deleted_count > 0) {
|
||||||
|
id = (AssetID)reuse_id((int32_t *)am->reusable_ids, am->deleted_count);
|
||||||
|
am->deleted_count -= 1;
|
||||||
|
am->count += 1;
|
||||||
|
} else {
|
||||||
|
id = (am->count)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
am->assets[id].type = type;
|
||||||
|
|
||||||
|
am->assets[id].path = (char *)malloc(strlen(path) + 1);
|
||||||
|
strcpy(am->assets[id].path, path);
|
||||||
|
|
||||||
|
am->assets[id].data = asset.data;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ASSET_TYPE_IMAGE:
|
||||||
|
asset.data.surface = NULL;
|
||||||
|
break;
|
||||||
|
case ASSET_TYPE_AUDIO_FX:
|
||||||
|
asset.data.chunk = NULL;
|
||||||
|
break;
|
||||||
|
case ASSET_TYPE_MUSIC:
|
||||||
|
asset.data.music = NULL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_asset(AssetManager *am, AssetID id) {
|
||||||
|
if (!am || am->count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(am->assets[id].path);
|
||||||
|
am->assets[id].path = 0;
|
||||||
|
|
||||||
|
switch (am->assets[id].type) {
|
||||||
|
case ASSET_TYPE_IMAGE:
|
||||||
|
SDL_FreeSurface(am->assets[id].data.surface);
|
||||||
|
am->assets[id].data.surface = NULL;
|
||||||
|
break;
|
||||||
|
case ASSET_TYPE_AUDIO_FX:
|
||||||
|
break;
|
||||||
|
case ASSET_TYPE_MUSIC:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
am->reusable_ids[(am->deleted_count)++] = id;
|
||||||
|
|
||||||
|
am->count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_asset_manager(AssetManager *am) {
|
||||||
|
if (!am) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < am->count; ++i) {
|
||||||
|
free(am->assets[i].path);
|
||||||
|
am->assets[i].path = NULL;
|
||||||
|
|
||||||
|
switch (am->assets[i].type) {
|
||||||
|
case ASSET_TYPE_IMAGE:
|
||||||
|
SDL_FreeSurface(am->assets[i].data.surface);
|
||||||
|
am->assets[i].data.surface = NULL;
|
||||||
|
break;
|
||||||
|
case ASSET_TYPE_AUDIO_FX:
|
||||||
|
break;
|
||||||
|
case ASSET_TYPE_MUSIC:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
am->count = 0;
|
||||||
|
am->deleted_count = 0;
|
||||||
|
|
||||||
|
free(am);
|
||||||
|
am = NULL;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "GameManager.h"
|
#include "GameEngine.h"
|
||||||
#include "ObjectStore.h"
|
#include "ObjectStore.h"
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include "SDL_error.h"
|
#include "SDL_error.h"
|
||||||
@@ -66,23 +66,22 @@ bool init_game(const char *game_name, int window_width, int window_height,
|
|||||||
|
|
||||||
bool game_running() { return Game.running; }
|
bool game_running() { return Game.running; }
|
||||||
|
|
||||||
void add_object(void *object, const char *object_tag,
|
ObjectID add_object(void *object, EventHandlerPtr event_handler,
|
||||||
EventHandlerPtr event_handler, UpdateFuncPtr update_func,
|
UpdateFuncPtr update_func, RenderFuncPtr render_func) {
|
||||||
RenderFuncPtr render_func) {
|
|
||||||
if (!(Game.running) || !(Game.object_store)) {
|
if (!(Game.running) || !(Game.object_store)) {
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_object_to_store(Game.object_store, object, object_tag, event_handler,
|
return add_object_to_store(Game.object_store, object, event_handler,
|
||||||
update_func, render_func);
|
update_func, render_func);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_object(const char *object_tag) {
|
void remove_object(ObjectID id) {
|
||||||
if (!(Game.running) || !(Game.object_store)) {
|
if (!(Game.running) || !(Game.object_store)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_object_from_store(Game.object_store, object_tag);
|
remove_object_from_store(Game.object_store, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_events() {
|
void handle_events() {
|
||||||
-198
@@ -1,198 +0,0 @@
|
|||||||
#include "Hashmap.h"
|
|
||||||
#include "constants.h"
|
|
||||||
#include "engine_build_config.h"
|
|
||||||
#include <math.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
|
|
||||||
struct hashmap {
|
|
||||||
size_t count;
|
|
||||||
char *keys[MAX_OBJ_COUNT];
|
|
||||||
int32_t active_indices[MAX_OBJ_COUNT];
|
|
||||||
};
|
|
||||||
|
|
||||||
Hashmap *create_hashmap() {
|
|
||||||
Hashmap *map = malloc(sizeof(Hashmap));
|
|
||||||
|
|
||||||
map->count = 0;
|
|
||||||
|
|
||||||
memset(map->keys, 0, MAX_OBJ_COUNT);
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_OBJ_COUNT; ++i) {
|
|
||||||
map->active_indices[i] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t hash_key(const char *key) {
|
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
for (size_t j = 0; key[j]; ++j) {
|
|
||||||
i += (size_t)key[j] * HASH_PRIME_01 % MAX_OBJ_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = (i * HASH_PRIME_02) % MAX_OBJ_COUNT;
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t add_object_to_map(Hashmap *map, const char *key) {
|
|
||||||
if (map->count + 1 == MAX_OBJ_COUNT) {
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
printf("Map full\n");
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = hash_key(key);
|
|
||||||
|
|
||||||
if (map->keys[index]) {
|
|
||||||
if (strcmp(map->keys[index], key) == 0) {
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
printf("Key \"%s\" already exists\n", key);
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
} else {
|
|
||||||
// TODO (Abdelrahman): Implement a way to resolve collisions between
|
|
||||||
// multiple keys
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
printf("Collision detected. Key \"%s\" collides with key \"%s\"\n", key,
|
|
||||||
map->keys[index]);
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
map->keys[index] = malloc(strlen(key) + 1);
|
|
||||||
strcpy(map->keys[index], key);
|
|
||||||
map->active_indices[map->count] = index;
|
|
||||||
map->count += 1;
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t get_object_index(Hashmap *map, const char *key) {
|
|
||||||
if (map->count == 0) {
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
printf("Key \"%s\" doesn't exist\n", key);
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = hash_key(key);
|
|
||||||
|
|
||||||
if (!(map->keys[index]) || strcmp(map->keys[index], key) != 0) {
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
printf("Key \"%s\" doesn't exist\n", key);
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int32_t)index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t delete_object_from_map(Hashmap *map, const char *key) {
|
|
||||||
if (map->count == 0) {
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
printf("Key \"%s\" doesn't exist\n", key);
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = hash_key(key);
|
|
||||||
|
|
||||||
if (!(map->keys[index]) || strcmp(map->keys[index], key) != 0) {
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
printf("Key \"%s\" doesn't exist\n", key);
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(map->keys[index]);
|
|
||||||
map->keys[index] = NULL;
|
|
||||||
|
|
||||||
size_t packing_start_index = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < map->count; ++i) {
|
|
||||||
if (map->active_indices[i] == index) {
|
|
||||||
map->active_indices[i] = -1;
|
|
||||||
packing_start_index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the indices are packed in the array
|
|
||||||
for (; packing_start_index < map->count; ++packing_start_index) {
|
|
||||||
if (packing_start_index + 1 == MAX_OBJ_COUNT) {
|
|
||||||
// Make sure we don't go out of bounds if we are at the last item
|
|
||||||
map->active_indices[packing_start_index] = -1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
map->active_indices[packing_start_index] =
|
|
||||||
map->active_indices[packing_start_index + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
map->count -= 1;
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_hashmap(Hashmap *map) {
|
|
||||||
if (!map) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < map->count; ++i) {
|
|
||||||
size_t index = map->active_indices[i];
|
|
||||||
|
|
||||||
if (index == -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map->keys[index]) {
|
|
||||||
free(map->keys[index]);
|
|
||||||
map->keys[index] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
map->active_indices[i] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
map->count = 0;
|
|
||||||
|
|
||||||
free(map);
|
|
||||||
map = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_ENGINE
|
|
||||||
void print_hashmap(Hashmap *map) {
|
|
||||||
if (map->count == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < map->count; ++i) {
|
|
||||||
size_t index = map->active_indices[i];
|
|
||||||
printf("%d: %s\n", i, map->keys[index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_non_empty(Hashmap *map) {
|
|
||||||
for (int i = 0; i < MAX_OBJ_COUNT; ++i) {
|
|
||||||
if (map->active_indices[i] != -1) {
|
|
||||||
printf("%d is not set to -1\n", i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* DEBUG_ENGINE */
|
|
||||||
+76
-139
@@ -1,25 +1,28 @@
|
|||||||
#include "ObjectStore.h"
|
#include "ObjectStore.h"
|
||||||
#include "Hashmap.h"
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
#include "engine_build_config.h"
|
||||||
|
#include "reusable_ids.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENGINE
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#endif /* DEBUG_ENGINE */
|
||||||
|
|
||||||
struct object_store {
|
struct object_store {
|
||||||
size_t count;
|
size_t count;
|
||||||
int32_t active_indices[MAX_OBJ_COUNT];
|
size_t deleted_count;
|
||||||
Hashmap *map;
|
ObjectID reusable_ids[MAX_OBJ_COUNT];
|
||||||
void **object_ptrs;
|
void *object_ptrs[MAX_OBJ_COUNT];
|
||||||
EventHandlerPtr *event_handlers;
|
EventHandlerPtr event_handlers[MAX_OBJ_COUNT];
|
||||||
UpdateFuncPtr *update_funcs;
|
UpdateFuncPtr update_funcs[MAX_OBJ_COUNT];
|
||||||
RenderFuncPtr *render_funcs;
|
RenderFuncPtr render_funcs[MAX_OBJ_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
ObjectStore *create_object_store() {
|
ObjectStore *create_object_store() {
|
||||||
bool allocation_complete = true;
|
|
||||||
|
|
||||||
ObjectStore *os = (ObjectStore *)malloc(sizeof(ObjectStore));
|
ObjectStore *os = (ObjectStore *)malloc(sizeof(ObjectStore));
|
||||||
if (!os) {
|
if (!os) {
|
||||||
// If allocating memory to the store fails, we just return NULL. No need to
|
// If allocating memory to the store fails, we just return NULL. No need to
|
||||||
@@ -27,149 +30,104 @@ ObjectStore *create_object_store() {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
os->map = create_hashmap();
|
|
||||||
if (!(os->map)) {
|
|
||||||
allocation_complete = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
os->count = 0;
|
os->count = 0;
|
||||||
|
os->deleted_count = 0;
|
||||||
os->object_ptrs = malloc(sizeof(void *) * MAX_OBJ_COUNT);
|
memset(os->reusable_ids, 0, sizeof(os->reusable_ids));
|
||||||
if (!(os->object_ptrs)) {
|
memset(os->object_ptrs, 0, sizeof(os->object_ptrs));
|
||||||
allocation_complete = false;
|
memset(os->event_handlers, 0, sizeof(os->event_handlers));
|
||||||
}
|
memset(os->update_funcs, 0, sizeof(os->update_funcs));
|
||||||
|
memset(os->render_funcs, 0, sizeof(os->render_funcs));
|
||||||
os->event_handlers =
|
|
||||||
(EventHandlerPtr *)malloc(sizeof(EventHandlerPtr) * MAX_OBJ_COUNT);
|
|
||||||
if (!(os->event_handlers)) {
|
|
||||||
allocation_complete = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
os->update_funcs =
|
|
||||||
(UpdateFuncPtr *)malloc(sizeof(UpdateFuncPtr) * MAX_OBJ_COUNT);
|
|
||||||
if (!(os->update_funcs)) {
|
|
||||||
allocation_complete = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
os->render_funcs =
|
|
||||||
(RenderFuncPtr *)malloc(sizeof(RenderFuncPtr) * MAX_OBJ_COUNT);
|
|
||||||
if (!(os->render_funcs)) {
|
|
||||||
allocation_complete = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!allocation_complete) {
|
|
||||||
// If any of the allocations failed, we need to call the destroy function
|
|
||||||
// to free any resources that have been allocated successfully.
|
|
||||||
destroy_object_store(os);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_OBJ_COUNT; ++i) {
|
|
||||||
os->active_indices[i] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_object_to_store(ObjectStore *os, void *object, const char *object_tag,
|
ObjectID add_object_to_store(ObjectStore *os, void *object, EventHandlerPtr eh,
|
||||||
EventHandlerPtr eh, UpdateFuncPtr uf,
|
UpdateFuncPtr uf, RenderFuncPtr rf) {
|
||||||
RenderFuncPtr rf) {
|
if (!os || !object ||
|
||||||
int32_t index = add_object_to_map(os->map, object_tag);
|
(os->count + 1 == MAX_OBJ_COUNT && os->deleted_count == 0)) {
|
||||||
|
#ifdef DEBUG_ENGINE
|
||||||
|
printf("Failed to add object\n");
|
||||||
|
#endif /* DEBUG_ENGINE */
|
||||||
|
|
||||||
if (index < 0) {
|
return -1;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
os->active_indices[os->count] = index;
|
// TODO (Abdelrahman): Need to figure out a way to check that the object
|
||||||
os->count += 1;
|
// hasn't already been added to the store. If it has been added before, the
|
||||||
os->object_ptrs[index] = object;
|
// function should return -1 indicating an error to ensure that there aren't
|
||||||
os->event_handlers[index] = eh;
|
// two references to the same object without the user being aware that they
|
||||||
os->update_funcs[index] = uf;
|
// refer to the same thing. If the user intentionally copies the ID of one
|
||||||
os->render_funcs[index] = rf;
|
// object, then it would be their responsibility to make sure they don't
|
||||||
|
// delete the object using one of the IDs, then try to use it with the other.
|
||||||
|
|
||||||
|
ObjectID id;
|
||||||
|
|
||||||
|
if (os->deleted_count > 0) {
|
||||||
|
id = (ObjectID)reuse_id((int32_t *)os->reusable_ids, os->deleted_count);
|
||||||
|
os->deleted_count -= 1;
|
||||||
|
os->count += 1;
|
||||||
|
} else {
|
||||||
|
id = (os->count)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
os->object_ptrs[id] = object;
|
||||||
|
os->event_handlers[id] = eh;
|
||||||
|
os->update_funcs[id] = uf;
|
||||||
|
os->render_funcs[id] = rf;
|
||||||
|
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_object_from_store(ObjectStore *os, const char *object_tag) {
|
void remove_object_from_store(ObjectStore *os, ObjectID id) {
|
||||||
if (os->count == 0) {
|
if (!os || os->count == 0 || !(os->object_ptrs[id])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t index = delete_object_from_map(os->map, object_tag);
|
os->object_ptrs[id] = NULL;
|
||||||
|
os->event_handlers[id] = NULL;
|
||||||
|
os->update_funcs[id] = NULL;
|
||||||
|
os->render_funcs[id] = NULL;
|
||||||
|
|
||||||
if (index < 0) {
|
os->reusable_ids[(os->deleted_count)++] = id;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
os->object_ptrs[index] = NULL;
|
|
||||||
os->event_handlers[index] = NULL;
|
|
||||||
os->update_funcs[index] = NULL;
|
|
||||||
os->render_funcs[index] = NULL;
|
|
||||||
|
|
||||||
size_t packing_start_index = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < os->count; ++i) {
|
|
||||||
if (os->active_indices[i] == index) {
|
|
||||||
os->active_indices[i] = -1;
|
|
||||||
packing_start_index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the indices are packed in the array
|
|
||||||
for (; packing_start_index < os->count; ++packing_start_index) {
|
|
||||||
if (packing_start_index + 1 == MAX_OBJ_COUNT) {
|
|
||||||
// Make sure we don't go out of bounds if we are at the last item
|
|
||||||
os->active_indices[packing_start_index] = -1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
os->active_indices[packing_start_index] =
|
|
||||||
os->active_indices[packing_start_index + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
os->count -= 1;
|
os->count -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_all_objects_events(ObjectStore *os, const SDL_Event *event) {
|
void handle_all_objects_events(ObjectStore *os, const SDL_Event *event) {
|
||||||
if (os->count == 0) {
|
if (!os || os->count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < os->count; ++i) {
|
for (int i = 0; i < os->count; ++i) {
|
||||||
int32_t index = os->active_indices[i];
|
if (os->event_handlers[i]) {
|
||||||
|
void *object = os->object_ptrs[i];
|
||||||
if (os->event_handlers[index]) {
|
(os->event_handlers[i])(event, object);
|
||||||
void *object = os->object_ptrs[index];
|
|
||||||
(os->event_handlers[index])(event, object);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_all_objects(ObjectStore *os, float delta) {
|
void update_all_objects(ObjectStore *os, float delta) {
|
||||||
if (os->count == 0) {
|
if (!os || os->count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < os->count; ++i) {
|
for (int i = 0; i < os->count; ++i) {
|
||||||
int32_t index = os->active_indices[i];
|
if (os->update_funcs[i]) {
|
||||||
|
void *object = os->object_ptrs[i];
|
||||||
if (os->update_funcs[index]) {
|
(os->update_funcs[i])(delta, object);
|
||||||
void *object = os->object_ptrs[index];
|
|
||||||
(os->update_funcs[index])(delta, object);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_all_objects(ObjectStore *os, Renderer *renderer) {
|
void render_all_objects(ObjectStore *os, Renderer *renderer) {
|
||||||
if (os->count == 0) {
|
if (!os || os->count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < os->count; ++i) {
|
for (int i = 0; i < os->count; ++i) {
|
||||||
int32_t index = os->active_indices[i];
|
if (os->render_funcs[i]) {
|
||||||
|
void *object = os->object_ptrs[i];
|
||||||
if (os->render_funcs[index]) {
|
(os->render_funcs[i])(renderer, object);
|
||||||
void *object = os->object_ptrs[index];
|
|
||||||
(os->render_funcs[index])(renderer, object);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,41 +137,20 @@ void destroy_object_store(ObjectStore *os) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < os->count; ++i) {
|
for (size_t i = 0; i < os->count; ++i) {
|
||||||
os->active_indices[i] = -1;
|
os->object_ptrs[i] = NULL;
|
||||||
|
os->event_handlers[i] = NULL;
|
||||||
|
os->update_funcs[i] = NULL;
|
||||||
|
os->render_funcs[i] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
os->count = 0;
|
os->count = 0;
|
||||||
|
os->deleted_count = 0;
|
||||||
if (os->event_handlers) {
|
|
||||||
free(os->event_handlers);
|
|
||||||
os->event_handlers = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (os->update_funcs) {
|
|
||||||
free(os->update_funcs);
|
|
||||||
os->update_funcs = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (os->render_funcs) {
|
|
||||||
free(os->render_funcs);
|
|
||||||
os->render_funcs = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (os->object_ptrs) {
|
|
||||||
free(os->object_ptrs);
|
|
||||||
os->object_ptrs = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (os->map) {
|
|
||||||
destroy_hashmap(os->map);
|
|
||||||
os->map = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(os);
|
free(os);
|
||||||
os = NULL;
|
os = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_ENGINE
|
#ifdef DEBUG_ENGINE
|
||||||
void print_objects(ObjectStore *os) { print_hashmap(os->map); }
|
void print_objects(ObjectStore *os) {}
|
||||||
#endif /* DEBUG_ENGINE */
|
#endif /* DEBUG_ENGINE */
|
||||||
@@ -31,6 +31,10 @@ bool init_renderer(Renderer *renderer, SDL_Window *window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void render_clear(const Renderer *renderer, SDL_Color color) {
|
void render_clear(const Renderer *renderer, SDL_Color color) {
|
||||||
|
if (!renderer || !(renderer->SDL_base_renderer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_SetRenderDrawColor(renderer->SDL_base_renderer, color.r, color.r, color.b,
|
SDL_SetRenderDrawColor(renderer->SDL_base_renderer, color.r, color.r, color.b,
|
||||||
color.a);
|
color.a);
|
||||||
|
|
||||||
@@ -38,6 +42,10 @@ void render_clear(const Renderer *renderer, SDL_Color color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void swap_buffers(const Renderer *renderer) {
|
void swap_buffers(const Renderer *renderer) {
|
||||||
|
if (!renderer || !(renderer->SDL_base_renderer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_RenderPresent(renderer->SDL_base_renderer);
|
SDL_RenderPresent(renderer->SDL_base_renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-1
@@ -9,6 +9,10 @@
|
|||||||
|
|
||||||
bool init_window(Window *wnd, const char *title, int width, int height,
|
bool init_window(Window *wnd, const char *title, int width, int height,
|
||||||
bool full_screen) {
|
bool full_screen) {
|
||||||
|
if (!wnd) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Uint32 window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
|
Uint32 window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
|
||||||
|
|
||||||
if (full_screen) {
|
if (full_screen) {
|
||||||
@@ -43,10 +47,20 @@ bool init_window(Window *wnd, const char *title, int width, int height,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fill_window_with_color(const Window *wnd, SDL_Color color) {
|
void fill_window_with_color(const Window *wnd, SDL_Color color) {
|
||||||
|
if (!wnd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
render_clear(wnd->renderer, color);
|
render_clear(wnd->renderer, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap_render_buffers(const Window *wnd) { swap_buffers(wnd->renderer); }
|
void swap_render_buffers(const Window *wnd) {
|
||||||
|
if (!wnd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
swap_buffers(wnd->renderer);
|
||||||
|
}
|
||||||
|
|
||||||
void destroy_window(Window *wnd) {
|
void destroy_window(Window *wnd) {
|
||||||
if (!wnd) {
|
if (!wnd) {
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#include "reusable_ids.h"
|
||||||
|
|
||||||
|
int32_t reuse_id(int32_t *ids, size_t count) {
|
||||||
|
int32_t id = ids[0];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
// Remove the first id and make sure the remaining ones are packed
|
||||||
|
ids[i] = ids[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user