#pragma once #include <vector> #include <unordered_map> #include <memory> #include <queue> #include <mutex> #include <condition_variable> #include <thread> #include <algorithm> #include <random> #include <GL/glew.h> #include <glm/glm.hpp> // world dimensions static constexpr int CHUNK_SIZE = 16; static constexpr int CHUNK_HEIGHT = 64; struct ChunkKey { int x,z; bool operator==(ChunkKey const& o) const { return x==o.x && z==o.z; } }; struct ChunkKeyHash { std::size_t operator()(ChunkKey const& k) const noexcept { return std::hash<long long>()(((long long)k.x<<32)|(unsigned)k.z); } }; struct Chunk { int x,z; // voxels[x][y][z], y ∈ [0,CHUNK_HEIGHT) std::vector<std::vector<std::vector<int>>> voxels; }; struct ChunkMesh { GLuint vao,vbo,ibo; GLsizei indexCount; }; struct Vertex { glm::vec3 pos; glm::vec3 normal; glm::vec2 uv; }; struct RawMesh { ChunkKey key; std::vector<Vertex> verts; std::vector<uint32_t> indices; }; class VoxelGame { public: VoxelGame(); ~VoxelGame(); // initialize with screen size (for cascades) bool init(int screenW,int screenH); void update(float dt, glm::vec3 const& camPos); void render(glm::mat4 const& view, glm::mat4 const& proj); void debugUI(); private: // chunk generation Chunk generateChunk(int cx,int cz); void updateChunks(glm::vec3 const& camPos); // CPU meshing void workerLoop(); void greedyMesh(Chunk const&, std::vector<Vertex>&, std::vector<uint32_t>&); void destroyMesh(ChunkMesh*); std::unordered_map<ChunkKey,Chunk,ChunkKeyHash> chunks; std::unordered_map<ChunkKey,std::unique_ptr<ChunkMesh>,ChunkKeyHash> meshes; std::mutex queueMutex; std::condition_variable queueCV; std::queue<ChunkKey> toBuild; std::queue<RawMesh> readyToUpload; bool stopWorkers=false; std::vector<std::thread> workers; // stats int totalChunksLoaded=0, totalChunksEverLoaded=0; int chunkGenCount=0; double totalChunkGenTime=0, lastChunkGenTime=0; float fps=0, lastDeltaTime=0; double frameTimeSum=0; int frameCount=0; int lastFaceCount=0, lastGLCalls=0; // textures GLuint textureDirt, textureGrass, textureWood; // cascaded shadow maps static constexpr int NUM_CASCADES = 3; static constexpr int SHADOW_MAP_SIZE = 1024; GLuint depthFBO[NUM_CASCADES]; GLuint depthTex[NUM_CASCADES]; float cascadeSplits[NUM_CASCADES]; glm::mat4 lightSpaceMat[NUM_CASCADES]; // shaders GLuint depthProg, mainProg; // light & camera glm::vec3 lightDir = glm::normalize(glm::vec3(1.0f, -1.0f, 0.5f)); glm::vec3 cameraPos; int screenW, screenH; int renderDistance = 2; bool loadTextures(); bool loadShaders(); void initShadowMaps(); void renderScene(GLuint prog); };