Made a big mess of the codebase

This commit is contained in:
YourWishes committed 2025-08-28 07:14:13 -05:00
1 parent 30232d1275
commit af1329710d
44 files changed
+1363 -388

No files matched your search

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
-1
View File
@@ -30,7 +30,6 @@ add_subdirectory(assert)
add_subdirectory(asset)
add_subdirectory(console)
add_subdirectory(display)
add_subdirectory(ecs)
add_subdirectory(engine)
add_subdirectory(error)
add_subdirectory(input)
+30 -34
View File
@@ -8,67 +8,63 @@
#include "camera.h"
#include "display/display.h"
#include "assert/assert.h"
#include "scene/node.h"
#include "display/framebuffer/framebuffer.h"
camera_t CAMERA_DATA[ECS_ENTITY_COUNT_MAX] = { 0 };
ecscomponent_t CAMERA_COMPONENT = ecsComponentInit(
CAMERA_DATA,
((ecscomponentcallbacks_t){
.init = NULL,
.entityAdd = cameraEntityAdded
})
);
camera_t CAMERA_DATA[CAMERA_COUNT_MAX] = { 0 };
camera_t *CAMERA_MAIN = NULL;
ecsid_t CAMERA_MAIN = -1;
void cameraInit(camera_t *camera) {
assertNotNull(camera, "Not a camera component");
void cameraEntityAdded(const ecsid_t id) {
if(CAMERA_MAIN == -1) CAMERA_MAIN = id;
camera->type = CAMERA_TYPE_PERSPECTIVE
;
glm_mat4_identity(camera->transform);
camera->perspective.fov = 45.0f;
camera_t *cam = cameraGet(id);
cam->type = CAMERA_TYPE_PERSPECTIVE;
cam->perspective.fov = glm_rad(90.0f);
cam->nearClip = 0.1f;
cam->farClip = 1000.0f;
camera->nearClip = 0.1f;
camera->farClip = 100.0f;
glm_look(
(vec3){ 3.0f, 3.0f, 3.0f },
(vec3){ 0.0f, 0.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f },
camera->transform
);
}
void cameraPush(const ecsid_t id) {
assertTrue(cameraHas(id), "Not a camera component");
void cameraPush(camera_t *camera) {
assertNotNull(camera, "Not a camera component");
camera_t *cam = cameraGet(id);
mat4 projection;
mat4 projection, view;
nodeMatrixGet(id, view);
switch(cam->type) {
switch(camera->type) {
case CAMERA_TYPE_ORTHOGRAPHIC:
glm_ortho(
cam->orthographic.left,
cam->orthographic.right,
cam->orthographic.bottom,
cam->orthographic.top,
cam->nearClip,
cam->farClip,
camera->orthographic.left,
camera->orthographic.right,
camera->orthographic.bottom,
camera->orthographic.top,
camera->nearClip,
camera->farClip,
projection
);
break;
case CAMERA_TYPE_PERSPECTIVE:
glm_perspective(
cam->perspective.fov,
camera->perspective.fov,
(
(float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND) /
(float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND)
),
cam->nearClip,
cam->farClip,
camera->nearClip,
camera->farClip,
projection
);
}
#if DISPLAY_SDL2
mat4 pv;
glm_mat4_mul(projection, view, pv);
glm_mat4_mul(projection, camera->transform, pv);
glPushMatrix();
glMatrixMode(GL_PROJECTION);
+11 -16
View File
@@ -6,9 +6,11 @@
*/
#pragma once
#include "ecs/ecscomponent.h"
#include "dusk.h"
#include "display/color.h"
#define CAMERA_COUNT_MAX 4
typedef enum {
CAMERA_TYPE_PERSPECTIVE,
CAMERA_TYPE_ORTHOGRAPHIC
@@ -17,6 +19,8 @@ typedef enum {
typedef struct {
cameraprojectiontype_t type;
mat4 transform;
union {
struct {
float_t fov;
@@ -34,31 +38,22 @@ typedef struct {
float_t farClip;
} camera_t;
extern camera_t CAMERA_DATA[ECS_ENTITY_COUNT_MAX];
extern ecscomponent_t CAMERA_COMPONENT;
extern ecsid_t CAMERA_MAIN;
#define cameraAdd(id) ((camera_t*)ecsComponentDataAdd(&CAMERA_COMPONENT, id))
#define cameraGet(id) ((camera_t*)ecsComponentDataGet(&CAMERA_COMPONENT, id))
#define cameraHas(id) ecsComponentDataHas(&CAMERA_COMPONENT, id)
#define cameraRemove(id) ecsComponentDataRemove(&CAMERA_COMPONENT, id)
extern camera_t CAMERA_DATA[CAMERA_COUNT_MAX];
extern camera_t *CAMERA_MAIN;
/**
* Callback function called when a new entity is added to the camera component.
* Initializes the camera data for the entity.
*
* @param id The ID of the newly added entity.
* Initializes a camera to default values.
*/
void cameraEntityAdded(const ecsid_t id);
void cameraInit(camera_t *camera);
/**
* Pushes the camera's view matrix onto the matrix stack.
*
* @param id The ID of the camera entity to use.
*/
void cameraPush(const ecsid_t id);
void cameraPushMatrix(camera_t* camera);
/**
* Pops the camera's view matrix off the matrix stack.
*/
void cameraPop(void);
void cameraPopMatrix(void);
+1 -2
View File
@@ -8,7 +8,6 @@
#include "display/display.h"
#include "console/console.h"
#include "display/renderer.h"
#include "ecs/ecssystem.h"
#include "display/framebuffer/framebuffer.h"
#include "display/mesh/quad.h"
@@ -84,7 +83,7 @@ errorret_t displayUpdate(void) {
glViewport(0, 0, windowWidth, windowHeight);
#endif
rendererRender(CAMERA_MAIN);
// rendererRender(CAMERA_MAIN);
#if DISPLAY_SDL2
SDL_GL_SwapWindow(DISPLAY.window);
-1
View File
@@ -8,5 +8,4 @@ target_sources(${DUSK_TARGET_NAME}
PRIVATE
mesh.c
quad.c
meshrenderer.c
)
-28
View File
@@ -1,28 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "meshrenderer.h"
#include "time/time.h"
meshrenderer_t MESH_RENDERER_DATA[ECS_ENTITY_COUNT_MAX] = { 0 };
ecscomponent_t MESH_RENDERER_COMPONENT = ecsComponentInit(
MESH_RENDERER_DATA,
((ecscomponentcallbacks_t){
.init = NULL,
.entityAdd = NULL,
.entityRemove = NULL
})
);
void meshRendererDraw(const ecsid_t id) {
if(!meshRendererHas(id)) return;
meshrenderer_t *renderer = &MESH_RENDERER_DATA[id];
if(!renderer->mesh) return;
textureBind(renderer->texture);
meshDraw(renderer->mesh, 0, -1);
}
-37
View File
@@ -1,37 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "ecs/ecscomponent.h"
#include "display/mesh/mesh.h"
#include "display/texture/texture.h"
typedef struct {
mesh_t *mesh;
texture_t *texture;
} meshrenderer_t;
extern meshrenderer_t MESH_RENDERER_DATA[ECS_ENTITY_COUNT_MAX];
extern ecscomponent_t MESH_RENDERER_COMPONENT;
#define meshRendererAdd(id) \
((meshrenderer_t*)ecsComponentDataAdd(&MESH_RENDERER_COMPONENT, id))
#define meshRendererGet(id) \
((meshrenderer_t*)ecsComponentDataGet(&MESH_RENDERER_COMPONENT, id))
#define meshRendererHas(id) \
(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.
*
* @param id The ID of the entity with the mesh renderer component.
*/
void meshRendererDraw(const ecsid_t id);
+20 -22
View File
@@ -6,29 +6,27 @@
*/
#include "renderer.h"
#include "display/mesh/meshrenderer.h"
#include "scene/node.h"
#include "display/framebuffer/framebuffer.h"
void rendererRender(const ecsid_t camera) {
if(camera == -1) return;
// void rendererRender(const ecsid_t camera) {
// if(camera == -1) return;
// Get the meshes.
uint32_t meshCount;
ecsid_t meshes[ECS_ENTITY_COUNT_MAX];
ecsid_t id;
meshCount = meshRendererGetAll(meshes);
// // Get the meshes.
// uint32_t meshCount;
// ecsid_t meshes[ECS_ENTITY_COUNT_MAX];
// ecsid_t id;
// meshCount = meshRendererGetAll(meshes);
frameBufferBind(NULL);
frameBufferClear(
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
COLOR_CORNFLOWER_BLUE
);
cameraPush(camera);
for(uint32_t i = 0; i < meshCount; i++) {
id = meshes[i];
nodeMatrixPush(id);
meshRendererDraw(id);
}
cameraPop();
}
// frameBufferBind(NULL);
// frameBufferClear(
// FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
// COLOR_CORNFLOWER_BLUE
// );
// cameraPush(camera);
// for(uint32_t i = 0; i < meshCount; i++) {
// id = meshes[i];
// nodeMatrixPush(id);
// meshRendererDraw(id);
// }
// cameraPop();
// }
+1 -1
View File
@@ -13,4 +13,4 @@
*
* @param camera The ID of the camera entity to render from.
*/
void rendererRender(const ecsid_t camera);
// void rendererRender(const ecsid_t camera);
-4
View File
@@ -10,8 +10,6 @@
#include "time/time.h"
#include "console/console.h"
#include "display/display.h"
#include "ecs/ecssystem.h"
#include "scene/node.h"
#include "asset/asset.h"
#include "scene/test/scenetest.h"
@@ -31,7 +29,6 @@ errorret_t engineInit(void) {
// Init systems. Order is important.
timeInit();
consoleInit();
ecsSystemInit();
errorChain(assetInit());
errorChain(displayInit());
@@ -52,7 +49,6 @@ errorret_t engineUpdate(void) {
}
errorret_t engineDispose(void) {
ecsSystemDispose();
errorChain(displayDispose());
assetDispose();
consoleDispose();
+14
View File
@@ -0,0 +1,14 @@
# 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
)
# Subdirs
add_subdirectory(entity)
add_subdirectory(item)
add_subdirectory(world)
+13
View File
@@ -0,0 +1,13 @@
# 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
direction.c
entity.c
player.c
npc.c
)
+53
View File
@@ -0,0 +1,53 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "direction.h"
#include "assert/assert.h"
float_t directionToAngle(const direction_t dir) {
switch(dir) {
case DIRECTION_NORTH: return (M_PI_2);
case DIRECTION_SOUTH: return -(M_PI_2);
case DIRECTION_EAST: return 0;
case DIRECTION_WEST: return (M_PI);
default: return 0; // Should never happen
}
}
void directionGetCoordinates(
const direction_t dir,
int8_t *x, int8_t *y
) {
assertNotNull(x, "X coordinate pointer cannot be NULL");
assertNotNull(y, "Y coordinate pointer cannot be NULL");
switch(dir) {
case DIRECTION_NORTH:
*x = 0;
*y = -1;
break;
case DIRECTION_SOUTH:
*x = 0;
*y = 1;
break;
case DIRECTION_EAST:
*x = 1;
*y = 0;
break;
case DIRECTION_WEST:
*x = -1;
*y = 0;
break;
default:
assertUnreachable("Invalid direction");
break;
}
}
+41
View File
@@ -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 "dusk.h"
typedef enum {
DIRECTION_SOUTH = 0,
DIRECTION_EAST = 1,
DIRECTION_WEST = 2,
DIRECTION_NORTH = 3,
DIRECTION_UP = DIRECTION_NORTH,
DIRECTION_DOWN = DIRECTION_SOUTH,
DIRECTION_LEFT = DIRECTION_WEST,
DIRECTION_RIGHT = DIRECTION_EAST,
} direction_t;
/**
* Converts a direction to an angle in float_t format.
*
* @param dir The direction to convert.
* @return The angle corresponding to the direction.
*/
float_t directionToAngle(const direction_t dir);
/**
* Gets the relative coordinates for a given direction.
*
* @param dir The direction to get coordinates for.
* @param x Pointer to store the x coordinate.
* @param y Pointer to store the y coordinate.
*/
void directionGetCoordinates(
const direction_t dir,
int8_t *x, int8_t *y
);
+123
View File
@@ -0,0 +1,123 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
#include "assert/assert.h"
#include "util/memory.h"
// #include "world/world.h"
// #include "world/tiledata.h"
#include "time.h"
entity_t ENTITIES[ENTITY_COUNT_MAX] = {0};
// entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
// {NULL}, // ENTITY_TYPE_NULL
// {
// .load = playerEntityLoad,
// .update = playerEntityUpdate,
// },
// {
// .load = npcLoad,
// .update = npcUpdate,
// .interact = npcInteract,
// },
// };
void entityInit(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
memoryZero(entity, sizeof(entity_t));
// entity->type = ENTITY_TYPE_NULL;
// entity->x = 0;
// entity->y = 0;
// entity->dir = DIRECTION_SOUTH;
// entity->id = 0;
// ENTITY_CALLBACKS[entity->type].load(entity, source);
}
void entityUpdate(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
// assertNotNull(
// ENTITY_CALLBACKS[entity->type].update,
// "Entity type has no update callback"
// );
// ENTITY_CALLBACKS[entity->type].update(entity);
if(entity->subX > 0) {
entity->subX -= entity->moveSpeed;
} else if(entity->subX < 0) {
entity->subX += entity->moveSpeed;
}
if(entity->subY > 0) {
entity->subY -= entity->moveSpeed;
} else if(entity->subY < 0) {
entity->subY += entity->moveSpeed;
}
}
void entityMove(entity_t *entity, const uint8_t moveSpeed) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
assertFalse(
entityIsMoving(entity),
"Entity is already moving, cannot move again"
);
int8_t x = 0, y = 0;
directionGetCoordinates(entity->dir, &x, &y);
// entity in way?
entity_t *ent = entityGetAt(entity->x + x, entity->y + y);
if(ent != NULL) return;
entity->x += x;
entity->y += y;
// entity->subX = TILE_WIDTH_HEIGHT * -x;
// entity->subY = TILE_WIDTH_HEIGHT * -y;
entity->moveSpeed = moveSpeed;
}
void entityTurn(entity_t *entity, const direction_t dir) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
assertTrue(
dir >= DIRECTION_SOUTH && dir <= DIRECTION_NORTH, "Invalid direction"
);
assertFalse(
entityIsMoving(entity), "Entity is already moving, cannot turn"
);
entity->dir = dir;
}
bool_t entityIsMoving(const entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
return entity->subX != 0 || entity->subY != 0;
}
entity_t * entityGetAt(
const int32_t tileX,
const int32_t tileY
) {
entity_t *entity = ENTITIES;
do {
if(entity->type == ENTITY_TYPE_NULL) continue;
if(entity->x == tileX && entity->y == tileY) return entity;
} while((entity++) < &ENTITIES[ENTITY_COUNT_MAX - 1]);
return NULL;
}
+99
View File
@@ -0,0 +1,99 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "direction.h"
#include "player.h"
#include "npc.h"
#define ENTITY_COUNT_MAX 32
#define ENTITY_TURN_DURATION 0.075f // Duration for turning in seconds
#define ENTITY_MOVE_DURATION 0.1f // Duration for moving 1 tile, in seconds.
#define ENTITY_PLAYER_INDEX 0
typedef enum {
ENTITY_TYPE_NULL = 0,
ENTITY_TYPE_PLAYER = 1,
ENTITY_TYPE_NPC = 2,
} entitytype_t;
#define ENTITY_TYPE_COUNT 3
typedef struct _entity_t {
uint32_t id;// Completely unique ID for this entity.
int32_t x, y;
int8_t subX, subY;
uint8_t moveSpeed;
entitytype_t type;
direction_t dir;
union {
npc_t npc;
playerentity_t player;
};
} entity_t;
// typedef struct {
// void (*load) (entity_t *entity, const entity_t *source);
// void (*update) (entity_t *entity);
// void (*interact)(entity_t *player, entity_t *self);
// } entitycallback_t;
extern entity_t ENTITIES[ENTITY_COUNT_MAX];
// extern entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
/**
* Loads an entity from the generated entity data.
*
* @param entity Pointer to the entity to initialize.
* @param source Pointer to the source entity data.
*/
void entityInit(entity_t *entity);
/**
* Updates the entity's state.
*
* @param entity Pointer to the entity to update.
*/
void entityUpdate(entity_t *entity);
/**
* Moves the entity by the specified x and y offsets.
*
* @param entity Pointer to the entity to move.
* @param moveSpeed The speed at which to move the entity.
*/
void entityMove(entity_t *entity, const uint8_t moveSpeed);
/**
* Turns the entity to face the specified direction.
*
* @param entity Pointer to the entity to turn.
* @param dir The direction to turn the entity to.
*/
void entityTurn(entity_t *entity, const direction_t dir);
/**
* Returns whether or not an entity is currently moving.
*
* @param entity Pointer to the entity to check.
* @return True if the entity is moving, false otherwise.
*/
bool_t entityIsMoving(const entity_t *entity);
/**
* Gets the entity at the specified tile coordinates.
*
* @param tileX The x coordinate of the tile to get the entity from.
* @param tileY The y coordinate of the tile to get the entity from.
* @return Pointer to the entity at the specified coordinates, or NULL if no
* entity exists there.
*/
entity_t *entityGetAt(
const int32_t tileX,
const int32_t tileY
);
+45
View File
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
// #include "ui/uitextbox.h"
// #include "locale/language.h"
#include "assert/assert.h"
void npcLoad(entity_t *entity, const entity_t *source) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertNotNull(source, "Source entity pointer cannot be NULL");
assertTrue(source->type == ENTITY_TYPE_NPC, "Source entity type must be NPC");
entity->npc = source->npc;
}
void npcUpdate(entity_t *entity) {
}
void npcInteract(entity_t *player, entity_t *self) {
assertTrue(self->type == ENTITY_TYPE_NPC, "Entity must be of type NPC");
switch(self->npc.interactType) {
case NPC_INTERACT_TYPE_NONE:
break;
case NPC_INTERACT_TYPE_TEXT:
// uiTextboxSetText(languageGet(self->npc.text));
break;
case NPC_INTERACT_TYPE_CONVO:
break;
case NPC_INTERACT_TYPE_EVENT:
// eventSetActive(self->npc.eventData);
break;
default:
assertUnreachable("Unknown NPC interaction type");
}
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include "dusk.h"
typedef struct _entity_t entity_t;
typedef enum {
NPC_INTERACT_TYPE_NONE = 0,
NPC_INTERACT_TYPE_TEXT = 1,
NPC_INTERACT_TYPE_CONVO = 2,
NPC_INTERACT_TYPE_EVENT = 3,
} npcinteracttype_t;
typedef struct {
npcinteracttype_t interactType;
union {
const char_t* text;
// const eventdata_t *eventData;
};
} npc_t;
/**
* Initializes the NPC entity.
*
* @param entity The entity to initialize.
* @param source The source entity to copy data from.
*/
void npcLoad(entity_t *entity, const entity_t *source);
/**
* Updates the NPC entity.
*
* @param entity The entity to update.
*/
void npcUpdate(entity_t *entity);
/**
* Handles interaction between the player and the NPC.
*
* @param player The player entity interacting with the NPC.
* @param self The NPC entity being interacted with.
*/
void npcInteract(entity_t *player, entity_t *self);
+86
View File
@@ -0,0 +1,86 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
#include "assert/assert.h"
#include "input/input.h"
// #include "display/render.h"
// #include "world/world.h"
// #include "ui/uitextbox.h"
inventory_t PLAYER_INVENTORY;
void playerInit() {
entity_t *ent = &ENTITIES[ENTITY_PLAYER_INDEX];
entityInit(ent);
inventoryInit(&PLAYER_INVENTORY, INVENTORY_SIZE_MAX);
}
void playerEntityLoad(entity_t *entity, const entity_t *source) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertNotNull(source, "Source entity pointer cannot be NULL");
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
assertTrue(source->type == entity->type, "Source/Entity type mismatch");
}
void playerEntityUpdate(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
// TODO: make this just a method somewhere.
// if(UI_TEXTBOX.visible) return;
if(entityIsMoving(entity)) return;
const uint8_t moveSpeed = inputIsDown(INPUT_BIND_CANCEL) ? PLAYER_SPEED_RUN : PLAYER_SPEED_WALK;
if(inputIsDown(INPUT_BIND_UP)) {
if(entity->dir != DIRECTION_NORTH) {
entityTurn(entity, DIRECTION_NORTH);
return;
}
entityMove(entity, moveSpeed);
return;
} else if(inputIsDown(INPUT_BIND_DOWN)) {
if(entity->dir != DIRECTION_SOUTH) {
entityTurn(entity, DIRECTION_SOUTH);
return;
}
entityMove(entity, moveSpeed);
return;
} else if(inputIsDown(INPUT_BIND_LEFT)) {
if(entity->dir != DIRECTION_WEST) {
entityTurn(entity, DIRECTION_WEST);
return;
}
entityMove(entity, moveSpeed);
return;
} else if(inputIsDown(INPUT_BIND_RIGHT)) {
if(entity->dir != DIRECTION_EAST) {
entityTurn(entity, DIRECTION_EAST);
return;
}
entityMove(entity, moveSpeed);
return;
}
// Interact
if(inputPressed(INPUT_BIND_ACTION)) {
int8_t x, y;
directionGetCoordinates(entity->dir, &x, &y);
entity_t *ent = entityGetAt(entity->x + x, entity->y + y);
// if(ent != NULL && ENTITY_CALLBACKS[ent->type].interact != NULL) {
// assertTrue(ent->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
// ENTITY_CALLBACKS[ent->type].interact(entity, ent);
// }
}
}
+43
View File
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "item/inventory.h"
#define PLAYER_SPEED_WALK 1
#define PLAYER_SPEED_RUN 2
typedef struct _entity_t entity_t;
typedef struct {
uint32_t nothing;
} playerentity_t;
#define PLAYER_ENTITY_ID (UINT32_MAX-1)
extern inventory_t PLAYER_INVENTORY;
/**
* Initializes the player and all player-related entities.
*/
void playerInit(void);
/**
* Loads the player entity.
*
* @param entity The entity to initialize.
* @param source The source entity to copy data from.
*/
void playerEntityLoad(entity_t *entity, const entity_t *source);
/**
* Updates the player entity.
*
* @param entity The entity to update.
*/
void playerEntityUpdate(entity_t *entity);
+10
View File
@@ -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
inventory.c
)
+68
View File
@@ -0,0 +1,68 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "inventory.h"
#include "util/memory.h"
#include "assert/assert.h"
void inventoryInit(inventory_t *inventory, const uint8_t size) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(size <= INVENTORY_SIZE_MAX, "size exceeding INVENTORY_SIZE_MAX");
assertTrue(size > 0, "size must be greater than 0");
memoryZero(inventory, sizeof(inventory_t));
inventory->size = size;
}
uint8_t inventoryItemIndexByType(
const inventory_t *inventory,
const itemtype_t type
) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
uint8_t item = inventory->itemCount;
while(item--) {
if(inventory->items[item].type == type) return item;
}
return INVENTORY_SIZE_MAX;
}
uint8_t inventoryItemCount(
const inventory_t *inventory,
const itemtype_t type
) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
const uint8_t index = inventoryItemIndexByType(inventory, type);
if(index == INVENTORY_SIZE_MAX) return 0;
return inventory->items[index].count;
}
void inventoryItemSet(
inventory_t *inventory,
const itemtype_t type,
const uint8_t count
) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
assertTrue(count > 0, "count must be greater than 0");
const uint8_t index = inventoryItemIndexByType(inventory, type);
if(index == INVENTORY_SIZE_MAX) {
// Item does not exist, add it
assertTrue(inventory->itemCount < inventory->size, "inventory is full");
inventory->items[inventory->itemCount].type = type;
inventory->items[inventory->itemCount].count = count;
inventory->itemCount++;
} else {
// Item exists, update the count
inventory->items[index].count = count;
}
}
+63
View File
@@ -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 "itemstack.h"
#define INVENTORY_SIZE_MAX UINT8_MAX
typedef struct {
itemstack_t items[INVENTORY_SIZE_MAX];
uint8_t itemCount;
uint8_t size;
} inventory_t;
/**
* Initializes an inventory with a specified size.
*
* @param inventory Pointer to the inventory to initialize.
* @param size The size of the inventory (maximum is INVENTORY_SIZE_MAX).
*/
void inventoryInit(inventory_t *inventory, const uint8_t size);
/**
* Finds the index of the item of a specified type in the inventory.
*
* @param inventory Pointer to the inventory to search.
* @param type The type of item to find.
* @return The index of the item, or INVENTORY_SIZE_MAX if not found.
*/
uint8_t inventoryItemIndexByType(
const inventory_t *inventory,
const itemtype_t type
);
/**
* Gets the count of items of a specified type in the inventory.
*
* @param inventory Pointer to the inventory to check.
* @param type The type of item to count.
* @return The count of items of the specified type.
*/
uint8_t inventoryItemCount(
const inventory_t *inventory,
const itemtype_t type
);
/**
* Sets the count of items of a specified type in the inventory.
* If the item does not exist, it will be added.
*
* @param inventory Pointer to the inventory to modify.
* @param type The type of item to set.
* @param count The count of items to set.
*/
void inventoryItemSet(
inventory_t *inventory,
const itemtype_t type,
const uint8_t count
);
+14
View File
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "itemtype.h"
typedef struct {
itemtype_t type;
uint8_t count;
} itemstack_t;
+24
View File
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef enum {
ITEM_TYPE_NULL = 0,
// MEDICINE
ITEM_TYPE_POTION,
// INGREDIENTS
ITEM_TYPE_ONION,
ITEM_TYPE_SWEET_POTATO,
ITEM_TYPE_CARROT,
// COOKED FOOD
ITEM_TYPE_BAKED_SWEET_POTATO,
} itemtype_t;
+11
View File
@@ -0,0 +1,11 @@
# 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
chunk.c
overworld.c
)
+321
View File
@@ -0,0 +1,321 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "chunk.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "world/world.h"
void renderChunkUpdated(chunk_t *chunk);
chunkmap_t CHUNK_MAP;
void chunkMapInit() {
memoryZero(&CHUNK_MAP, sizeof(chunkmap_t));
// Load default chunks, YX order.
uint16_t i = 0;
chunk_t *chunk;
for(uint8_t y = 0; y < CHUNK_MAP_HEIGHT; y++) {
for(uint8_t x = 0; x < CHUNK_MAP_WIDTH; x++) {
assertTrue(i < CHUNK_MAP_COUNT, "Chunk index out of bounds");
chunk = CHUNK_MAP.chunks + i;
CHUNK_MAP.chunkOrder[i] = chunk;
chunkLoad(chunk, x, y);
assertTrue(
chunk->x == x && chunk->y == y,
"Chunk coordinates do not match expected values"
);
i++;
}
}
}
void chunkMapShift(const int16_t x, const int16_t y) {
if(x == 0 && y == 0) assertUnreachable("ChunkMapShift called with no shift");
chunk_t *newChunkOrder[CHUNK_MAP_COUNT];
chunk_t *unloadedChunks[CHUNK_MAP_COUNT];
chunk_t *chunk;
uint8_t i, j;
uint16_t
/** New Map Coordinates */
newX, newY,
newChunkX, newChunkY
;
// Calculate the new map coordinates
newX = CHUNK_MAP.topLeftX + x;
newY = CHUNK_MAP.topLeftY + y;
// Zero the new chunk order
memoryZero(newChunkOrder, sizeof(newChunkOrder));
// For each chunk...
j = 0;
chunk = CHUNK_MAP.chunks;
do {
// Is this chunk still going to be within the map bounds?
if(
chunk->x < newX || chunk->y < newY ||
chunk->x >= newX + CHUNK_MAP_WIDTH ||
chunk->y >= newY + CHUNK_MAP_HEIGHT
) {
// No, it's not, let's unload it and make it available for reuse.
chunkUnload(chunk);
assertTrue(
j < CHUNK_MAP_COUNT,
"Unloaded chunk index out of bounds"
);
unloadedChunks[j++] = chunk;
chunk++;
continue;
}
// Yes it is still valid, determine the new index that it will be at
i = (chunk->y - newY) * CHUNK_MAP_WIDTH + (chunk->x - newX);
assertTrue(
i < CHUNK_MAP_COUNT,
"Chunk index out of bounds after shifting"
);
assertNull(
newChunkOrder[i],
"New chunk order index is already occupied"
);
// Set the new chunk order
newChunkOrder[i] = chunk;
chunk++;
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
// Now check the new chunk order list for missing chunks.
i = 0;
do {
assertTrue(
i < CHUNK_MAP_COUNT,
"New chunk order index out of bounds after shifting"
);
// Is this chunk loaded still?
chunk = newChunkOrder[i];
if(chunk != NULL) {
i++;
continue;
}
// Determine the new chunk coordinates.
newChunkX = i % CHUNK_MAP_WIDTH + newX;
newChunkY = i / CHUNK_MAP_WIDTH + newY;
assertTrue(
newChunkX >= newX && newChunkX < newX + CHUNK_MAP_WIDTH,
"New chunk X coordinate out of bounds after shifting"
);
assertTrue(
newChunkY >= newY && newChunkY < newY + CHUNK_MAP_HEIGHT,
"New chunk Y coordinate out of bounds after shifting"
);
// Pop a chunk from the unloaded chunks list.
assertTrue(j > 0, "No unloaded chunks available to reuse");
chunk = unloadedChunks[--j];
assertNotNull(chunk, "Unloaded chunk pointer is null");
// Load the chunk at the new coordinates.
chunkLoad(chunk, newChunkX, newChunkY);
assertTrue(
chunk->x == newChunkX && chunk->y == newChunkY,
"Chunk coordinates do not match expected values after shifting"
);
// Set it in order.
newChunkOrder[i] = chunk;
i++;
} while(i < CHUNK_MAP_COUNT);
// Update Absolutes.
CHUNK_MAP.topLeftX = newX;
CHUNK_MAP.topLeftY = newY;
// Update the chunk order.
memoryCopy(
CHUNK_MAP.chunkOrder,
newChunkOrder,
sizeof(CHUNK_MAP.chunkOrder)
);
}
void chunkMapSetPosition(const uint16_t x, const uint16_t y) {
if(x == CHUNK_MAP.topLeftX && y == CHUNK_MAP.topLeftY) {
return;
}
int16_t shiftX = x - CHUNK_MAP.topLeftX;
int16_t shiftY = y - CHUNK_MAP.topLeftY;
// Are we shifting the entire map?
if(
shiftX >= CHUNK_MAP_WIDTH || shiftX < -CHUNK_MAP_WIDTH ||
shiftY >= CHUNK_MAP_HEIGHT || shiftY < -CHUNK_MAP_HEIGHT
) {
printf("Shifting chunk map to new position (%u, %u)\n", x, y);
}
// Shift the chunk map by the specified offsets.
chunkMapShift(shiftX, shiftY);
}
chunk_t * chunkGetChunkAt(const uint16_t chunkX, const uint16_t chunkY) {
assertTrue(
chunkX < WORLD_WIDTH && chunkY < WORLD_HEIGHT,
"Chunk coordinates out of bounds"
);
chunk_t *chunk = CHUNK_MAP.chunks;
do {
if(chunk->x == chunkX && chunk->y == chunkY) return chunk;
chunk++;
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
return NULL;
}
void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y) {
assertNotNull(chunk, "Chunk pointer is null");
// Zero out the chunk data.
memoryZero(chunk, sizeof(chunk_t));
// Set the chunk coordinates.
chunk->x = x;
chunk->y = y;
// Only load data if the chunk is within bounds.
if(x >= WORLD_WIDTH || y >= WORLD_HEIGHT) {
memorySet(chunk->tilesBase, 0, sizeof(chunk->tilesBase));
memorySet(chunk->tilesBaseOverlay, 0, sizeof(chunk->tilesBaseOverlay));
return;
}
// Is chunk data defined?
const chunkdata_t *chunkData = WORLD_CHUNKS[y * WORLD_WIDTH + x];
if(chunkData == NULL) {
memorySet(chunk->tilesBase, 0, sizeof(chunk->tilesBase));
memorySet(chunk->tilesBaseOverlay, 0, sizeof(chunk->tilesBaseOverlay));
return;
}
// Load tile data into chunk
// printf("Loading chunk at (%u, %u)\n", x, y);
memoryCopy(
chunk->tilesBase,
chunkData->layerBase,
sizeof(chunk->tilesBase)
);
memoryCopy(
chunk->tilesBaseOverlay,
chunkData->layerBaseOverlay,
sizeof(chunk->tilesBaseOverlay)
);
// Load chunk entities
const entity_t *data;
entity_t *entity;
data = chunkData->entities;
while(data < chunkData->entities + CHUNK_ENTITY_COUNT_MAX) {
if(data->type == ENTITY_TYPE_NULL) break;
// Store that this chunk owns this entity ID.
chunk->entityIDs[chunk->entityCount++] = data->id;
// Check entity isn't loaded (still).
entity = ENTITIES;
do {
if(entity->type != ENTITY_TYPE_NULL && entity->id == data->id) break;
entity++;
} while(entity < ENTITIES + ENTITY_COUNT_MAX);
if(entity != ENTITIES + ENTITY_COUNT_MAX) {
// Entity is already loaded, skip it.
printf("Entity ID %u already loaded, skipping...\n", data->id);
data++;
continue;
}
// Find an empty entity slot.
entity = ENTITIES;
while(true) {
assertTrue(
entity < ENTITIES + ENTITY_COUNT_MAX,
"Out of available entities"
);
if(entity->type == ENTITY_TYPE_NULL) break;
entity++;
};
// Load this entity.
entityLoad(entity, data);
data++;
}
// Allow the rendering platform to know this chunk is loaded.
renderChunkUpdated(chunk);
}
void chunkUnload(chunk_t *chunk) {
uint8_t i;
entity_t *entity;
uint32_t id;
assertNotNull(chunk, "Chunk pointer is null");
// Iterate over each entity this chunk owns.
i = 0;
while(i < chunk->entityCount) {
id = chunk->entityIDs[i++];
// Now, do we need to unload this entity?
bool_t shouldUnload = false;
// Now, find the entity loaded with this ID. It should be impossible for
// this entity to be unloaded (but may change in future).
entity = ENTITIES;
do {
if(entity->type != ENTITY_TYPE_NULL && entity->id == id) break;
entity++;
} while(entity < ENTITIES + ENTITY_COUNT_MAX);
assertTrue(
entity < ENTITIES + ENTITY_COUNT_MAX,
"Entity ID not found in ENTITIES array, cannot unload"
);
// If the entity is still within our chunk bounds, it's getting unloaded
if(
floorf(entity->x) >= chunk->x * CHUNK_WIDTH * TILE_WIDTH_HEIGHT &&
ceilf(entity->x) < (chunk->x + 1) * CHUNK_WIDTH * TILE_WIDTH_HEIGHT &&
floorf(entity->y) >= chunk->y * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT &&
ceilf(entity->y) < (chunk->y + 1) * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT
) {
shouldUnload = true;
} else {
assertUnreachable(
"Entity has left its chunk bounds, we should not be unloading it but "
"I have yet to implement that properly. It will need to self-manage "
"its own unloading somehow, and also not be in a null chunk "
"when it does so."
);
}
// This entity is still in use, leave it loaded.
if(!shouldUnload) continue;
// NULL the entity type, effectively unloading it.
entity->type = ENTITY_TYPE_NULL;
}
}
+84
View File
@@ -0,0 +1,84 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "tile.h"
#include "display/render.h"
#define CHUNK_WIDTH 8
#define CHUNK_HEIGHT 8
#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT)
#define CHUNK_ENTITY_COUNT_MAX 8
#define CHUNK_MAP_WIDTH (((RENDER_WIDTH / TILE_WIDTH_HEIGHT)/CHUNK_WIDTH)+2)
#define CHUNK_MAP_HEIGHT (((RENDER_HEIGHT / TILE_WIDTH_HEIGHT)/CHUNK_HEIGHT)+2)
#define CHUNK_MAP_COUNT (CHUNK_MAP_WIDTH * CHUNK_MAP_HEIGHT)
typedef struct {
uint16_t x, y;
tile_t tilesBase[CHUNK_TILE_COUNT];
tile_t tilesBaseOverlay[CHUNK_TILE_COUNT];
uint32_t entityIDs[CHUNK_ENTITY_COUNT_MAX];
uint8_t entityCount;
} chunk_t;
typedef struct {
chunk_t chunks[CHUNK_MAP_COUNT];
chunk_t *chunkOrder[CHUNK_MAP_COUNT];
uint16_t topLeftX;
uint16_t topLeftY;
} chunkmap_t;
extern chunkmap_t CHUNK_MAP;
/**
* Initializes the chunk map.
*/
void chunkMapInit();
/**
* Shifts the chunk map by the specified x and y offsets.
*
* @param x The x offset to shift the chunk map.
* @param y The y offset to shift the chunk map.
*/
void chunkMapShift(const int16_t x, const int16_t y);
/**
* Sets the position of the chunk map to the specified coordinates.
*
* @param x The x coordinate of the top-left chunk.
* @param y The y coordinate of the top-left chunk.
*/
void chunkMapSetPosition(const uint16_t x, const uint16_t y);
/**
* Gets the chunk at the specified chunk coordinates.
*
* @param chunkX The x coordinate of the chunk.
* @param chunkY The y coordinate of the chunk.
* @return A pointer to the chunk at the specified chunk coordinates, or NULL if
* no chunk exists at those coordinates.
*/
chunk_t * chunkGetChunkAt(const uint16_t chunkX, const uint16_t chunkY);
/**
* Loads a chunk at the specified coordinates.
*
* @param chunk The chunk to load.
* @param x The x coordinate of the chunk.
* @param y The y coordinate of the chunk.
*/
void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y);
/**
* Unloads a chunk (that is currently loaded).
*
* @param chunk The chunk to unload.
*/
void chunkUnload(chunk_t *chunk);
+16
View File
@@ -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 "chunk.h"
#include "entity/entity.h"
typedef struct {
uint8_t layerBase[CHUNK_TILE_COUNT];
uint8_t layerBaseOverlay[CHUNK_TILE_COUNT];
entity_t entities[CHUNK_ENTITY_COUNT_MAX];
} chunkdata_t;
+72
View File
@@ -0,0 +1,72 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "overworld.h"
#include "chunk.h"
#include "display/render.h"
#include "assert/assert.h"
#include "entity/entity.h"
uint32_t OVERWORLD_CAMERA_X;
uint32_t OVERWORLD_CAMERA_Y;
overworldcameratype_t OVERWORLD_CAMERA_TYPE;
void overworldInit(void) {
playerInit();
chunkMapInit();
OVERWORLD_CAMERA_X = 0;
OVERWORLD_CAMERA_Y = 0;
OVERWORLD_CAMERA_TYPE = OVERWORLD_CAMERA_TYPE_CENTERED_POSITION;
}
void overworldUpdate() {
entity_t *entity;
assertTrue(
OVERWORLD_CAMERA_X < OVERWORLD_CAMERA_LIMIT_X,
"Camera position limit (just because I haven't tested properly)"
);
assertTrue(
OVERWORLD_CAMERA_Y < OVERWORLD_CAMERA_LIMIT_Y,
"Camera position limit (just because I haven't tested properly)"
);
entity = ENTITIES;
do {
entityUpdate(entity++);
} while(entity->type != ENTITY_TYPE_NULL);
// Testing, follow player
entity = &ENTITIES[0]; // Player entity
assertTrue(
entity->type == ENTITY_TYPE_PLAYER,
"First entity must be player"
);
OVERWORLD_CAMERA_X = entity->x * TILE_WIDTH_HEIGHT + entity->subX;
OVERWORLD_CAMERA_Y = entity->y * TILE_WIDTH_HEIGHT + entity->subY;
uint16_t x, y;
uint16_t halfWidth, halfHeight;
halfWidth = ((CHUNK_MAP_WIDTH - 1) * CHUNK_WIDTH * TILE_WIDTH_HEIGHT) / 2;
halfHeight = ((CHUNK_MAP_HEIGHT - 1) * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT) / 2;
// Calculate the chunk map position based on the camera position.
if(OVERWORLD_CAMERA_X < halfWidth) {
x = 0;
} else {
x = (OVERWORLD_CAMERA_X - halfWidth) / (CHUNK_WIDTH*TILE_WIDTH_HEIGHT);
}
if(OVERWORLD_CAMERA_Y < halfHeight) {
y = 0;
} else {
y = (OVERWORLD_CAMERA_Y - halfHeight) / (CHUNK_HEIGHT*TILE_WIDTH_HEIGHT);
}
chunkMapSetPosition(x, y);
}
+30
View File
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef enum {
OVERWORLD_CAMERA_TYPE_CENTERED_POSITION,
} overworldcameratype_t;
extern uint32_t OVERWORLD_CAMERA_X;
extern uint32_t OVERWORLD_CAMERA_Y;
extern overworldcameratype_t OVERWORLD_CAMERA_TYPE;
#define OVERWORLD_CAMERA_LIMIT_X (UINT32_MAX / 4)
#define OVERWORLD_CAMERA_LIMIT_Y (UINT32_MAX / 4)
/**
* Initializes the overworld.
*/
void overworldInit(void);
/**
* Updates the overworld.
*/
void overworldUpdate(void);
+26
View File
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define TILE_WIDTH_HEIGHT 16
typedef uint8_t tile_t;
typedef enum {
TILE_SOLID_NONE = 0,
TILE_SOLID_FULL = 1,
TILE_SOLID_TRIANGLE_TOP_RIGHT = 2,
TILE_SOLID_TRIANGLE_TOP_LEFT = 3,
TILE_SOLID_TRIANGLE_BOTTOM_RIGHT = 4,
TILE_SOLID_TRIANGLE_BOTTOM_LEFT = 5,
} tilesolidtype_t;
typedef struct {
tilesolidtype_t solidType;
} tilemetadata_t;
-1
View File
@@ -6,7 +6,6 @@
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
node.c
)
# Subdirs
-129
View File
@@ -1,129 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "node.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "display/display.h"
node_t NODE_DATA[ECS_ENTITY_COUNT_MAX] = { 0 };
ecscomponent_t NODE_COMPONENT = ecsComponentInit(
NODE_DATA,
((ecscomponentcallbacks_t){
.init = nodeInit,
.entityAdd = nodeEntityAdded,
.entityRemove = nodeEntityRemoved
})
);
void nodeInit(void) {
}
void nodeEntityAdded(const ecsid_t id) {
glm_mat4_identity(NODE_DATA[id].transform);
}
void nodeEntityRemoved(const ecsid_t 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);
// Extract position, scale, rotation from the matrix.
node->position[0] = in[3][0];
node->position[1] = in[3][1];
node->position[2] = in[3][2];
node->scale[0] = glm_vec3_norm((vec3){ in[0][0], in[0][1], in[0][2] });
node->scale[1] = glm_vec3_norm((vec3){ in[1][0], in[1][1], in[1][2] });
node->scale[2] = glm_vec3_norm((vec3){ in[2][0], in[2][1], in[2][2] });
// Remove scale from the matrix to extract rotation.
if(node->scale[0] != 0.0f) {
in[0][0] /= node->scale[0];
in[0][1] /= node->scale[0];
in[0][2] /= node->scale[0];
glm_vec3_copy(in[0], node->rotation);
glm_vec3_copy(in[1], node->rotation);
glm_vec3_copy(in[2], node->rotation);
node->rotation[1] = asinf(-in[0][2]);
if (cosf(node->rotation[1]) != 0.0f) {
node->rotation[0] = atan2f(in[1][2], in[2][2]);
node->rotation[2] = atan2f(in[0][1], in[0][0]);
} else {
node->rotation[0] = 0.0f;
node->rotation[2] = atan2f(-in[1][0], in[1][1]);
}
} else {
node->rotation[0] = 0.0f;
node->rotation[1] = 0.0f;
node->rotation[2] = 0.0f;
}
}
void nodePositionGet(const ecsid_t id, vec3 out) {
node_t *node;
if(nodeHas(id)) {
node = &NODE_DATA[id];
} else {
node = nodeAdd(id);
}
glm_vec3_copy(node->position, out);
}
void nodeMatrixUpdate(const ecsid_t id) {
node_t *node;
if(nodeHas(id)) {
node = &NODE_DATA[id];
} else {
node = nodeAdd(id);
}
glm_mat4_identity(node->transform);
mat4 rot;
glm_euler(node->rotation, rot);
glm_mat4_mul(node->transform, rot, node->transform);
// glm_scale(node->transform, node->scale);
}
void nodeMatrixPush(const ecsid_t id) {
assertTrue(nodeHas(id), "Not a node component");
node_t *node = nodeGet(id);
#if DISPLAY_SDL2
glPushMatrix();
glMultMatrixf((const GLfloat*)node->transform);
#endif
}
void nodeMatrixPop(void) {
#if DISPLAY_SDL2
glPopMatrix();
#endif
}
-83
View File
@@ -1,83 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "ecs/ecscomponent.h"
#define SCENE_ITEM_CHILD_MAX 16
typedef struct {
mat4 transform;
vec3 position;
vec3 scale;
vec3 rotation; // Euler angles in radians
} node_t;
extern node_t NODE_DATA[ECS_ENTITY_COUNT_MAX];
extern ecscomponent_t NODE_COMPONENT;
#define nodeHas(id) ecsComponentDataHas(&NODE_COMPONENT, id)
#define nodeGet(id) \
((node_t*)ecsComponentDataGet(&NODE_COMPONENT, id))
#define nodeAdd(id) \
((node_t*)ecsComponentDataAdd(&NODE_COMPONENT, id))
#define nodeRemove(id) ecsComponentDataRemove(&NODE_COMPONENT, id)
/**
* Initialize the node component.
*/
void nodeInit(void);
/**
* Callback for when an entity is added to the ECS.
*
* @param id The ID of the entity being added.
*/
void nodeEntityAdded(const ecsid_t id);
/**
* Callback for when an entity is removed from the ECS.
*
* @param id The ID of the entity being removed.
*/
void nodeEntityRemoved(const ecsid_t id);
/**
* Get the local transformation matrix of a node.
*
* @param id The ID of the node.
* @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 node.
*
* @param id The ID of the node.
* @param in Pointer to a mat4 containing the new local matrix.
*/
void nodeMatrixSet(const ecsid_t id, mat4 in);
/**
* Get the local position of a node.
*
* @param id The ID of the node.
*/
void nodeMatrixUpdate(const ecsid_t id);
/**
* Push the node's transformation matrix onto the OpenGL matrix stack.
*
* @param id The ID of the node.
*/
void nodeMatrixPush(const ecsid_t id);
/**
* Pop the last transformation matrix from the OpenGL matrix stack.
*/
void nodeMatrixPop(void);
-28
View File
@@ -6,38 +6,10 @@
*/
#include "scenetest.h"
#include "scene/node.h"
#include "display/camera.h"
#include "display/mesh/meshrenderer.h"
#include "display/mesh/quad.h"
texture_t test;
void sceneTestAdd(void) {
// Initialize the entity with a camera component
ecsid_t camera = ecsEntityAdd();
node_t *node = nodeAdd(camera);
camera_t *camData = cameraAdd(camera);
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);
// color4b_t pixels[4] = {
// COLOR_RED, COLOR_GREEN,
// COLOR_BLUE, COLOR_WHITE
// };
// textureInit(&test, 2, 2, TEXTURE_FORMAT_RGBA, pixels);
// Test cube
ecsid_t cube = ecsEntityAdd();
node = nodeAdd(cube);
meshrenderer_t *renderer = meshRendererAdd(cube);
renderer->mesh = &QUAD_MESH_SIMPLE;
renderer->texture = &test;
}
-1
View File
@@ -6,7 +6,6 @@
*/
#pragma once
#include "ecs/ecssystem.h"
#include "display/texture/texture.h"
extern texture_t test;