diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index 7f68dce1..0075aac3 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -51,6 +51,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { consolePrint("Engine initialized"); sceneSet(SCENE_TYPE_OVERWORLD); + errorOk(); } diff --git a/src/dusk/rpg/overworld/chunk.h b/src/dusk/rpg/overworld/chunk.h index a81fcb4d..39643f5f 100644 --- a/src/dusk/rpg/overworld/chunk.h +++ b/src/dusk/rpg/overworld/chunk.h @@ -20,6 +20,7 @@ typedef struct chunk_s { tile_t tiles[CHUNK_TILE_COUNT]; spritebatchsprite_t sprites[CHUNK_TILE_COUNT]; + color_t testColor; uint32_t spriteCount; // uint8_t meshCount; diff --git a/src/dusk/rpg/overworld/map.c b/src/dusk/rpg/overworld/map.c index cf3f85c2..fb01630e 100644 --- a/src/dusk/rpg/overworld/map.c +++ b/src/dusk/rpg/overworld/map.c @@ -16,68 +16,86 @@ map_t MAP; errorret_t mapInit() { memoryZero(&MAP, sizeof(map_t)); - errorOk(); -} - -bool_t mapIsLoaded() { - return MAP.filePath[0] != '\0'; -} - -errorret_t mapLoad(const char_t *path, const chunkpos_t position) { - assertStrLenMin(path, 1, "Map file path cannot be empty"); - assertStrLenMax(path, MAP_FILE_PATH_MAX - 1, "Map file path too long"); - - if(stringCompare(MAP.filePath, path) == 0) { - // Same map, no need to reload - errorOk(); - } - - chunkindex_t i; - - // Unload all loaded chunks - if(mapIsLoaded()) { - for(i = 0; i < MAP_CHUNK_COUNT; i++) { - mapChunkUnload(&MAP.chunks[i]); - } - } - - // Store the map file path - stringCopy(MAP.filePath, path, MAP_FILE_PATH_MAX); - - // Determine directory path (it is dirname) - stringCopy(MAP.dirPath, path, MAP_FILE_PATH_MAX); - char_t *last = stringFindLastChar(MAP.dirPath, '/'); - if(last == NULL) errorThrow("Invalid map file path"); - - // Store filename, sans extension - stringCopy(MAP.fileName, last + 1, MAP_FILE_PATH_MAX); - *last = '\0'; // Terminate to get directory path - - last = stringFindLastChar(MAP.fileName, '.'); - if(last == NULL) errorThrow("Map file name has no extension"); - *last = '\0'; // Terminate to remove extension - - // Reset map position - MAP.chunkPosition = position; // Perform "initial load" - i = 0; + MAP.loaded = true; + int32_t i = 0; for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) { for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) { for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) { chunk_t *chunk = &MAP.chunks[i]; - chunk->position.x = x + position.x; - chunk->position.y = y + position.y; - chunk->position.z = z + position.z; + chunk->position.x = x + MAP.chunkPosition.x; + chunk->position.y = y + MAP.chunkPosition.y; + chunk->position.z = z + MAP.chunkPosition.z; MAP.chunkOrder[i] = chunk; errorChain(mapChunkLoad(chunk)); i++; } } } + errorOk(); } +bool_t mapIsLoaded() { + return MAP.loaded; +} + +// errorret_t mapLoad(const char_t *path, const chunkpos_t position) { +// assertStrLenMin(path, 1, "Map file path cannot be empty"); +// assertStrLenMax(path, MAP_FILE_PATH_MAX - 1, "Map file path too long"); + +// if(stringCompare(MAP.filePath, path) == 0) { +// // Same map, no need to reload +// errorOk(); +// } + +// chunkindex_t i; + +// // Unload all loaded chunks +// if(mapIsLoaded()) { +// for(i = 0; i < MAP_CHUNK_COUNT; i++) { +// mapChunkUnload(&MAP.chunks[i]); +// } +// } + +// // Store the map file path +// stringCopy(MAP.filePath, path, MAP_FILE_PATH_MAX); + +// // Determine directory path (it is dirname) +// stringCopy(MAP.dirPath, path, MAP_FILE_PATH_MAX); +// char_t *last = stringFindLastChar(MAP.dirPath, '/'); +// if(last == NULL) errorThrow("Invalid map file path"); + +// // Store filename, sans extension +// stringCopy(MAP.fileName, last + 1, MAP_FILE_PATH_MAX); +// *last = '\0'; // Terminate to get directory path + +// last = stringFindLastChar(MAP.fileName, '.'); +// if(last == NULL) errorThrow("Map file name has no extension"); +// *last = '\0'; // Terminate to remove extension + +// // Reset map position +// MAP.chunkPosition = position; + +// // Perform "initial load" +// i = 0; +// for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) { +// for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) { +// for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) { +// chunk_t *chunk = &MAP.chunks[i]; +// chunk->position.x = x + position.x; +// chunk->position.y = y + position.y; +// chunk->position.z = z + position.z; +// MAP.chunkOrder[i] = chunk; +// errorChain(mapChunkLoad(chunk)); +// i++; +// } +// } +// } +// errorOk(); +// } + errorret_t mapPositionSet(const chunkpos_t newPos) { if(!mapIsLoaded()) errorThrow("No map loaded"); @@ -177,21 +195,79 @@ void mapChunkUnload(chunk_t* chunk) { entity->type = ENTITY_TYPE_NULL; } - for(uint8_t i = 0; i < chunk->meshCount; i++) { - if(chunk->meshes[i].vertexCount == 0) continue; - meshDispose(&chunk->meshes[i]); - } + // for(uint8_t i = 0; i < chunk->meshCount; i++) { + // if(chunk->meshes[i].vertexCount == 0) continue; + // meshDispose(&chunk->meshes[i]); + // } } errorret_t mapChunkLoad(chunk_t* chunk) { if(!mapIsLoaded()) errorThrow("No map loaded"); - - char_t buffer[64]; - - // TODO: Can probably move this to asset load logic? - chunk->meshCount = 0; - memoryZero(chunk->meshes, sizeof(chunk->meshes)); + + color_t color = COLOR_WHITE; + if(chunk->position.y % 2 == 0) { + if(chunk->position.x % 2 == 0) { + color = COLOR_BLACK; + } else { + color = COLOR_WHITE; + } + } else { + if(chunk->position.x % 2 == 0) { + color = COLOR_WHITE; + } else { + color = COLOR_BLACK; + } + } + // if(chunk->position.x == 0 && chunk->position.y == 0 && chunk->position.z == 0) { + // color = COLOR_RED; + // } + chunk->testColor = color; + + memorySet(chunk->tiles, TILE_SHAPE_GROUND, sizeof(chunk->tiles)); + memoryZero(chunk->sprites, sizeof(chunk->sprites)); memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); + chunk->spriteCount = 0; + + if(chunk->position.z != 0) { + errorOk(); + } + + // Set Chunk sprites. + uint32_t i = 0; + vec3 spriteMin = { + chunk->position.x * CHUNK_WIDTH, + chunk->position.y * CHUNK_HEIGHT, + chunk->position.z * CHUNK_DEPTH + }; + for(uint8_t x = 0; x < CHUNK_WIDTH; x++) { + for(uint8_t y = 0; y < CHUNK_HEIGHT; y++) { + glm_vec3_copy(spriteMin, chunk->sprites[i].min); + glm_vec3_add( + chunk->sprites[i].min, + (vec3){ x, y, 0 }, + chunk->sprites[i].min + ); + + glm_vec3_copy(chunk->sprites[i].min, chunk->sprites[i].max); + glm_vec3_add( + chunk->sprites[i].max, + (vec3){ 1, 1, 0 }, + chunk->sprites[i].max + ); + + glm_vec2_copy((vec2){ 0, 0 }, chunk->sprites[i].uvMin); + glm_vec2_copy((vec2){ 1, 1 }, chunk->sprites[i].uvMax); + + chunk->spriteCount++; + i++; + } + } + + // char_t buffer[64]; + // TODO: Can probably move this to asset load logic? + // chunk->meshCount = 0; + // memoryZero(chunk->meshes, sizeof(chunk->meshes)); + // memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); // Load. errorOk(); diff --git a/src/dusk/rpg/overworld/map.h b/src/dusk/rpg/overworld/map.h index 8373fca0..f97628e9 100644 --- a/src/dusk/rpg/overworld/map.h +++ b/src/dusk/rpg/overworld/map.h @@ -11,9 +11,10 @@ #define MAP_FILE_PATH_MAX 128 typedef struct map_s { - char_t filePath[MAP_FILE_PATH_MAX]; - char_t dirPath[MAP_FILE_PATH_MAX]; - char_t fileName[MAP_FILE_PATH_MAX]; + // char_t filePath[MAP_FILE_PATH_MAX]; + // char_t dirPath[MAP_FILE_PATH_MAX]; + // char_t fileName[MAP_FILE_PATH_MAX]; + bool_t loaded; chunk_t chunks[MAP_CHUNK_COUNT]; chunk_t *chunkOrder[MAP_CHUNK_COUNT]; @@ -43,10 +44,10 @@ bool_t mapIsLoaded(); * @param position The initial chunk position. * @return An error code. */ -errorret_t mapLoad( - const char_t *path, - const chunkpos_t position -); +// errorret_t mapLoad( +// const char_t *path, +// const chunkpos_t position +// ); /** * Updates the map. diff --git a/src/dusk/rpg/overworld/tile.h b/src/dusk/rpg/overworld/tile.h index 622f6ee2..c2bbcac1 100644 --- a/src/dusk/rpg/overworld/tile.h +++ b/src/dusk/rpg/overworld/tile.h @@ -12,7 +12,7 @@ typedef enum { TILE_SHAPE_NULL, - TILE_SHAPE_SOLID, + TILE_SHAPE_GROUND, TILE_SHAPE_RAMP_NORTH, TILE_SHAPE_RAMP_SOUTH, TILE_SHAPE_RAMP_EAST, diff --git a/src/dusk/rpg/rpg.c b/src/dusk/rpg/rpg.c index 3c04d0cf..a7604c86 100644 --- a/src/dusk/rpg/rpg.c +++ b/src/dusk/rpg/rpg.c @@ -26,14 +26,16 @@ errorret_t rpgInit(void) { rpgCameraInit(); rpgTextboxInit(); + // Init world + errorChain(mapPositionSet((chunkpos_t){ 0, 0, 0 })); + // TEST: Create some entities. uint8_t entIndex = entityGetAvailable(); assertTrue(entIndex != 0xFF, "No available entity slots!."); entity_t *ent = &ENTITIES[entIndex]; entityInit(ent, ENTITY_TYPE_PLAYER); - // RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY; - // RPG_CAMERA.followEntity.followEntityId = ent->id; - ent->position.x = 2, ent->position.y = 2; + RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY; + RPG_CAMERA.followEntity.followEntityId = ent->id; // All Good! errorOk(); diff --git a/src/dusk/rpg/rpgcamera.c b/src/dusk/rpg/rpgcamera.c index 8a709cb4..0ab0c39e 100644 --- a/src/dusk/rpg/rpgcamera.c +++ b/src/dusk/rpg/rpgcamera.c @@ -11,44 +11,38 @@ #include "rpg/overworld/map.h" #include "assert/assert.h" +#include "display/screen/screen.h" + rpgcamera_t RPG_CAMERA; void rpgCameraInit(void) { memoryZero(&RPG_CAMERA, sizeof(rpgcamera_t)); - } -errorret_t rpgCameraUpdate(void) { - glm_lookat( - (vec3){ 3, 3, 3 }, - (vec3){ 0, 0, 0 }, // center - (vec3){ 0, 1, 0 }, // up - RPG_CAMERA.eye - ); - - if(!mapIsLoaded()) errorOk(); - chunkpos_t chunkPos; - +worldpos_t rpgCameraGetPosition(void) { switch(RPG_CAMERA.mode) { case RPG_CAMERA_MODE_FREE: - worldPosToChunkPos(&RPG_CAMERA.free, &chunkPos); - break; + return RPG_CAMERA.free; case RPG_CAMERA_MODE_FOLLOW_ENTITY: { entity_t *entity = &ENTITIES[RPG_CAMERA.followEntity.followEntityId]; if(entity->type == ENTITY_TYPE_NULL) { - errorOk(); + return (worldpos_t){ 0, 0, 0 }; } - - // Update map position to match camera. By default map wants to know the - // top left but we want to set the center, so we need to sub half map size - worldPosToChunkPos(&entity->position, &chunkPos); - break; + return entity->position; } default: assertUnreachable("Invalid RPG camera mode"); } +} + +errorret_t rpgCameraUpdate(void) { + if(!mapIsLoaded()) errorOk(); + + chunkpos_t chunkPos; + worldpos_t worldPos = rpgCameraGetPosition(); + worldPosToChunkPos(&worldPos, &chunkPos); errorChain(mapPositionSet((chunkpos_t){ .x = chunkPos.x - (MAP_CHUNK_WIDTH / 2), diff --git a/src/dusk/rpg/rpgcamera.h b/src/dusk/rpg/rpgcamera.h index 82d7ca6f..824ca8c7 100644 --- a/src/dusk/rpg/rpgcamera.h +++ b/src/dusk/rpg/rpgcamera.h @@ -19,6 +19,7 @@ typedef struct { union { worldpos_t free; + struct { uint8_t followEntityId; } followEntity; @@ -34,6 +35,13 @@ extern rpgcamera_t RPG_CAMERA; */ void rpgCameraInit(void); +/** + * Gets the RPG camera's position. + * + * @return The RPG camera's position. + */ +worldpos_t rpgCameraGetPosition(void); + /** * Updates the RPG camera. * diff --git a/src/dusk/scene/overworld/sceneoverworld.c b/src/dusk/scene/overworld/sceneoverworld.c index d73987ca..4684dd26 100644 --- a/src/dusk/scene/overworld/sceneoverworld.c +++ b/src/dusk/scene/overworld/sceneoverworld.c @@ -14,6 +14,8 @@ #include "display/shader/shaderunlit.h" #include "display/spritebatch/spritebatch.h" +#include "rpg/overworld/map.h" +#include "rpg/entity/entity.h" #include "rpg/rpgcamera.h" errorret_t sceneOverworldInit(scenedata_t *sceneData) { @@ -39,38 +41,121 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) { glm_mat4_identity(model); - glm_perspective( - glm_rad(45), - (float)SCREEN.width / (float)SCREEN.height, - 0.1f, - 100.0f, - proj - ); errorChain(shaderBind(&SHADER_UNLIT)); errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &TEXTURE_TEST)); errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE)); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model)); - errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, RPG_CAMERA.eye)); + + // Camera projection + float_t fov = glm_rad(45.0f); + glm_perspective( + fov, + (float_t)SCREEN.width / (float_t)SCREEN.height, + 0.1f, + 100.0f, + proj + ); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj)); + + // Camera Eye + float_t pixelsPerUnit = 16.0f; + float_t worldH = (float)SCREEN.height / pixelsPerUnit; + float_t z = (worldH * 0.5f) / tanf(fov * 0.5f); + worldpos_t worldPos = rpgCameraGetPosition(); + float_t offset = 16.0f; + vec3 worldPosVec = { + worldPos.x, + worldPos.y, + worldPos.z + }; + glm_vec3_add(worldPosVec, (vec3){ 0.5f, 0.5f, 0.5f }, worldPosVec); + + glm_lookat( + (vec3){ + worldPosVec[0], + worldPosVec[1] + offset, + worldPosVec[2] + z + }, + worldPosVec, + (vec3){ 0, -1, 0 }, // up + RPG_CAMERA.eye + ); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, RPG_CAMERA.eye)); + + // Chunk Data { - uint8_t spriteCount = 1; - spritebatchsprite_t sprites[spriteCount]; + shadermaterial_t chunkMaterial = { + .unlit = { + .color = COLOR_WHITE + } + }; - glm_vec3_copy((vec3){ -0.5f, -0.5f, 0 }, sprites[0].min); - glm_vec3_copy((vec3){ 0.5f, 0.5f, 0 }, sprites[0].max); - glm_vec2_copy((vec2){ 0, 0 }, sprites[0].uvMin); - glm_vec2_copy((vec2){ 1, 1 }, sprites[0].uvMax); + uint32_t i = 0; + for(uint8_t x = 0; x < MAP_CHUNK_WIDTH; x++) { + for(uint8_t y = 0; y < MAP_CHUNK_HEIGHT; y++) { + for(uint8_t z = 0; z < MAP_CHUNK_DEPTH; z++) { + chunk_t *chunk = &MAP.chunks[i]; + if(chunk->spriteCount == 0) { + i++; + continue; + } - shadermaterial_t material; - material.unlit.color = COLOR_WHITE; - material.unlit.texture = &TEXTURE_TEST; + chunkMaterial.unlit.color = chunk->testColor; + + spriteBatchBuffer( + chunk->sprites, chunk->spriteCount, &SHADER_UNLIT, chunkMaterial + ); + i++; + } + } + } - spriteBatchBuffer(sprites, spriteCount, &SHADER_UNLIT, material); spriteBatchFlush(); } + // Entities + { + uint8_t spriteCount = 0; + spritebatchsprite_t sprites[ENTITY_COUNT]; + for(uint8_t i = 0; i < ENTITY_COUNT; i++) { + entity_t *ent = &ENTITIES[i]; + if(ent->type == ENTITY_TYPE_NULL) continue; + + vec3 position = { + ent->position.x, + ent->position.y, + ((float_t)ent->position.z) + 0.01f + }; + + glm_vec3_copy(position, sprites[spriteCount].min); + glm_vec3_copy(position, sprites[spriteCount].max); + glm_vec3_add( + sprites[spriteCount].max, + (vec3){ 1, 1, 0 }, + sprites[spriteCount].max + ); + + glm_vec2_copy((vec2){ 0, 0 }, sprites[spriteCount].uvMin); + glm_vec2_copy((vec2){ 1, 1 }, sprites[spriteCount].uvMax); + + spriteCount++; + } + + if(spriteCount) { + shadermaterial_t material = { + .unlit = { + .color = COLOR_CYAN, + .texture = NULL + } + }; + // material.unlit.texture = &TEXTURE_TEST; + spriteBatchBuffer(sprites, spriteCount, &SHADER_UNLIT, material); + spriteBatchFlush(); + } + } + errorOk(); }