pxl8/demo3d/client/world/demo3d_entity.c

496 lines
18 KiB
C

#include "demo3d_entity.h"
#include <string.h>
#include "pxl8_mem.h"
#define DEMO3D_ENTITY_COMPONENT_NAME_MAX 32
#define DEMO3D_ENTITY_RELATIONSHIP_NAME_MAX 32
typedef struct demo3d_component_type {
char name[DEMO3D_ENTITY_COMPONENT_NAME_MAX];
u32 size;
} demo3d_component_type;
typedef struct demo3d_component_storage {
u32* sparse;
void* dense_data;
demo3d_entity* dense_entities;
u32 count;
} demo3d_component_storage;
typedef struct demo3d_relationship_type {
char name[DEMO3D_ENTITY_RELATIONSHIP_NAME_MAX];
} demo3d_relationship_type;
typedef struct demo3d_relationship_entry {
demo3d_entity subject;
demo3d_entity object;
demo3d_entity_relationship rel;
u32 next_by_subject;
u32 next_by_object;
} demo3d_relationship_entry;
struct demo3d_entity_pool {
u32* generations;
u32* free_list;
u32 free_count;
u32 capacity;
u32 alive_count;
demo3d_component_type* component_types;
demo3d_component_storage* component_storage;
u32 component_type_count;
u32 component_type_capacity;
demo3d_relationship_type* relationship_types;
u32 relationship_type_count;
u32 relationship_type_capacity;
demo3d_relationship_entry* relationships;
u32* rel_by_subject;
u32* rel_by_object;
u32 relationship_count;
u32 relationship_capacity;
};
demo3d_entity_pool* demo3d_entity_pool_create(u32 capacity) {
demo3d_entity_pool* pool = pxl8_calloc(1, sizeof(demo3d_entity_pool));
if (!pool) return NULL;
pool->capacity = capacity;
pool->generations = pxl8_calloc(capacity, sizeof(u32));
pool->free_list = pxl8_malloc(capacity * sizeof(u32));
if (!pool->generations || !pool->free_list) {
demo3d_entity_pool_destroy(pool);
return NULL;
}
for (u32 i = 0; i < capacity; i++) {
pool->free_list[i] = capacity - 1 - i;
}
pool->free_count = capacity;
pool->component_type_capacity = 16;
pool->component_types = pxl8_calloc(pool->component_type_capacity, sizeof(demo3d_component_type));
pool->component_storage = pxl8_calloc(pool->component_type_capacity, sizeof(demo3d_component_storage));
pool->relationship_type_capacity = 16;
pool->relationship_types = pxl8_calloc(pool->relationship_type_capacity, sizeof(demo3d_relationship_type));
pool->relationship_capacity = 256;
pool->relationships = pxl8_malloc(pool->relationship_capacity * sizeof(demo3d_relationship_entry));
pool->rel_by_subject = pxl8_malloc(capacity * sizeof(u32));
pool->rel_by_object = pxl8_malloc(capacity * sizeof(u32));
for (u32 i = 0; i < capacity; i++) {
pool->rel_by_subject[i] = UINT32_MAX;
pool->rel_by_object[i] = UINT32_MAX;
}
return pool;
}
void demo3d_entity_pool_clear(demo3d_entity_pool* pool) {
if (!pool) return;
for (u32 i = 0; i < pool->capacity; i++) {
pool->generations[i] = 0;
pool->free_list[i] = pool->capacity - 1 - i;
pool->rel_by_subject[i] = UINT32_MAX;
pool->rel_by_object[i] = UINT32_MAX;
}
pool->free_count = pool->capacity;
pool->alive_count = 0;
for (u32 i = 0; i < pool->component_type_count; i++) {
pool->component_storage[i].count = 0;
}
pool->relationship_count = 0;
}
void demo3d_entity_pool_destroy(demo3d_entity_pool* pool) {
if (!pool) return;
for (u32 i = 0; i < pool->component_type_count; i++) {
pxl8_free(pool->component_storage[i].sparse);
pxl8_free(pool->component_storage[i].dense_data);
pxl8_free(pool->component_storage[i].dense_entities);
}
pxl8_free(pool->component_types);
pxl8_free(pool->component_storage);
pxl8_free(pool->relationship_types);
pxl8_free(pool->relationships);
pxl8_free(pool->rel_by_subject);
pxl8_free(pool->rel_by_object);
pxl8_free(pool->generations);
pxl8_free(pool->free_list);
pxl8_free(pool);
}
demo3d_entity demo3d_entity_spawn(demo3d_entity_pool* pool) {
if (!pool || pool->free_count == 0) return DEMO3D_ENTITY_INVALID;
u32 idx = pool->free_list[--pool->free_count];
pool->generations[idx]++;
pool->alive_count++;
return (demo3d_entity){ .idx = idx, .gen = pool->generations[idx] };
}
void demo3d_entity_despawn(demo3d_entity_pool* pool, demo3d_entity e) {
if (!pool || !demo3d_entity_alive(pool, e)) return;
for (u32 i = 0; i < pool->component_type_count; i++) {
demo3d_entity_component_remove(pool, e, i + 1);
}
pool->free_list[pool->free_count++] = e.idx;
pool->generations[e.idx]++;
pool->alive_count--;
}
bool demo3d_entity_alive(const demo3d_entity_pool* pool, demo3d_entity e) {
if (!pool || e.idx >= pool->capacity) return false;
return pool->generations[e.idx] == e.gen && e.gen != 0;
}
u32 demo3d_entity_count(const demo3d_entity_pool* pool) {
return pool ? pool->alive_count : 0;
}
demo3d_entity_component demo3d_entity_component_register(demo3d_entity_pool* pool, const char* name, u32 size) {
if (!pool || !name || size == 0) return DEMO3D_ENTITY_COMPONENT_INVALID;
demo3d_entity_component existing = demo3d_entity_component_find(pool, name);
if (existing != DEMO3D_ENTITY_COMPONENT_INVALID) return existing;
if (pool->component_type_count >= pool->component_type_capacity) {
u32 new_capacity = pool->component_type_capacity * 2;
demo3d_component_type* new_types = pxl8_realloc(pool->component_types, new_capacity * sizeof(demo3d_component_type));
demo3d_component_storage* new_storage = pxl8_realloc(pool->component_storage, new_capacity * sizeof(demo3d_component_storage));
if (!new_types || !new_storage) return DEMO3D_ENTITY_COMPONENT_INVALID;
pool->component_types = new_types;
pool->component_storage = new_storage;
memset(&pool->component_types[pool->component_type_capacity], 0, (new_capacity - pool->component_type_capacity) * sizeof(demo3d_component_type));
memset(&pool->component_storage[pool->component_type_capacity], 0, (new_capacity - pool->component_type_capacity) * sizeof(demo3d_component_storage));
pool->component_type_capacity = new_capacity;
}
u32 type_idx = pool->component_type_count++;
strncpy(pool->component_types[type_idx].name, name, DEMO3D_ENTITY_COMPONENT_NAME_MAX - 1);
pool->component_types[type_idx].size = size;
demo3d_component_storage* storage = &pool->component_storage[type_idx];
storage->sparse = pxl8_malloc(pool->capacity * sizeof(u32));
storage->dense_data = pxl8_malloc(pool->capacity * size);
storage->dense_entities = pxl8_malloc(pool->capacity * sizeof(demo3d_entity));
storage->count = 0;
for (u32 i = 0; i < pool->capacity; i++) {
storage->sparse[i] = UINT32_MAX;
}
return type_idx + 1;
}
demo3d_entity_component demo3d_entity_component_find(const demo3d_entity_pool* pool, const char* name) {
if (!pool || !name) return DEMO3D_ENTITY_COMPONENT_INVALID;
for (u32 i = 0; i < pool->component_type_count; i++) {
if (strcmp(pool->component_types[i].name, name) == 0) {
return i + 1;
}
}
return DEMO3D_ENTITY_COMPONENT_INVALID;
}
const char* demo3d_entity_component_name(const demo3d_entity_pool* pool, demo3d_entity_component comp) {
if (!pool || comp == 0 || comp > pool->component_type_count) return NULL;
return pool->component_types[comp - 1].name;
}
void* demo3d_entity_component_add(demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp) {
if (!pool || !demo3d_entity_alive(pool, e)) return NULL;
if (comp == 0 || comp > pool->component_type_count) return NULL;
u32 type_idx = comp - 1;
demo3d_component_storage* storage = &pool->component_storage[type_idx];
u32 size = pool->component_types[type_idx].size;
if (storage->sparse[e.idx] != UINT32_MAX) {
return (u8*)storage->dense_data + storage->sparse[e.idx] * size;
}
u32 dense_idx = storage->count++;
storage->sparse[e.idx] = dense_idx;
storage->dense_entities[dense_idx] = e;
void* data = (u8*)storage->dense_data + dense_idx * size;
memset(data, 0, size);
return data;
}
void* demo3d_entity_component_get(const demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp) {
if (!pool || !demo3d_entity_alive(pool, e)) return NULL;
if (comp == 0 || comp > pool->component_type_count) return NULL;
u32 type_idx = comp - 1;
const demo3d_component_storage* storage = &pool->component_storage[type_idx];
if (storage->sparse[e.idx] == UINT32_MAX) return NULL;
u32 size = pool->component_types[type_idx].size;
return (u8*)storage->dense_data + storage->sparse[e.idx] * size;
}
void demo3d_entity_component_remove(demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp) {
if (!pool || !demo3d_entity_alive(pool, e)) return;
if (comp == 0 || comp > pool->component_type_count) return;
u32 type_idx = comp - 1;
demo3d_component_storage* storage = &pool->component_storage[type_idx];
u32 size = pool->component_types[type_idx].size;
u32 dense_idx = storage->sparse[e.idx];
if (dense_idx == UINT32_MAX) return;
u32 last_idx = storage->count - 1;
if (dense_idx != last_idx) {
demo3d_entity last_entity = storage->dense_entities[last_idx];
memcpy((u8*)storage->dense_data + dense_idx * size,
(u8*)storage->dense_data + last_idx * size, size);
storage->dense_entities[dense_idx] = last_entity;
storage->sparse[last_entity.idx] = dense_idx;
}
storage->sparse[e.idx] = UINT32_MAX;
storage->count--;
}
bool demo3d_entity_component_has(const demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp) {
if (!pool || !demo3d_entity_alive(pool, e)) return false;
if (comp == 0 || comp > pool->component_type_count) return false;
return pool->component_storage[comp - 1].sparse[e.idx] != UINT32_MAX;
}
demo3d_entity_relationship demo3d_entity_relationship_register(demo3d_entity_pool* pool, const char* name) {
if (!pool || !name) return DEMO3D_ENTITY_RELATIONSHIP_INVALID;
demo3d_entity_relationship existing = demo3d_entity_relationship_find(pool, name);
if (existing != DEMO3D_ENTITY_RELATIONSHIP_INVALID) return existing;
if (pool->relationship_type_count >= pool->relationship_type_capacity) {
u32 new_capacity = pool->relationship_type_capacity * 2;
demo3d_relationship_type* new_types = pxl8_realloc(pool->relationship_types, new_capacity * sizeof(demo3d_relationship_type));
if (!new_types) return DEMO3D_ENTITY_RELATIONSHIP_INVALID;
pool->relationship_types = new_types;
memset(&pool->relationship_types[pool->relationship_type_capacity], 0, (new_capacity - pool->relationship_type_capacity) * sizeof(demo3d_relationship_type));
pool->relationship_type_capacity = new_capacity;
}
u32 type_idx = pool->relationship_type_count++;
strncpy(pool->relationship_types[type_idx].name, name, DEMO3D_ENTITY_RELATIONSHIP_NAME_MAX - 1);
return type_idx + 1;
}
demo3d_entity_relationship demo3d_entity_relationship_find(const demo3d_entity_pool* pool, const char* name) {
if (!pool || !name) return DEMO3D_ENTITY_RELATIONSHIP_INVALID;
for (u32 i = 0; i < pool->relationship_type_count; i++) {
if (strcmp(pool->relationship_types[i].name, name) == 0) {
return i + 1;
}
}
return DEMO3D_ENTITY_RELATIONSHIP_INVALID;
}
const char* demo3d_entity_relationship_name(const demo3d_entity_pool* pool, demo3d_entity_relationship rel) {
if (!pool || rel == 0 || rel > pool->relationship_type_count) return NULL;
return pool->relationship_types[rel - 1].name;
}
void demo3d_entity_relationship_add(demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity object) {
if (!pool) return;
if (!demo3d_entity_alive(pool, subject) || !demo3d_entity_alive(pool, object)) return;
if (rel == 0 || rel > pool->relationship_type_count) return;
if (demo3d_entity_relationship_has(pool, subject, rel, object)) return;
if (pool->relationship_count >= pool->relationship_capacity) {
u32 new_capacity = pool->relationship_capacity * 2;
demo3d_relationship_entry* new_rels = pxl8_realloc(pool->relationships, new_capacity * sizeof(demo3d_relationship_entry));
if (!new_rels) return;
pool->relationships = new_rels;
pool->relationship_capacity = new_capacity;
}
u32 entry_idx = pool->relationship_count++;
demo3d_relationship_entry* entry = &pool->relationships[entry_idx];
entry->subject = subject;
entry->object = object;
entry->rel = rel;
entry->next_by_subject = pool->rel_by_subject[subject.idx];
pool->rel_by_subject[subject.idx] = entry_idx;
entry->next_by_object = pool->rel_by_object[object.idx];
pool->rel_by_object[object.idx] = entry_idx;
}
void demo3d_entity_relationship_remove(demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity object) {
if (!pool) return;
if (rel == 0 || rel > pool->relationship_type_count) return;
u32* prev_ptr = &pool->rel_by_subject[subject.idx];
u32 idx = *prev_ptr;
while (idx != UINT32_MAX) {
demo3d_relationship_entry* entry = &pool->relationships[idx];
if (demo3d_entity_eq(entry->subject, subject) &&
demo3d_entity_eq(entry->object, object) &&
entry->rel == rel) {
*prev_ptr = entry->next_by_subject;
u32* obj_prev = &pool->rel_by_object[object.idx];
while (*obj_prev != UINT32_MAX) {
if (*obj_prev == idx) {
*obj_prev = entry->next_by_object;
break;
}
obj_prev = &pool->relationships[*obj_prev].next_by_object;
}
if (idx != pool->relationship_count - 1) {
u32 last_idx = pool->relationship_count - 1;
demo3d_relationship_entry* last = &pool->relationships[last_idx];
u32* last_subj_prev = &pool->rel_by_subject[last->subject.idx];
while (*last_subj_prev != UINT32_MAX) {
if (*last_subj_prev == last_idx) {
*last_subj_prev = idx;
break;
}
last_subj_prev = &pool->relationships[*last_subj_prev].next_by_subject;
}
u32* last_obj_prev = &pool->rel_by_object[last->object.idx];
while (*last_obj_prev != UINT32_MAX) {
if (*last_obj_prev == last_idx) {
*last_obj_prev = idx;
break;
}
last_obj_prev = &pool->relationships[*last_obj_prev].next_by_object;
}
*entry = *last;
}
pool->relationship_count--;
return;
}
prev_ptr = &entry->next_by_subject;
idx = *prev_ptr;
}
}
bool demo3d_entity_relationship_has(const demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity object) {
if (!pool) return false;
if (rel == 0 || rel > pool->relationship_type_count) return false;
u32 idx = pool->rel_by_subject[subject.idx];
while (idx != UINT32_MAX) {
const demo3d_relationship_entry* entry = &pool->relationships[idx];
if (demo3d_entity_eq(entry->subject, subject) &&
demo3d_entity_eq(entry->object, object) &&
entry->rel == rel) {
return true;
}
idx = entry->next_by_subject;
}
return false;
}
u32 demo3d_entity_relationship_subjects(const demo3d_entity_pool* pool, demo3d_entity object, demo3d_entity_relationship rel, demo3d_entity* out, u32 max) {
if (!pool || !out || max == 0) return 0;
if (rel == 0 || rel > pool->relationship_type_count) return 0;
u32 count = 0;
u32 idx = pool->rel_by_object[object.idx];
while (idx != UINT32_MAX && count < max) {
const demo3d_relationship_entry* entry = &pool->relationships[idx];
if (demo3d_entity_eq(entry->object, object) && entry->rel == rel) {
out[count++] = entry->subject;
}
idx = entry->next_by_object;
}
return count;
}
u32 demo3d_entity_relationship_objects(const demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity* out, u32 max) {
if (!pool || !out || max == 0) return 0;
if (rel == 0 || rel > pool->relationship_type_count) return 0;
u32 count = 0;
u32 idx = pool->rel_by_subject[subject.idx];
while (idx != UINT32_MAX && count < max) {
const demo3d_relationship_entry* entry = &pool->relationships[idx];
if (demo3d_entity_eq(entry->subject, subject) && entry->rel == rel) {
out[count++] = entry->object;
}
idx = entry->next_by_subject;
}
return count;
}
void demo3d_entity_each(demo3d_entity_pool* pool, demo3d_entity_component comp, demo3d_entity_each_fn fn, void* ctx) {
if (!pool || !fn) return;
if (comp == 0 || comp > pool->component_type_count) return;
demo3d_component_storage* storage = &pool->component_storage[comp - 1];
for (u32 i = 0; i < storage->count; i++) {
demo3d_entity e = storage->dense_entities[i];
if (demo3d_entity_alive(pool, e)) {
fn(pool, e, ctx);
}
}
}
void demo3d_entity_each_with(demo3d_entity_pool* pool, const demo3d_entity_component* comps, u32 count, demo3d_entity_each_fn fn, void* ctx) {
if (!pool || !comps || !fn || count == 0) return;
u32 smallest_idx = 0;
u32 smallest_count = UINT32_MAX;
for (u32 i = 0; i < count; i++) {
if (comps[i] == 0 || comps[i] > pool->component_type_count) return;
u32 storage_count = pool->component_storage[comps[i] - 1].count;
if (storage_count < smallest_count) {
smallest_count = storage_count;
smallest_idx = i;
}
}
demo3d_component_storage* storage = &pool->component_storage[comps[smallest_idx] - 1];
for (u32 i = 0; i < storage->count; i++) {
demo3d_entity e = storage->dense_entities[i];
if (!demo3d_entity_alive(pool, e)) continue;
bool has_all = true;
for (u32 j = 0; j < count && has_all; j++) {
if (j != smallest_idx) {
has_all = demo3d_entity_component_has(pool, e, comps[j]);
}
}
if (has_all) {
fn(pool, e, ctx);
}
}
}