diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index cdde149..ba78673 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -8,6 +8,7 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE display.c camera.c + renderer.c ) # Subdirectories diff --git a/src/display/camera.c b/src/display/camera.c index 3fddce8..6abf772 100644 --- a/src/display/camera.c +++ b/src/display/camera.c @@ -8,6 +8,7 @@ #include "camera.h" #include "display/display.h" #include "assert/assert.h" +#include "scene/node.h" camera_t CAMERA_DATA[ECS_ENTITY_COUNT_MAX] = { 0 }; ecscomponent_t CAMERA_COMPONENT = ecsComponentInit( @@ -24,8 +25,8 @@ void cameraEntityAdded(const ecsid_t id) { if(CAMERA_MAIN == -1) CAMERA_MAIN = id; camera_t *cam = cameraGet(id); - cam->type = CAMERA_TYPE_ORTHOGRAPHIC; - cam->perspective.fov = glm_rad(75.0f); + cam->type = CAMERA_TYPE_PERSPECTIVE; + cam->perspective.fov = glm_rad(90.0f); cam->nearClip = 0.1f; cam->farClip = 1000.0f; cam->clearColor = COLOR_CORNFLOWER_BLUE; @@ -36,7 +37,9 @@ void cameraPush(const ecsid_t id) { camera_t *cam = cameraGet(id); - mat4 projection; + mat4 projection, view; + nodeMatrixGet(id, view); + switch(cam->type) { case CAMERA_TYPE_ORTHOGRAPHIC: glm_ortho( @@ -61,10 +64,13 @@ void cameraPush(const ecsid_t id) { } #if DUSK_DISPLAY_SDL2 + mat4 pv; + glm_mat4_mul(projection, view, pv); + glPushMatrix(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glLoadMatrixf((const GLfloat*)projection); + glLoadMatrixf((const GLfloat*)pv); glClearColor( cam->clearColor.r / 255.0f, @@ -73,6 +79,9 @@ void cameraPush(const ecsid_t id) { cam->clearColor.a / 255.0f ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); #endif } diff --git a/src/display/display.c b/src/display/display.c index 0360eea..3ba5f02 100644 --- a/src/display/display.c +++ b/src/display/display.c @@ -7,18 +7,11 @@ #include "display/display.h" #include "console/console.h" -#include "display/camera.h" -#include "display/mesh/mesh.h" +#include "display/renderer.h" +#include "ecs/ecssystem.h" display_t DISPLAY; -mesh_t mesh; -meshvertex_t triangle[3] = { - {{255, 0, 0, 255}, {0.0f, 0.0f}, {0.0f, 1.0f}}, // Vertex 1 - {{0, 255, 0, 255}, {1.0f, 0.0f}, {1.0f, 1.0f}}, // Vertex 2 - {{0, 0, 255, 255}, {0.5f, 1.0f}, {0.5f, 0.0f}} // Vertex 3 -}; - errorret_t displayInit(void) { #if DUSK_DISPLAY_SDL2 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) != 0) { @@ -61,8 +54,6 @@ errorret_t displayInit(void) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); #endif - - meshInit(&mesh, MESH_PRIMITIVE_TRIANGLES, 3, triangle); // For now, we just return an OK error. errorOk(); @@ -88,11 +79,7 @@ errorret_t displayUpdate(void) { glViewport(0, 0, windowWidth, windowHeight); #endif - if(CAMERA_MAIN != -1) { - cameraPush(CAMERA_MAIN); - meshDraw(&mesh, 0, -1); - cameraPop(); - } + rendererRender(CAMERA_MAIN); #if DUSK_DISPLAY_SDL2 SDL_GL_SwapWindow(DISPLAY.window); @@ -103,8 +90,6 @@ errorret_t displayUpdate(void) { } errorret_t displayDispose(void) { - meshDispose(&mesh); - #if DUSK_DISPLAY_SDL2 if(DISPLAY.glContext) { SDL_GL_DeleteContext(DISPLAY.glContext); diff --git a/src/display/mesh/CMakeLists.txt b/src/display/mesh/CMakeLists.txt index 9befe9a..45708f9 100644 --- a/src/display/mesh/CMakeLists.txt +++ b/src/display/mesh/CMakeLists.txt @@ -8,4 +8,5 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE mesh.c quad.c + meshrenderer.c ) \ No newline at end of file diff --git a/archive/dusksdl2/display/mesh/meshrenderer.c b/src/display/mesh/meshrenderer.c similarity index 79% rename from archive/dusksdl2/display/mesh/meshrenderer.c rename to src/display/mesh/meshrenderer.c index 7ca5a32..1c14301 100644 --- a/archive/dusksdl2/display/mesh/meshrenderer.c +++ b/src/display/mesh/meshrenderer.c @@ -18,10 +18,9 @@ ecscomponent_t MESH_RENDERER_COMPONENT = ecsComponentInit( ); void meshRendererDraw(const ecsid_t id) { - if (!meshRendererHas(id)) return; - - meshrenderer_t *renderer = meshRendererGet(id); - if (!renderer || !renderer->mesh) return; + if(!meshRendererHas(id)) return; + meshrenderer_t *renderer = &MESH_RENDERER_DATA[id]; + if(!renderer->mesh) return; meshDraw(renderer->mesh, 0, -1); } \ No newline at end of file diff --git a/archive/dusksdl2/display/mesh/meshrenderer.h b/src/display/mesh/meshrenderer.h similarity index 91% rename from archive/dusksdl2/display/mesh/meshrenderer.h rename to src/display/mesh/meshrenderer.h index 73a55a9..e2cea80 100644 --- a/archive/dusksdl2/display/mesh/meshrenderer.h +++ b/src/display/mesh/meshrenderer.h @@ -24,6 +24,8 @@ extern ecscomponent_t MESH_RENDERER_COMPONENT; (ecsComponentDataHas(&MESH_RENDERER_COMPONENT, id)) #define meshRendererRemove(id) \ ecsComponentDataRemove(&MESH_RENDERER_COMPONENT, id) +#define meshRendererGetAll(out) \ + ecsComponentGetAll(&MESH_RENDERER_COMPONENT, out) /** * Draw the mesh for the given entity. diff --git a/src/display/renderer.c b/src/display/renderer.c new file mode 100644 index 0000000..e4e265b --- /dev/null +++ b/src/display/renderer.c @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "renderer.h" +#include "display/mesh/meshrenderer.h" + +void rendererRender(const ecsid_t camera) { + if(camera == -1) return; + + // Get the meshes. + uint32_t meshCount; + ecsid_t meshes[ECS_ENTITY_COUNT_MAX]; + meshCount = meshRendererGetAll(meshes); + + cameraPush(camera); + for(uint32_t i = 0; i < meshCount; i++) { + meshRendererDraw(meshes[i]); + } + cameraPop(); +} \ No newline at end of file diff --git a/src/display/renderer.h b/src/display/renderer.h new file mode 100644 index 0000000..05a6cf3 --- /dev/null +++ b/src/display/renderer.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/camera.h" + +/** + * Render the given scene from the perspective of the given camera. + * + * @param camera The ID of the camera entity to render from. + */ +void rendererRender(const ecsid_t camera); \ No newline at end of file diff --git a/src/ecs/ecscomponent.c b/src/ecs/ecscomponent.c index d7af6e2..49c81c7 100644 --- a/src/ecs/ecscomponent.c +++ b/src/ecs/ecscomponent.c @@ -58,8 +58,7 @@ void * ecsComponentDataAdd(ecscomponent_t *cmp, const ecsid_t id) { ); cmp->entityFlags[id] |= ECS_COMPONENT_ENTITY_FLAG_USED; if(cmp->callbacks.entityAdd) cmp->callbacks.entityAdd(id); - cmp->entitiesWithData[id] = id; - cmp->entitiesWithDataCount++; + cmp->entitiesWithData[cmp->entitiesWithDataCount++] = id; return ecsComponentDataGet(cmp, id); } @@ -92,6 +91,22 @@ void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id) { if(cmp->callbacks.entityRemove) cmp->callbacks.entityRemove(id); } +uint32_t ecsComponentGetAll(const ecscomponent_t *cmp, ecsid_t *out) { + assertTrue( + ecsComponentIsInitialized(cmp), + "Component was never initialized." + ); + + if(!out) return cmp->entitiesWithDataCount; + + memoryCopy( + out, + cmp->entitiesWithData, + sizeof(ecsid_t) * cmp->entitiesWithDataCount + ); + return cmp->entitiesWithDataCount; +} + void ecsComponentDispose(ecscomponent_t *cmp) { assertNotNull(cmp, "Component pointer cannot be NULL."); assertTrue( diff --git a/src/ecs/ecscomponent.h b/src/ecs/ecscomponent.h index 8731c19..b52daad 100644 --- a/src/ecs/ecscomponent.h +++ b/src/ecs/ecscomponent.h @@ -93,6 +93,16 @@ void * ecsComponentDataAdd(ecscomponent_t *cmp, const ecsid_t id); */ void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id); +/** + * Gets all entities that have data in the component. Passing NULL for out will + * just return the count of entities. + * + * @param cmp Pointer to the ecscomponent_t. + * @param out Pointer to an array to store the entity IDs. + * @return The number of entities with data. + */ +uint32_t ecsComponentGetAll(const ecscomponent_t *cmp, ecsid_t *out); + /** * Disposes the component, freeing any resources it holds. * diff --git a/src/ecs/ecssystem.c b/src/ecs/ecssystem.c index a5eec2d..f00d0ef 100644 --- a/src/ecs/ecssystem.c +++ b/src/ecs/ecssystem.c @@ -24,9 +24,6 @@ void ecsSystemInit() { ECS_SYSTEM.available[i] = &ECS_SYSTEM.entities[i]; } ECS_SYSTEM.availableCount = ECS_ENTITY_COUNT_MAX; - - // Reserve root entity - ECS_SYSTEM.root = ecsEntityAdd(); } ecsid_t ecsEntityAdd() { diff --git a/src/ecs/ecssystem.h b/src/ecs/ecssystem.h index 2de4a39..e1561d8 100644 --- a/src/ecs/ecssystem.h +++ b/src/ecs/ecssystem.h @@ -19,8 +19,6 @@ typedef struct { ecscomponent_t *components[ECS_SYSTEM_ECS_COMPONENTS_MAX]; uint32_t componentCount; - - ecsid_t root; } ecssystem_t; extern ecssystem_t ECS_SYSTEM; diff --git a/src/engine/engine.c b/src/engine/engine.c index 1b80d0a..30edf7e 100644 --- a/src/engine/engine.c +++ b/src/engine/engine.c @@ -27,8 +27,7 @@ errorret_t engineInit(void) { ecsSystemInit(); errorChain(displayInit()); - ecsid_t sceneTest = sceneTestAdd(); - nodeChildAdd(ECS_SYSTEM.root, sceneTest); + sceneTestAdd(); errorOk(); } diff --git a/src/scene/node.c b/src/scene/node.c index adbc982..c49b037 100644 --- a/src/scene/node.c +++ b/src/scene/node.c @@ -17,111 +17,37 @@ ecscomponent_t NODE_COMPONENT = ecsComponentInit( }) ); -ecsid_t nodeParentGet(const ecsid_t child) { - assertTrue(child >= 0 && child < ECS_ENTITY_COUNT_MAX, "Invalid child ID"); - if(!nodeHas(child)) return -1; - return NODE_DATA[child].parent; -} - -uint8_t nodeChildGetAll(const ecsid_t id, ecsid_t *children) { - if(!nodeHas(id)) return 0; - if(children == NULL) return NODE_DATA[id].childCount; - - memoryCopy( - children, - NODE_DATA[id].children, - sizeof(ecsid_t) * NODE_DATA[id].childCount - ); - - return NODE_DATA[id].childCount; -} - -void nodeChildAdd(const ecsid_t parent, const ecsid_t child) { - if(!nodeHas(parent)) nodeAdd(parent); - if(!nodeHas(child)) nodeAdd(child); - - node_t *parentNode = &NODE_DATA[parent]; - assertTrue(parentNode->childCount < SCENE_ITEM_CHILD_MAX, "Parent full."); - - // Add child to parent's children array - parentNode->children[parentNode->childCount] = child; - parentNode->childCount++; - - // Set child's parent - NODE_DATA[child].parent = parent; -} - -void nodeChildRemove(const ecsid_t parent, const ecsid_t child) { - assertTrue(nodeHas(parent), "NODE ECS lacks Parent."); - assertTrue(nodeHas(child), "NODE ECS lacks Child."); - - node_t *childNode = &NODE_DATA[child]; - node_t *parentNode = &NODE_DATA[parent]; - assertTrue(childNode->parent == parent, "Child does not belong to parent"); - assertTrue(parentNode->childCount > 0, "Parent has no children"); - - // Remove child from parent's children array - for (uint8_t i = 0; i < parentNode->childCount; i++) { - if (parentNode->children[i] == child) { - // Shift remaining children down - for (uint8_t j = i; j < parentNode->childCount - 1; j++) { - parentNode->children[j] = parentNode->children[j + 1]; - } - parentNode->childCount--; - break; - } - } - - // Clear child's parent - childNode->parent = -1; - - if(childNode->childCount == 0) nodeRemove(child); - if(parentNode->childCount == 0) nodeRemove(parent); -} - -void nodeChildRemoveAll(const ecsid_t parent) { - assertTrue(nodeHas(parent), "NODE ECS lacks Parent."); - - node_t *parentNode = &NODE_DATA[parent]; - for(uint8_t i = 0; i < parentNode->childCount; i++) { - ecsid_t child = parentNode->children[i]; - NODE_DATA[child].parent = -1; - - if(NODE_DATA[child].childCount == 0) nodeRemove(child); - } - - nodeRemove(parent); -} - -bool_t nodeChildInTree(const ecsid_t parent, const ecsid_t child) { - assertTrue(nodeHas(parent), "NODE ECS lacks Parent."); - assertTrue(nodeHas(child), "NODE ECS lacks 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."); - - node_t *childNode = &NODE_DATA[child]; - while(childNode) { - if(childNode->parent == parent) return true; - if(childNode->parent == -1) break; // No parent means we've reached the root - childNode = &NODE_DATA[childNode->parent]; - } - - return false; -} - void nodeInit(void) { - for(ecsid_t i = 0; i < ECS_ENTITY_COUNT_MAX; i++) { - NODE_DATA[i].parent = -1; - } } void nodeEntityAdded(const ecsid_t id) { - NODE_DATA[id].parent = -1; + glm_mat4_identity(NODE_DATA[id].transform); } void nodeEntityRemoved(const ecsid_t id) { - if(NODE_DATA[id].parent == -1) return; - nodeChildRemove(NODE_DATA[id].parent, id); + +} + +void nodeMatrixGet(const ecsid_t id, mat4 dest) { + node_t *node; + + if(nodeHas(id)) { + node = &NODE_DATA[id]; + } else { + node = nodeAdd(id); + } + + glm_mat4_copy(node->transform, dest); +} + +void nodeMatrixSet(const ecsid_t id, mat4 in) { + node_t *node; + + if(nodeHas(id)) { + node = &NODE_DATA[id]; + } else { + node = nodeAdd(id); + } + + glm_mat4_copy(in, node->transform); } \ No newline at end of file diff --git a/src/scene/node.h b/src/scene/node.h index a613d29..2b5f503 100644 --- a/src/scene/node.h +++ b/src/scene/node.h @@ -11,13 +11,7 @@ #define SCENE_ITEM_CHILD_MAX 16 typedef struct { - ecsid_t children[SCENE_ITEM_CHILD_MAX]; - uint8_t childCount; - ecsid_t parent; - - vec3 position; - vec3 rotation; - vec3 scale; + mat4 transform; } node_t; extern node_t NODE_DATA[ECS_ENTITY_COUNT_MAX]; @@ -30,64 +24,6 @@ extern ecscomponent_t NODE_COMPONENT; ((node_t*)ecsComponentDataAdd(&NODE_COMPONENT, id)) #define nodeRemove(id) ecsComponentDataRemove(&NODE_COMPONENT, id) -/** - * Create a new scene item in the scene tree. - * This will create a new entity in the ECS and return its ID. - * - * @return The ID of the newly created scene item. - */ -ecsid_t nodeCreate(void); - -/** - * Get the parent of a given scene item. - * - * @param id The ID of the scene item. - * @return The ID of the parent scene item, or -1 if it has no parent. - */ -ecsid_t nodeParentGet(const ecsid_t child); - -/** - * Get the children of a given scene item. If children is NULL only the count - * will be returned. - * - * @param id The ID of the scene item. - * @param children Pointer to an array where the children IDs will be stored. - * @return The number of children found. - */ -uint8_t nodeChildGetAll(const ecsid_t id, ecsid_t *children); - -/** - * Add a child to a parent in the scene tree. - * - * @param parent The ID of the parent scene item. - * @param child The ID of the child scene item to add. - */ -void nodeChildAdd(const ecsid_t parent, const ecsid_t child); - -/** - * Remove a child from a parent in the scene tree. - * - * @param prnt The ID of the parent scene item. - * @param child The ID of the child scene item to remove. - */ -void nodeChildRemove(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 nodeChildRemoveAll(const ecsid_t parent); - -/** - * Returns true if the child is within the parent's children, recursively. - * - * @param parent The ID of the parent scene item. - * @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 nodeChildInTree(const ecsid_t parent, const ecsid_t child); - /** * Initialize the scene tree. * @@ -109,4 +45,20 @@ void nodeEntityAdded(const ecsid_t id); * * @param id The ID of the entity being removed. */ -void nodeEntityRemoved(const ecsid_t id); \ No newline at end of file +void nodeEntityRemoved(const ecsid_t id); + +/** + * Get the local transformation matrix of a scene item. + * + * @param id The ID of the scene item. + * @param out Pointer to a mat4 where the local matrix will be stored. + */ +void nodeMatrixGet(const ecsid_t id, mat4 out); + +/** + * Set the local transformation matrix of a scene item. + * + * @param id The ID of the scene item. + * @param in Pointer to a mat4 containing the new local matrix. + */ +void nodeMatrixSet(const ecsid_t id, mat4 in); \ No newline at end of file diff --git a/src/scene/test/scenetest.c b/src/scene/test/scenetest.c index af6e41d..938e1d0 100644 --- a/src/scene/test/scenetest.c +++ b/src/scene/test/scenetest.c @@ -9,23 +9,35 @@ #include "scene/node.h" #include "display/camera.h" #include "display/display.h" +#include "display/mesh/meshrenderer.h" -ecsid_t sceneTestAdd(void) { - ecsid_t id = ecsEntityAdd(); +mesh_t mesh; +meshvertex_t triangle[3] = { + {{255, 0, 0, 255}, {0.0f, 0.0f}, {1.0f, 0.0f}}, // Vertex 1 + {{0, 255, 0, 255}, {1.0f, 0.0f}, {-1.0f, 0.0f}}, // Vertex 2 + {{0, 0, 255, 255}, {0.5f, 1.0f}, {0, 2.0f}} // Vertex 3 +}; + +void sceneTestAdd(void) { + meshInit(&mesh, MESH_PRIMITIVE_TRIANGLES, 3, triangle); // Initialize the entity with a camera component ecsid_t camera = ecsEntityAdd(); - nodeChildAdd(id, camera); + node_t *node = nodeAdd(camera); camera_t *camData = cameraAdd(camera); - camData->type = CAMERA_TYPE_ORTHOGRAPHIC; - camData->orthographic.left = 0.0f; - camData->orthographic.right = 1.0f; - camData->orthographic.top = 0.0f; - camData->orthographic.bottom = 1.0f; - camData->nearClip = -1.0f; - camData->farClip = 1.0f; + + mat4 lookAt; + glm_lookat( + (vec3){ 3.0f, 3.0f, 3.0f }, + (vec3){ 0.0f, 0.0f, 0.0f }, + (vec3){ 0.0f, 1.0f, 0.0f }, + lookAt + ); + nodeMatrixSet(camera, lookAt); - // Optionally, you can set other properties or components here - - return id; + // Test cube + ecsid_t cube = ecsEntityAdd(); + node = nodeAdd(cube); + meshrenderer_t *renderer = meshRendererAdd(cube); + renderer->mesh = &mesh; } \ No newline at end of file diff --git a/src/scene/test/scenetest.h b/src/scene/test/scenetest.h index 42a4532..2f9fb91 100644 --- a/src/scene/test/scenetest.h +++ b/src/scene/test/scenetest.h @@ -8,4 +8,4 @@ #pragma once #include "ecs/ecssystem.h" -ecsid_t sceneTestAdd(void); \ No newline at end of file +void sceneTestAdd(void); \ No newline at end of file