diff --git a/README.md b/README.md index 3b06880..edb8c1d 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,15 @@ Implements a voxel chunk renderer. Chunks are stored as a 3D array of block stru Code is rough and messy and not much is implemented yet. +Controls: + - WASD - move + - Mouse - look + - LMB - Remove block + - RMB - Place Block + - ESC - Pause + - F11 - Fullscreen + - 1-8 - Select Block + Depends on Raylib. ## Build Instructions: @@ -12,4 +21,5 @@ Depends on Raylib. 1) Install Raylib. 2) Clone the repo. 3) Build by running make. +4) run bin/voxelThing diff --git a/assets/TextureAtlas.png b/assets/TextureAtlas.png index 50cca6d..e90c0ac 100644 Binary files a/assets/TextureAtlas.png and b/assets/TextureAtlas.png differ diff --git a/assets/TextureAtlas.xcf b/assets/TextureAtlas.xcf index a416b97..a5ed02c 100644 Binary files a/assets/TextureAtlas.xcf and b/assets/TextureAtlas.xcf differ diff --git a/include/atlasDefinitions.h b/include/atlasDefinitions.h index a2ba30b..ebedf99 100644 --- a/include/atlasDefinitions.h +++ b/include/atlasDefinitions.h @@ -14,5 +14,6 @@ #define TILE_LOG_TOP 6 #define TILE_LOG_SIDE 7 #define TILE_LEAF 8 +#define TILE_PLANK 9 #endif diff --git a/include/blockTypes.h b/include/blockTypes.h index 36eb485..44bf5cf 100644 --- a/include/blockTypes.h +++ b/include/blockTypes.h @@ -11,6 +11,7 @@ #define BLOCK_GRAVEL 5 // 5 #define BLOCK_LOG 6 // top 6, sides 7, bottom 6 #define BLOCK_LEAF 7 // 8 +#define BLOCK_PLANK 8 // 9 typedef struct { int id; diff --git a/include/chunkStructures.h b/include/chunkStructures.h index c4d4a37..1918df2 100644 --- a/include/chunkStructures.h +++ b/include/chunkStructures.h @@ -6,7 +6,7 @@ #include "raylib.h" #define CHUNK_SIZE_X 16 -#define CHUNK_SIZE_Y 16 +#define CHUNK_SIZE_Y 256 #define CHUNK_SIZE_Z 16 diff --git a/source/blockTypes.c b/source/blockTypes.c index fa9ce4f..862e04e 100644 --- a/source/blockTypes.c +++ b/source/blockTypes.c @@ -37,16 +37,9 @@ static const BlockType blockTable[] = { .faceTiles = { TILE_SAND, TILE_SAND, TILE_SAND, TILE_SAND, TILE_SAND, TILE_SAND } }, [BLOCK_GRAVEL] = { - .id = BLOCK_GRASS, - .name = "Grass", - .faceTiles = { - TILE_GRASS_SIDE, // -X - TILE_GRASS_SIDE, // +X - TILE_DIRT, // -Y - TILE_GRASS_TOP, // +Y - TILE_GRASS_SIDE, // -Z - TILE_GRASS_SIDE // +Z - } + .id = BLOCK_GRAVEL, + .name = "Gravel", + .faceTiles = { TILE_GRAVEL, TILE_GRAVEL, TILE_GRAVEL, TILE_GRAVEL, TILE_GRAVEL, TILE_GRAVEL } }, [BLOCK_LOG] = { .id = BLOCK_LOG, @@ -65,6 +58,11 @@ static const BlockType blockTable[] = { .name = "Leaves", .faceTiles = { TILE_LEAF, TILE_LEAF, TILE_LEAF, TILE_LEAF, TILE_LEAF, TILE_LEAF } }, + [BLOCK_PLANK] = { + .id = BLOCK_PLANK, + .name = "Planks", + .faceTiles = { TILE_PLANK, TILE_PLANK, TILE_PLANK, TILE_PLANK, TILE_PLANK, TILE_PLANK } + }, }; const BlockType *GetBlockType(int blockID) { diff --git a/source/voxelThing.c b/source/voxelThing.c index 9af4648..95f04fb 100644 --- a/source/voxelThing.c +++ b/source/voxelThing.c @@ -14,9 +14,14 @@ #include "playerController.h" #include "chunkGenerator.h" +// Bunch of global variables to clean up later. float cameraYaw = 0; float cameraPitch = 0; +bool paused = false; +RaycastHit hit; +int blockSelection = BLOCK_SAND; +// Random helper function for returning what direction you're facing.' const char* GetCompassDirection(float yaw) { yaw = fmodf(yaw * RAD2DEG + 360.0f, 360.0f); if (yaw < 22.5f || yaw >= 337.5f) return "South (+Z)"; @@ -29,11 +34,18 @@ const char* GetCompassDirection(float yaw) { return "Southeast (-X+Z)"; } +// Another random helper function, this time for checking if a Vector3 is non-zero. +static inline bool Vector3IsNonZero(Vector3 v) { + return v.x != 0.0f || v.y != 0.0f || v.z != 0.0f; +} + int main(void) { // --- Screen setup --- - const int screenWidth = 800; - const int screenHeight = 600; + int screenWidth = 800; + int screenHeight = 600; + SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(screenWidth, screenHeight, "VoxelThing"); + SetExitKey(-1); DisableCursor(); // Lock mouse to window for FPS-style camera // --- World generation --- @@ -52,68 +64,97 @@ int main(void) { camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Initial camera position camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Looking toward origin camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Y-up world - camera.fovy = 45.0f; // Field of view + camera.fovy = 90.0f; // Field of view camera.projection = CAMERA_PERSPECTIVE; // Use perspective projection SetTargetFPS(60); // --- Main game loop --- while (!WindowShouldClose()) { + screenWidth = GetScreenWidth(); + screenHeight = GetScreenHeight(); BeginDrawing(); ClearBackground(RAYWHITE); - // --- Update camera and direction --- - - UpdateFreeCamera(&camera, 10.0f, &cameraYaw, &cameraPitch); // Move camera with user input - - - // --- Raycasting from screen center --- - Vector2 screenCenter = { screenWidth / 2.0f, screenHeight / 2.0f }; - // This is where we grab the ray... - // Ray ray = GetMouseRay(screenCenter, camera); - Vector3 camDir = { - cosf(cameraPitch) * sinf(cameraYaw), - sinf(cameraPitch), - cosf(cameraPitch) * cosf(cameraYaw) - }; - - Ray ray = { - .position = camera.position, - .direction = Vector3Normalize(camDir) - }; - RaycastHit hit = RaycastChunk(&chunk, ray.position, ray.direction, 10.0f); - - // --- Begin 3D rendering --- - BeginMode3D(camera); - - DrawMesh(chunkMesh, mat, MatrixIdentity()); - - if (hit.hit) { - // Draw a wireframe cube where the ray hit - DrawCubeWires(Vector3Add(hit.position, (Vector3){0.5f, 0.5f, 0.5f}), 1.02f, 1.02f, 1.02f, RED); - DrawFaceHighlight(hit.position, hit.normal); // Highlight the specific face hit + // --- Handle jumping to fullscreen mode with F11 --- + if (IsKeyPressed(KEY_F11)) { + ToggleFullscreen(); + Vector2 center = { GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f }; + SetMousePosition(center.x, center.y); } - // Draw debug ray. - camDir = Vector3Normalize(Vector3Subtract(camera.target, camera.position)); - Vector3 rayEnd = Vector3Add(camera.position, Vector3Scale(camDir, 10.0f)); - DrawLine3D(camera.position, rayEnd, PURPLE); - Vector3 hitPoint = Vector3Add(ray.position, Vector3Scale(ray.direction, hit.t)); - DrawCubeWires(hitPoint, 0.05f, 0.05f, 0.05f, RED); + // --- Handle pausing --- + if (IsKeyPressed(KEY_ESCAPE)) { + paused = !paused; + if (paused) EnableCursor(); + else DisableCursor(); + } - EndMode3D(); + // --- Handle selecting blocks --- - Vector3 testPoint = Vector3Add(camera.position, Vector3Scale(camDir, 2.0f)); - Vector2 projected = GetWorldToScreen(testPoint, camera); - DrawCircleV(projected, 4, RED); + if (IsKeyPressed(KEY_ONE)) blockSelection = BLOCK_STONE; + if (IsKeyPressed(KEY_TWO)) blockSelection = BLOCK_DIRT; + if (IsKeyPressed(KEY_THREE)) blockSelection = BLOCK_GRASS; + if (IsKeyPressed(KEY_FOUR)) blockSelection = BLOCK_SAND; + if (IsKeyPressed(KEY_FIVE)) blockSelection = BLOCK_GRAVEL; + if (IsKeyPressed(KEY_SIX)) blockSelection = BLOCK_LOG; + if (IsKeyPressed(KEY_SEVEN)) blockSelection = BLOCK_LEAF; + if (IsKeyPressed(KEY_EIGHT)) blockSelection = BLOCK_PLANK; - // --- Draw crosshair in screen center --- - DrawLine(screenWidth/2 - 5, screenHeight/2, screenWidth/2 + 5, screenHeight/2, DARKGRAY); - DrawLine(screenWidth/2, screenHeight/2 - 5, screenWidth/2, screenHeight/2 + 5, DARKGRAY); + if (!paused) { + // --- Update camera and direction --- - // -- Draw debug info --- - DrawText(TextFormat("Facing: %s", GetCompassDirection(cameraYaw)), 10, 10, 20, DARKGRAY); - DrawText(TextFormat("Yaw: %.1f° Pitch: %.1f°", cameraYaw * RAD2DEG, cameraPitch * RAD2DEG), 10, 30, 20, GRAY); + UpdateFreeCamera(&camera, 10.0f, &cameraYaw, &cameraPitch); // Move camera with user input + + + // --- Raycasting from screen center --- + Vector2 screenCenter = { screenWidth / 2.0f, screenHeight / 2.0f }; + // This is where we grab the ray... + // Ray ray = GetMouseRay(screenCenter, camera); + Vector3 camDir = { + cosf(cameraPitch) * sinf(cameraYaw), + sinf(cameraPitch), + cosf(cameraPitch) * cosf(cameraYaw) + }; + + Ray ray = { + .position = camera.position, + .direction = Vector3Normalize(camDir) + }; + hit = RaycastChunk(&chunk, ray.position, ray.direction, 10.0f); + + // --- Begin 3D rendering --- + BeginMode3D(camera); + + DrawMesh(chunkMesh, mat, MatrixIdentity()); + + if (hit.hit) { + // Draw a wireframe cube where the ray hit + DrawCubeWires(Vector3Add(hit.position, (Vector3){0.5f, 0.5f, 0.5f}), 1.02f, 1.02f, 1.02f, BLACK); + //DrawFaceHighlight(hit.position, hit.normal); // Highlight the specific face hit + } + + // Draw a lil debug cube where the player is looking. + //Vector3 hitPoint = Vector3Add(ray.position, Vector3Scale(ray.direction, hit.t)); + //DrawCubeWires(hitPoint, 0.05f, 0.05f, 0.05f, RED); + + EndMode3D(); + + // --- Draw crosshair in screen center --- + DrawLine(screenWidth/2 - 5, screenHeight/2, screenWidth/2 + 5, screenHeight/2, DARKGRAY); + DrawLine(screenWidth/2, screenHeight/2 - 5, screenWidth/2, screenHeight/2 + 5, DARKGRAY); + + // -- Draw debug info --- + DrawText(TextFormat("Facing: %s", GetCompassDirection(cameraYaw)), 10, 10, 20, DARKGRAY); + DrawText(TextFormat("Yaw: %.1f° Pitch: %.1f°", cameraYaw * RAD2DEG, cameraPitch * RAD2DEG), 10, 30, 20, GRAY); + } + + // --- Draw pause menu if paused --- + if (paused) { + DrawRectangle(0, 0, screenWidth, screenHeight, Fade(DARKGRAY, 0.5f)); + DrawText("Paused", screenWidth / 2 - MeasureText("Paused", 40) / 2, screenHeight / 2 - 20, 40, RAYWHITE); + DrawText("Press ESC to resume", screenWidth / 2 - MeasureText("Press ESC to resume", 20) / 2, screenHeight / 2 + 30, 20, LIGHTGRAY); + } EndDrawing(); @@ -136,7 +177,7 @@ int main(void) { py >= 0 && py < CHUNK_SIZE_Y && pz >= 0 && pz < CHUNK_SIZE_Z) { - chunk.blocks[px][py][pz].type = BLOCK_SAND; + chunk.blocks[px][py][pz].type = blockSelection; chunkMesh = GenerateChunkMesh(&chunk); printf("Hit at (%f %f %f), normal (%f %f %f), placing at (%d %d %d)\n",