190 lines
4.8 KiB
C
190 lines
4.8 KiB
C
#include "ObjectStore.h"
|
|
#include "aliases.h"
|
|
#include "constants.h"
|
|
#include "engine_build_config.h"
|
|
#include "reusable_ids.h"
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef DEBUG_ENGINE
|
|
#include <stdio.h>
|
|
#endif /* DEBUG_ENGINE */
|
|
|
|
struct object_store {
|
|
u64 count;
|
|
u64 deleted_count;
|
|
void *object_ptrs[MAX_OBJ_COUNT];
|
|
EventHandlerPtr event_handlers[MAX_OBJ_COUNT];
|
|
UpdateFuncPtr update_funcs[MAX_OBJ_COUNT];
|
|
RenderFuncPtr render_funcs[MAX_OBJ_COUNT];
|
|
ObjectID reusable_ids[MAX_OBJ_COUNT];
|
|
};
|
|
|
|
bool object_exists(ObjectStore *os, void *object);
|
|
|
|
ObjectStore *create_object_store() {
|
|
ObjectStore *os = (ObjectStore *)malloc(sizeof(ObjectStore));
|
|
if (!os) {
|
|
// If allocating memory to the store fails, we just return NULL. No need to
|
|
// continue with the rest of the function or to call any cleanup functions
|
|
return NULL;
|
|
}
|
|
|
|
os->count = 0;
|
|
os->deleted_count = 0;
|
|
memset(os->object_ptrs, 0, sizeof(os->object_ptrs));
|
|
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));
|
|
memset(os->reusable_ids, 0, sizeof(os->reusable_ids));
|
|
|
|
return os;
|
|
}
|
|
|
|
ObjectID add_object_to_store(ObjectStore *os, void *object, EventHandlerPtr eh,
|
|
UpdateFuncPtr uf, RenderFuncPtr rf) {
|
|
// If all function pointers are NULL, no point of adding the object to the
|
|
// store
|
|
bool no_funcs_passed = !eh && !uf && !rf;
|
|
|
|
if (!os || !object || object_exists(os, object) || no_funcs_passed ||
|
|
(os->count + 1 == MAX_OBJ_COUNT && os->deleted_count == 0)) {
|
|
#ifdef DEBUG_ENGINE
|
|
printf("Failed to add object\n");
|
|
#endif /* DEBUG_ENGINE */
|
|
|
|
return INVALID_ID;
|
|
}
|
|
|
|
ObjectID id;
|
|
|
|
if (os->deleted_count > 0) {
|
|
id = (ObjectID)reuse_id((i32 *)os->reusable_ids, os->deleted_count);
|
|
os->deleted_count -= 1;
|
|
|
|
if (id == os->count) {
|
|
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, ObjectID *id_ptr) {
|
|
ObjectID id = *id_ptr;
|
|
|
|
if (!os || os->count == 0 || id == INVALID_ID || !(os->object_ptrs[id])) {
|
|
return;
|
|
}
|
|
|
|
*id_ptr = INVALID_ID;
|
|
|
|
os->object_ptrs[id] = NULL;
|
|
os->event_handlers[id] = NULL;
|
|
os->update_funcs[id] = NULL;
|
|
os->render_funcs[id] = NULL;
|
|
|
|
os->reusable_ids[(os->deleted_count)++] = id;
|
|
|
|
if (id == (os->count - 1)) {
|
|
// Only decrement the count if the last object is deleted. Otherwise, the
|
|
// count shouldn't be decremented since the deleted object would be in the
|
|
// middle, which means that the functions that loop over all objects in the
|
|
// store will not actually loop over everything
|
|
|
|
u64 new_count = 0;
|
|
|
|
// Figure out how much we need to decrement the count. Since, a lot of
|
|
// objects may have been removed, the count needs to equal the last active
|
|
// object.
|
|
for (size_t i = id - 1; i >= 0; --i) {
|
|
if (os->object_ptrs[i]) {
|
|
new_count = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
os->count = new_count;
|
|
}
|
|
}
|
|
|
|
void handle_all_objects_events(ObjectStore *os, const SDL_Event *event) {
|
|
if (!os || os->count == 0) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < os->count; ++i) {
|
|
if (os->event_handlers[i]) {
|
|
void *object = os->object_ptrs[i];
|
|
(os->event_handlers[i])(object, event);
|
|
}
|
|
}
|
|
}
|
|
|
|
void update_all_objects(ObjectStore *os, f32 delta) {
|
|
if (!os || os->count == 0) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < os->count; ++i) {
|
|
if (os->update_funcs[i]) {
|
|
void *object = os->object_ptrs[i];
|
|
(os->update_funcs[i])(object, delta);
|
|
}
|
|
}
|
|
}
|
|
|
|
void render_all_objects(ObjectStore *os) {
|
|
if (!os || os->count == 0) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < os->count; ++i) {
|
|
if (os->render_funcs[i]) {
|
|
void *object = os->object_ptrs[i];
|
|
(os->render_funcs[i])(object);
|
|
}
|
|
}
|
|
}
|
|
|
|
void destroy_object_store(ObjectStore **os) {
|
|
if (!(*os)) {
|
|
return;
|
|
}
|
|
|
|
memset((*os)->object_ptrs, 0, sizeof((*os)->object_ptrs));
|
|
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));
|
|
memset((*os)->reusable_ids, 0, sizeof((*os)->reusable_ids));
|
|
|
|
(*os)->count = 0;
|
|
(*os)->deleted_count = 0;
|
|
|
|
free(*os);
|
|
*os = NULL;
|
|
}
|
|
|
|
bool object_exists(ObjectStore *os, void *object) {
|
|
// This is a crude implementation for checking that an object has already
|
|
// been added.
|
|
// TODO (Abdelrahman): Figure out if there is a better way of doing this
|
|
for (size_t i = 0; i < MAX_OBJ_COUNT; ++i) {
|
|
if (object == os->object_ptrs[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|