ECS Enhancements

This commit is contained in:
2026-04-10 07:09:25 -05:00
parent c52e1d22b7
commit 42099f7241
16 changed files with 289 additions and 36 deletions
+10 -2
View File
@@ -40,8 +40,16 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(gameInit());
// FOF
entityid_t ent0 = entityManagerAdd();
componentid_t ent0c0 = entityAddComponent(ent0, COMPONENT_TYPE_POSITION);
entityid_t cam = entityManagerAdd();
componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION);
entityPositionLookAt(
cam,
camPos,
(vec3){ 0.0f, 0.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f },
(vec3){ 5.0f, 5.0f, 5.0f }
);
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
entityid_t ent1 = entityManagerAdd();
+47 -6
View File
@@ -30,7 +30,8 @@ void componentInit(
assertTrue(type < COMPONENT_TYPE_COUNT, "Component type OOB");
assertTrue(type != COMPONENT_TYPE_NULL, "Cannot initialize null component");
component_t *cmp = &ENTITY_MANAGER.entities[entityId].components[componentId];
componentindex_t index = componentGetIndex(entityId, componentId);
component_t *cmp = &ENTITY_MANAGER.components[index];
memoryZero(cmp, sizeof(component_t));
cmp->type = type;
@@ -40,16 +41,55 @@ void componentInit(
}
void * componentGetData(
const entityid_t entityId,
const componentid_t componentId,
const componenttype_t type
) {
assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB");
assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB");
assertTrue(type < COMPONENT_TYPE_COUNT, "Component type OOB");
assertTrue(type != COMPONENT_TYPE_NULL, "Cannot get data of null component");
componentindex_t index = componentGetIndex(entityId, componentId);
component_t *cmp = &ENTITY_MANAGER.components[index];
assertTrue(cmp->type == type, "Component type mismatch");
return &cmp->data;
}
componentindex_t componentGetIndex(
const entityid_t entityId,
const componentid_t componentId
) {
assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB");
assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB");
component_t *cmp = &ENTITY_MANAGER.entities[entityId].components[componentId];
if(cmp->type == COMPONENT_TYPE_NULL) return NULL;
return (entityId * ENTITY_COMPONENT_COUNT_MAX) + componentId;
}
return &cmp->data;
entityid_t componentGetEntitiesWithComponent(
const componenttype_t type,
entityid_t outEntities[ENTITY_COUNT_MAX]
) {
assertTrue(type < COMPONENT_TYPE_COUNT, "Component type OOB");
assertTrue(type != COMPONENT_TYPE_NULL, "Cannot check NULL type");
assertNotNull(outEntities, "Output entities array cannot be null");
entityid_t written = 0;
for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) {
entityid_t used = ENTITY_MANAGER.entitiesWithComponent[
type * ENTITY_COUNT_MAX + i
];
if(used == 0xFF) continue;
outEntities[written++] = used;
assertTrue(
used == i, "Entity ID mismatch in entitiesWithComponent lookup"
);
assertTrue(
(ENTITY_MANAGER.entities[used].state & ENTITY_STATE_ACTIVE) != 0,
"Inactive entity in entitiesWithComponent lookup"
);
}
return written;
}
void componentDispose(
@@ -59,7 +99,8 @@ void componentDispose(
assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB");
assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB");
component_t *cmp = &ENTITY_MANAGER.entities[entityId].components[componentId];
componentindex_t index = componentGetIndex(entityId, componentId);
component_t *cmp = &ENTITY_MANAGER.components[index];
if(cmp->type == COMPONENT_TYPE_NULL) return;
if(COMPONENT_DEFINITIONS[cmp->type].dispose) {
+27
View File
@@ -60,13 +60,40 @@ void componentInit(
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param type The type of the component to get, only used for assertion.
* @return A pointer to the component data.
*/
void * componentGetData(
const entityid_t entityId,
const componentid_t componentId,
const componenttype_t type
);
/**
* Gets the index of a component for the entity with component ID.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @return The index of the component in the component array.
*/
componentindex_t componentGetIndex(
const entityid_t entityId,
const componentid_t componentId
);
/**
* Gets the entity IDs of all entities with a component of the given type.
*
* @param type The type of the component to get entities for.
* @param outEntities An array to write the entity IDs to, must be at least
* ENTITY_COUNT_MAX in size.
* @return The number of entity IDs written to outEntities.
*/
entityid_t componentGetEntitiesWithComponent(
const componenttype_t type,
entityid_t outEntities[ENTITY_COUNT_MAX]
);
/**
* Disposes of a component for the entity with component ID.
*
+1 -5
View File
@@ -3,8 +3,4 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
entityposition.c
)
add_subdirectory(display)
@@ -0,0 +1,11 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
entityposition.c
entitycamera.c
)
@@ -0,0 +1,59 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity/entitymanager.h"
#include "display/framebuffer/framebuffer.h"
#include "display/screen/screen.h"
void entityCameraInit(const entityid_t ent, const componentid_t comp) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
cam->nearClip = 0.1f;
cam->farClip = 100.0f;
cam->projType = CAMERA_PROJECTION_TYPE_PERSPECTIVE;
cam->perspective.fov = 60.0f;
}
void entityCameraGetProjection(
const entityid_t ent,
const componentid_t comp,
mat4 out
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
if(
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
) {
glm_mat4_identity(out);
glm_perspective(
cam->perspective.fov,
SCREEN.aspect,
cam->nearClip,
cam->farClip,
out
);
if(cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED) {
out[1][1] *= -1.0f;
}
} else if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
glm_mat4_identity(out);
glm_ortho(
cam->orthographic.left,
cam->orthographic.right,
cam->orthographic.top,
cam->orthographic.bottom,
cam->nearClip,
cam->farClip,
out
);
}
}
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "entity/entitybase.h"
#include "display/camera/camera.h"
typedef struct {
union {
struct {
float_t fov;
} perspective;
struct {
float_t left;
float_t right;
float_t top;
float_t bottom;
} orthographic;
};
float_t nearClip;
float_t farClip;
cameraprojectiontype_t projType;
} entitycamera_t;
/**
* Initializes an entity camera component.
*
* @param ent The entity ID.
* @param comp The component ID.
*/
void entityCameraInit(const entityid_t ent, const componentid_t comp);
/**
* Renders out the projection matrix for the given camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @param out The output projection matrix.
*/
void entityCameraGetProjection(
const entityid_t ent,
const componentid_t comp,
mat4 out
);
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity/entitymanager.h"
void entityPositionInit(
const entityid_t entityId,
const componentid_t componentId
) {
entityposition_t *pos = componentGetData(
entityId, componentId, COMPONENT_TYPE_POSITION
);
glm_mat4_identity(pos->transform);
}
void entityPositionLookAt(
const entityid_t entityId,
const componentid_t componentId,
vec3 target,
vec3 up,
vec3 eye
) {
entityposition_t *pos = componentGetData(
entityId, componentId, COMPONENT_TYPE_POSITION
);
glm_lookat(eye, target, up, pos->transform);
}
@@ -21,4 +21,21 @@ typedef struct {
void entityPositionInit(
const entityid_t entityId,
const componentid_t componentId
);
/**
* Transforms the entity's position to look at a target point.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param target The target point to look at.
* @param up The up vector for the look at transformation.
* @param eye The position of the camera/eye for the look at transformation.
*/
void entityPositionLookAt(
const entityid_t entityId,
const componentid_t componentId,
vec3 target,
vec3 up,
vec3 eye
);
@@ -1,17 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity/entitymanager.h"
void entityPositionInit(
const entityid_t entityId,
const componentid_t componentId
) {
entityposition_t *pos = componentGetData(entityId, componentId);
glm_mat4_identity(pos->transform);
}
+4 -2
View File
@@ -5,6 +5,8 @@
* https://opensource.org/licenses/MIT
*/
#include "entity/component/entityposition.h"
#include "entity/component/display/entityposition.h"
#include "entity/component/display/entitycamera.h"
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
+23 -2
View File
@@ -14,6 +14,17 @@ void entityInit(const entityid_t entityId) {
memoryZero(ent, sizeof(entity_t));
// Mark all component types not using this entity.
for(
componenttype_t compType = 0;
compType < COMPONENT_TYPE_COUNT;
compType++
) {
ENTITY_MANAGER.entitiesWithComponent[
compType * ENTITY_COUNT_MAX + entityId
] = 0xFF;
}
ent->state |= ENTITY_STATE_ACTIVE;
}
@@ -21,11 +32,16 @@ componentid_t entityAddComponent(
const entityid_t entityId,
const componenttype_t type
) {
componentindex_t compInd;
entity_t *ent = &ENTITY_MANAGER.entities[entityId];
for(componentid_t i = 0; i < ENTITY_COMPONENT_COUNT_MAX; i++) {
if(ent->components[i].type != COMPONENT_TYPE_NULL) continue;
compInd = componentGetIndex(entityId, i);
if(ENTITY_MANAGER.components[compInd].type != COMPONENT_TYPE_NULL) continue;
componentInit(entityId, i, type);
ENTITY_MANAGER.entitiesWithComponent[
type * ENTITY_COUNT_MAX + entityId
] = entityId;
return i;
}
@@ -34,11 +50,16 @@ componentid_t entityAddComponent(
}
void entityDispose(const entityid_t entityId) {
componentindex_t compInd;
entity_t *ent = &ENTITY_MANAGER.entities[entityId];
for(componentid_t i = 0; i < ENTITY_COMPONENT_COUNT_MAX; i++) {
if(ent->components[i].type == COMPONENT_TYPE_NULL) continue;
compInd = componentGetIndex(entityId, i);
if(ENTITY_MANAGER.components[compInd].type == COMPONENT_TYPE_NULL) continue;
componentDispose(entityId, i);
ENTITY_MANAGER.entitiesWithComponent[
ENTITY_MANAGER.components[compInd].type * ENTITY_COUNT_MAX + entityId
] = 0xFF;
}
ent->state = 0;
-1
View File
@@ -12,7 +12,6 @@
typedef struct {
uint8_t state;
component_t components[ENTITY_COMPONENT_COUNT_MAX];
} entity_t;
/**
+2 -1
View File
@@ -12,4 +12,5 @@
#define ENTITY_COMPONENT_COUNT_MAX 24
typedef uint8_t entityid_t;
typedef uint8_t componentid_t;
typedef uint8_t componentid_t;
typedef uint16_t componentindex_t;
+4
View File
@@ -13,6 +13,10 @@ entitymanager_t ENTITY_MANAGER;
void entityManagerInit(void) {
memoryZero(&ENTITY_MANAGER, sizeof(entitymanager_t));
memorySet(
ENTITY_MANAGER.entitiesWithComponent, 0xFF,
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
);
}
entityid_t entityManagerAdd() {
+2
View File
@@ -10,6 +10,8 @@
typedef struct {
entity_t entities[ENTITY_COUNT_MAX];
component_t components[ENTITY_COUNT_MAX * ENTITY_COMPONENT_COUNT_MAX];
entityid_t entitiesWithComponent[COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX];
} entitymanager_t;
extern entitymanager_t ENTITY_MANAGER;