ECS improvements!

This commit is contained in:
2025-08-21 18:58:34 -05:00
parent 9f5894cdcb
commit 1b4c270ccb
8 changed files with 70 additions and 37 deletions

View File

@@ -8,4 +8,4 @@
#include "camera.h" #include "camera.h"
camera_t CAMERA_DATA[ECS_ENTITY_COUNT_MAX] = { 0 }; camera_t CAMERA_DATA[ECS_ENTITY_COUNT_MAX] = { 0 };
ecscomponent_t CAMERA_COMPONENT = ecsComponentInit(CAMERA_DATA); ecscomponent_t CAMERA_COMPONENT = ecsComponentInit(CAMERA_DATA, NULL);

View File

@@ -20,7 +20,7 @@ extern ecscomponent_t CAMERA_COMPONENT;
* *
* @param id The ID of the entity to initialize the camera for. * @param id The ID of the entity to initialize the camera for.
*/ */
#define cameraAdd(id) ecsComponentDataAdd(&CAMERA_COMPONENT, id) #define cameraAdd(id) ((camera_t*)ecsComponentDataAdd(&CAMERA_COMPONENT, id))
/** /**
* Gets the camera component data for a specific entity. * Gets the camera component data for a specific entity.
@@ -28,7 +28,7 @@ extern ecscomponent_t CAMERA_COMPONENT;
* @param id The ID of the entity to get the camera data for. * @param id The ID of the entity to get the camera data for.
* @return Pointer to the camera data for the entity. * @return Pointer to the camera data for the entity.
*/ */
#define cameraGet(id) ecsComponentDataGet(&CAMERA_COMPONENT, id) #define cameraGet(id) ((camera_t*)ecsComponentDataGet(&CAMERA_COMPONENT, id))
/** /**
* Checks if the camera component has data for a specific entity. * Checks if the camera component has data for a specific entity.

View File

@@ -7,4 +7,5 @@
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
ecssystem.c ecssystem.c
ecscomponent.c
) )

View File

@@ -12,8 +12,12 @@
bool_t ecsComponentDataHas(const ecscomponent_t *cmp, const ecsid_t id) { bool_t ecsComponentDataHas(const ecscomponent_t *cmp, const ecsid_t id) {
assertNotNull(cmp, "Component pointer cannot be NULL."); assertNotNull(cmp, "Component pointer cannot be NULL.");
assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID."); assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID.");
assertTrue(
(cmp->componentFlags & ECS_COMPONENT_FLAG_INITIALIZED) != 0,
"Component must be initialized before checking data."
);
return cmp->entityFlags[id] != 0; return cmp->entityFlags[id] & ECS_COMPONENT_ENTITY_FLAG_USED;
} }
void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id) { void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id) {
@@ -23,9 +27,16 @@ void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id) {
return (void *)((uint8_t *)cmp->data + (id * cmp->dataSize)); return (void *)((uint8_t *)cmp->data + (id * cmp->dataSize));
} }
void * ecsComponentDataAdd(const ecscomponent_t *cmp, const ecsid_t id) { void * ecsComponentDataAdd(ecscomponent_t *cmp, const ecsid_t id) {
assertNotNull(cmp, "Component pointer cannot be NULL."); assertNotNull(cmp, "Component pointer cannot be NULL.");
assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID."); assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID.");
// Initialize the component if it hasn't been initialized yet.
if((cmp->componentFlags & ECS_COMPONENT_FLAG_INITIALIZED) == 0) {
if(cmp->componentInit) cmp->componentInit();
cmp->componentFlags |= ECS_COMPONENT_FLAG_INITIALIZED;
}
assertFalse(ecsComponentDataHas(cmp, id), "Entity already has data."); assertFalse(ecsComponentDataHas(cmp, id), "Entity already has data.");
// Zero out the entity's data. // Zero out the entity's data.
@@ -35,7 +46,7 @@ void * ecsComponentDataAdd(const ecscomponent_t *cmp, const ecsid_t id) {
); );
// Mark the entity as having data. // Mark the entity as having data.
cmp->entityFlags[id] = ECS_COMPONENT_FLAG_USED; cmp->entityFlags[id] = ECS_COMPONENT_ENTITY_FLAG_USED;
return ecsComponentDataGet(cmp, id); return ecsComponentDataGet(cmp, id);
} }
@@ -43,6 +54,10 @@ void * ecsComponentDataAdd(const ecscomponent_t *cmp, const ecsid_t id) {
void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id) { void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id) {
assertNotNull(cmp, "Component pointer cannot be NULL."); assertNotNull(cmp, "Component pointer cannot be NULL.");
assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID."); assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID.");
assertTrue(
(cmp->componentFlags & ECS_COMPONENT_FLAG_INITIALIZED) != 0,
"Component must be initialized before removing data."
);
assertTrue(ecsComponentDataHas(cmp, id), "Entity does not have data."); assertTrue(ecsComponentDataHas(cmp, id), "Entity does not have data.");
// Clear the entity's data flag. // Clear the entity's data flag.

View File

@@ -8,25 +8,33 @@
#pragma once #pragma once
#include "ecs/ecsentity.h" #include "ecs/ecsentity.h"
#define ECS_COMPONENT_FLAG_USED (1 << 0) #define ECS_COMPONENT_ENTITY_FLAG_USED (1 << 0)
#define ECS_COMPONENT_FLAG_INITIALIZED (1 << 0)
typedef struct { typedef struct {
void *data; void *data;
size_t dataSize; size_t dataSize;
uint8_t entityFlags[ECS_ENTITY_COUNT_MAX]; uint8_t entityFlags[ECS_ENTITY_COUNT_MAX];
uint8_t componentFlags;
void (*componentInit)();
} ecscomponent_t; } ecscomponent_t;
/** /**
* Initializes an ECS Component. * Initializes an ECS Component.
* *
* @param dataPointer Pointer to the data that the component owns. * @param dPointer Pointer to the data that the component owns.
* @param cInit Function to call to initialize the component.
*/ */
#define ecsComponentInit(dataPointer) \ #define ecsComponentInit(dPointer, cInit) \
(ecscomponent_t){ \ (ecscomponent_t){ \
.data = dataPointer, \ .data = dPointer, \
.dataSize = (sizeof(*dataPointer) / ECS_ENTITY_COUNT_MAX), \ .dataSize = sizeof(*dPointer), \
.entityFlags = {0} \ .entityFlags = 0, \
}; .componentFlags = 0, \
.componentInit = cInit \
}
/** /**
* Checks if the component has data for a specific entity. * Checks if the component has data for a specific entity.
@@ -52,7 +60,7 @@ void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id);
* @param cmp Pointer to the ecscomponent_t. * @param cmp Pointer to the ecscomponent_t.
* @param id The ID of the entity to add data for. * @param id The ID of the entity to add data for.
*/ */
void * ecsComponentDataAdd(const ecscomponent_t *cmp, const ecsid_t id); void * ecsComponentDataAdd(ecscomponent_t *cmp, const ecsid_t id);
/** /**
* Removes data for a specific entity in the component. * Removes data for a specific entity in the component.

View File

@@ -13,6 +13,8 @@
#include "ecs/ecssystem.h" #include "ecs/ecssystem.h"
#include "scene/scenetree.h" #include "scene/scenetree.h"
#include "scene/test/scenetest.h"
engine_t ENGINE; engine_t ENGINE;
errorret_t engineInit(void) { errorret_t engineInit(void) {
@@ -26,6 +28,9 @@ errorret_t engineInit(void) {
sceneTreeInit(); sceneTreeInit();
errorChain(displayInit()); errorChain(displayInit());
ecsid_t sceneTest = sceneTestAdd();
sceneTreeChildAdd(ECS_SYSTEM.root, sceneTest);
errorOk(); errorOk();
} }

View File

@@ -7,29 +7,36 @@
#include "util/memory.h" #include "util/memory.h"
#include "assert/assert.h" #include "assert/assert.h"
scenetree_t SCENE_TREE; scenetreenode_t SCENE_TREE_DATA[ECS_ENTITY_COUNT_MAX] = { 0 };
ecscomponent_t SCENE_TREE_COMPONENT = ecsComponentInit(
SCENE_TREE_DATA,
sceneTreeInit
);
void sceneTreeInit(void) { void sceneTreeInit(void) {
memoryZero(&SCENE_TREE, sizeof(scenetree_t));
// Default all items to have no parent // Default all items to have no parent
for (ecsid_t i = 0; i < ECS_ENTITY_COUNT_MAX; i++) { for (ecsid_t i = 0; i < ECS_ENTITY_COUNT_MAX; i++) {
SCENE_TREE.items[i].parent = -1; SCENE_TREE_DATA[i].parent = -1;
} }
} }
ecsid_t sceneTreeParentGet(const ecsid_t child) { ecsid_t sceneTreeParentGet(const ecsid_t child) {
assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID"); assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID");
return SCENE_TREE.items[child].parent; return SCENE_TREE_DATA[child].parent;
} }
uint8_t sceneTreeChildGetAll(const ecsid_t id, ecsid_t *children) { uint8_t sceneTreeChildGetAll(const ecsid_t id, ecsid_t *children) {
assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid parent ID"); assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid parent ID");
const scenetreenode_t *node = &SCENE_TREE.items[id]; if(children == NULL) return SCENE_TREE_DATA[id].childCount;
if(children == NULL) return node->childCount;
return node->childCount; memoryCopy(
children,
SCENE_TREE_DATA[id].children,
sizeof(ecsid_t) * SCENE_TREE_DATA[id].childCount
);
return SCENE_TREE_DATA[id].childCount;
} }
void sceneTreeChildAdd(const ecsid_t parent, const ecsid_t child) { void sceneTreeChildAdd(const ecsid_t parent, const ecsid_t child) {
@@ -45,7 +52,7 @@ void sceneTreeChildAdd(const ecsid_t parent, const ecsid_t child) {
"Child cannot be a deep child of itself." "Child cannot be a deep child of itself."
); );
scenetreenode_t *parentNode = &SCENE_TREE.items[parent]; scenetreenode_t *parentNode = &SCENE_TREE_DATA[parent];
assertTrue(parentNode->childCount < SCENE_ITEM_CHILD_MAX, "Parent full."); assertTrue(parentNode->childCount < SCENE_ITEM_CHILD_MAX, "Parent full.");
// Add child to parent's children array // Add child to parent's children array
@@ -53,7 +60,7 @@ void sceneTreeChildAdd(const ecsid_t parent, const ecsid_t child) {
parentNode->childCount++; parentNode->childCount++;
// Set child's parent // Set child's parent
SCENE_TREE.items[child].parent = parent; SCENE_TREE_DATA[child].parent = parent;
} }
void sceneTreeChildRemove( void sceneTreeChildRemove(
@@ -64,8 +71,8 @@ void sceneTreeChildRemove(
assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID"); assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID");
assertTrue(parent != child, "Child cannot be a child of itself."); assertTrue(parent != child, "Child cannot be a child of itself.");
scenetreenode_t *childNode = &SCENE_TREE.items[child]; scenetreenode_t *childNode = &SCENE_TREE_DATA[child];
scenetreenode_t *parentNode = &SCENE_TREE.items[parent]; scenetreenode_t *parentNode = &SCENE_TREE_DATA[parent];
assertTrue(childNode->parent == parent, "Child does not belong to parent"); assertTrue(childNode->parent == parent, "Child does not belong to parent");
assertTrue(parentNode->childCount > 0, "Parent has no children"); assertTrue(parentNode->childCount > 0, "Parent has no children");
@@ -88,10 +95,10 @@ void sceneTreeChildRemove(
void sceneTreeChildRemoveAll(const ecsid_t parent) { void sceneTreeChildRemoveAll(const ecsid_t parent) {
assertTrue(parent >= 0 && parent < ECS_ENTITY_COUNT_MAX, "Invalid parent ID"); assertTrue(parent >= 0 && parent < ECS_ENTITY_COUNT_MAX, "Invalid parent ID");
scenetreenode_t *parentNode = &SCENE_TREE.items[parent]; scenetreenode_t *parentNode = &SCENE_TREE_DATA[parent];
for(uint8_t i = 0; i < parentNode->childCount; i++) { for(uint8_t i = 0; i < parentNode->childCount; i++) {
ecsid_t child = parentNode->children[i]; ecsid_t child = parentNode->children[i];
SCENE_TREE.items[child].parent = -1; SCENE_TREE_DATA[child].parent = -1;
} }
// Reset child count // Reset child count
@@ -106,11 +113,11 @@ bool_t sceneTreeChildInTree(
assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID"); assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID");
assertTrue(parent != child, "Child cannot be a child of itself."); assertTrue(parent != child, "Child cannot be a child of itself.");
scenetreenode_t *childNode = &SCENE_TREE.items[child]; scenetreenode_t *childNode = &SCENE_TREE_DATA[child];
while(childNode) { while(childNode) {
if(childNode->parent == parent) return true; if(childNode->parent == parent) return true;
if(childNode->parent == -1) break; // No parent means we've reached the root if(childNode->parent == -1) break; // No parent means we've reached the root
childNode = &SCENE_TREE.items[childNode->parent]; childNode = &SCENE_TREE_DATA[childNode->parent];
} }
return false; return false;

View File

@@ -6,7 +6,7 @@
*/ */
#pragma once #pragma once
#include "ecs/ecsentity.h" #include "ecs/ecscomponent.h"
#define SCENE_ITEM_CHILD_MAX 16 #define SCENE_ITEM_CHILD_MAX 16
@@ -16,11 +16,8 @@ typedef struct {
ecsid_t parent; ecsid_t parent;
} scenetreenode_t; } scenetreenode_t;
typedef struct scene_s { extern scenetreenode_t SCENE_TREE_DATA[ECS_ENTITY_COUNT_MAX];
scenetreenode_t items[ECS_ENTITY_COUNT_MAX]; extern ecscomponent_t SCENE_TREE_COMPONENT;
} scenetree_t;
extern scenetree_t SCENE_TREE;
/** /**
* Initialize the scene tree. * Initialize the scene tree.