Made a big mess of the codebase
This commit is contained in:
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
@@ -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);
|
||||
|
@@ -8,5 +8,4 @@ target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
mesh.c
|
||||
quad.c
|
||||
meshrenderer.c
|
||||
)
|
@@ -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);
|
||||
}
|
@@ -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);
|
@@ -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();
|
||||
// }
|
@@ -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);
|
@@ -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
src/rpg/CMakeLists.txt
Normal file
14
src/rpg/CMakeLists.txt
Normal 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
src/rpg/entity/CMakeLists.txt
Normal file
13
src/rpg/entity/CMakeLists.txt
Normal 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
src/rpg/entity/direction.c
Normal file
53
src/rpg/entity/direction.c
Normal 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
src/rpg/entity/direction.h
Normal file
41
src/rpg/entity/direction.h
Normal 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
src/rpg/entity/entity.c
Normal file
123
src/rpg/entity/entity.c
Normal 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
src/rpg/entity/entity.h
Normal file
99
src/rpg/entity/entity.h
Normal 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
src/rpg/entity/npc.c
Normal file
45
src/rpg/entity/npc.c
Normal 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
src/rpg/entity/npc.h
Normal file
44
src/rpg/entity/npc.h
Normal 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
src/rpg/entity/player.c
Normal file
86
src/rpg/entity/player.c
Normal 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
src/rpg/entity/player.h
Normal file
43
src/rpg/entity/player.h
Normal 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
src/rpg/item/CMakeLists.txt
Normal file
10
src/rpg/item/CMakeLists.txt
Normal 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
src/rpg/item/inventory.c
Normal file
68
src/rpg/item/inventory.c
Normal 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
src/rpg/item/inventory.h
Normal file
63
src/rpg/item/inventory.h
Normal 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
src/rpg/item/itemstack.h
Normal file
14
src/rpg/item/itemstack.h
Normal 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
src/rpg/item/itemtype.h
Normal file
24
src/rpg/item/itemtype.h
Normal 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
src/rpg/world/CMakeLists.txt
Normal file
11
src/rpg/world/CMakeLists.txt
Normal 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
src/rpg/world/chunk.c
Normal file
321
src/rpg/world/chunk.c
Normal 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
src/rpg/world/chunk.h
Normal file
84
src/rpg/world/chunk.h
Normal 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
src/rpg/world/chunkdata.h
Normal file
16
src/rpg/world/chunkdata.h
Normal 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
src/rpg/world/overworld.c
Normal file
72
src/rpg/world/overworld.c
Normal 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
src/rpg/world/overworld.h
Normal file
30
src/rpg/world/overworld.h
Normal 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
src/rpg/world/tile.h
Normal file
26
src/rpg/world/tile.h
Normal 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;
|
@@ -6,7 +6,6 @@
|
||||
# Sources
|
||||
target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
node.c
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
|
129
src/scene/node.c
129
src/scene/node.c
@@ -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
|
||||
}
|
@@ -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);
|
@@ -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;
|
||||
}
|
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "ecs/ecssystem.h"
|
||||
#include "display/texture/texture.h"
|
||||
|
||||
extern texture_t test;
|
||||
|
Reference in New Issue
Block a user