From ba7857f4dff574764319c86d58ca2e583c470d5a Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Thu, 21 May 2026 18:24:18 -0500 Subject: [PATCH] Fix rendering --- src/dusk/engine/engine.c | 1 + .../entity/component/display/entityposition.c | 205 +++++++++++++++++- .../entity/component/display/entityposition.h | 145 +++++++++---- src/dusk/entity/entity.c | 53 +++++ src/dusk/entity/entity.h | 54 ++++- src/dusk/entity/entitymanager.c | 10 + src/dusk/entity/entitymanager.h | 5 + src/dusk/input/input.c | 24 +- src/dusk/input/input.h | 16 ++ src/dusk/scene/initial/initialscene.c | 24 +- src/dusk/scene/scene.c | 6 - 11 files changed, 487 insertions(+), 56 deletions(-) diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index 32649669..7983e2d1 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -68,6 +68,7 @@ errorret_t engineUpdate(void) { timeUpdate(); inputUpdate(); consoleUpdate(); + entityManagerUpdate(); uiUpdate(); errorChain(uiTextboxUpdate()); physicsManagerUpdate(); diff --git a/src/dusk/entity/component/display/entityposition.c b/src/dusk/entity/component/display/entityposition.c index 0716192d..a277f8bf 100644 --- a/src/dusk/entity/component/display/entityposition.c +++ b/src/dusk/entity/component/display/entityposition.c @@ -166,7 +166,7 @@ void entityPositionGetLocalPosition( glm_vec3_copy(pos->position, dest); } -void entityPositionGetPosition( +void entityPositionGetWorldPosition( const entityid_t entityId, const componentid_t componentId, vec3 dest @@ -174,7 +174,6 @@ void entityPositionGetPosition( entityposition_t *pos = componentGetData( entityId, componentId, COMPONENT_TYPE_POSITION ); - // Parentless fast path: world position == pos->position, no matrix needed. if(pos->parentEntityId == ENTITY_ID_INVALID) { entityPositionEnsurePRS(pos); glm_vec3_copy(pos->position, dest); @@ -186,7 +185,36 @@ void entityPositionGetPosition( dest[2] = pos->worldTransform[3][2]; } -void entityPositionSetPosition( +void entityPositionSetWorldPosition( + const entityid_t entityId, + const componentid_t componentId, + vec3 position +) { + entityposition_t *pos = componentGetData( + entityId, componentId, COMPONENT_TYPE_POSITION + ); + if(pos->parentEntityId == ENTITY_ID_INVALID) { + glm_vec3_copy(position, pos->position); + pos->flags = (pos->flags | ENTITY_POSITION_FLAG_POSITION_DIRTY) + & ~ENTITY_POSITION_FLAG_PRS_DIRTY; + entityPositionMarkDirty(pos); + return; + } + entityposition_t *parent = componentGetData( + pos->parentEntityId, pos->parentComponentId, COMPONENT_TYPE_POSITION + ); + entityPositionEnsureWorld(parent); + mat4 invParent; + glm_mat4_inv(parent->worldTransform, invParent); + vec3 localPos; + glm_mat4_mulv3(invParent, position, 1.0f, localPos); + glm_vec3_copy(localPos, pos->position); + pos->flags = (pos->flags | ENTITY_POSITION_FLAG_POSITION_DIRTY) + & ~ENTITY_POSITION_FLAG_PRS_DIRTY; + entityPositionMarkDirty(pos); +} + +void entityPositionSetLocalPosition( const entityid_t entityId, const componentid_t componentId, vec3 position @@ -200,7 +228,7 @@ void entityPositionSetPosition( entityPositionMarkDirty(pos); } -void entityPositionGetRotation( +void entityPositionGetLocalRotation( const entityid_t entityId, const componentid_t componentId, vec3 dest @@ -212,7 +240,44 @@ void entityPositionGetRotation( glm_vec3_copy(pos->rotation, dest); } -void entityPositionSetRotation( +void entityPositionGetWorldRotation( + const entityid_t entityId, + const componentid_t componentId, + vec3 dest +) { + entityposition_t *pos = componentGetData( + entityId, componentId, COMPONENT_TYPE_POSITION + ); + if(pos->parentEntityId == ENTITY_ID_INVALID) { + entityPositionEnsurePRS(pos); + glm_vec3_copy(pos->rotation, dest); + return; + } + entityPositionEnsureWorld(pos); + const float (*wt)[4] = pos->worldTransform; + const float sx = sqrtf(wt[0][0]*wt[0][0] + wt[0][1]*wt[0][1] + wt[0][2]*wt[0][2]); + const float sy = sqrtf(wt[1][0]*wt[1][0] + wt[1][1]*wt[1][1] + wt[1][2]*wt[1][2]); + const float sz = sqrtf(wt[2][0]*wt[2][0] + wt[2][1]*wt[2][1] + wt[2][2]*wt[2][2]); + const float r00 = sx > 0.0f ? wt[0][0]/sx : 0.0f; + const float r10 = sy > 0.0f ? wt[1][0]/sy : 0.0f; + const float r20 = sz > 0.0f ? wt[2][0]/sz : 0.0f; + const float r01 = sx > 0.0f ? wt[0][1]/sx : 0.0f; + const float r11 = sy > 0.0f ? wt[1][1]/sy : 0.0f; + const float r21 = sz > 0.0f ? wt[2][1]/sz : 0.0f; + const float r22 = sz > 0.0f ? wt[2][2]/sz : 0.0f; + const float sinBeta = glm_clamp(r20, -1.0f, 1.0f); + dest[1] = asinf(sinBeta); + const float cosBeta = cosf(dest[1]); + if(fabsf(cosBeta) > 1e-6f) { + dest[0] = atan2f(-r21, r22); + dest[2] = atan2f(-r10, r00); + } else { + dest[2] = 0.0f; + dest[0] = (sinBeta > 0.0f) ? atan2f(r01, r11) : -atan2f(r01, r11); + } +} + +void entityPositionSetLocalRotation( const entityid_t entityId, const componentid_t componentId, vec3 rotation @@ -226,7 +291,82 @@ void entityPositionSetRotation( entityPositionMarkDirty(pos); } -void entityPositionGetScale( +void entityPositionSetWorldRotation( + const entityid_t entityId, + const componentid_t componentId, + vec3 rotation +) { + entityposition_t *pos = componentGetData( + entityId, componentId, COMPONENT_TYPE_POSITION + ); + if(pos->parentEntityId == ENTITY_ID_INVALID) { + glm_vec3_copy(rotation, pos->rotation); + pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) + & ~ENTITY_POSITION_FLAG_PRS_DIRTY; + entityPositionMarkDirty(pos); + return; + } + entityposition_t *parent = componentGetData( + pos->parentEntityId, pos->parentComponentId, COMPONENT_TYPE_POSITION + ); + entityPositionEnsureWorld(parent); + + // Build target world rotation matrix (unit scale) from XYZ euler. + const float c0 = cosf(rotation[0]), s0 = sinf(rotation[0]); + const float c1 = cosf(rotation[1]), s1 = sinf(rotation[1]); + const float c2 = cosf(rotation[2]), s2 = sinf(rotation[2]); + const float s0s1 = s0*s1, c0s1 = c0*s1; + // Named wr[col_stored][row_stored] matching cglm column-major layout. + const float wr00 = c1*c2, wr01 = c0*s2 + s0s1*c2, wr02 = s0*s2 - c0s1*c2; + const float wr10 = -c1*s2, wr11 = c0*c2 - s0s1*s2, wr12 = s0*c2 + c0s1*s2; + const float wr20 = s1, wr21 = -s0*c1, wr22 = c0*c1; + + // Normalize parent world columns to extract pure rotation. + const float (*pt)[4] = parent->worldTransform; + const float psx = sqrtf(pt[0][0]*pt[0][0] + pt[0][1]*pt[0][1] + pt[0][2]*pt[0][2]); + const float psy = sqrtf(pt[1][0]*pt[1][0] + pt[1][1]*pt[1][1] + pt[1][2]*pt[1][2]); + const float psz = sqrtf(pt[2][0]*pt[2][0] + pt[2][1]*pt[2][1] + pt[2][2]*pt[2][2]); + const float pr00 = psx > 0.f ? pt[0][0]/psx : 0.f; + const float pr01 = psx > 0.f ? pt[0][1]/psx : 0.f; + const float pr02 = psx > 0.f ? pt[0][2]/psx : 0.f; + const float pr10 = psy > 0.f ? pt[1][0]/psy : 0.f; + const float pr11 = psy > 0.f ? pt[1][1]/psy : 0.f; + const float pr12 = psy > 0.f ? pt[1][2]/psy : 0.f; + const float pr20 = psz > 0.f ? pt[2][0]/psz : 0.f; + const float pr21 = psz > 0.f ? pt[2][1]/psz : 0.f; + const float pr22 = psz > 0.f ? pt[2][2]/psz : 0.f; + + // local_R = parent_R^T * world_R (R^-1 == R^T for orthogonal matrices). + // Compute only the 7 entries of the local rotation matrix needed for XYZ + // euler extraction (stored column-major: [col][row] = math [row][col]). + // sinBeta = stored[2][0] = math[0][2] + // r21/r22 = stored[2][1..2] = math[1..2][2] + // r10/r00 = stored[1][0], stored[0][0] = math[0][1], math[0][0] + // gimbal = stored[0][1], stored[1][1] = math[1][0], math[1][1] + const float lr00 = pr00*wr00 + pr01*wr10 + pr02*wr20; // math[0][0] + const float lr10 = pr00*wr01 + pr01*wr11 + pr02*wr21; // math[0][1] + const float lr20 = pr00*wr02 + pr01*wr12 + pr02*wr22; // math[0][2] → sinBeta + const float lr01 = pr10*wr00 + pr11*wr10 + pr12*wr20; // math[1][0] + const float lr11 = pr10*wr01 + pr11*wr11 + pr12*wr21; // math[1][1] + const float lr21 = pr10*wr02 + pr11*wr12 + pr12*wr22; // math[1][2] → r21 + const float lr22 = pr20*wr02 + pr21*wr12 + pr22*wr22; // math[2][2] → r22 + + const float sinBeta = glm_clamp(lr20, -1.0f, 1.0f); + pos->rotation[1] = asinf(sinBeta); + const float cosBeta = cosf(pos->rotation[1]); + if(fabsf(cosBeta) > 1e-6f) { + pos->rotation[0] = atan2f(-lr21, lr22); + pos->rotation[2] = atan2f(-lr10, lr00); + } else { + pos->rotation[2] = 0.0f; + pos->rotation[0] = (sinBeta > 0.0f) ? atan2f(lr01, lr11) : -atan2f(lr01, lr11); + } + pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) + & ~ENTITY_POSITION_FLAG_PRS_DIRTY; + entityPositionMarkDirty(pos); +} + +void entityPositionGetLocalScale( const entityid_t entityId, const componentid_t componentId, vec3 dest @@ -238,7 +378,27 @@ void entityPositionGetScale( glm_vec3_copy(pos->scale, dest); } -void entityPositionSetScale( +void entityPositionGetWorldScale( + const entityid_t entityId, + const componentid_t componentId, + vec3 dest +) { + entityposition_t *pos = componentGetData( + entityId, componentId, COMPONENT_TYPE_POSITION + ); + if(pos->parentEntityId == ENTITY_ID_INVALID) { + entityPositionEnsurePRS(pos); + glm_vec3_copy(pos->scale, dest); + return; + } + entityPositionEnsureWorld(pos); + const float (*wt)[4] = pos->worldTransform; + dest[0] = sqrtf(wt[0][0]*wt[0][0] + wt[0][1]*wt[0][1] + wt[0][2]*wt[0][2]); + dest[1] = sqrtf(wt[1][0]*wt[1][0] + wt[1][1]*wt[1][1] + wt[1][2]*wt[1][2]); + dest[2] = sqrtf(wt[2][0]*wt[2][0] + wt[2][1]*wt[2][1] + wt[2][2]*wt[2][2]); +} + +void entityPositionSetLocalScale( const entityid_t entityId, const componentid_t componentId, vec3 scale @@ -252,6 +412,37 @@ void entityPositionSetScale( entityPositionMarkDirty(pos); } +void entityPositionSetWorldScale( + const entityid_t entityId, + const componentid_t componentId, + vec3 scale +) { + entityposition_t *pos = componentGetData( + entityId, componentId, COMPONENT_TYPE_POSITION + ); + if(pos->parentEntityId == ENTITY_ID_INVALID) { + glm_vec3_copy(scale, pos->scale); + pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) + & ~ENTITY_POSITION_FLAG_PRS_DIRTY; + entityPositionMarkDirty(pos); + return; + } + entityposition_t *parent = componentGetData( + pos->parentEntityId, pos->parentComponentId, COMPONENT_TYPE_POSITION + ); + entityPositionEnsureWorld(parent); + const float (*pt)[4] = parent->worldTransform; + const float psx = sqrtf(pt[0][0]*pt[0][0] + pt[0][1]*pt[0][1] + pt[0][2]*pt[0][2]); + const float psy = sqrtf(pt[1][0]*pt[1][0] + pt[1][1]*pt[1][1] + pt[1][2]*pt[1][2]); + const float psz = sqrtf(pt[2][0]*pt[2][0] + pt[2][1]*pt[2][1] + pt[2][2]*pt[2][2]); + pos->scale[0] = psx > 0.0f ? scale[0] / psx : scale[0]; + pos->scale[1] = psy > 0.0f ? scale[1] / psy : scale[1]; + pos->scale[2] = psz > 0.0f ? scale[2] / psz : scale[2]; + pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) + & ~ENTITY_POSITION_FLAG_PRS_DIRTY; + entityPositionMarkDirty(pos); +} + void entityPositionSetParent( const entityid_t entityId, const componentid_t componentId, diff --git a/src/dusk/entity/component/display/entityposition.h b/src/dusk/entity/component/display/entityposition.h index bce8a436..d3d307aa 100644 --- a/src/dusk/entity/component/display/entityposition.h +++ b/src/dusk/entity/component/display/entityposition.h @@ -15,7 +15,7 @@ * PRS cache is stale. localTransform was written directly (e.g. lookAt) and * position/rotation/scale need to be decomposed before they can be read. */ -#define ENTITY_POSITION_FLAG_PRS_DIRTY (1 << 0) +#define ENTITY_POSITION_FLAG_PRS_DIRTY (1 << 0) /** * Columns 0-2 of localTransform are stale. Rotation or scale changed; the @@ -34,7 +34,7 @@ * worldTransform is stale. Either the local matrix changed or an ancestor * moved; the full parent-chain multiply must be rerun before world data is read. */ -#define ENTITY_POSITION_FLAG_WORLD_DIRTY (1 << 3) +#define ENTITY_POSITION_FLAG_WORLD_DIRTY (1 << 3) typedef struct { /* @@ -81,7 +81,7 @@ typedef struct { * Initializes the entity position component, setting identity transforms and * zeroing all parent/child state. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. */ void entityPositionInit( @@ -92,7 +92,7 @@ void entityPositionInit( /** * Transforms the entity's local transform to look at a target point. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. * @param eye The eye/camera position. * @param target The target point to look at. @@ -137,9 +137,9 @@ void entityPositionGetLocalTransform( * first if ENTITY_POSITION_FLAG_PRS_DIRTY is set; never triggers a matrix * rebuild or world-transform update. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. - * @param dest Destination vector. + * @param dest Destination vector. */ void entityPositionGetLocalPosition( const entityid_t entityId, @@ -148,29 +148,43 @@ void entityPositionGetLocalPosition( ); /** - * Gets the world-space position. Rebuilds localTransform from PRS if - * ENTITY_POSITION_FLAG_LOCAL_DIRTY is set, then recomputes worldTransform - * from the parent chain if ENTITY_POSITION_FLAG_WORLD_DIRTY is set. + * Gets the world-space position. For parentless entities this is the same as + * the local position. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. - * @param dest Destination vector. + * @param dest Destination vector. */ -void entityPositionGetPosition( +void entityPositionGetWorldPosition( const entityid_t entityId, const componentid_t componentId, vec3 dest ); /** - * Sets the local position, updates the PRS cache immediately, and lazily - * flags localTransform and worldTransform (self + descendants) for rebuild. + * Sets the world-space position. For parentless entities this is equivalent to + * entityPositionSetLocalPosition. For parented entities the position is + * converted to local space via the inverted parent world transform. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. - * @param position The new local position. + * @param position The desired world-space position. */ -void entityPositionSetPosition( +void entityPositionSetWorldPosition( + const entityid_t entityId, + const componentid_t componentId, + vec3 position +); + +/** + * Sets the local position, marks localTransform and worldTransform (self + + * descendants) dirty. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param position The new local position. + */ +void entityPositionSetLocalPosition( const entityid_t entityId, const componentid_t componentId, vec3 position @@ -178,55 +192,112 @@ void entityPositionSetPosition( /** * Gets the cached local euler rotation (XYZ, radians). Decomposes - * localTransform into PRS first if ENTITY_POSITION_FLAG_PRS_DIRTY is set. + * localTransform first if ENTITY_POSITION_FLAG_PRS_DIRTY is set. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. - * @param dest Destination vector. + * @param dest Destination vector. */ -void entityPositionGetRotation( +void entityPositionGetLocalRotation( const entityid_t entityId, const componentid_t componentId, vec3 dest ); /** - * Sets the local euler rotation (XYZ, radians), updates the PRS cache - * immediately, and lazily flags localTransform and worldTransform for rebuild. + * Gets the world-space euler rotation (XYZ, radians) by decomposing the world + * transform. For parentless entities this is the same as local rotation. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. - * @param rotation The new local rotation. + * @param dest Destination vector. */ -void entityPositionSetRotation( +void entityPositionGetWorldRotation( + const entityid_t entityId, + const componentid_t componentId, + vec3 dest +); + +/** + * Sets the local euler rotation (XYZ, radians) and marks transforms dirty. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param rotation The new local rotation. + */ +void entityPositionSetLocalRotation( const entityid_t entityId, const componentid_t componentId, vec3 rotation ); /** - * Gets the cached local scale. Decomposes localTransform into PRS first if + * Sets the world-space euler rotation (XYZ, radians). For parentless entities + * this is equivalent to entityPositionSetLocalRotation. For parented entities + * the rotation is converted to local space by removing the parent world + * rotation. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param rotation The desired world-space euler rotation. + */ +void entityPositionSetWorldRotation( + const entityid_t entityId, + const componentid_t componentId, + vec3 rotation +); + +/** + * Gets the cached local scale. Decomposes localTransform first if * ENTITY_POSITION_FLAG_PRS_DIRTY is set. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. - * @param dest Destination vector. + * @param dest Destination vector. */ -void entityPositionGetScale( +void entityPositionGetLocalScale( const entityid_t entityId, const componentid_t componentId, vec3 dest ); /** - * Sets the local scale, updates the PRS cache immediately, and lazily flags - * localTransform and worldTransform for rebuild. + * Gets the world-space scale by extracting column lengths from the world + * transform. For parentless entities this is the same as local scale. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. - * @param scale The new local scale. + * @param dest Destination vector. */ -void entityPositionSetScale( +void entityPositionGetWorldScale( + const entityid_t entityId, + const componentid_t componentId, + vec3 dest +); + +/** + * Sets the local scale and marks transforms dirty. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param scale The new local scale. + */ +void entityPositionSetLocalScale( + const entityid_t entityId, + const componentid_t componentId, + vec3 scale +); + +/** + * Sets the world-space scale. For parentless entities this is equivalent to + * entityPositionSetLocalScale. For parented entities the scale is converted to + * local space by dividing by the parent world scale (assumes no shear). + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param scale The desired world-space scale. + */ +void entityPositionSetWorldScale( const entityid_t entityId, const componentid_t componentId, vec3 scale @@ -254,7 +325,7 @@ void entityPositionSetParent( * set ENTITY_POSITION_FLAG_WORLD_DIRTY on self and descendants. After * modifying PRS directly, call entityPositionRebuild() instead. * - * @param entityId The entity ID. + * @param entityId The entity ID. * @param componentId The component ID. * @return Pointer to the component data. */ @@ -287,7 +358,7 @@ void entityPositionMarkDirty(entityposition_t *pos); * Disposes this entity and all of its position-component descendants * recursively. Detaches from any parent before destroying. * - * @param entityId The root entity ID. + * @param entityId The root entity ID. * @param componentId The root position component ID. */ void entityPositionDisposeDeep( diff --git a/src/dusk/entity/entity.c b/src/dusk/entity/entity.c index 332dd8df..d74e15d4 100644 --- a/src/dusk/entity/entity.c +++ b/src/dusk/entity/entity.c @@ -80,10 +80,21 @@ void entityDisposeDeep(const entityid_t entityId) { } } +void entityUpdate(const entityid_t entityId) { + entity_t *ent = &ENTITY_MANAGER.entities[entityId]; + for(uint8_t i = 0; i < ent->updateCount; i++) { + ent->onUpdate[i](entityId); + } +} + void entityDispose(const entityid_t entityId) { componentindex_t compInd; entity_t *ent = &ENTITY_MANAGER.entities[entityId]; + for(uint8_t i = 0; i < ent->disposeCount; i++) { + ent->onDispose[i](entityId); + } + for(componentid_t i = 0; i < ENTITY_COMPONENT_COUNT_MAX; i++) { compInd = componentGetIndex(entityId, i); componenttype_t type = ENTITY_MANAGER.components[compInd].type; @@ -95,4 +106,46 @@ void entityDispose(const entityid_t entityId) { } ent->state = 0; +} + +void entityUpdateAdd(const entityid_t entityId, const entitycallback_t callback) { + entity_t *ent = &ENTITY_MANAGER.entities[entityId]; + assertTrue( + ent->updateCount < ENTITY_UPDATE_CALLBACK_COUNT_MAX, + "Entity update callback slots full" + ); + ent->onUpdate[ent->updateCount++] = callback; +} + +void entityUpdateRemove(const entityid_t entityId, const entitycallback_t callback) { + entity_t *ent = &ENTITY_MANAGER.entities[entityId]; + for(uint8_t i = 0; i < ent->updateCount; i++) { + if(ent->onUpdate[i] != callback) continue; + ent->updateCount--; + for(uint8_t j = i; j < ent->updateCount; j++) { + ent->onUpdate[j] = ent->onUpdate[j + 1]; + } + return; + } +} + +void entityDisposeAdd(const entityid_t entityId, const entitycallback_t callback) { + entity_t *ent = &ENTITY_MANAGER.entities[entityId]; + assertTrue( + ent->disposeCount < ENTITY_DISPOSE_CALLBACK_COUNT_MAX, + "Entity dispose callback slots full" + ); + ent->onDispose[ent->disposeCount++] = callback; +} + +void entityDisposeRemove(const entityid_t entityId, const entitycallback_t callback) { + entity_t *ent = &ENTITY_MANAGER.entities[entityId]; + for(uint8_t i = 0; i < ent->disposeCount; i++) { + if(ent->onDispose[i] != callback) continue; + ent->disposeCount--; + for(uint8_t j = i; j < ent->disposeCount; j++) { + ent->onDispose[j] = ent->onDispose[j + 1]; + } + return; + } } \ No newline at end of file diff --git a/src/dusk/entity/entity.h b/src/dusk/entity/entity.h index de437669..0e2acd80 100644 --- a/src/dusk/entity/entity.h +++ b/src/dusk/entity/entity.h @@ -10,8 +10,17 @@ #define ENTITY_STATE_ACTIVE (1 << 0) +#define ENTITY_UPDATE_CALLBACK_COUNT_MAX 5 +#define ENTITY_DISPOSE_CALLBACK_COUNT_MAX 5 + +typedef void (*entitycallback_t)(const entityid_t entityId); + typedef struct { uint8_t state; + uint8_t updateCount; + uint8_t disposeCount; + entitycallback_t onUpdate[ENTITY_UPDATE_CALLBACK_COUNT_MAX]; + entitycallback_t onDispose[ENTITY_DISPOSE_CALLBACK_COUNT_MAX]; } entity_t; /** @@ -47,7 +56,15 @@ componentid_t entityGetComponent( ); /** - * Disposes of an entity with the given ID. + * Runs all registered update callbacks for the entity. + * + * @param entityId The ID of the entity to update. + */ +void entityUpdate(const entityid_t entityId); + +/** + * Disposes of an entity with the given ID. Fires all dispose callbacks before + * cleaning up components and state. * * @param entityId The ID of the entity to dispose of. */ @@ -60,4 +77,37 @@ void entityDispose(const entityid_t entityId); * * @param entityId The root entity ID. */ -void entityDisposeDeep(const entityid_t entityId); \ No newline at end of file +void entityDisposeDeep(const entityid_t entityId); + +/** + * Registers an update callback, invoked each time entityUpdate is called. + * + * @param entityId The entity to register on. + * @param callback The function to call. + */ +void entityUpdateAdd(const entityid_t entityId, const entitycallback_t callback); + +/** + * Removes a previously registered update callback. + * + * @param entityId The entity to remove from. + * @param callback The function to remove. + */ +void entityUpdateRemove(const entityid_t entityId, const entitycallback_t callback); + +/** + * Registers a dispose callback, invoked at the start of entityDispose before + * any component or state cleanup. + * + * @param entityId The entity to register on. + * @param callback The function to call. + */ +void entityDisposeAdd(const entityid_t entityId, const entitycallback_t callback); + +/** + * Removes a previously registered dispose callback. + * + * @param entityId The entity to remove from. + * @param callback The function to remove. + */ +void entityDisposeRemove(const entityid_t entityId, const entitycallback_t callback); \ No newline at end of file diff --git a/src/dusk/entity/entitymanager.c b/src/dusk/entity/entitymanager.c index a74a424b..10269223 100644 --- a/src/dusk/entity/entitymanager.c +++ b/src/dusk/entity/entitymanager.c @@ -36,6 +36,16 @@ entityid_t entityManagerAdd() { return ENTITY_ID_INVALID; } +void entityManagerUpdate(void) { + entityid_t i = 0; + while(i < ENTITY_COUNT_MAX) { + if((ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) != 0) { + entityUpdate(i); + } + i++; + } +} + void entityManagerDispose(void) { for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) { if((ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) == 0) continue; diff --git a/src/dusk/entity/entitymanager.h b/src/dusk/entity/entitymanager.h index 3d4a0f68..0952561e 100644 --- a/src/dusk/entity/entitymanager.h +++ b/src/dusk/entity/entitymanager.h @@ -28,6 +28,11 @@ void entityManagerInit(void); */ entityid_t entityManagerAdd(); +/** + * Updates all active entities. + */ +void entityManagerUpdate(void); + /** * Disposes of the entity manager, in turn freeing all entities and components. */ diff --git a/src/dusk/input/input.c b/src/dusk/input/input.c index c67d9a2f..d0854638 100644 --- a/src/dusk/input/input.c +++ b/src/dusk/input/input.c @@ -162,6 +162,28 @@ void inputAxis2D( result[1] = inputAxis(negY, posY); } +void inputAngle2D( + const inputaction_t negX, const inputaction_t posX, + const inputaction_t negY, const inputaction_t posY, + vec2 result +) { + assertNotNull(result, "Result vector cannot be null"); + float_t x = inputAxis(negX, posX); + float_t y = inputAxis(negY, posY); + float_t mag = sqrtf(x * x + y * y); + if(mag <= 0.0f) { + result[0] = 0.0f; + result[1] = 0.0f; + return; + } + if(mag > 1.0f) { + x /= mag; + y /= mag; + } + result[0] = x; + result[1] = y; +} + void inputBind(const inputbutton_t button, const inputaction_t act) { assertTrue( act < INPUT_ACTION_COUNT, @@ -177,8 +199,6 @@ void inputBind(const inputbutton_t button, const inputaction_t act) { data->action = act; } - - float_t inputDeadzone(const float_t rawValue, const float_t deadzone) { if(rawValue < deadzone) return 0.0f; return (rawValue - deadzone) / (1.0f - deadzone); diff --git a/src/dusk/input/input.h b/src/dusk/input/input.h index 8289af45..b93e48b0 100644 --- a/src/dusk/input/input.h +++ b/src/dusk/input/input.h @@ -126,6 +126,22 @@ void inputAxis2D( vec2 result ); +/** + * Returns an angled 2D vector based on the input of 4 actions. Functionally + * using atan2 to get the angle of the input then multiplying by a unit vector. + * + * @param negX The action representing the negative direction of the X axis. + * @param posX The action representing the positive direction of the X axis. + * @param negY The action representing the negative direction of the Y axis. + * @param posY The action representing the positive direction of the Y axis. + * @param result A vec2 to store the resulting axis values (-1.0f to 1.0f). + */ +void inputAngle2D( + const inputaction_t negX, const inputaction_t posX, + const inputaction_t negY, const inputaction_t posY, + vec2 result +); + /** * Binds an input button to an action. * diff --git a/src/dusk/scene/initial/initialscene.c b/src/dusk/scene/initial/initialscene.c index cf712e47..099fe21d 100644 --- a/src/dusk/scene/initial/initialscene.c +++ b/src/dusk/scene/initial/initialscene.c @@ -10,8 +10,27 @@ #include "display/spritebatch/spritebatch.h" #include "display/screen/screen.h" #include "entity/entitymanager.h" +#include "input/input.h" -#include "display/shader/shaderunlit.h" +void initialSceneCubeUpdate(const entityid_t entityId) { + vec3 pos; + componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION); + + vec2 movement; + inputAngle2D( + INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT, + INPUT_ACTION_DOWN, INPUT_ACTION_UP, + movement + ); + if(movement[0] == 0.0f && movement[1] == 0.0f) return; + + const float_t speed = 1.0f; + + entityPositionGetLocalPosition(entityId, posComp, pos); + pos[0] += movement[0] * TIME.delta * speed; + pos[2] -= movement[1] * TIME.delta * speed; + entityPositionSetLocalPosition(entityId, posComp, pos); +} void initialSceneInit(void) { consolePrint("Initial scene initialized"); @@ -23,8 +42,9 @@ void initialSceneInit(void) { // entitycamera_t *camData = (entitycamera_t*)entityGetComponent(camera, camCam); entityid_t cube = entityManagerAdd(); - // componentid_t cubePos = entityAddComponent(cube, COMPONENT_TYPE_POSITION); + (void)entityAddComponent(cube, COMPONENT_TYPE_POSITION); componentid_t cubeDraw = entityAddComponent(cube, COMPONENT_TYPE_RENDERABLE); + entityUpdateAdd(cube, initialSceneCubeUpdate); // entityrenderable_t *cubeDrawData = ( // (entityrenderable_t*)entityGetComponent(cube, cubeDraw) // ); diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c index b5d46699..32bd7ebf 100644 --- a/src/dusk/scene/scene.c +++ b/src/dusk/scene/scene.c @@ -34,10 +34,6 @@ errorret_t sceneInit(void) { } errorret_t sceneUpdate(void) { - if(SCENE.nextType == SCENE_TYPE_NULL) { - errorOk(); - } - // Handle scene change if(SCENE.nextType != SCENE_TYPE_NULL) { // Dispose current scene. @@ -72,8 +68,6 @@ errorret_t sceneUpdate(void) { errorOk(); } -dusktimeepoch_t LAST; - errorret_t sceneRender(void) { mat4 proj, view, model, ident; glm_mat4_identity(ident);