diff --git a/code/.vscode/launch.json b/code/.vscode/launch.json new file mode 100644 index 0000000..407f136 --- /dev/null +++ b/code/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug 'main.c' Project", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/../build/ldebug", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "Build" + } + ] +} diff --git a/code/.vscode/tasks.json b/code/.vscode/tasks.json new file mode 100644 index 0000000..b17da5a --- /dev/null +++ b/code/.vscode/tasks.json @@ -0,0 +1,20 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build", + "type": "shell", + "command": "/home/matt/Repos/ld58/linux", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "never" + }, + "problemMatcher": [ + "$gcc" + ] + } + ] +} diff --git a/code/core/platform.h b/code/core/platform.h index a6cbda1..5047a9f 100644 --- a/code/core/platform.h +++ b/code/core/platform.h @@ -71,6 +71,8 @@ #pragma warning(disable : 4201) #elif OS_LINUX #include + #include + #include #endif #endif // LD_CORE_PLATFORM_H_ diff --git a/code/core/types.h b/code/core/types.h index 73f2a4d..cf33cd6 100644 --- a/code/core/types.h +++ b/code/core/types.h @@ -29,6 +29,18 @@ struct Str8 { U8 *data; }; +typedef struct V2f V2f; +struct V2f { + F32 x; + F32 y; +}; + +typedef struct V2i V2i; +struct V2i { + U32 x; + U32 y; +}; + #define U8_MAX ((U8) -1) #define U16_MAX ((U16) -1) #define U32_MAX ((U32) -1) diff --git a/code/first.c b/code/first.c index 6b8cd35..549af0b 100644 --- a/code/first.c +++ b/code/first.c @@ -6,23 +6,25 @@ #include #include "core/core.h" +#include "core/types.h" #include "os/core.h" #include "vulkan/core.h" - #include "game/core.h" int main(int argc, char **argv) { (void) argc; (void) argv; - if (!SDL_Init(SDL_INIT_VIDEO)) { + if (!SDL_Init(SDL_INIT_VIDEO)) + { printf("[Error] :: Failed to initialise SDL3 (%s)\n", SDL_GetError()); return 1; } SDL_Window *window = SDL_CreateWindow("Ludum", 1280, 720, SDL_WINDOW_HIGH_PIXEL_DENSITY); - if (!window) { + if (!window) + { printf("[Error] :: Failed to create window (%s)\n", SDL_GetError()); return 1; } @@ -58,12 +60,22 @@ int main(int argc, char **argv) { } bool running = true; - while (running) { + Player player; + player.pos.x = 0; + player.pos.y = 0; + while (running) + { SDL_Event e; - while (SDL_PollEvent(&e)) { - if (e.type == SDL_EVENT_QUIT) { running = false; } + while (SDL_PollEvent(&e)) + { + PlayerUpdate(&e, &player); + if (e.type == SDL_EVENT_QUIT) + { + running = false; + } } + int w, h; SDL_GetWindowSizeInPixels(window, &w, &h); @@ -76,20 +88,20 @@ int main(int argc, char **argv) { clear_colour.color.float32[2] = 0.0f; clear_colour.color.float32[3] = 1.0f; - VkRenderingAttachmentInfo colour_attachment = { 0 }; - colour_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - colour_attachment.imageView = vk.swapchain.views[frame->image]; + VkRenderingAttachmentInfo colour_attachment = {0}; + colour_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + colour_attachment.imageView = vk.swapchain.views[frame->image]; colour_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - colour_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colour_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colour_attachment.clearValue = clear_colour; + colour_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colour_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colour_attachment.clearValue = clear_colour; - VkRenderingInfo rendering_info = { 0 }; - rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; - rendering_info.renderArea = (VkRect2D) { 0, 0, w, h }; - rendering_info.layerCount = 1; + VkRenderingInfo rendering_info = {0}; + rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + rendering_info.renderArea = (VkRect2D){0, 0, w, h}; + rendering_info.layerCount = 1; rendering_info.colorAttachmentCount = 1; - rendering_info.pColorAttachments = &colour_attachment; + rendering_info.pColorAttachments = &colour_attachment; vk.CmdBeginRendering(cmd, &rendering_info); @@ -148,6 +160,7 @@ int main(int argc, char **argv) { vk.CmdEndRendering(cmd); + Vk_FrameEnd(); } diff --git a/code/game/aabb.h b/code/game/aabb.h new file mode 100644 index 0000000..9e78752 --- /dev/null +++ b/code/game/aabb.h @@ -0,0 +1,18 @@ +#if !defined(LD_GAME_AABB_H_) +#define LD_GAME_AABB_H_ + +#include "../core/types.h" +#include "../core/macros.h" + + +typedef struct AABB AABB; +struct AABB { + V2f pos; + V2f size; +}; + + +function bool AABB_Collide(AABB a, AABB b); +function bool AABB_Point(AABB a, V2f v); + +#endif // LD_GAME_AABB_H_ diff --git a/code/game/core.c b/code/game/core.c index 7caf30e..61840c7 100644 --- a/code/game/core.c +++ b/code/game/core.c @@ -1,4 +1,3 @@ - void G_ImagesLoad(G_State *game) { M_TempScope(0, 0) { FS_List assets = FS_PathList(temp.arena, S("assets")); @@ -193,3 +192,7 @@ void G_PipelinesLoad(G_State *game) { Vk_PipelineCreate(basic); } + +#include "impl/aabb.c" +#include "impl/nav.c" +#include "impl/player.c" diff --git a/code/game/core.h b/code/game/core.h index 37df55b..70f4fa6 100644 --- a/code/game/core.h +++ b/code/game/core.h @@ -31,4 +31,10 @@ struct G_State { function void G_ImagesLoad(G_State *game); function void G_PipelinesLoad(G_State *game); +#include "aabb.h" +#include "player.h" +#include "nav.h" +#include "npc.h" +#include "world.h" + #endif // LD_GAME_CORE_H_ diff --git a/code/game/impl/aabb.c b/code/game/impl/aabb.c new file mode 100644 index 0000000..c1d1fba --- /dev/null +++ b/code/game/impl/aabb.c @@ -0,0 +1,15 @@ +#include "game/aabb.h" +#include "core/types.h" + +bool AABB_Collide(AABB a, AABB b) { + bool collision_x = a.pos.x + a.size.x >= b.pos.x && b.pos.x + b.size.x >= a.pos.x; + bool collision_y = a.pos.y + a.size.x >= b.pos.y && b.pos.y + b.size.y >= a.pos.y; + return collision_x && collision_y; +} + +bool AABB_Point(AABB a, V2f v) { + bool collision_x = a.pos.x + a.size.x >= v.x && a.pos.x <= v.x; + bool collision_y = a.pos.x + a.size.y >= v.y && a.pos.y <= v.y; + return collision_x && collision_y; +} + diff --git a/code/game/impl/nav.c b/code/game/impl/nav.c new file mode 100644 index 0000000..96f78ae --- /dev/null +++ b/code/game/impl/nav.c @@ -0,0 +1,101 @@ +#include "game/nav.h" +#include "core/types.h" + +#include + +#define MAX_UNFINISHED 128 + +typedef struct navSearchNodeState navSearchNodeState; +struct navSearchNodeState{ + bool visited; + U64 distance; + U32 shortest; + bool addedToUnvisited; +}; + +typedef struct navSearchState navSearchState; +struct navSearchState{ + navSearchNodeState nodeStates[NAV_MAX_NODES]; +}; + +navSearchState initState(U32 start, U32 meshSize) { + navSearchState state = {}; + for(U32 i = 0; i < meshSize; i++) { + state.nodeStates[i].visited = false; + state.nodeStates[i].addedToUnvisited = false; + // underflow to the max :) + state.nodeStates[i].distance = U64_MAX; + state.nodeStates[i].shortest = 0; + } + state.nodeStates[start].distance = 0; + return state; +} + +U32 getLowestState(U32 unfinishedIndexes[128], U32 unfinishedCount, navSearchState state, U32 *offset) { + U32 lowest = U32_MAX; + U32 lowestI = U32_MAX; + bool startFound = false; + for(U32 i = *offset; i < unfinishedCount; i++) { + navSearchNodeState checkNode = state.nodeStates[unfinishedIndexes[i]]; + if(checkNode.visited) { + if(!startFound) { + *offset = i; + } + continue; + } + startFound = true; + if (lowest > checkNode.distance) { + lowest = cast(U32) checkNode.distance; + lowestI = unfinishedIndexes[i]; + } + } + return lowestI; +} + +// Generate a path to follow between the start and end node. +NavPath Nav_Path(NavMesh mesh, U32 start, U32 end) { + navSearchState state = initState(start, mesh.nodeCount); + U32 unfinishedCount = 1; + U32 unfinishedIndexes[NAV_MAX_NODES] = {start}; + // I don't want to spend time removing items from + // the unfinished nodes, so when checking for a lowest + // if I find the first N items have been checked, I'll mark + // an offset to skip the first N items. + U32 unfinishedOffset = 0; + U32 lowestNodeIndex = start; + bool found = false; + while(!found) { + for(int connectionI = 0 ; connectionI < mesh.nodes[lowestNodeIndex].connectionCount; connectionI++) { + NavConnection connection = mesh.nodes[lowestNodeIndex].connections[connectionI]; + navSearchNodeState *testNode = &state.nodeStates[connection.NodeIndex]; + if(testNode->visited) {continue;} + U32 distance = cast(U32) (state.nodeStates[lowestNodeIndex].distance + connection.Cost); + distance += cast(U32) (mesh.nodes[end].pos.x - mesh.nodes[connection.NodeIndex].pos.x); + distance += cast(U32) (mesh.nodes[end].pos.y - mesh.nodes[connection.NodeIndex].pos.y); + if(testNode->distance > distance) { + testNode->distance = distance; + testNode->shortest = lowestNodeIndex; + } + if(!testNode->addedToUnvisited) { + unfinishedIndexes[unfinishedCount] = connection.NodeIndex; + unfinishedCount++; + testNode->addedToUnvisited = true; + } + } + state.nodeStates[lowestNodeIndex].visited = true; + lowestNodeIndex = getLowestState(unfinishedIndexes, unfinishedCount, state, &unfinishedOffset); + if(lowestNodeIndex == end) { + found = true; + } + } + NavPath res_path = {0}; + U32 index = end; + while(index!=start) { + res_path.indexes[res_path.nodeCount] = index; + res_path.nodeCount++; + index = state.nodeStates[index].shortest; + } + res_path.indexes[res_path.nodeCount] = start; + res_path.nodeCount++; + return res_path; +} diff --git a/code/game/impl/player.c b/code/game/impl/player.c new file mode 100644 index 0000000..4c26667 --- /dev/null +++ b/code/game/impl/player.c @@ -0,0 +1,22 @@ +#include "../player.h" + +void PlayerUpdate(SDL_Event *event, Player *player) +{ + SDL_KeyboardEvent key = event->key; + if (key.key == SDLK_W) + { + player->pos.y += 10; + } + if (key.key == SDLK_A) + { + player->pos.x -= 10; + } + if (key.key == SDLK_D) + { + player->pos.x += 10; + } + if (key.key == SDLK_S) + { + player->pos.y -= 10; + } +} diff --git a/code/game/nav.h b/code/game/nav.h new file mode 100644 index 0000000..b407c26 --- /dev/null +++ b/code/game/nav.h @@ -0,0 +1,40 @@ +#if !defined(LD_GAME_NAV_H_) +#define LD_GAME_NAV_H_ + +#include "core/types.h" +#include "core/macros.h" + +#define NAV_MAX_PATH 1024 +#define NAV_MAX_CONNECTIONS 8 +#define NAV_MAX_NODES 4096 + +typedef struct NavNode NavNode; + +typedef struct NavConnection NavConnection; +struct NavConnection{ + F32 Cost; + U32 NodeIndex; +}; + +struct NavNode { + V2f pos; + U8 connectionCount; + NavConnection connections[NAV_MAX_CONNECTIONS]; +}; + +typedef struct NavPath NavPath; +struct NavPath { + U32 nodeCount; + U32 indexes[NAV_MAX_PATH]; +}; + +typedef struct NavMesh NavMesh; +struct NavMesh{ + U32 nodeCount; + NavNode nodes[NAV_MAX_NODES]; +}; + + +function NavPath Nav_Path(NavMesh mesh, U32 start, U32 end); + +#endif diff --git a/code/game/npc.h b/code/game/npc.h new file mode 100644 index 0000000..a50868a --- /dev/null +++ b/code/game/npc.h @@ -0,0 +1,10 @@ +#if !defined(LD_GAME_NPC_H_) +#define LD_GAME_NPC_H_ +#include "aabb.h" + +typedef struct NPC NPC; +struct NPC { + AABB collision; +}; + +#endif // LD_GAME_NPC_H_ diff --git a/code/game/player.h b/code/game/player.h new file mode 100644 index 0000000..8067026 --- /dev/null +++ b/code/game/player.h @@ -0,0 +1,17 @@ +#if !defined(LD_GAME_PLAYER_H_) +#define LD_GAME_PLAYER_H_ + +#include "../core/types.h" +#include "../core/macros.h" + +#include + +typedef struct Player Player; +struct Player +{ + V2f pos; +}; + +function void PlayerUpdate(SDL_Event *event, Player *player); + +#endif // LD_GAME_PLAYER_H_ diff --git a/code/game/world.h b/code/game/world.h new file mode 100644 index 0000000..7b75092 --- /dev/null +++ b/code/game/world.h @@ -0,0 +1,10 @@ +#if !defined(LD_GAME_WORLD_H_) +#define LD_GAME_WORLD_H_ + +typedef struct World World; +struct World { + NPC npcs[128]; + U32 npcCount; +}; + +#endif // LD_GAME_WORLD_H_ diff --git a/code/os/core.c b/code/os/core.c index 15ce626..dd739ff 100644 --- a/code/os/core.c +++ b/code/os/core.c @@ -1,5 +1,7 @@ #if OS_WINDOWS #include "impl/windows/core.c" +#elif OS_LINUX + #include "impl/linux/core.c" #endif diff --git a/code/os/impl/linux/core.c b/code/os/impl/linux/core.c new file mode 100644 index 0000000..1dd1cd9 --- /dev/null +++ b/code/os/impl/linux/core.c @@ -0,0 +1,31 @@ +U64 VM_PageSize() { + U64 result = getpagesize(); + return result; +} + +U64 VM_AllocationGranularity() { + U64 result = getpagesize(); + return result; +} + +void *VM_Reserve(U64 size) { + void *addr = mmap(0, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); + + void *result = (addr == MAP_FAILED) ? 0 : addr; + return result; +} + +B32 VM_Commit(void *base, U64 size) { + B32 result = mprotect(base, size, PROT_READ | PROT_WRITE) == 0; + return result; +} + +void VM_Decommit(void *base, U64 size) { + mprotect(base, size, PROT_NONE); +} + +void VM_Release(void *base, U64 size) { + munmap(base, size); +} + + diff --git a/linux b/linux index b44be73..2930c89 100755 --- a/linux +++ b/linux @@ -37,7 +37,7 @@ fi echo "[Building source]" -COMPILER_OPTS="-Wall -Wno-missing-braces -I'deps/stb'" +COMPILER_OPTS="-Wall -Wno-missing-braces -Wno-unused-function -I'deps/stb'" LINKER_OPTS="-lSDL3" if [[ $release == 1 ]] diff --git a/windows.bat b/windows.bat index e1b3c00..2e578cf 100644 --- a/windows.bat +++ b/windows.bat @@ -67,7 +67,7 @@ glslangValidator -o "assets\shaders\basic.frag.spv" --target-env vulkan1.3 "..\c ECHO [Building source] -SET COMPILER_OPTS=-nologo -W4 -I"deps\SDL3\include" -I"deps\stb" -I"%VULKAN_SDK%\Include" +SET COMPILER_OPTS=-nologo -W4 -I"deps\SDL3\include" -I"deps\stb" -I"%VULKAN_SDK%\Include" -I"..\code" SET LINKER_OPTS=-LIBPATH:"deps\SDL3\lib" SDL3.lib IF %release% equ 1 (