diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index 905ee41..52bda08 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE display.c + camera.c ) if(DUSK_TARGET_SYSTEM STREQUAL "linux") diff --git a/src/display/camera.c b/src/display/camera.c new file mode 100644 index 0000000..c7a19e1 --- /dev/null +++ b/src/display/camera.c @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "camera.h" + +camera_t CAMERA_DATA[ECS_ENTITY_COUNT_MAX] = { 0 }; +ecscomponent_t CAMERA_COMPONENT = ecsComponentInit(CAMERA_DATA); diff --git a/src/display/camera.h b/src/display/camera.h new file mode 100644 index 0000000..10a8f23 --- /dev/null +++ b/src/display/camera.h @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "ecs/ecscomponent.h" + +typedef struct { + float_t view[16]; // 4x4 matrix for view transformation +} camera_t; + +extern camera_t CAMERA_DATA[ECS_ENTITY_COUNT_MAX]; +extern ecscomponent_t CAMERA_COMPONENT; + +/** + * Initializes the camera component. + * + * @param id The ID of the entity to initialize the camera for. + */ +#define cameraAdd(id) ecsComponentDataAdd(&CAMERA_COMPONENT, id) + +/** + * Gets the camera component data for a specific entity. + * + * @param id The ID of the entity to get the camera data for. + * @return Pointer to the camera data for the entity. + */ +#define cameraGet(id) ecsComponentDataGet(&CAMERA_COMPONENT, id) + +/** + * Checks if the camera component has data for a specific entity. + * + * @param id The ID of the entity to check. + * @return True if the camera component has data for the entity, false otherwise. + */ +#define cameraHas(id) ecsComponentDataHas(&CAMERA_COMPONENT, id) + +/** + * Removes the camera component data for a specific entity. + * + * @param id The ID of the entity to remove the camera data for. + */ +#define cameraRemove(id) ecsComponentDataRemove(&CAMERA_COMPONENT, id) \ No newline at end of file diff --git a/src/ecs/CMakeLists.txt b/src/ecs/CMakeLists.txt index 72e981c..c97bd8f 100644 --- a/src/ecs/CMakeLists.txt +++ b/src/ecs/CMakeLists.txt @@ -6,5 +6,5 @@ # Sources target_sources(${DUSK_TARGET_NAME} PRIVATE - ecs.c + ecssystem.c ) \ No newline at end of file diff --git a/src/ecs/ecs.h b/src/ecs/ecs.h index 76edcbc..9783ab3 100644 --- a/src/ecs/ecs.h +++ b/src/ecs/ecs.h @@ -8,44 +8,4 @@ #pragma once #include "dusk.h" -typedef int_fast16_t ecsentityid_t; - -#define ECS_ENTITY_COUNT_MAX 2048 -#define ECS_ENTITY_FLAG_USED (1 << 0) - -typedef struct ecsentity_s { - ecsentityid_t id; - uint8_t flags; -} ecsentity_t; - -typedef struct ecs_s { - ecsentity_t entities[ECS_ENTITY_COUNT_MAX]; - - ecsentity_t *available[ECS_ENTITY_COUNT_MAX]; - uint32_t availableCount; - - ecsentityid_t root; -} ecs_t; - -extern ecs_t ECS; - -/** - * Initialize the given ECS system. - */ -void ecsInit(); - -/** - * Create a new entity in the ECS. This locks an id and gives it to the caller, - * who will be responsible for managing the entity's lifecycle. - * - * @return The ID of the newly created entity. - */ -ecsentityid_t ecsEntityCreate(); - -/** - * Disposes an entity in the ECS. This will free the entity's ID and make it - * available for reuse. - * - * @param id The ID of the entity to destroy. - */ -void ecsEntityDispose(const ecsentityid_t id); \ No newline at end of file +typedef int_fast16_t ecsid_t; \ No newline at end of file diff --git a/src/ecs/ecscomponent.c b/src/ecs/ecscomponent.c new file mode 100644 index 0000000..dbfee10 --- /dev/null +++ b/src/ecs/ecscomponent.c @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "ecscomponent.h" +#include "assert/assert.h" +#include "util/memory.h" + +bool_t ecsComponentDataHas(const ecscomponent_t *cmp, const ecsid_t id) { + assertNotNull(cmp, "Component pointer cannot be NULL."); + assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID."); + + return cmp->entityFlags[id] != 0; +} + +void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id) { + assertTrue(ecsComponentDataHas(cmp, id), "No data for entity ID."); + + // Calculate the offset for the entity's data. + return (void *)((uint8_t *)cmp->data + (id * cmp->dataSize)); +} + +void * ecsComponentDataAdd(const ecscomponent_t *cmp, const ecsid_t id) { + assertNotNull(cmp, "Component pointer cannot be NULL."); + assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID."); + assertFalse(ecsComponentDataHas(cmp, id), "Entity already has data."); + + // Zero out the entity's data. + memoryZero( + (uint8_t *)cmp->data + (id * cmp->dataSize), + cmp->dataSize + ); + + // Mark the entity as having data. + cmp->entityFlags[id] = ECS_COMPONENT_FLAG_USED; + + return ecsComponentDataGet(cmp, id); +} + +void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id) { + assertNotNull(cmp, "Component pointer cannot be NULL."); + assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID."); + assertTrue(ecsComponentDataHas(cmp, id), "Entity does not have data."); + + // Clear the entity's data flag. + cmp->entityFlags[id] = 0; +} \ No newline at end of file diff --git a/src/ecs/ecscomponent.h b/src/ecs/ecscomponent.h new file mode 100644 index 0000000..a80ef67 --- /dev/null +++ b/src/ecs/ecscomponent.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "ecs/ecsentity.h" + +#define ECS_COMPONENT_FLAG_USED (1 << 0) + +typedef struct { + void *data; + size_t dataSize; + uint8_t entityFlags[ECS_ENTITY_COUNT_MAX]; +} ecscomponent_t; + +/** + * Initializes an ECS Component. + * + * @param dataPointer Pointer to the data that the component owns. + */ +#define ecsComponentInit(dataPointer) \ + (ecscomponent_t){ \ + .data = dataPointer, \ + .dataSize = (sizeof(*dataPointer) / ECS_ENTITY_COUNT_MAX), \ + .entityFlags = {0} \ + }; + +/** + * Checks if the component has data for a specific entity. + * + * @param cmp Pointer to the ecscomponent_t. + * @param id The ID of the entity to check. + * @return True if the component has data for the entity, false otherwise. + */ +bool_t ecsComponentDataHas(const ecscomponent_t *cmp, const ecsid_t id); + +/** + * Gets the data for a specific entity in the component. + * + * @param cmp Pointer to the ecscomponent_t. + * @param id The ID of the entity to get data for. + * @return Pointer to the data for the entity. + */ +void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id); + +/** + * Adds data for a specific entity in the component. + * + * @param cmp Pointer to the ecscomponent_t. + * @param id The ID of the entity to add data for. + */ +void * ecsComponentDataAdd(const ecscomponent_t *cmp, const ecsid_t id); + +/** + * Removes data for a specific entity in the component. + * + * @param cmp Pointer to the ecscomponent_t. + * @param id The ID of the entity to remove data for. + */ +void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id); \ No newline at end of file diff --git a/src/ecs/ecsentity.h b/src/ecs/ecsentity.h new file mode 100644 index 0000000..cfe0817 --- /dev/null +++ b/src/ecs/ecsentity.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "ecs.h" + +#define ECS_ENTITY_FLAG_USED (1 << 0) + +#define ECS_ENTITY_COUNT_MAX 2048 + +typedef struct ecsentity_s { + ecsid_t id; + uint8_t flags; +} ecsentity_t; \ No newline at end of file diff --git a/src/ecs/ecs.c b/src/ecs/ecssystem.c similarity index 59% rename from src/ecs/ecs.c rename to src/ecs/ecssystem.c index 3028305..5737eac 100644 --- a/src/ecs/ecs.c +++ b/src/ecs/ecssystem.c @@ -5,35 +5,35 @@ * https://opensource.org/licenses/MIT */ -#include "ecs.h" +#include "ecssystem.h" #include "util/memory.h" #include "assert/assert.h" -ecs_t ECS; +ecssystem_t ECS_SYSTEM; -void ecsInit() { - memoryZero(&ECS, sizeof(ecs_t)); +void ecsSystemInit() { + memoryZero(&ECS_SYSTEM, sizeof(ecssystem_t)); // Fill ECS ids for(uint32_t i = 0; i < ECS_ENTITY_COUNT_MAX; i++) { - ECS.entities[i].id = i; + ECS_SYSTEM.entities[i].id = i; } // Fill the available array. for(uint32_t i = 0; i < ECS_ENTITY_COUNT_MAX; i++) { - ECS.available[i] = &ECS.entities[i]; + ECS_SYSTEM.available[i] = &ECS_SYSTEM.entities[i]; } - ECS.availableCount = ECS_ENTITY_COUNT_MAX; + ECS_SYSTEM.availableCount = ECS_ENTITY_COUNT_MAX; // Reserve root entity - ECS.root = ecsEntityCreate(); + ECS_SYSTEM.root = ecsEntityAdd(); } -ecsentityid_t ecsEntityCreate() { - assertTrue(ECS.availableCount > 0, "No available entities to create"); +ecsid_t ecsEntityAdd() { + assertTrue(ECS_SYSTEM.availableCount > 0, "No available entities to create"); // Pop off the last available entity. - ecsentity_t *entity = ECS.available[--ECS.availableCount]; + ecsentity_t *entity = ECS_SYSTEM.available[--ECS_SYSTEM.availableCount]; assertTrue((entity->flags & ECS_ENTITY_FLAG_USED) == 0, "Entity is used."); assertTrue(entity->id >= 0, "Entity is invalid."); assertTrue(entity->id < ECS_ENTITY_COUNT_MAX, "Entity ID out of bounds"); @@ -43,17 +43,17 @@ ecsentityid_t ecsEntityCreate() { return entity->id; } -void ecsEntityDispose(const ecsentityid_t id) { +void ecsEntityRemove(const ecsid_t id) { assertTrue(id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID"); - ecsentity_t *entity = &ECS.entities[id]; + ecsentity_t *entity = &ECS_SYSTEM.entities[id]; assertTrue(entity->id >= 0, "Entity is invalid."); assertTrue((entity->flags & ECS_ENTITY_FLAG_USED) != 0, "Entity is not used."); // Mark the entity as available. - ECS.available[ECS.availableCount++] = entity; + ECS_SYSTEM.available[ECS_SYSTEM.availableCount++] = entity; assertTrue( - ECS.availableCount <= ECS_ENTITY_COUNT_MAX, + ECS_SYSTEM.availableCount <= ECS_ENTITY_COUNT_MAX, "Available count exceeded maximum limit" ); } \ No newline at end of file diff --git a/src/ecs/ecssystem.h b/src/ecs/ecssystem.h new file mode 100644 index 0000000..e0de899 --- /dev/null +++ b/src/ecs/ecssystem.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "ecsentity.h" + +typedef struct { + ecsentity_t entities[ECS_ENTITY_COUNT_MAX]; + + ecsentity_t *available[ECS_ENTITY_COUNT_MAX]; + uint32_t availableCount; + + ecsid_t root; +} ecssystem_t; + +extern ecssystem_t ECS_SYSTEM; + +/** + * Initialize the ECS system. + */ +void ecsSystemInit(); + +/** + * Create a new entity in the ECS. This locks an id and gives it to the caller, + * who will be responsible for managing the entity's lifecycle. + * + * @return The ID of the newly created entity. + */ +ecsid_t ecsEntityAdd(); + +/** + * Disposes an entity in the ECS. This will free the entity's ID and make it + * available for reuse. + * + * @param id The ID of the entity to destroy. + */ +void ecsEntityRemove(const ecsid_t id); \ No newline at end of file diff --git a/src/engine/engine.c b/src/engine/engine.c index 2c2741b..5e5c1f1 100644 --- a/src/engine/engine.c +++ b/src/engine/engine.c @@ -10,7 +10,7 @@ #include "time/time.h" #include "console/console.h" #include "display/display.h" -#include "ecs/ecs.h" +#include "ecs/ecssystem.h" #include "scene/scenetree.h" engine_t ENGINE; @@ -22,7 +22,7 @@ errorret_t engineInit(void) { // Init systems. Order is important. timeInit(); consoleInit(); - ecsInit(); + ecsSystemInit(); sceneTreeInit(); errorChain(displayInit()); diff --git a/src/scene/CMakeLists.txt b/src/scene/CMakeLists.txt index b49a86a..08f302c 100644 --- a/src/scene/CMakeLists.txt +++ b/src/scene/CMakeLists.txt @@ -7,4 +7,7 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE scenetree.c -) \ No newline at end of file +) + +# Subdirs +add_subdirectory(test) \ No newline at end of file diff --git a/src/scene/scenetree.c b/src/scene/scenetree.c index a73ca18..27f5844 100644 --- a/src/scene/scenetree.c +++ b/src/scene/scenetree.c @@ -13,17 +13,17 @@ void sceneTreeInit(void) { memoryZero(&SCENE_TREE, sizeof(scenetree_t)); // Default all items to have no parent - for (ecsentityid_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; } } -ecsentityid_t sceneTreeParentGet(const ecsentityid_t child) { +ecsid_t sceneTreeParentGet(const ecsid_t child) { assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID"); return SCENE_TREE.items[child].parent; } -uint8_t sceneTreeChildGetAll(const ecsentityid_t id, ecsentityid_t *children) { +uint8_t sceneTreeChildGetAll(const ecsid_t id, ecsid_t *children) { assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid parent ID"); const scenetreenode_t *node = &SCENE_TREE.items[id]; @@ -32,7 +32,7 @@ uint8_t sceneTreeChildGetAll(const ecsentityid_t id, ecsentityid_t *children) { return node->childCount; } -void sceneTreeChildAdd(const ecsentityid_t parent, const ecsentityid_t child) { +void sceneTreeChildAdd(const ecsid_t parent, const ecsid_t child) { assertTrue(parent >= 0 && parent < ECS_ENTITY_COUNT_MAX, "Invalid parent ID"); assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID"); assertTrue(parent != child, "Child cannot be a child of itself."); @@ -57,8 +57,8 @@ void sceneTreeChildAdd(const ecsentityid_t parent, const ecsentityid_t child) { } void sceneTreeChildRemove( - const ecsentityid_t parent, - const ecsentityid_t child + const ecsid_t parent, + const ecsid_t child ) { assertTrue(parent >= 0 && parent < ECS_ENTITY_COUNT_MAX, "Invalid parent ID"); assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID"); @@ -85,12 +85,12 @@ void sceneTreeChildRemove( childNode->parent = -1; } -void sceneTreeChildRemoveAll(const ecsentityid_t parent) { +void sceneTreeChildRemoveAll(const ecsid_t parent) { assertTrue(parent >= 0 && parent < ECS_ENTITY_COUNT_MAX, "Invalid parent ID"); scenetreenode_t *parentNode = &SCENE_TREE.items[parent]; for(uint8_t i = 0; i < parentNode->childCount; i++) { - ecsentityid_t child = parentNode->children[i]; + ecsid_t child = parentNode->children[i]; SCENE_TREE.items[child].parent = -1; } @@ -99,8 +99,8 @@ void sceneTreeChildRemoveAll(const ecsentityid_t parent) { } bool_t sceneTreeChildInTree( - const ecsentityid_t parent, - const ecsentityid_t child + const ecsid_t parent, + const ecsid_t child ) { assertTrue(parent >= 0 && parent < ECS_ENTITY_COUNT_MAX, "Invalid parent ID"); assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID"); diff --git a/src/scene/scenetree.h b/src/scene/scenetree.h index e6a2320..e0296d9 100644 --- a/src/scene/scenetree.h +++ b/src/scene/scenetree.h @@ -6,14 +6,14 @@ */ #pragma once -#include "ecs/ecs.h" +#include "ecs/ecsentity.h" #define SCENE_ITEM_CHILD_MAX 16 typedef struct { - ecsentityid_t children[SCENE_ITEM_CHILD_MAX]; + ecsid_t children[SCENE_ITEM_CHILD_MAX]; uint8_t childCount; - ecsentityid_t parent; + ecsid_t parent; } scenetreenode_t; typedef struct scene_s { @@ -35,7 +35,7 @@ void sceneTreeInit(void); * * @return The ID of the newly created scene item. */ -ecsentityid_t sceneTreeCreate(void); +ecsid_t sceneTreeCreate(void); /** * Get the parent of a given scene item. @@ -43,7 +43,7 @@ ecsentityid_t sceneTreeCreate(void); * @param id The ID of the scene item. * @return The ID of the parent scene item, or -1 if it has no parent. */ -ecsentityid_t sceneTreeParentGet(const ecsentityid_t child); +ecsid_t sceneTreeParentGet(const ecsid_t child); /** * Get the children of a given scene item. If children is NULL only the count @@ -53,7 +53,7 @@ ecsentityid_t sceneTreeParentGet(const ecsentityid_t child); * @param children Pointer to an array where the children IDs will be stored. * @return The number of children found. */ -uint8_t sceneTreeChildGetAll(const ecsentityid_t id, ecsentityid_t *children); +uint8_t sceneTreeChildGetAll(const ecsid_t id, ecsid_t *children); /** * Add a child to a parent in the scene tree. @@ -61,7 +61,7 @@ uint8_t sceneTreeChildGetAll(const ecsentityid_t id, ecsentityid_t *children); * @param parent The ID of the parent scene item. * @param child The ID of the child scene item to add. */ -void sceneTreeChildAdd(const ecsentityid_t parent, const ecsentityid_t child); +void sceneTreeChildAdd(const ecsid_t parent, const ecsid_t child); /** * Remove a child from a parent in the scene tree. @@ -69,14 +69,14 @@ void sceneTreeChildAdd(const ecsentityid_t parent, const ecsentityid_t child); * @param prnt The ID of the parent scene item. * @param child The ID of the child scene item to remove. */ -void sceneTreeChildRemove(const ecsentityid_t prnt, const ecsentityid_t child); +void sceneTreeChildRemove(const ecsid_t prnt, const ecsid_t child); /** * Remove all children from a parent in the scene tree. * * @param parent The ID of the parent scene item. */ -void sceneTreeChildRemoveAll(const ecsentityid_t parent); +void sceneTreeChildRemoveAll(const ecsid_t parent); /** * Returns true if the child is within the parent's children, recursively. @@ -85,7 +85,4 @@ void sceneTreeChildRemoveAll(const ecsentityid_t parent); * @param child The ID of the child scene item to check. * @return True if the child is in the parent's children, false otherwise. */ -bool_t sceneTreeChildInTree( - const ecsentityid_t parent, - const ecsentityid_t child -); \ No newline at end of file +bool_t sceneTreeChildInTree(const ecsid_t parent, const ecsid_t child); \ No newline at end of file diff --git a/src/scene/test/CMakeLists.txt b/src/scene/test/CMakeLists.txt new file mode 100644 index 0000000..02f9e89 --- /dev/null +++ b/src/scene/test/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_TARGET_NAME} + PRIVATE + scenetest.c +) \ No newline at end of file diff --git a/src/scene/test/scenetest.c b/src/scene/test/scenetest.c new file mode 100644 index 0000000..3293a31 --- /dev/null +++ b/src/scene/test/scenetest.c @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "scenetest.h" +#include "scene/scenetree.h" +#include "display/camera.h" + +ecsid_t sceneTestAdd(void) { + ecsid_t id = ecsEntityAdd(); + + // Initialize the entity with a camera component + ecsid_t camera = ecsEntityAdd(); + sceneTreeChildAdd(id, camera); + camera_t *camData = cameraAdd(camera); + + // Optionally, you can set other properties or components here + + return id; +} \ No newline at end of file diff --git a/src/scene/test/scenetest.h b/src/scene/test/scenetest.h new file mode 100644 index 0000000..42a4532 --- /dev/null +++ b/src/scene/test/scenetest.h @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "ecs/ecssystem.h" + +ecsid_t sceneTestAdd(void); \ No newline at end of file