// voxelThing.c // A voxel chunk rendering doodad written in C with Raylib // External libraries #include "raylib.h" #include "raymath.h" #include "rlgl.h" #include "stdio.h" // Project files #include "chunkStructures.h" #include "chunkRenderer.h" #include "blockTypes.h" #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)"; if (yaw < 67.5f) return "Southwest (+X+Z)"; if (yaw < 112.5f) return "West (+X)"; if (yaw < 157.5f) return "Northwest (+X-Z)"; if (yaw < 202.5f) return "North (-Z)"; if (yaw < 247.5f) return "Northeast (-X-Z)"; if (yaw < 292.5f) return "East (-X)"; 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 --- 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 --- Chunk chunk; GenerateFlatChunk(&chunk); PlaceTreeAt(&chunk, 8, 7, 8); Mesh chunkMesh = GenerateChunkMesh(&chunk); // --- Load textures and materials --- Texture2D atlas = LoadTexture("assets/TextureAtlas.png"); Material mat = LoadMaterialDefault(); mat.maps[MATERIAL_MAP_DIFFUSE].texture = atlas; // --- Initialize camera --- Camera3D camera = { 0 }; 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 = 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); // --- 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); } // --- Handle pausing --- if (IsKeyPressed(KEY_ESCAPE)) { paused = !paused; if (paused) EnableCursor(); else DisableCursor(); } // --- Handle selecting blocks --- 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; if (!paused) { // --- 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) }; 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(); // TODO: Shoudn't all this block handling be handled in playerController.c? // --- Handle block removal (left click) --- if (hit.hit && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { chunk.blocks[(int)hit.position.x][(int)hit.position.y][(int)hit.position.z].type = BLOCK_AIR; chunkMesh = GenerateChunkMesh(&chunk); // Rebuild mesh after change } // --- Handle block placement (right click) --- if (hit.hit && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { int px = (int)(hit.position.x + hit.normal.x); int py = (int)(hit.position.y + hit.normal.y); int pz = (int)(hit.position.z + hit.normal.z); // Bounds check if (px >= 0 && px < CHUNK_SIZE_X && py >= 0 && py < CHUNK_SIZE_Y && pz >= 0 && pz < CHUNK_SIZE_Z) { 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", hit.position.x, hit.position.y, hit.position.z, hit.normal.x, hit.normal.y, hit.normal.z, px, py, pz); } } } // --- Cleanup --- UnloadTexture(atlas); UnloadMesh(chunkMesh); UnloadMaterial(mat); CloseWindow(); return 0; }