From 7bf191992decda5b7fb0cc0579f5d32c0a866532 Mon Sep 17 00:00:00 2001 From: Dawid Krystian Czaplicki <100421659+dawc17@users.noreply.github.com> Date: Wed, 11 Feb 2026 10:58:40 +0100 Subject: [PATCH 1/4] add frustum culling and chunk prioritization Introduce view frustum culling and chunk load/mesh prioritization to reduce wasted GPU work and smooth streaming. Adds a Frustum class (rendering/Frustum.h/.cpp) and integrates it into Renderer::renderChunks and renderWater to cull chunk AABBs, plus frustum counters in MainGlobals and DebugUI. Updates CMake to include the new source. In main.cpp, precompute sorted load offsets, cache load radius, add adaptive enqueue limits based on pending jobs, batch/enqueue chunk loads and meshes with distance-based prioritization, and limit per-frame enqueues to avoid overloading the job system. --- src/CMakeLists.txt | 1 + src/core/MainGlobals.cpp | 6 +++ src/core/MainGlobals.h | 6 +++ src/core/Renderer.cpp | 87 +++++++++++++++++++++++-------- src/core/main.cpp | 107 +++++++++++++++++++++++++++++++------- src/rendering/Frustum.cpp | 53 +++++++++++++++++++ src/rendering/Frustum.h | 18 +++++++ src/ui/DebugUI.cpp | 2 + 8 files changed, 239 insertions(+), 41 deletions(-) create mode 100644 src/rendering/Frustum.cpp create mode 100644 src/rendering/Frustum.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4fa1b2e..3bc7485 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,7 @@ set(SOURCES ui/HUD.cpp core/WorldSession.cpp core/Renderer.cpp + rendering/Frustum.cpp ) add_executable(VoxelEngine ${SOURCES}) diff --git a/src/core/MainGlobals.cpp b/src/core/MainGlobals.cpp index 29a873c..7c3499c 100644 --- a/src/core/MainGlobals.cpp +++ b/src/core/MainGlobals.cpp @@ -74,6 +74,12 @@ bool enableWaterSimulation = true; int waterTickRate = 5; float waterTickAccumulator = 0.0f; const float WATER_TICK_INTERVAL = 0.05f; +int frustumSolidTested = 0; +int frustumSolidCulled = 0; +int frustumSolidDrawn = 0; +int frustumWaterTested = 0; +int frustumWaterCulled = 0; +int frustumWaterDrawn = 0; Player* g_player = nullptr; ChunkManager* g_chunkManager = nullptr; diff --git a/src/core/MainGlobals.h b/src/core/MainGlobals.h index 6dc6bd8..8de167b 100644 --- a/src/core/MainGlobals.h +++ b/src/core/MainGlobals.h @@ -64,6 +64,12 @@ extern bool enableWaterSimulation; extern int waterTickRate; extern float waterTickAccumulator; extern const float WATER_TICK_INTERVAL; +extern int frustumSolidTested; +extern int frustumSolidCulled; +extern int frustumSolidDrawn; +extern int frustumWaterTested; +extern int frustumWaterCulled; +extern int frustumWaterDrawn; extern Player* g_player; extern ChunkManager* g_chunkManager; diff --git a/src/core/Renderer.cpp b/src/core/Renderer.cpp index d1fd3f7..de66cf5 100644 --- a/src/core/Renderer.cpp +++ b/src/core/Renderer.cpp @@ -6,8 +6,10 @@ #include "../rendering/Meshing.h" #include "../rendering/ItemModelGenerator.h" #include "../rendering/ToolModelGenerator.h" +#include "../rendering/Frustum.h" #include "../thirdparty/stb_image.h" #include "../world/Chunk.h" +#include "glm/ext/matrix_transform.hpp" #include #include #include @@ -229,6 +231,13 @@ void Renderer::cleanup() void Renderer::beginFrame(const FrameParams& fp) { + frustumSolidTested = 0; + frustumSolidCulled = 0; + frustumSolidDrawn = 0; + frustumWaterTested = 0; + frustumWaterCulled = 0; + frustumWaterDrawn = 0; + shaderProgram->Activate(); glBindTexture(GL_TEXTURE_2D_ARRAY, textureArray); @@ -251,22 +260,39 @@ void Renderer::beginFrame(const FrameParams& fp) void Renderer::renderChunks(const FrameParams& fp, ChunkManager& cm) { + const glm::mat4 viewProj = fp.proj * fp.view; + const Frustum frustum = Frustum::fromMatrix(viewProj); + const float chunkSizeF = static_cast(CHUNK_SIZE); + for (auto& pair : cm.chunks) { Chunk* chunk = pair.second.get(); - if (chunk->indexCount > 0) + if (chunk->indexCount == 0) + continue; + frustumSolidTested++; + + const glm::vec3 chunkMin = glm::vec3(chunk->position) * chunkSizeF; + const glm::vec3 chunkMax = chunkMin + glm::vec3(chunkSizeF); + + if (!frustum.intersectsAABB(chunkMin, chunkMax)) { - glm::mat4 chunkModel = glm::translate(glm::mat4(1.0f), - glm::vec3(chunk->position.x * CHUNK_SIZE, - chunk->position.y * CHUNK_SIZE, - chunk->position.z * CHUNK_SIZE)); - glm::mat4 chunkMVP = fp.proj * fp.view * chunkModel; - glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(chunkMVP)); - glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(chunkModel)); - - glBindVertexArray(chunk->vao); - glDrawElements(GL_TRIANGLES, chunk->indexCount, GL_UNSIGNED_INT, 0); + frustumSolidCulled++; + continue; } + + glm::mat4 chunkModel = glm::translate( + glm::mat4(1.0f), + glm::vec3(chunk->position.x * CHUNK_SIZE, + chunk->position.y * CHUNK_SIZE, + chunk->position.z * CHUNK_SIZE)); + glm::mat4 chunkMVP = viewProj * chunkModel; + + glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(chunkMVP)); + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(chunkModel)); + + glBindVertexArray(chunk->vao); + glDrawElements(GL_TRIANGLES, chunk->indexCount, GL_UNSIGNED_INT, 0); + frustumSolidDrawn++; } } @@ -288,22 +314,39 @@ void Renderer::renderWater(const FrameParams& fp, ChunkManager& cm) glUniform1f(waterAmbientLightLoc, fp.ambientLight); glUniform1i(waterEnableCausticsLoc, enableCaustics ? 1 : 0); + const glm::mat4 viewProj = fp.proj * fp.view; + const Frustum frustum = Frustum::fromMatrix(viewProj); + const float chunkSizeF = static_cast(CHUNK_SIZE); + for (auto& pair : cm.chunks) { Chunk* chunk = pair.second.get(); - if (chunk->waterIndexCount > 0) + if (chunk->waterIndexCount == 0) + continue; + frustumWaterTested++; + + const glm::vec3 chunkMin = glm::vec3(chunk->position) * chunkSizeF; + const glm::vec3 chunkMax = chunkMin + glm::vec3(chunkSizeF); + + if (!frustum.intersectsAABB(chunkMin, chunkMax)) { - glm::mat4 chunkModel = glm::translate(glm::mat4(1.0f), - glm::vec3(chunk->position.x * CHUNK_SIZE, - chunk->position.y * CHUNK_SIZE, - chunk->position.z * CHUNK_SIZE)); - glm::mat4 chunkMVP = fp.proj * fp.view * chunkModel; - glUniformMatrix4fv(waterTransformLoc, 1, GL_FALSE, glm::value_ptr(chunkMVP)); - glUniformMatrix4fv(waterModelLoc, 1, GL_FALSE, glm::value_ptr(chunkModel)); - - glBindVertexArray(chunk->waterVao); - glDrawElements(GL_TRIANGLES, chunk->waterIndexCount, GL_UNSIGNED_INT, 0); + frustumWaterCulled++; + continue; } + + glm::mat4 chunkModel = glm::translate( + glm::mat4(1.0f), + glm::vec3(chunk->position.x * CHUNK_SIZE, + chunk->position.y * CHUNK_SIZE, + chunk->position.z * CHUNK_SIZE)); + glm::mat4 chunkMVP = viewProj * chunkModel; + + glUniformMatrix4fv(waterTransformLoc, 1, GL_FALSE, glm::value_ptr(chunkMVP)); + glUniformMatrix4fv(waterModelLoc, 1, GL_FALSE, glm::value_ptr(chunkModel)); + + glBindVertexArray(chunk->waterVao); + glDrawElements(GL_TRIANGLES, chunk->waterIndexCount, GL_UNSIGNED_INT, 0); + frustumWaterDrawn++; } glDepthMask(GL_TRUE); diff --git a/src/core/main.cpp b/src/core/main.cpp index 55b94f9..49bef50 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -131,6 +131,8 @@ int main(int argc, char* argv[]) auto& waterSimulator = session.waterSimulator; auto& regionManager = session.regionManager; auto& selectedBlock = session.selectedBlock; + std::vector loadOffsets; + int cachedLoadRadius = -1; while (!glfwWindowShouldClose(window)) { @@ -384,27 +386,73 @@ int main(int argc, char* argv[]) const int UNLOAD_RADIUS = LOAD_RADIUS + 2; const int CHUNK_HEIGHT_MIN = 0; const int CHUNK_HEIGHT_MAX = (256 / CHUNK_SIZE) - 1; - - if (currentState == GameState::Playing) + if (cachedLoadRadius != LOAD_RADIUS) { + loadOffsets.clear(); for (int dx = -LOAD_RADIUS; dx <= LOAD_RADIUS; dx++) { for (int dz = -LOAD_RADIUS; dz <= LOAD_RADIUS; dz++) { - for (int cy = CHUNK_HEIGHT_MIN; cy <= CHUNK_HEIGHT_MAX; cy++) + loadOffsets.push_back({dx, dz}); + } + } + std::sort(loadOffsets.begin(), loadOffsets.end(), + [](const glm::ivec2& a, const glm::ivec2& b) { - int chunkX = cx + dx; - int chunkZ = cz + dz; + int da = a.x * a.x + a.y * a.y; + int db = b.x * b.x + b.y * b.y; + return da < db; + }); + cachedLoadRadius = LOAD_RADIUS; + } + size_t pendingJobs = jobSystem ? jobSystem->pendingJobCount() : 0; + int maxLoadEnqueuePerFrame = 32; + int maxMeshEnqueuePerFrame = 16; + if (pendingJobs > 200) + { + maxLoadEnqueuePerFrame = 4; + maxMeshEnqueuePerFrame = 0; + } + else if (pendingJobs > 140) + { + maxLoadEnqueuePerFrame = 8; + maxMeshEnqueuePerFrame = 2; + } + else if (pendingJobs > 90) + { + maxLoadEnqueuePerFrame = 12; + maxMeshEnqueuePerFrame = 4; + } + else if (pendingJobs > 50) + { + maxLoadEnqueuePerFrame = 20; + maxMeshEnqueuePerFrame = 8; + } - if (!chunkManager->hasChunk(chunkX, cy, chunkZ) && - !chunkManager->isLoading(chunkX, cy, chunkZ) && - !chunkManager->isSaving(chunkX, cy, chunkZ)) + if (currentState == GameState::Playing) + { + int enqueuedLoads = 0; + for (const glm::ivec2& offset : loadOffsets) + { + if (useAsyncLoading && enqueuedLoads >= maxLoadEnqueuePerFrame) + break; + int chunkX = cx + offset.x; + int chunkZ = cz + offset.y; + for (int cy = CHUNK_HEIGHT_MIN; cy <= CHUNK_HEIGHT_MAX; cy++) + { + if (useAsyncLoading && enqueuedLoads >= maxLoadEnqueuePerFrame) + break; + if (!chunkManager->hasChunk(chunkX, cy, chunkZ) && + !chunkManager->isLoading(chunkX, cy, chunkZ) && + !chunkManager->isSaving(chunkX, cy, chunkZ)) + { + if (useAsyncLoading) { - if (useAsyncLoading) - chunkManager->enqueueLoadChunk(chunkX, cy, chunkZ); - else - chunkManager->loadChunk(chunkX, cy, chunkZ); + chunkManager->enqueueLoadChunk(chunkX, cy, chunkZ); + enqueuedLoads++; } + else + chunkManager->loadChunk(chunkX, cy, chunkZ); } } } @@ -430,6 +478,8 @@ int main(int argc, char* argv[]) } } + std::vector> meshCandidates; + meshCandidates.reserve(chunkManager->chunks.size()); for (auto& pair : chunkManager->chunks) { Chunk* chunk = pair.second.get(); @@ -456,16 +506,35 @@ int main(int argc, char* argv[]) if (neighborsReady) { - if (useAsyncLoading) - chunkManager->enqueueMeshChunk(chunk->position.x, chunk->position.y, chunk->position.z); - else - { - buildChunkMesh(*chunk, *chunkManager); - chunk->dirtyMesh = false; - } + int dx = chunk->position.x - cx; + int dz = chunk->position.z - cz; + int dist2 = dx * dx + dz * dz; + meshCandidates.push_back({dist2, chunk}); } } } + std::sort(meshCandidates.begin(), meshCandidates.end(), + [](const std::pair& a, const std::pair& b) + { + return a.first < b.first; + }); + int enqueuedMeshes = 0; + for (const auto& candidate : meshCandidates) + { + Chunk* chunk = candidate.second; + if (useAsyncLoading) + { + if (enqueuedMeshes >= maxMeshEnqueuePerFrame) + break; + chunkManager->enqueueMeshChunk(chunk->position.x, chunk->position.y, chunk->position.z); + enqueuedMeshes++; + } + else + { + buildChunkMesh(*chunk, *chunkManager); + chunk->dirtyMesh = false; + } + } renderer.renderChunks(fp, *chunkManager); renderer.renderWater(fp, *chunkManager); diff --git a/src/rendering/Frustum.cpp b/src/rendering/Frustum.cpp new file mode 100644 index 0000000..29d293b --- /dev/null +++ b/src/rendering/Frustum.cpp @@ -0,0 +1,53 @@ +#include "Frustum.h" + +#include +#include + +Frustum Frustum::fromMatrix(const glm::mat4& m) +{ + Frustum f; + + const std::array rawPlanes = { + glm::vec4(m[0][3] + m[0][0], m[1][3] + m[1][0], m[2][3] + m[2][0], m[3][3] + m[3][0]), + glm::vec4(m[0][3] - m[0][0], m[1][3] - m[1][0], m[2][3] - m[2][0], m[3][3] - m[3][0]), + glm::vec4(m[0][3] + m[0][1], m[1][3] + m[1][1], m[2][3] + m[2][1], m[3][3] + m[3][1]), + glm::vec4(m[0][3] - m[0][1], m[1][3] - m[1][1], m[2][3] - m[2][1], m[3][3] - m[3][1]), + glm::vec4(m[0][3] + m[0][2], m[1][3] + m[1][2], m[2][3] + m[2][2], m[3][3] + m[3][2]), + glm::vec4(m[0][3] - m[0][2], m[1][3] - m[1][2], m[2][3] - m[2][2], m[3][3] - m[3][2]), + }; + + for (size_t i = 0; i < rawPlanes.size(); i++) + { + glm::vec3 n(rawPlanes[i].x, rawPlanes[i].y, rawPlanes[i].z); + float len = glm::length(n); + if (len > 0.0f) + { + f.planes[i].normal = n / len; + f.planes[i].distance = rawPlanes[i].w / len; + } + else + { + f.planes[i].normal = glm::vec3(0.0f, 0.0f, 0.0f); + f.planes[i].distance = 0.0f; + } + } + + return f; +} + +bool Frustum::intersectsAABB(const glm::vec3& minPoint, const glm::vec3& maxPoint) const +{ + for (const Plane& plane : planes) + { + glm::vec3 p = minPoint; + + if (plane.normal.x >= 0.0f) p.x = maxPoint.x; + if (plane.normal.y >= 0.0f) p.y = maxPoint.y; + if (plane.normal.z >= 0.0f) p.z = maxPoint.z; + + if (glm::dot(plane.normal, p) + plane.distance < 0.0f) + return false; + } + + return true; +} \ No newline at end of file diff --git a/src/rendering/Frustum.h b/src/rendering/Frustum.h new file mode 100644 index 0000000..b26d90f --- /dev/null +++ b/src/rendering/Frustum.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +struct Plane +{ + glm::vec3 normal; + float distance; +}; + +struct Frustum +{ + std::array planes; + + static Frustum fromMatrix(const glm::mat4& viewProjection); + bool intersectsAABB(const glm::vec3& minPoint, const glm::vec3& maxPoint) const; +}; \ No newline at end of file diff --git a/src/ui/DebugUI.cpp b/src/ui/DebugUI.cpp index 0c5902f..203cd60 100644 --- a/src/ui/DebugUI.cpp +++ b/src/ui/DebugUI.cpp @@ -83,6 +83,8 @@ void drawDebugUI( ImGui::Text("Chunks loading: %zu", chunkManager->loadingChunks.size()); ImGui::Text("Chunks meshing: %zu", chunkManager->meshingChunks.size()); ImGui::Text("Jobs pending: %zu", jobSystem->pendingJobCount()); + ImGui::Text("Frustum solid tested:%d culled:%d drawn:%d", frustumSolidTested, frustumSolidCulled, frustumSolidDrawn); + ImGui::Text("Frustum water tested:%d culled:%d drawn:%d", frustumWaterTested, frustumWaterCulled, frustumWaterDrawn); ImGui::EndTabItem(); } From ee496981d31ed4089907199aae223e906625640c Mon Sep 17 00:00:00 2001 From: Dawid Krystian Czaplicki <100421659+dawc17@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:21:16 +0100 Subject: [PATCH 2/4] add biomes and integrate into terrain gen Introduce a biome system (Biome.h / Biome.cpp) with BiomeID, BiomeDefinition, tree types and climate-based picking. Integrate biomes into TerrainGenerator: sample temperature/humidity noise, adjust terrain amplitude, surface/filler blocks, tree type, density and trunk height; add getBiomeAt API. Update perlin seeds and reseeding for biome noise, adjust tree placement hashing and spawn chance to use biome density. Expose biome in DebugUI and add new source to CMakeLists.txt. --- src/CMakeLists.txt | 1 + src/ui/DebugUI.cpp | 15 ++++++ src/world/Biome.cpp | 72 ++++++++++++++++++++++++++ src/world/Biome.h | 30 +++++++++++ src/world/TerrainGenerator.cpp | 92 ++++++++++++++++++++++++++-------- src/world/TerrainGenerator.h | 3 ++ 6 files changed, 192 insertions(+), 21 deletions(-) create mode 100644 src/world/Biome.cpp create mode 100644 src/world/Biome.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3bc7485..a1b0a3c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ set(SOURCES world/RegionManager.cpp utils/JobSystem.cpp world/TerrainGenerator.cpp + world/Biome.cpp world/CaveGenerator.cpp world/WaterSimulator.cpp rendering/ParticleSystem.cpp diff --git a/src/ui/DebugUI.cpp b/src/ui/DebugUI.cpp index 203cd60..36bf326 100644 --- a/src/ui/DebugUI.cpp +++ b/src/ui/DebugUI.cpp @@ -2,6 +2,7 @@ #include "../core/MainGlobals.h" #include "../utils/BlockTypes.h" #include "../gameplay/Inventory.h" +#include "../world/TerrainGenerator.h" #include "../../libs/imgui/imgui.h" #include @@ -40,6 +41,20 @@ void drawDebugUI( int chunkZDbg = static_cast(floor(player.position.z / 16.0f)); ImGui::Text("Chunk: (%d, %d)", chunkXDbg, chunkZDbg); + int playerBlockX = static_cast(std::floor(player.position.x)); + int playerBlockZ = static_cast(std::floor(player.position.z)); + BiomeID biome = getBiomeAt(playerBlockX, playerBlockZ); + const char* biomeName = "Unknown"; + switch (biome) + { + case BiomeID::Desert: biomeName = "Desert"; break; + case BiomeID::Forest: biomeName = "Forest"; break; + case BiomeID::Tundra: biomeName = "Tundra"; break; + case BiomeID::Plains: biomeName = "Plains"; break; + default: break; + } + ImGui::Text("Biome: %s", biomeName); + if (selectedBlock.has_value()) { ImGui::Separator(); diff --git a/src/world/Biome.cpp b/src/world/Biome.cpp new file mode 100644 index 0000000..1c2d811 --- /dev/null +++ b/src/world/Biome.cpp @@ -0,0 +1,72 @@ +#include "Biome.h" + +namespace +{ + const BiomeDefinition DESERT{ + BiomeID::Desert, + 4, + 4, + TreeType::None, + 0.55f, + 0.0f + }; + + const BiomeDefinition FOREST{ + BiomeID::Forest, + 2, + 1, + TreeType::Oak, + 1.15f, + 1.60f + }; + + const BiomeDefinition TUNDRA{ + BiomeID::Tundra, + 2, + 1, + TreeType::Spruce, + 0.75, + 0.45f + }; + + const BiomeDefinition PLAINS{ + BiomeID::Plains, + 2, + 1, + TreeType::Oak, + 0.90f, + 0.60f + }; +} + +const BiomeDefinition& getBiomeDefinition(BiomeID biome) +{ + switch (biome) + { + case BiomeID::Desert: return DESERT; + case BiomeID::Forest: return FOREST; + case BiomeID::Tundra: return TUNDRA; + case BiomeID::Plains: return PLAINS; + default: return PLAINS; + } +} + +BiomeID pickBiomeFromClimate(float temperature, float humidity) +{ + if (temperature > 0.68f && humidity < 0.45f) + { + return BiomeID::Desert; + } + + if (temperature < 0.35f) + { + return BiomeID::Tundra; + } + + if (humidity > 0.60f) + { + return BiomeID::Forest; + } + + return BiomeID::Plains; +} \ No newline at end of file diff --git a/src/world/Biome.h b/src/world/Biome.h new file mode 100644 index 0000000..3ba7a34 --- /dev/null +++ b/src/world/Biome.h @@ -0,0 +1,30 @@ +#pragma once +#include + +enum class BiomeID : uint8_t +{ + Desert = 0, + Forest = 1, + Tundra = 2, + Plains = 3 +}; + +enum class TreeType : uint8_t +{ + None = 0, + Oak = 1, + Spruce = 2 +}; + +struct BiomeDefinition +{ + BiomeID id; + uint8_t surfaceBlock; + uint8_t fillerBlock; + TreeType treeType; + float terrainAmplitude; + float treeDensity; +}; + +const BiomeDefinition& getBiomeDefinition(BiomeID biome); +BiomeID pickBiomeFromClimate(float temperature, float humidity); \ No newline at end of file diff --git a/src/world/TerrainGenerator.cpp b/src/world/TerrainGenerator.cpp index 6b84052..20e6185 100644 --- a/src/world/TerrainGenerator.cpp +++ b/src/world/TerrainGenerator.cpp @@ -1,4 +1,5 @@ #include "TerrainGenerator.h" +#include "Biome.h" #include "../thirdparty/PerlinNoise.hpp" #include #include @@ -6,7 +7,8 @@ static siv::PerlinNoise::seed_type TERRAIN_SEED = 6767420; static siv::PerlinNoise perlin{TERRAIN_SEED}; static siv::PerlinNoise perlinDetail{TERRAIN_SEED + 1}; -static siv::PerlinNoise perlinTrees{TERRAIN_SEED + 2}; +static siv::PerlinNoise perlinBiomeTemp{TERRAIN_SEED + 3}; +static siv::PerlinNoise perlinBiomeHumidity{TERRAIN_SEED + 4}; constexpr int BASE_HEIGHT = 100; constexpr int HEIGHT_VARIATION = 40; @@ -29,19 +31,27 @@ constexpr int TREE_GRID_SIZE = 7; constexpr int TREE_OFFSET_RANGE = 10; constexpr float TREE_SPAWN_CHANCE = 0.2f; -static bool shouldPlaceTree(int worldX, int worldZ) +static bool shouldPlaceTree(int worldX, int worldZ, float chance) { + if (chance <= 0.0f) + return false; + + if (chance > 1.0f) + chance = 1.0f; + int cellX = worldX >= 0 ? worldX / TREE_GRID_SIZE : (worldX - TREE_GRID_SIZE + 1) / TREE_GRID_SIZE; int cellZ = worldZ >= 0 ? worldZ / TREE_GRID_SIZE : (worldZ - TREE_GRID_SIZE + 1) / TREE_GRID_SIZE; + unsigned int seedHash = static_cast(TERRAIN_SEED) * 83492791u; unsigned int cellHash = static_cast(cellX * 73856093) ^ - static_cast(cellZ * 19349663); + static_cast(cellZ * 19349663) ^ + seedHash; float spawnChance = (cellHash % 10000) / 10000.0f; - if (spawnChance >= TREE_SPAWN_CHANCE) + if (spawnChance >= chance) return false; - unsigned int offsetHash = cellHash * 31337; + unsigned int offsetHash = cellHash * 31337u; int offsetX = static_cast(offsetHash % TREE_OFFSET_RANGE); int offsetZ = static_cast((offsetHash / TREE_OFFSET_RANGE) % TREE_OFFSET_RANGE); @@ -51,7 +61,29 @@ static bool shouldPlaceTree(int worldX, int worldZ) return worldX == treePosX && worldZ == treePosZ; } -static double getTerrainHeight(float worldX, float worldZ) +static BiomeID sampleBiome(float worldX, float worldZ) +{ + double temperature = perlinBiomeTemp.octave2D_01( + worldX * 0.0015, + worldZ * 0.0015, + 3, + 0.5 + ); + + double humidity = perlinBiomeHumidity.octave2D_01( + worldX * 0.0015, + worldZ * 0.0015, + 3, + 0.5 + ); + + return pickBiomeFromClimate( + static_cast(temperature), + static_cast(humidity) + ); +} + +static double getTerrainHeight(float worldX, float worldZ, const BiomeDefinition& biome) { double continentNoise = perlin.octave2D_01( worldX * 0.002, @@ -79,7 +111,7 @@ static double getTerrainHeight(float worldX, float worldZ) double blendedNoise = continentNoise * 0.4 + hillNoise * 0.5 + detailNoise * 0.1; blendedNoise = blendedNoise * blendedNoise * (3.0 - 2.0 * blendedNoise); - return BASE_HEIGHT + blendedNoise * HEIGHT_VARIATION; + return BASE_HEIGHT + blendedNoise * (HEIGHT_VARIATION * biome.terrainAmplitude); } static void setBlockIfInChunk(BlockID* blocks, int localX, int localY, int localZ, uint8_t blockId, bool overwriteSolid = false) @@ -109,7 +141,9 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) float worldX = static_cast(worldOffsetX + x); float worldZ = static_cast(worldOffsetZ + z); - int terrainHeight = static_cast(std::round(getTerrainHeight(worldX, worldZ))); + BiomeID biomeId = sampleBiome(worldX, worldZ); + const BiomeDefinition& biome = getBiomeDefinition(biomeId); + int terrainHeight = static_cast(std::round(getTerrainHeight(worldX, worldZ, biome))); for (int y = 0; y < CHUNK_SIZE; y++) { @@ -129,17 +163,13 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) } else if (worldY == terrainHeight) { - if (terrainHeight <= SEA_LEVEL + 2 && terrainHeight >= SEA_LEVEL - 3) + if (terrainHeight <= SEA_LEVEL + 2) { blocks[i] = BLOCK_SAND; } - else if (terrainHeight > SEA_LEVEL) - { - blocks[i] = BLOCK_GRASS; - } else { - blocks[i] = BLOCK_SAND; + blocks[i] = biome.surfaceBlock; } } else if (worldY > terrainHeight - DIRT_DEPTH) @@ -150,7 +180,7 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) } else { - blocks[i] = BLOCK_DIRT; + blocks[i] = biome.fillerBlock; } } else @@ -168,18 +198,29 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) int worldX = worldOffsetX + x; int worldZ = worldOffsetZ + z; - if (!shouldPlaceTree(worldX, worldZ)) + BiomeID biomeId = sampleBiome(static_cast(worldX), static_cast(worldZ)); + const BiomeDefinition& biome = getBiomeDefinition(biomeId); + + if (biome.treeType == TreeType::None) + continue; + + if (!shouldPlaceTree(worldX, worldZ, TREE_SPAWN_CHANCE * biome.treeDensity)) continue; int terrainHeight = static_cast(std::round(getTerrainHeight( - static_cast(worldX), static_cast(worldZ)))); + static_cast(worldX), static_cast(worldZ), biome))); if (terrainHeight <= SEA_LEVEL + 2) continue; int treeBaseY = terrainHeight + 1; + int trunkHeight = TREE_TRUNK_HEIGHT; + if (biome.treeType == TreeType::Spruce) + { + trunkHeight = TREE_TRUNK_HEIGHT + 1; + } - for (int ty = 0; ty < TREE_TRUNK_HEIGHT; ty++) + for (int ty = 0; ty < trunkHeight; ty++) { int localX = x; int localY = treeBaseY + ty - worldOffsetY; @@ -187,7 +228,7 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) setBlockIfInChunk(blocks, localX, localY, localZ, BLOCK_LOG, true); } - int leafCenterY = treeBaseY + TREE_TRUNK_HEIGHT - 1; + int leafCenterY = treeBaseY + trunkHeight - 1; for (int lx = -TREE_LEAF_RADIUS; lx <= TREE_LEAF_RADIUS; lx++) { for (int ly = -1; ly <= TREE_LEAF_RADIUS; ly++) @@ -212,10 +253,18 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) } } +BiomeID getBiomeAt(int worldX, int worldZ) +{ + return sampleBiome(static_cast(worldX), static_cast(worldZ)); +} + int getTerrainHeightAt(int worldX, int worldZ) { + BiomeID biomeId = sampleBiome(static_cast(worldX), static_cast(worldZ)); + const BiomeDefinition& biome = getBiomeDefinition(biomeId); + return static_cast(std::round(getTerrainHeight( - static_cast(worldX), static_cast(worldZ)))); + static_cast(worldX), static_cast(worldZ), biome))); } void getTerrainHeightsForChunk(int cx, int cz, int* outHeights) @@ -242,5 +291,6 @@ void setWorldSeed(uint32_t seed) TERRAIN_SEED = seed; perlin.reseed(TERRAIN_SEED); perlinDetail.reseed(TERRAIN_SEED + 1); - perlinTrees.reseed(TERRAIN_SEED + 2); + perlinBiomeTemp.reseed(TERRAIN_SEED + 3); + perlinBiomeHumidity.reseed(TERRAIN_SEED + 4); } \ No newline at end of file diff --git a/src/world/TerrainGenerator.h b/src/world/TerrainGenerator.h index 1fd2aba..7218c1d 100644 --- a/src/world/TerrainGenerator.h +++ b/src/world/TerrainGenerator.h @@ -1,5 +1,6 @@ #pragma once #include "Chunk.h" +#include "Biome.h" #include uint32_t getWorldSeed(); @@ -7,6 +8,8 @@ void setWorldSeed(uint32_t seed); void generateTerrain(BlockID* blocks, int cx, int cy, int cz); +BiomeID getBiomeAt(int worldX, int worldZ); + int getTerrainHeightAt(int worldX, int worldZ); void getTerrainHeightsForChunk(int cx, int cz, int* outHeights); From afc9062db46da4a92066ca364da1142878918178 Mon Sep 17 00:00:00 2001 From: Dawid Krystian Czaplicki <100421659+dawc17@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:46:47 +0100 Subject: [PATCH 3/4] add biome debug tinting and save optimizations Introduce per-vertex biome debug tinting and a UI toggle to visualize biome colors, and reduce unnecessary region file writes plus improve terrain amplitude sampling. - Rendering/meshing: add a biomeTint vec3 to Vertex and propagate it through Meshing -> vertex attrib (location 5) -> vertex shader -> fragment shader. Fragment shader multiplies lit color by BiomeTint when useBiomeDebugTint is enabled. Renderer exposes a new uniform (useBiomeDebugTint) and reads the global showBiomeDebugColors flag from MainGlobals. Add UI checkbox to toggle "Biome Debug Colors". - Meshing: compute biome tint per block via getBiomeDebugTint() and pass chunkWorldOrigin into greedy mesher/buildChunkMeshOffThread so world coords are available. Wire up vertex attribute upload for the new tint. - Globals and UI: add showBiomeDebugColors global and ImGui entry. - Dirty-data tracking and saving: add Chunk::dirtyData and mark chunks dirty when blocks change. WorldSession, ChunkManager and enqueue/save/unload logic now only save chunks with dirtyData. RegionManager::saveColumn now reuses existing allocated sectors if they fit and avoids rewriting identical compressed data: compressed blocks are computed once and compared; skip writes if unchanged. - Job system: pass chunkWorldOrigin into buildChunkMeshOffThread for off-thread meshing. - Terrain generation: add sampleTerrainAmplitude() to blend terrain amplitudes from nearby biomes and use it in height calculations; handle beach/sand placement and adjust tree placement logic accordingly. - Minor tweaks: small biome parameter adjustments and ImGui window position/size tweak. These changes add a debug visualization for biome tinting while improving IO by skipping redundant region writes and making terrain height sample more spatially coherent. --- imgui.ini | 4 +- src/core/MainGlobals.cpp | 1 + src/core/MainGlobals.h | 1 + src/core/Renderer.cpp | 4 ++ src/core/Renderer.h | 1 + src/core/WorldSession.cpp | 2 + src/gameplay/Raycast.cpp | 1 + src/rendering/Meshing.cpp | 40 ++++++++++++++++++-- src/rendering/Meshing.h | 2 + src/shaders/default.frag | 6 +++ src/shaders/default.vert | 3 ++ src/ui/DebugUI.cpp | 1 + src/utils/JobSystem.cpp | 3 +- src/world/Biome.cpp | 8 ++-- src/world/Chunk.h | 1 + src/world/ChunkManager.cpp | 4 +- src/world/RegionManager.cpp | 23 ++++++++++-- src/world/TerrainGenerator.cpp | 69 ++++++++++++++++++++++------------ 18 files changed, 134 insertions(+), 40 deletions(-) diff --git a/imgui.ini b/imgui.ini index ec7be15..8e04ae3 100644 --- a/imgui.ini +++ b/imgui.ini @@ -15,8 +15,8 @@ Pos=710,245 Size=500,500 [Window][Debug] -Pos=60,60 -Size=500,541 +Pos=56,60 +Size=555,571 [Window][Fun Bullshit] Pos=325,62 diff --git a/src/core/MainGlobals.cpp b/src/core/MainGlobals.cpp index 7c3499c..fe3cd52 100644 --- a/src/core/MainGlobals.cpp +++ b/src/core/MainGlobals.cpp @@ -47,6 +47,7 @@ std::string currentWorldName; float fov = 70.0f; float mouseSensitivity = 0.1f; bool wireframeMode = false; +bool showBiomeDebugColors = false; bool drunkMode = false; float drunkIntensity = 1.0f; diff --git a/src/core/MainGlobals.h b/src/core/MainGlobals.h index 8de167b..0e1ea20 100644 --- a/src/core/MainGlobals.h +++ b/src/core/MainGlobals.h @@ -35,6 +35,7 @@ extern std::string currentWorldName; extern float fov; extern float mouseSensitivity; extern bool wireframeMode; +extern bool showBiomeDebugColors; extern bool drunkMode; extern float drunkIntensity; diff --git a/src/core/Renderer.cpp b/src/core/Renderer.cpp index de66cf5..475aaa3 100644 --- a/src/core/Renderer.cpp +++ b/src/core/Renderer.cpp @@ -17,6 +17,8 @@ #include #include +extern bool showBiomeDebugColors; + void Renderer::init() { shaderProgram = std::make_unique("default.vert", "default.frag"); @@ -30,6 +32,7 @@ void Renderer::init() fogColorLoc = glGetUniformLocation(shaderProgram->ID, "fogColor"); fogDensityLoc = glGetUniformLocation(shaderProgram->ID, "fogDensity"); ambientLightLoc = glGetUniformLocation(shaderProgram->ID, "ambientLight"); + biomeDebugTintLoc = glGetUniformLocation(shaderProgram->ID, "useBiomeDebugTint"); stbi_set_flip_vertically_on_load(false); @@ -256,6 +259,7 @@ void Renderer::beginFrame(const FrameParams& fp) glUniform3fv(fogColorLoc, 1, glm::value_ptr(fp.fogCol)); glUniform1f(fogDensityLoc, fp.effectiveFogDensity); glUniform1f(ambientLightLoc, fp.ambientLight); + glUniform1i(biomeDebugTintLoc, showBiomeDebugColors ? 1 : 0); } void Renderer::renderChunks(const FrameParams& fp, ChunkManager& cm) diff --git a/src/core/Renderer.h b/src/core/Renderer.h index 175a809..0d54b06 100644 --- a/src/core/Renderer.h +++ b/src/core/Renderer.h @@ -31,6 +31,7 @@ struct Renderer GLint transformLoc = 0, modelLoc = 0, timeOfDayLoc = 0; GLint cameraPosLoc = 0, skyColorLoc = 0, fogColorLoc = 0; GLint fogDensityLoc = 0, ambientLightLoc = 0; + GLint biomeDebugTintLoc = 0; unsigned int textureArray = 0; diff --git a/src/core/WorldSession.cpp b/src/core/WorldSession.cpp index 12f0f68..30a0719 100644 --- a/src/core/WorldSession.cpp +++ b/src/core/WorldSession.cpp @@ -131,6 +131,8 @@ void WorldSession::shutdown(Player& player, GLFWwindow* window) for (auto& pair : chunkManager->chunks) { Chunk* chunk = pair.second.get(); + if (!chunk->dirtyData) + continue; regionManager->saveChunkData( chunk->position.x, chunk->position.y, chunk->position.z, chunk->blocks); } diff --git a/src/gameplay/Raycast.cpp b/src/gameplay/Raycast.cpp index 21dd237..b075988 100644 --- a/src/gameplay/Raycast.cpp +++ b/src/gameplay/Raycast.cpp @@ -27,6 +27,7 @@ void setBlockAtWorld(int wx, int wy, int wz, uint8_t blockId, ChunkManager& chun c->blocks[blockIndex(local.x, local.y, local.z)] = blockId; c->dirtyMesh = true; c->dirtyLight = true; + c->dirtyData = true; for (int i = 0; i < 6; i++) { diff --git a/src/rendering/Meshing.cpp b/src/rendering/Meshing.cpp index 6c6b6be..fd2fbf7 100644 --- a/src/rendering/Meshing.cpp +++ b/src/rendering/Meshing.cpp @@ -1,6 +1,7 @@ #include "Meshing.h" #include "../utils/BlockTypes.h" #include "../world/ChunkManager.h" +#include "../world/TerrainGenerator.h" #include "../world/WaterSimulator.h" #include #include @@ -24,6 +25,19 @@ static const uint32_t FACE_INDICES[6] = { 0, 1, 2, 0, 2, 3}; +static glm::vec3 getBiomeDebugTint(int worldX, int worldZ) +{ + BiomeID biome = getBiomeAt(worldX, worldZ); + switch (biome) + { + case BiomeID::Desert: return glm::vec3(1.00f, 0.88f, 0.52f); + case BiomeID::Forest: return glm::vec3(0.55f, 1.00f, 0.55f); + case BiomeID::Tundra: return glm::vec3(0.72f, 0.84f, 1.00f); + case BiomeID::Plains: return glm::vec3(1.00f, 0.78f, 0.78f); + default: return glm::vec3(1.0f); + } +} + static float getFluidHeight(BlockGetter getBlock, int cornerX, int cornerY, int cornerZ) { int count = 0; @@ -169,6 +183,7 @@ static WaterVertexUV calculateWaterUV(float angle) static void buildGreedyMesh( const BlockID* blocks, + const glm::ivec3& chunkWorldOrigin, BlockGetter getBlock, LightGetter getSkyLight, std::vector& outVertices, @@ -343,6 +358,9 @@ static void buildGreedyMesh( int blockX = blockWorldPos.x; int blockY = blockWorldPos.y; int blockZ = blockWorldPos.z; + glm::vec3 biomeTint = getBiomeDebugTint( + chunkWorldOrigin.x + blockX, + chunkWorldOrigin.z + blockZ); for (int vIdx = 0; vIdx < 4; vIdx++) { @@ -443,6 +461,7 @@ static void buildGreedyMesh( vtx.tileIndex = static_cast(tileIndex); vtx.skyLight = skyLightNormalized; vtx.faceShade = faceShade; + vtx.biomeTint = biomeTint; outVertices.push_back(vtx); } @@ -662,8 +681,12 @@ void buildChunkMesh(Chunk &c, ChunkManager &chunkManager) std::vector waterVerts; std::vector waterInds; - buildGreedyMesh(c.blocks, getBlock, getSkyLight, verts, inds, false); - buildGreedyMesh(c.blocks, getBlock, getSkyLight, waterVerts, waterInds, true); + glm::ivec3 chunkWorldOrigin( + c.position.x * CHUNK_SIZE, + c.position.y * CHUNK_SIZE, + c.position.z * CHUNK_SIZE); + buildGreedyMesh(c.blocks, chunkWorldOrigin, getBlock, getSkyLight, verts, inds, false); + buildGreedyMesh(c.blocks, chunkWorldOrigin, getBlock, getSkyLight, waterVerts, waterInds, true); uploadToGPU(c, verts, inds); uploadWaterToGPU(c, waterVerts, waterInds); @@ -705,6 +728,10 @@ void uploadToGPU(Chunk &c, const std::vector &verts, const std::vector(inds.size()); c.vertexCount = static_cast(verts.size()); } @@ -759,6 +786,10 @@ void uploadWaterToGPU(Chunk &c, const std::vector &verts, const std::vec (void *)offsetof(Vertex, faceShade)); glEnableVertexAttribArray(4); + glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), + (void *)offsetof(Vertex, biomeTint)); + glEnableVertexAttribArray(5); + c.waterIndexCount = static_cast(inds.size()); c.waterVertexCount = static_cast(verts.size()); } @@ -766,6 +797,7 @@ void uploadWaterToGPU(Chunk &c, const std::vector &verts, const std::vec void buildChunkMeshOffThread( const BlockID* blocks, const uint8_t* skyLight, + const glm::ivec3& chunkWorldOrigin, BlockGetter getBlock, LightGetter getSkyLight, std::vector& outVertices, @@ -773,6 +805,6 @@ void buildChunkMeshOffThread( std::vector& outWaterVertices, std::vector& outWaterIndices) { - buildGreedyMesh(blocks, getBlock, getSkyLight, outVertices, outIndices, false); - buildGreedyMesh(blocks, getBlock, getSkyLight, outWaterVertices, outWaterIndices, true); + buildGreedyMesh(blocks, chunkWorldOrigin, getBlock, getSkyLight, outVertices, outIndices, false); + buildGreedyMesh(blocks, chunkWorldOrigin, getBlock, getSkyLight, outWaterVertices, outWaterIndices, true); } diff --git a/src/rendering/Meshing.h b/src/rendering/Meshing.h index d7e52d6..6f8b97e 100644 --- a/src/rendering/Meshing.h +++ b/src/rendering/Meshing.h @@ -12,6 +12,7 @@ struct Vertex float tileIndex; float skyLight; float faceShade; + glm::vec3 biomeTint; }; enum FaceDir { @@ -44,6 +45,7 @@ using LightGetter = std::function; void buildChunkMeshOffThread( const BlockID* blocks, const uint8_t* skyLight, + const glm::ivec3& chunkWorldOrigin, BlockGetter getBlock, LightGetter getSkyLight, std::vector& outVertices, diff --git a/src/shaders/default.frag b/src/shaders/default.frag index 6b2c27a..3a62981 100644 --- a/src/shaders/default.frag +++ b/src/shaders/default.frag @@ -7,6 +7,7 @@ in float SkyLight; in float FaceShade; in float FragDepth; in vec3 WorldPos; +in vec3 BiomeTint; uniform sampler2DArray textureArray; uniform float timeOfDay; @@ -15,6 +16,7 @@ uniform vec3 skyColor; uniform vec3 fogColor; uniform float fogDensity; uniform float ambientLight; +uniform bool useBiomeDebugTint; void main() { @@ -29,6 +31,10 @@ void main() float finalLight = totalLight * FaceShade; vec3 litColor = texColor.rgb * finalLight; + if (useBiomeDebugTint) + { + litColor *= BiomeTint; + } float dist = length(WorldPos - cameraPos); float fogFactor = 1.0 - exp(-dist * fogDensity); diff --git a/src/shaders/default.vert b/src/shaders/default.vert index d2b7f21..b2c8fcc 100644 --- a/src/shaders/default.vert +++ b/src/shaders/default.vert @@ -4,6 +4,7 @@ layout (location = 1) in vec2 aLocalUV; layout (location = 2) in float aTileIndex; layout (location = 3) in float aSkyLight; layout (location = 4) in float aFaceShade; +layout (location = 5) in vec3 aBiomeTint; out vec2 LocalUV; flat out float TileIndex; @@ -11,6 +12,7 @@ out float SkyLight; out float FaceShade; out float FragDepth; out vec3 WorldPos; +out vec3 BiomeTint; uniform mat4 transform; uniform mat4 model; @@ -25,4 +27,5 @@ void main() FaceShade = aFaceShade; FragDepth = gl_Position.z; WorldPos = worldPosition.xyz; + BiomeTint = aBiomeTint; } diff --git a/src/ui/DebugUI.cpp b/src/ui/DebugUI.cpp index 36bf326..ea43b79 100644 --- a/src/ui/DebugUI.cpp +++ b/src/ui/DebugUI.cpp @@ -110,6 +110,7 @@ void drawDebugUI( ImGui::Separator(); ImGui::Checkbox("Wireframe mode", &wireframeMode); + ImGui::Checkbox("Biome Debug Colors", &showBiomeDebugColors); ImGui::Checkbox("Noclip mode", &player.noclip); ImGui::Checkbox("Async Loading", &useAsyncLoading); ImGui::SliderFloat("Move Speed", &cameraSpeed, 0.0f, 60.0f); diff --git a/src/utils/JobSystem.cpp b/src/utils/JobSystem.cpp index 1fc2d45..1dedbd2 100644 --- a/src/utils/JobSystem.cpp +++ b/src/utils/JobSystem.cpp @@ -307,7 +307,8 @@ void JobSystem::processMeshJob(MeshChunkJob* job) return MAX_SKY_LIGHT; }; - buildChunkMeshOffThread(job->blocks, job->skyLight, getBlock, getSkyLight, + glm::ivec3 chunkWorldOrigin(job->cx * CHUNK_SIZE, job->cy * CHUNK_SIZE, job->cz * CHUNK_SIZE); + buildChunkMeshOffThread(job->blocks, job->skyLight, chunkWorldOrigin, getBlock, getSkyLight, job->vertices, job->indices, job->waterVertices, job->waterIndices); } diff --git a/src/world/Biome.cpp b/src/world/Biome.cpp index 1c2d811..5a3a567 100644 --- a/src/world/Biome.cpp +++ b/src/world/Biome.cpp @@ -7,7 +7,7 @@ namespace 4, 4, TreeType::None, - 0.55f, + 0.80f, 0.0f }; @@ -16,7 +16,7 @@ namespace 2, 1, TreeType::Oak, - 1.15f, + 1.05f, 1.60f }; @@ -25,7 +25,7 @@ namespace 2, 1, TreeType::Spruce, - 0.75, + 0.95f, 0.45f }; @@ -34,7 +34,7 @@ namespace 2, 1, TreeType::Oak, - 0.90f, + 1.00f, 0.60f }; } diff --git a/src/world/Chunk.h b/src/world/Chunk.h index 3ef5055..7e0779e 100644 --- a/src/world/Chunk.h +++ b/src/world/Chunk.h @@ -20,6 +20,7 @@ struct Chunk bool dirtyMesh = true; bool dirtyLight = true; + bool dirtyData = false; GLuint vao = 0, vbo = 0, ebo = 0; uint32_t indexCount = 0; uint32_t vertexCount = 0; diff --git a/src/world/ChunkManager.cpp b/src/world/ChunkManager.cpp index d5293a2..0e3ed6b 100644 --- a/src/world/ChunkManager.cpp +++ b/src/world/ChunkManager.cpp @@ -86,7 +86,7 @@ void ChunkManager::unloadChunk(int cx, int cy, int cz) if (it != chunks.end()) { - if (regionManager) + if (regionManager && it->second->dirtyData) { regionManager->saveChunkData(cx, cy, cz, it->second->blocks); } @@ -130,7 +130,7 @@ void ChunkManager::enqueueSaveAndUnload(int cx, int cy, int cz) if (!chunk) return; - if (jobSystem && regionManager) + if (jobSystem && regionManager && chunk->dirtyData) { savingChunks.insert(key); diff --git a/src/world/RegionManager.cpp b/src/world/RegionManager.cpp index 0825210..09b6076 100644 --- a/src/world/RegionManager.cpp +++ b/src/world/RegionManager.cpp @@ -126,7 +126,16 @@ void RegionFile::saveColumn(int localX, int localZ, const ColumnData& data) totalSize += 1 + 4 + static_cast(section.compressedBlocks.size()); } - uint32_t offset = allocateSectors(totalSize); + int idx = getEntryIndex(localX, localZ); + uint32_t offset = 0; + if (header[idx].offset != 0 && header[idx].size >= totalSize) + { + offset = header[idx].offset; + } + else + { + offset = allocateSectors(totalSize); + } file.seekp(offset, std::ios::beg); @@ -143,7 +152,6 @@ void RegionFile::saveColumn(int localX, int localZ, const ColumnData& data) file.write(reinterpret_cast(section.compressedBlocks.data()), compressedSize); } - int idx = getEntryIndex(localX, localZ); header[idx].offset = offset; header[idx].size = totalSize; headerDirty = true; @@ -359,12 +367,19 @@ void RegionManager::saveChunkData(int cx, int cy, int cz, const BlockID* blocks) ColumnData columnData; region->loadColumn(localX, localZ, columnData); + std::vector compressedBlocks; + compressBlocks(blocks, compressedBlocks); + if (compressedBlocks.empty()) + return; + bool found = false; for (auto& section : columnData.sections) { if (section.y == static_cast(cy)) { - compressBlocks(blocks, section.compressedBlocks); + if (section.compressedBlocks == compressedBlocks) + return; + section.compressedBlocks = std::move(compressedBlocks); found = true; break; } @@ -374,7 +389,7 @@ void RegionManager::saveChunkData(int cx, int cy, int cz, const BlockID* blocks) { SectionData newSection; newSection.y = static_cast(cy); - compressBlocks(blocks, newSection.compressedBlocks); + newSection.compressedBlocks = std::move(compressedBlocks); columnData.sections.push_back(std::move(newSection)); std::sort(columnData.sections.begin(), columnData.sections.end(), diff --git a/src/world/TerrainGenerator.cpp b/src/world/TerrainGenerator.cpp index 20e6185..286710d 100644 --- a/src/world/TerrainGenerator.cpp +++ b/src/world/TerrainGenerator.cpp @@ -83,7 +83,25 @@ static BiomeID sampleBiome(float worldX, float worldZ) ); } -static double getTerrainHeight(float worldX, float worldZ, const BiomeDefinition& biome) +static float sampleTerrainAmplitude(float worldX, float worldZ) +{ + constexpr float SAMPLE_OFFSET = 12.0f; + BiomeID b0 = sampleBiome(worldX, worldZ); + BiomeID b1 = sampleBiome(worldX + SAMPLE_OFFSET, worldZ); + BiomeID b2 = sampleBiome(worldX - SAMPLE_OFFSET, worldZ); + BiomeID b3 = sampleBiome(worldX, worldZ + SAMPLE_OFFSET); + BiomeID b4 = sampleBiome(worldX, worldZ - SAMPLE_OFFSET); + + float a0 = getBiomeDefinition(b0).terrainAmplitude; + float a1 = getBiomeDefinition(b1).terrainAmplitude; + float a2 = getBiomeDefinition(b2).terrainAmplitude; + float a3 = getBiomeDefinition(b3).terrainAmplitude; + float a4 = getBiomeDefinition(b4).terrainAmplitude; + + return a0 * 0.5f + (a1 + a2 + a3 + a4) * 0.125f; +} + +static double getTerrainHeight(float worldX, float worldZ, float terrainAmplitude) { double continentNoise = perlin.octave2D_01( worldX * 0.002, @@ -111,7 +129,7 @@ static double getTerrainHeight(float worldX, float worldZ, const BiomeDefinition double blendedNoise = continentNoise * 0.4 + hillNoise * 0.5 + detailNoise * 0.1; blendedNoise = blendedNoise * blendedNoise * (3.0 - 2.0 * blendedNoise); - return BASE_HEIGHT + blendedNoise * (HEIGHT_VARIATION * biome.terrainAmplitude); + return BASE_HEIGHT + blendedNoise * (HEIGHT_VARIATION * terrainAmplitude); } static void setBlockIfInChunk(BlockID* blocks, int localX, int localY, int localZ, uint8_t blockId, bool overwriteSolid = false) @@ -143,7 +161,24 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) BiomeID biomeId = sampleBiome(worldX, worldZ); const BiomeDefinition& biome = getBiomeDefinition(biomeId); - int terrainHeight = static_cast(std::round(getTerrainHeight(worldX, worldZ, biome))); + float terrainAmplitude = sampleTerrainAmplitude(worldX, worldZ); + int terrainHeight = static_cast(std::round(getTerrainHeight(worldX, worldZ, terrainAmplitude))); + uint8_t surfaceBlock = biome.surfaceBlock; + uint8_t fillerBlock = biome.fillerBlock; + if (terrainHeight < SEA_LEVEL) + { + surfaceBlock = biome.fillerBlock; + } + + int beachMaxHeight = biomeId == BiomeID::Plains ? SEA_LEVEL : SEA_LEVEL + 1; + bool isBeachColumn = biomeId != BiomeID::Desert && + terrainHeight >= SEA_LEVEL - 1 && + terrainHeight <= beachMaxHeight; + if (isBeachColumn) + { + surfaceBlock = BLOCK_SAND; + fillerBlock = BLOCK_SAND; + } for (int y = 0; y < CHUNK_SIZE; y++) { @@ -163,25 +198,11 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) } else if (worldY == terrainHeight) { - if (terrainHeight <= SEA_LEVEL + 2) - { - blocks[i] = BLOCK_SAND; - } - else - { - blocks[i] = biome.surfaceBlock; - } + blocks[i] = surfaceBlock; } else if (worldY > terrainHeight - DIRT_DEPTH) { - if (terrainHeight <= SEA_LEVEL + 2) - { - blocks[i] = BLOCK_SAND; - } - else - { - blocks[i] = biome.fillerBlock; - } + blocks[i] = fillerBlock; } else { @@ -207,8 +228,10 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) if (!shouldPlaceTree(worldX, worldZ, TREE_SPAWN_CHANCE * biome.treeDensity)) continue; + float terrainAmplitude = sampleTerrainAmplitude( + static_cast(worldX), static_cast(worldZ)); int terrainHeight = static_cast(std::round(getTerrainHeight( - static_cast(worldX), static_cast(worldZ), biome))); + static_cast(worldX), static_cast(worldZ), terrainAmplitude))); if (terrainHeight <= SEA_LEVEL + 2) continue; @@ -260,11 +283,11 @@ BiomeID getBiomeAt(int worldX, int worldZ) int getTerrainHeightAt(int worldX, int worldZ) { - BiomeID biomeId = sampleBiome(static_cast(worldX), static_cast(worldZ)); - const BiomeDefinition& biome = getBiomeDefinition(biomeId); + float terrainAmplitude = sampleTerrainAmplitude( + static_cast(worldX), static_cast(worldZ)); return static_cast(std::round(getTerrainHeight( - static_cast(worldX), static_cast(worldZ), biome))); + static_cast(worldX), static_cast(worldZ), terrainAmplitude))); } void getTerrainHeightsForChunk(int cx, int cz, int* outHeights) From 371f6c4e82120b4f3dc9e5c6ecee54a05517e990 Mon Sep 17 00:00:00 2001 From: Dawid Krystian Czaplicki <100421659+dawc17@users.noreply.github.com> Date: Thu, 12 Feb 2026 00:04:45 +0100 Subject: [PATCH 4/4] transfer to new titned texture system, new block icons, and other stuff Replace the old texture atlas with a GL_TEXTURE_2D_ARRAY loaded from individual PNG layers and populate a fixed texture enum. Add offscreen icon generation (FBO) that renders item models into 2D textures and store them as HUD icons. Propagate biome-based tinting everywhere: meshing, item model generation, particle spawning, and shaders (item/particle/default) so foliage/grass are tinted by biome. Introduce several new block types and textures (snow grass, spruce log/planks/leaves, cobblestone), update BlockTypes to use helpers and face tint flags, and add corresponding creative inventory entries. Misc: rename loadBlockIcons -> generateBlockIcons, adjust renderer initialization (create texture array, item model shader usage), remove old atlas-randomize UI buttons, fix particle texture coordinates, and various includes/cleanup. This enables per-layer textures, proper biome tinting, and generated item icons. --- assets/textures/array.png | Bin 1847 -> 0 bytes assets/textures/atlas.png | Bin 24337 -> 0 bytes assets/textures/blocks.png | Bin 127843 -> 0 bytes assets/textures/cobblestone.png | Bin 0 -> 568 bytes assets/textures/dirt.png | Bin 0 -> 266 bytes assets/textures/glass.png | Bin 0 -> 184 bytes assets/textures/grass_side_overlay.png | Bin 0 -> 219 bytes assets/textures/grass_side_snowed.png | Bin 0 -> 268 bytes assets/textures/grass_top.png | Bin 0 -> 560 bytes assets/textures/leaves_oak.png | Bin 0 -> 499 bytes assets/textures/leaves_spruce.png | Bin 0 -> 263 bytes assets/textures/log_oak.png | Bin 0 -> 528 bytes assets/textures/log_oak_top.png | Bin 0 -> 478 bytes assets/textures/log_spruce.png | Bin 0 -> 451 bytes assets/textures/log_spruce_top.png | Bin 0 -> 609 bytes assets/textures/planks_oak.png | Bin 0 -> 247 bytes assets/textures/planks_spruce.png | Bin 0 -> 254 bytes assets/textures/sand.png | Bin 0 -> 721 bytes assets/textures/snow.png | Bin 0 -> 207 bytes assets/textures/stone.png | Bin 0 -> 223 bytes assets/textures/water_flow.png | Bin 0 -> 10198 bytes assets/textures/water_still.png | Bin 0 -> 14567 bytes imgui.ini | 4 +- src/core/MainGlobals.cpp | 95 +++++++-- src/core/MainGlobals.h | 4 +- src/core/Renderer.cpp | 168 ++++++++++------ src/core/Renderer.h | 1 - src/core/main.cpp | 13 +- src/gameplay/Inventory.cpp | 5 + src/rendering/ItemModelGenerator.cpp | 18 +- src/rendering/ItemModelGenerator.h | 1 + src/rendering/Meshing.cpp | 26 +-- src/rendering/ParticleSystem.cpp | 4 +- src/rendering/ParticleSystem.h | 2 +- src/shaders/default.frag | 11 +- src/shaders/item_model.frag | 5 +- src/shaders/item_model.vert | 5 +- src/shaders/particle.frag | 7 +- src/shaders/particle.vert | 2 +- src/ui/DebugUI.cpp | 14 -- src/utils/BlockTypes.cpp | 265 ++++++++++++++----------- src/utils/BlockTypes.h | 39 ++-- src/world/Biome.cpp | 26 ++- src/world/Biome.h | 5 +- src/world/TerrainGenerator.cpp | 19 +- 45 files changed, 465 insertions(+), 274 deletions(-) delete mode 100644 assets/textures/array.png delete mode 100644 assets/textures/atlas.png delete mode 100644 assets/textures/blocks.png create mode 100644 assets/textures/cobblestone.png create mode 100644 assets/textures/dirt.png create mode 100644 assets/textures/glass.png create mode 100644 assets/textures/grass_side_overlay.png create mode 100644 assets/textures/grass_side_snowed.png create mode 100644 assets/textures/grass_top.png create mode 100644 assets/textures/leaves_oak.png create mode 100644 assets/textures/leaves_spruce.png create mode 100644 assets/textures/log_oak.png create mode 100644 assets/textures/log_oak_top.png create mode 100644 assets/textures/log_spruce.png create mode 100644 assets/textures/log_spruce_top.png create mode 100644 assets/textures/planks_oak.png create mode 100644 assets/textures/planks_spruce.png create mode 100644 assets/textures/sand.png create mode 100644 assets/textures/snow.png create mode 100644 assets/textures/stone.png create mode 100644 assets/textures/water_flow.png create mode 100644 assets/textures/water_still.png diff --git a/assets/textures/array.png b/assets/textures/array.png deleted file mode 100644 index f511a64c74349c74a3af4e1d53323b99d8b16503..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1847 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 diff --git a/assets/textures/blocks.png b/assets/textures/blocks.png deleted file mode 100644 index 2c8ae80a81996080ff11a099b58678fb661c67f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127843 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
    djjY;5N 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 i&t6I!Nu zI;ORT*!P_m?)!$6l0Q)HoMTT4*LB5l9JsEFYiXJy&vTA{===VwK?=yT+?SV^zdXRi Vczl&7)+PV|002ovPDHLkV1ku5TnqpJ literal 0 HcmV?d00001 diff --git a/assets/textures/grass_side_snowed.png b/assets/textures/grass_side_snowed.png new file mode 100644 index 0000000000000000000000000000000000000000..41f619774d864ddbb26e42f0174998f52cd6e546 GIT binary patch literal 268 zcmV+n0rUQeP)0pZ0MJ-^(J*l=ZM>h^*^g6SJD(jLTk>9( zA)K0luBXEW>;xR?HA}$OoyqQ#Ui!4whCvYBZ4&O6;~JA*&vqV)?$_fs0{9$EA+Rd} zAUU1P6P*lQqJl+LoW1uyfFyMLEpD*J S*+WhM0000nJC=}8J zv-|z7b5;sF3}>C>1NIICKE7TrnZl0ogeHsw8QL_1h<1j;1Rw`G(-?#g41*1YAbr1I z3xz_`jW;x6;(rg&30)#gsQADch=huFo?#Y=M1+ohGJw&JJyQf74p=Lc(Hahib;dh) znn)!~VMU*#k(iQ5kSJmZ=j495TuKTx$Ylda;GMwfzd#XMJ&L4_kkE+J1c?hnG16N( yEF4rx9#hfG{@H>z05dwG5;T%S-EWC!Q4gw00002%cg`~C7a+39quf4N-bdcBI?Jc@Sk>0q2vsbt#hcDv$oxfHlu zE}M}N*B?fskqn1JgEpJZ#Ow8{kFhG1iup#n(P)?=+D<;7R|l1l6pzQ%2M{X-gF(Hc z(Wu_JTuv;X&u8Ysazdey+StKju`rd>>9nx{8V-jg7K@1{-tTw)IG@kucDpGE=B?Fg z8ecA#0+vvW0JxLMq*#x~L&!j{*VAr55A;C*#AuUpu%mjwyyO{eaY>w;qG`2S#@}wY zQm@y40KxBn8XXRY*~MzLdf^*!3eIrO8!G#x3H|^eDi?wSxJ53{=d(#fi;yirip8SY zg>BnvW9;d4Dk=yB@Q64Ugha)$8Udh_{eJ&DRwk1%i2wn&6ctY@mC^^qMWS02(>+Zl plef9a0C-0wXoB4H;X##%e*vMZkFr!eT!#Pv002ovPDHLkV1h75>(&4O literal 0 HcmV?d00001 diff --git a/assets/textures/leaves_spruce.png b/assets/textures/leaves_spruce.png new file mode 100644 index 0000000000000000000000000000000000000000..602eab8e1bb11e36cb5cae37a633e3a04ac1368d GIT binary patch literal 263 zcmV+i0r>ujP)V)6U{-uK>4k;#VSO1J|=Gkd(J##0iihw#E}VA$ydG* zh&LM<^~D$B^pfv9Y4{$gCl@Wk;^;gj>EqFX!HnSh(XnTT))7O%H$terA-&?r@u>ne z62(R*_?ow@n(3QLQ6;bUub;>7DfvhLCPxjgi*rTM{8w!XL7UhhT0(H(me$ N002ovPDHLkV1k7ncSisK literal 0 HcmV?d00001 diff --git a/assets/textures/log_oak.png b/assets/textures/log_oak.png new file mode 100644 index 0000000000000000000000000000000000000000..914cb5fbc10515e052d18d78edcadb200f5b5d39 GIT binary patch literal 528 zcmV+r0`L8aP)q z*G@x03`F-Y=w0Li0x3Wmgp$w`dJRZCgx-4(;u8?Pi%&H=M!w?VYByecW;}LWf7~aV z(=DA0T`i~LNXvPX9Fsm-Jms0{<$6Za%eLhsry?ba}`Mxvyt=< z8z6i?OT7GrPM?{ui5D)x?J&bZjRHqlb(|8%tSFb zTXNCMRwsS#_2$Lvb{DTW{VUzaAovdsH|({A^spXe251tR<}m<_Re%J582|;v{!Ft1 zKm!<^C)^r!0mo>00BC?Xq6hMiR1f`RquQxpp^bna2!hSuiP2g>|MoOd40--Sg4xO@I+BV9MtmE&0&4!f19^t UfC(=i?EnA(07*qoM6N<$g2JKDPyhe` literal 0 HcmV?d00001 diff --git a/assets/textures/log_spruce.png b/assets/textures/log_spruce.png new file mode 100644 index 0000000000000000000000000000000000000000..dc1aa2f04a7e4ab23b6ba4b0a16c870a2e5f73cc GIT binary patch literal 451 zcmV;!0X+VRP)1qN&5QGPIy)`Pz#x))og%Hf4tGVAma*^ZxAEm?BQp1uTL(le6)!kDYMxE4dXD!<< zI`;iIwLv|xVsT>c`;iTssa;PR9>1A3?d4+NF)Tjv14&nBbt}$$)~zI7^m#vy#oPg< z;Ok*x(_X~^u#kj)HL=UQVe4_ti%3)vtmV53Wr+n^%G$>+03gT_Yqt1QdcmKr@m2ahKaVuXzEi+^k7_=k-Q1Du0QO0x& tm~`Mmf?2FMSsdTxXOyfF5f-0-{s2}3Rq*V~q9y~!x8`h literal 0 HcmV?d00001 diff --git a/assets/textures/log_spruce_top.png b/assets/textures/log_spruce_top.png new file mode 100644 index 0000000000000000000000000000000000000000..280c64e4a16379ae6a5b08706448c4eadd100ed3 GIT binary patch literal 609 zcmV-n0-pVeP)c^{cAg$ zO^6D?@t@U=;k|UR!V6BIj>hM2zX;WQ!MoE5H*t%ToV0BsWf4$ShQlGt?H1CmVguql zN5SKKAk0n`f8ffZ!l1lpZdYHENz}zQFytS)m3^OcY*NH%3%gH`X1q zd;_SlhfP?nYMg*sAGzApc<&JrVvL=CLJ0_}+WA8J@J>(zC%MaUnM38!6BRC3b@$1+ zjA%7q;D8Ri=>c7l7U}!ivd=lzTiavm?Tcev3*vY*9=_YK8i3m-~9fGQ|i5Y_?8BV}^zqfO1nc{wB!3Y;WZ#g@i zb_v^LOv@6r9*>8n1|ejCW75n*n-m;88VGf?PO=?cJ0Yn-j1gCse80NuX>4Mm`dbr1 z*yBE>L~J{00000NkvXXu0mjf7U~B; literal 0 HcmV?d00001 diff --git a/assets/textures/planks_oak.png b/assets/textures/planks_oak.png new file mode 100644 index 0000000000000000000000000000000000000000..346f77dc1f93d46f238292e8714ef0c92433325b GIT binary patch literal 247 zcmVT3Dox3KvB)duN`J}|W~1vPJeq-Zo6s0!6yWI+x56xVv&&z*!RpcOTy xo*qah|Am}@m#W+Y$74GF_~D-UpAJLNUWUc@J literal 0 HcmV?d00001 diff --git a/assets/textures/planks_spruce.png b/assets/textures/planks_spruce.png new file mode 100644 index 0000000000000000000000000000000000000000..f45fa94b0bc1eba667981a4a5aa0610868389164 GIT binary patch literal 254 zcmVJfw6Ds$j^(6bk*6h!*EKPIJWQ3mCt)v=czZ) zGsW^8%(sW$2?xJ!hu?~a9yr&vNDu`hDHka^xSn<^a39+g9LKRz(*S#%KQ8;NGjt=h zOQw=(Bg`q{$OR%NB;)8cscPV%)%-j#HRD3$H$RGK6dFVovbper3iw4_|II_Hf%jhn zxfA08T8V6`>49Y7zu*(_P^ELAn9kkC4|~slJT^vyFWiOWr%Wx}h5!Hn07*qoM6N<$ Eg5?fu2><{9 literal 0 HcmV?d00001 diff --git a/assets/textures/sand.png b/assets/textures/sand.png new file mode 100644 index 0000000000000000000000000000000000000000..86b96546ebb4c366e260f70270d9420dabca1eba GIT binary patch literal 721 zcmV;?0xtcDP)2b822s+hu01oQqSu_ehsVCS~`#n^v zyo`P;j9>jY%Gn9cG>LL4Zq6)Lha7 zr?LR!!~?tN!-wUE8u+cqB~aH{!)0StY({sUK%F;&Qb6>7Po7R?O`f^LI5G*Bkt)p8 z9OC`%KswcrELPt;b0+726wx zWq5IJtxD2VVdn!99xYHI8+rQO4S8>tLNGHk=;^GKqZKrZO9=TM;6f*#_%tMHOk+}* zt~JhQ=!U-UGFe08A92vP(e3#!^8Ggq_6%TXT+Zur4 z7P5%Gzg=!hNcogr;i?~zkB^av4&8%!rHS%Mm`jdG9;#slBu#GQ*6JfEA_D<$x(AZ@ zk1O{+&~DrI-%o8FrR1!87c4zbQ_Ie3)J7qwKe%_`Oz*>S?3G@$MeJ=4{BKM}JyJ_> zMHy{7ARjpqz>v*M#N03$6oaHP-bAP9m+`Uwvkv?(SISL>@^r7I00000NkvXXu0mjf DJBUnr literal 0 HcmV?d00001 diff --git a/assets/textures/snow.png b/assets/textures/snow.png new file mode 100644 index 0000000000000000000000000000000000000000..5c146cd37c206de9167916ba96f78d17fd25a551 GIT binary patch literal 207 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 WQVtXyOu?VYBT!Y+Qml}-3j04On!Z{9 literal 0 HcmV?d00001 diff --git a/assets/textures/water_still.png b/assets/textures/water_still.png new file mode 100644 index 0000000000000000000000000000000000000000..c7e90b07308c58daeab5432659e9ac45095204fc GIT binary patch literal 14567 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

    |NJRJB2jmbz# z)D!9#6^QP8TL+Henk^nm*0XHa>IWC#1RJR*O#3 zaL*qoah*PrH2SKeWNC#5Y~!d$Z%RhW*1+b_V_RyI&cT`U&F84=!>K*Hq!A+lzI(vE z3)GMUjqh^cW~*A0^OJlXk2s1A3#jLC)q>ON=rX(^l9v#AbVPc-^L9GwoM|7>GO=q%fr<9n>2`NpOn+;O6s_Jk|4^*Y(2xKLB$&!Rh$s RLHPgx002ovPDHLkV1n%S)6xI{ literal 0 HcmV?d00001 diff --git a/imgui.ini b/imgui.ini index 8e04ae3..11a3090 100644 --- a/imgui.ini +++ b/imgui.ini @@ -40,6 +40,6 @@ Pos=696,353 Size=528,284 [Window][##CreativeInventory] -Pos=696,399 -Size=528,192 +Pos=696,371 +Size=528,248 diff --git a/src/core/MainGlobals.cpp b/src/core/MainGlobals.cpp index fe3cd52..128ecce 100644 --- a/src/core/MainGlobals.cpp +++ b/src/core/MainGlobals.cpp @@ -7,16 +7,20 @@ #include #include +#include +#include #include "../../libs/imgui/imgui.h" #include "../utils/BlockTypes.h" #include "../rendering/Camera.h" +#include "../rendering/ItemModelGenerator.h" #include "../rendering/ToolModelGenerator.h" #include "../utils/CoordUtils.h" #include "GameState.h" #include "../gameplay/Player.h" #include "../gameplay/Raycast.h" #include "../rendering/opengl/ShaderClass.h" +#include "../world/Biome.h" #include "../world/TerrainGenerator.h" #define STB_IMAGE_IMPLEMENTATION @@ -141,29 +145,70 @@ GLuint loadHUDIcon(const std::string& path, bool useNearest) return texture; } -void loadBlockIcons(const std::string& basePath) +void generateBlockIcons(GLuint textureArray, Shader* itemModelShader) { - std::unordered_map iconFiles = { - {1, "natural_dirt.png"}, - {2, "natural_grass_block.png"}, - {3, "masonry_cobblestone.png"}, - {4, "natural_sand.png"}, - {5, "log_oak.png"}, - {6, "leaves_oak.png"}, - {7, "transparent_glass.png"}, - {8, "planks_oak.png"} - }; - - for (const auto& [blockId, filename] : iconFiles) + constexpr int ICON_SIZE = 128; + + GLuint fbo, rbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glGenRenderbuffers(1, &rbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, ICON_SIZE, ICON_SIZE); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + GLint prevViewport[4]; + glGetIntegerv(GL_VIEWPORT, prevViewport); + glViewport(0, 0, ICON_SIZE, ICON_SIZE); + + glm::vec3 center(0.5f, 0.5f, 0.5f); + glm::mat4 proj = glm::ortho(-0.85f, 0.85f, 0.85f, -0.85f, 0.1f, 10.0f); + glm::vec3 eye = center + glm::normalize(glm::vec3(1.0f, 0.8f, 1.0f)) * 2.5f; + glm::mat4 view = glm::lookAt(eye, center, glm::vec3(0.0f, 1.0f, 0.0f)); + glm::mat4 mvp = proj * view; + + itemModelShader->Activate(); + glUniform1f(glGetUniformLocation(itemModelShader->ID, "timeOfDay"), 1.0f); + glUniform1f(glGetUniformLocation(itemModelShader->ID, "ambientLight"), 1.0f); + glUniformMatrix4fv(glGetUniformLocation(itemModelShader->ID, "transform"), + 1, GL_FALSE, glm::value_ptr(mvp)); + glUniform1i(glGetUniformLocation(itemModelShader->ID, "textureArray"), 0); + + glEnable(GL_DEPTH_TEST); + + for (auto& [blockId, model] : g_itemModels) { - std::string fullPath = basePath + "/" + filename; - GLuint tex = loadHUDIcon(fullPath); - if (tex != 0) - { - g_blockIcons[blockId] = tex; - std::cout << "Loaded HUD icon for block " << (int)blockId << ": " << filename << std::endl; - } + GLuint iconTex; + glGenTextures(1, &iconTex); + glBindTexture(GL_TEXTURE_2D, iconTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ICON_SIZE, ICON_SIZE, 0, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, iconTex, 0); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_ARRAY, textureArray); + + glBindVertexArray(model.vao); + glDrawElements(GL_TRIANGLES, model.indexCount, GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + + g_blockIcons[blockId] = iconTex; } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + glDeleteRenderbuffers(1, &rbo); + glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); } void unloadBlockIcons() @@ -539,7 +584,15 @@ void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) skyLight = static_cast(c->skyLight[blockIndex(local.x, local.y, local.z)]) / static_cast(MAX_SKY_LIGHT); } } - g_particleSystem->spawnBlockBreakParticles(blockCenter, tileIndex, skyLight, 15); + glm::vec4 particleTint(1.0f); + if (g_blockTypes[oldBlock].faceTint[0]) + { + BiomeID biome = getBiomeAt(hit->blockPos.x, hit->blockPos.z); + bool isLeaf = g_blockTypes[oldBlock].transparent && g_blockTypes[oldBlock].solid; + glm::vec3 t = isLeaf ? getBiomeFoliageTint(biome) : getBiomeGrassTint(biome); + particleTint = glm::vec4(t, 1.0f); + } + g_particleSystem->spawnBlockBreakParticles(blockCenter, tileIndex, skyLight, 15, particleTint); } return; diff --git a/src/core/MainGlobals.h b/src/core/MainGlobals.h index 0e1ea20..d23c947 100644 --- a/src/core/MainGlobals.h +++ b/src/core/MainGlobals.h @@ -14,6 +14,8 @@ #include "../rendering/ParticleSystem.h" #include "GameState.h" +class Shader; + extern const int SCREEN_WIDTH; extern const int SCREEN_HEIGHT; extern const float MAX_RAYCAST_DISTANCE; @@ -86,6 +88,6 @@ void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods); void scrollCallback(GLFWwindow* window, double xoffset, double yoffset); std::string resolveTexturePath(const std::string& relativePath); GLuint loadHUDIcon(const std::string& path, bool useNearest = false); -void loadBlockIcons(const std::string& basePath); +void generateBlockIcons(GLuint textureArray, Shader* itemModelShader); void unloadBlockIcons(); void executeCommand(const std::string& input, Player& player); diff --git a/src/core/Renderer.cpp b/src/core/Renderer.cpp index 475aaa3..2c6bd43 100644 --- a/src/core/Renderer.cpp +++ b/src/core/Renderer.cpp @@ -13,11 +13,7 @@ #include #include #include -#include #include -#include - -extern bool showBiomeDebugColors; void Renderer::init() { @@ -32,7 +28,6 @@ void Renderer::init() fogColorLoc = glGetUniformLocation(shaderProgram->ID, "fogColor"); fogDensityLoc = glGetUniformLocation(shaderProgram->ID, "fogDensity"); ambientLightLoc = glGetUniformLocation(shaderProgram->ID, "ambientLight"); - biomeDebugTintLoc = glGetUniformLocation(shaderProgram->ID, "useBiomeDebugTint"); stbi_set_flip_vertically_on_load(false); @@ -49,64 +44,131 @@ void Renderer::init() glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &maxAnisotropy); glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, maxAnisotropy); - int width = 0, height = 0, nrChannels = 0; - std::string texturePath = resolveTexturePath("assets/textures/blocks.png"); - unsigned char* data = stbi_load(texturePath.c_str(), &width, &height, &nrChannels, 0); - - const int TILE_SIZE = 16; - const int TILES_X = 32; - const int TILES_Y = 32; - const int NUM_TILES = TILES_X * TILES_Y; + constexpr int TILE_SIZE = 16; + constexpr int NUM_LAYERS = TEX_COUNT; + + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, + TILE_SIZE, TILE_SIZE, NUM_LAYERS, 0, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + struct TexEntry { int layer; const char* filename; }; + TexEntry entries[] = { + { TEX_DIRT, "dirt.png" }, + { TEX_GRASS_TOP, "grass_top.png" }, + { TEX_GRASS_SIDE_SNOWED,"grass_side_snowed.png" }, + { TEX_STONE, "stone.png" }, + { TEX_SAND, "sand.png" }, + { TEX_LOG_OAK, "log_oak.png" }, + { TEX_LOG_OAK_TOP, "log_oak_top.png" }, + { TEX_LEAVES_OAK, "leaves_oak.png" }, + { TEX_GLASS, "glass.png" }, + { TEX_PLANKS_OAK, "planks_oak.png" }, + { TEX_COBBLESTONE, "cobblestone.png" }, + { TEX_LOG_SPRUCE, "log_spruce.png" }, + { TEX_LOG_SPRUCE_TOP, "log_spruce_top.png" }, + { TEX_LEAVES_SPRUCE, "leaves_spruce.png" }, + { TEX_PLANKS_SPRUCE, "planks_spruce.png" }, + { TEX_SNOW, "snow.png" }, + }; - if (data) + for (const auto& e : entries) { - GLenum internalFormat = (nrChannels == 4) ? GL_RGBA8 : GL_RGB8; - GLenum format = (nrChannels == 4) ? GL_RGBA : GL_RGB; - - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, internalFormat, - TILE_SIZE, TILE_SIZE, NUM_TILES, 0, - format, GL_UNSIGNED_BYTE, nullptr); - - std::vector tileData(TILE_SIZE * TILE_SIZE * nrChannels); - int tileSizeBytes = TILE_SIZE * nrChannels; - int atlasRowBytes = width * nrChannels; + std::string path = resolveTexturePath(std::string("assets/textures/") + e.filename); + int w = 0, h = 0, ch = 0; + unsigned char* pixels = stbi_load(path.c_str(), &w, &h, &ch, 4); + if (pixels) + { + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, + 0, 0, e.layer, + TILE_SIZE, TILE_SIZE, 1, + GL_RGBA, GL_UNSIGNED_BYTE, pixels); + stbi_image_free(pixels); + } + else + { + std::cerr << "Failed to load texture: " << path << std::endl; + unsigned char fallback[TILE_SIZE * TILE_SIZE * 4]; + for (int p = 0; p < TILE_SIZE * TILE_SIZE; p++) + { + fallback[p * 4 + 0] = 255; + fallback[p * 4 + 1] = 0; + fallback[p * 4 + 2] = 255; + fallback[p * 4 + 3] = 255; + } + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, + 0, 0, e.layer, + TILE_SIZE, TILE_SIZE, 1, + GL_RGBA, GL_UNSIGNED_BYTE, fallback); + } + } - for (int ty = 0; ty < TILES_Y; ty++) + { + std::string dirtPath = resolveTexturePath("assets/textures/dirt.png"); + std::string overlayPath = resolveTexturePath("assets/textures/grass_side_overlay.png"); + int dw = 0, dh = 0, dch = 0, ow = 0, oh = 0, och = 0; + unsigned char* dirtPx = stbi_load(dirtPath.c_str(), &dw, &dh, &dch, 4); + unsigned char* overlayPx = stbi_load(overlayPath.c_str(), &ow, &oh, &och, 4); + + unsigned char composite[TILE_SIZE * TILE_SIZE * 4]; + if (dirtPx && overlayPx) { - for (int tx = 0; tx < TILES_X; tx++) + for (int p = 0; p < TILE_SIZE * TILE_SIZE; p++) { - int tileIndex = ty * TILES_X + tx; - unsigned char* tileStart = data + (ty * TILE_SIZE) * atlasRowBytes + tx * tileSizeBytes; - for (int row = 0; row < TILE_SIZE; row++) + unsigned char oa = overlayPx[p * 4 + 3]; + if (oa > 0) + { + float a = static_cast(oa) / 255.0f; + for (int c = 0; c < 3; c++) + { + float bg = static_cast(dirtPx[p * 4 + c]) / 255.0f; + float fg = static_cast(overlayPx[p * 4 + c]) / 255.0f; + composite[p * 4 + c] = static_cast( + (fg * a + bg * (1.0f - a)) * 255.0f); + } + composite[p * 4 + 3] = 255; + } + else { - std::copy(tileStart + row * atlasRowBytes, - tileStart + row * atlasRowBytes + tileSizeBytes, - tileData.begin() + row * tileSizeBytes); + composite[p * 4 + 0] = dirtPx[p * 4 + 0]; + composite[p * 4 + 1] = dirtPx[p * 4 + 1]; + composite[p * 4 + 2] = dirtPx[p * 4 + 2]; + composite[p * 4 + 3] = 254; } - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, - 0, 0, tileIndex, - TILE_SIZE, TILE_SIZE, 1, - format, GL_UNSIGNED_BYTE, tileData.data()); } } - glGenerateMipmap(GL_TEXTURE_2D_ARRAY); - std::cout << "Loaded texture array with " << NUM_TILES << " tiles" << std::endl; - } - else - { - std::cerr << "Failed to load texture at " << texturePath << ": " - << stbi_failure_reason() << std::endl; - unsigned char fallback[] = {255, 0, 255, 255}; - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 1, 1, 1, 0, - GL_RGBA, GL_UNSIGNED_BYTE, fallback); + else + { + for (int p = 0; p < TILE_SIZE * TILE_SIZE; p++) + { + composite[p * 4 + 0] = 255; + composite[p * 4 + 1] = 0; + composite[p * 4 + 2] = 255; + composite[p * 4 + 3] = 255; + } + } + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, + 0, 0, TEX_GRASS_SIDE, + TILE_SIZE, TILE_SIZE, 1, + GL_RGBA, GL_UNSIGNED_BYTE, composite); + if (dirtPx) stbi_image_free(dirtPx); + if (overlayPx) stbi_image_free(overlayPx); } - stbi_image_free(data); + + + glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + std::cout << "Loaded " << NUM_LAYERS << " block textures" << std::endl; initBlockTypes(); - std::string hudIconPath = resolveTexturePath("assets/textures/hud_blocks"); - loadBlockIcons(hudIconPath); + itemModelShader = std::make_unique("item_model.vert", "item_model.frag"); + itemModelShader->Activate(); + glUniform1i(glGetUniformLocation(itemModelShader->ID, "textureArray"), 0); + itemTransformLoc = glGetUniformLocation(itemModelShader->ID, "transform"); + itemTimeOfDayLoc = glGetUniformLocation(itemModelShader->ID, "timeOfDay"); + itemAmbientLightLoc = glGetUniformLocation(itemModelShader->ID, "ambientLight"); + loadItemModels(); + generateBlockIcons(textureArray, itemModelShader.get()); loadToolModels(); for (int i = 0; i < 10; ++i) @@ -129,13 +191,6 @@ void Renderer::init() destroySkyLightLoc = glGetUniformLocation(destroyShader->ID, "SkyLight"); destroyFaceShadeLoc = glGetUniformLocation(destroyShader->ID, "FaceShade"); - itemModelShader = std::make_unique("item_model.vert", "item_model.frag"); - itemModelShader->Activate(); - glUniform1i(glGetUniformLocation(itemModelShader->ID, "textureArray"), 0); - itemTransformLoc = glGetUniformLocation(itemModelShader->ID, "transform"); - itemTimeOfDayLoc = glGetUniformLocation(itemModelShader->ID, "timeOfDay"); - itemAmbientLightLoc = glGetUniformLocation(itemModelShader->ID, "ambientLight"); - toolModelShader = std::make_unique("tool_model.vert", "tool_model.frag"); toolModelShader->Activate(); toolTransformLoc = glGetUniformLocation(toolModelShader->ID, "transform"); @@ -259,7 +314,6 @@ void Renderer::beginFrame(const FrameParams& fp) glUniform3fv(fogColorLoc, 1, glm::value_ptr(fp.fogCol)); glUniform1f(fogDensityLoc, fp.effectiveFogDensity); glUniform1f(ambientLightLoc, fp.ambientLight); - glUniform1i(biomeDebugTintLoc, showBiomeDebugColors ? 1 : 0); } void Renderer::renderChunks(const FrameParams& fp, ChunkManager& cm) diff --git a/src/core/Renderer.h b/src/core/Renderer.h index 0d54b06..175a809 100644 --- a/src/core/Renderer.h +++ b/src/core/Renderer.h @@ -31,7 +31,6 @@ struct Renderer GLint transformLoc = 0, modelLoc = 0, timeOfDayLoc = 0; GLint cameraPosLoc = 0, skyColorLoc = 0, fogColorLoc = 0; GLint fogDensityLoc = 0, ambientLightLoc = 0; - GLint biomeDebugTintLoc = 0; unsigned int textureArray = 0; diff --git a/src/core/main.cpp b/src/core/main.cpp index 49bef50..734f3bb 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -21,6 +21,9 @@ #include "../utils/BlockTypes.h" #include "../utils/CoordUtils.h" +#include "../world/Biome.h" +#include "../world/TerrainGenerator.h" + #include "../gameplay/Raycast.h" #include "../gameplay/Player.h" #include "../gameplay/SurvivalSystem.h" @@ -583,7 +586,15 @@ int main(int argc, char* argv[]) skyLightVal = static_cast(c->skyLight[blockIndex(local.x, local.y, local.z)]) / static_cast(MAX_SKY_LIGHT); } } - g_particleSystem->spawnBlockBreakParticles(blockCenter, tileIndex, skyLightVal, 15); + glm::vec4 particleTint(1.0f); + if (g_blockTypes[player.breakingBlockId].faceTint[0]) + { + BiomeID biome = getBiomeAt(hit->blockPos.x, hit->blockPos.z); + bool isLeaf = g_blockTypes[player.breakingBlockId].transparent && g_blockTypes[player.breakingBlockId].solid; + glm::vec3 t = isLeaf ? getBiomeFoliageTint(biome) : getBiomeGrassTint(biome); + particleTint = glm::vec4(t, 1.0f); + } + g_particleSystem->spawnBlockBreakParticles(blockCenter, tileIndex, skyLightVal, 15, particleTint); } player.isBreaking = false; diff --git a/src/gameplay/Inventory.cpp b/src/gameplay/Inventory.cpp index 360d6d1..ad7312f 100644 --- a/src/gameplay/Inventory.cpp +++ b/src/gameplay/Inventory.cpp @@ -20,6 +20,11 @@ const std::vector& getCreativeItems() {6, "Oak Leaves"}, {7, "Glass"}, {8, "Oak Planks"}, + {18, "Snow Grass"}, + {19, "Spruce Log"}, + {20, "Spruce Planks"}, + {21, "Spruce Leaves"}, + {22, "Cobblestone"}, {TOOL_DIAMOND_PICKAXE, "Diamond Pickaxe"}, }; return items; diff --git a/src/rendering/ItemModelGenerator.cpp b/src/rendering/ItemModelGenerator.cpp index 896425a..66f5dcc 100644 --- a/src/rendering/ItemModelGenerator.cpp +++ b/src/rendering/ItemModelGenerator.cpp @@ -1,5 +1,6 @@ #include "ItemModelGenerator.h" #include "../utils/BlockTypes.h" +#include "../world/Biome.h" #include "Meshing.h" #include #include @@ -36,6 +37,15 @@ ItemModel ItemModelGenerator::generateBlockCube(uint8_t blockId) int rotation = block.faceRotation[dir]; float faceShade = FACE_SHADE[dir]; + glm::vec3 tint(1.0f); + if (block.faceTint[dir]) + { + bool isLeaf = block.transparent && block.solid; + tint = isLeaf + ? getBiomeFoliageTint(BiomeID::Plains) + : getBiomeGrassTint(BiomeID::Plains); + } + uint32_t base = static_cast(vertices.size()); for (int vi = 0; vi < 4; vi++) @@ -74,7 +84,7 @@ ItemModel ItemModelGenerator::generateBlockCube(uint8_t blockId) break; } - vertices.push_back({pos, {localU, localV}, tileIndex, faceShade}); + vertices.push_back({pos, {localU, localV}, tileIndex, faceShade, tint}); } indices.push_back(base); @@ -119,6 +129,10 @@ ItemModel ItemModelGenerator::generateBlockCube(uint8_t blockId) (void*)offsetof(ItemVertex, faceShade)); glEnableVertexAttribArray(3); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(ItemVertex), + (void*)offsetof(ItemVertex, biomeTint)); + glEnableVertexAttribArray(4); + glBindVertexArray(0); model.indexCount = static_cast(indices.size()); @@ -135,7 +149,7 @@ void ItemModelGenerator::destroyModel(ItemModel& model) void loadItemModels() { - uint8_t blockIds[] = {1, 2, 3, 4, 5, 6, 7, 8}; + uint8_t blockIds[] = {1, 2, 3, 4, 5, 6, 7, 8, 18, 19, 20, 21, 22}; for (uint8_t blockId : blockIds) { ItemModel model = ItemModelGenerator::generateBlockCube(blockId); diff --git a/src/rendering/ItemModelGenerator.h b/src/rendering/ItemModelGenerator.h index b56f4da..53739a4 100644 --- a/src/rendering/ItemModelGenerator.h +++ b/src/rendering/ItemModelGenerator.h @@ -11,6 +11,7 @@ struct ItemVertex { glm::vec2 uv; float tileIndex; float faceShade; + glm::vec3 biomeTint; }; struct ItemModel { diff --git a/src/rendering/Meshing.cpp b/src/rendering/Meshing.cpp index fd2fbf7..e069253 100644 --- a/src/rendering/Meshing.cpp +++ b/src/rendering/Meshing.cpp @@ -25,18 +25,6 @@ static const uint32_t FACE_INDICES[6] = { 0, 1, 2, 0, 2, 3}; -static glm::vec3 getBiomeDebugTint(int worldX, int worldZ) -{ - BiomeID biome = getBiomeAt(worldX, worldZ); - switch (biome) - { - case BiomeID::Desert: return glm::vec3(1.00f, 0.88f, 0.52f); - case BiomeID::Forest: return glm::vec3(0.55f, 1.00f, 0.55f); - case BiomeID::Tundra: return glm::vec3(0.72f, 0.84f, 1.00f); - case BiomeID::Plains: return glm::vec3(1.00f, 0.78f, 0.78f); - default: return glm::vec3(1.0f); - } -} static float getFluidHeight(BlockGetter getBlock, int cornerX, int cornerY, int cornerZ) { @@ -358,9 +346,17 @@ static void buildGreedyMesh( int blockX = blockWorldPos.x; int blockY = blockWorldPos.y; int blockZ = blockWorldPos.z; - glm::vec3 biomeTint = getBiomeDebugTint( - chunkWorldOrigin.x + blockX, - chunkWorldOrigin.z + blockZ); + glm::vec3 biomeTint(1.0f); + if (g_blockTypes[type].faceTint[dir]) + { + BiomeID biome = getBiomeAt( + chunkWorldOrigin.x + blockX, + chunkWorldOrigin.z + blockZ); + bool isLeaf = g_blockTypes[type].transparent && g_blockTypes[type].solid; + biomeTint = isLeaf + ? getBiomeFoliageTint(biome) + : getBiomeGrassTint(biome); + } for (int vIdx = 0; vIdx < 4; vIdx++) { diff --git a/src/rendering/ParticleSystem.cpp b/src/rendering/ParticleSystem.cpp index 57de06a..77ba700 100644 --- a/src/rendering/ParticleSystem.cpp +++ b/src/rendering/ParticleSystem.cpp @@ -120,7 +120,7 @@ void ParticleSystem::render(const glm::mat4& view, const glm::mat4& projection, vao.Unbind(); } -void ParticleSystem::spawnBlockBreakParticles(const glm::vec3& blockCenter, int tileIndex, float skyLight, int count) +void ParticleSystem::spawnBlockBreakParticles(const glm::vec3& blockCenter, int tileIndex, float skyLight, int count, const glm::vec4& tint) { std::uniform_real_distribution posDist(-0.3f, 0.3f); std::uniform_real_distribution velXZ(-3.0f, 3.0f); @@ -137,7 +137,7 @@ void ParticleSystem::spawnBlockBreakParticles(const glm::vec3& blockCenter, int p.maxLifetime = p.lifetime; p.size = sizeDist(rng); p.tileIndex = static_cast(tileIndex); - p.color = glm::vec4(1.0f); + p.color = tint; p.skyLight = skyLight; particles.push_back(p); } diff --git a/src/rendering/ParticleSystem.h b/src/rendering/ParticleSystem.h index ac20aca..c352410 100644 --- a/src/rendering/ParticleSystem.h +++ b/src/rendering/ParticleSystem.h @@ -27,7 +27,7 @@ class ParticleSystem { void update(float dt); void render(const glm::mat4& view, const glm::mat4& projection, const glm::vec3& cameraPos, float timeOfDay, float ambientLight); - void spawnBlockBreakParticles(const glm::vec3& blockCenter, int tileIndex, float skyLight, int count = 12); + void spawnBlockBreakParticles(const glm::vec3& blockCenter, int tileIndex, float skyLight, int count = 12, const glm::vec4& tint = glm::vec4(1.0f)); void spawnParticle(const glm::vec3& pos, const glm::vec3& vel, float lifetime, float size, float tileIndex, float skyLight, const glm::vec4& color = glm::vec4(1.0f)); void clear(); diff --git a/src/shaders/default.frag b/src/shaders/default.frag index 3a62981..c441dbe 100644 --- a/src/shaders/default.frag +++ b/src/shaders/default.frag @@ -16,7 +16,6 @@ uniform vec3 skyColor; uniform vec3 fogColor; uniform float fogDensity; uniform float ambientLight; -uniform bool useBiomeDebugTint; void main() { @@ -30,11 +29,9 @@ void main() float totalLight = max(skyLightContribution, ambientLight); float finalLight = totalLight * FaceShade; - vec3 litColor = texColor.rgb * finalLight; - if (useBiomeDebugTint) - { - litColor *= BiomeTint; - } + float tintMask = step(0.998, texColor.a); + vec3 tint = mix(vec3(1.0), BiomeTint, tintMask); + vec3 litColor = texColor.rgb * tint * finalLight; float dist = length(WorldPos - cameraPos); float fogFactor = 1.0 - exp(-dist * fogDensity); @@ -46,5 +43,5 @@ void main() vec3 finalColor = mix(litColor, fogColor, fogFactor); - FragColor = vec4(finalColor, texColor.a); + FragColor = vec4(finalColor, 1.0); } diff --git a/src/shaders/item_model.frag b/src/shaders/item_model.frag index 81af8df..b867eed 100644 --- a/src/shaders/item_model.frag +++ b/src/shaders/item_model.frag @@ -4,6 +4,7 @@ out vec4 FragColor; in vec2 UV; flat in float TileIndex; in float FaceShade; +in vec3 BiomeTint; uniform sampler2DArray textureArray; uniform float timeOfDay; @@ -16,5 +17,7 @@ void main() discard; float totalLight = max(timeOfDay, ambientLight); float finalLight = totalLight * FaceShade; - FragColor = vec4(texColor.rgb * finalLight, texColor.a); + float tintMask = step(0.998, texColor.a); + vec3 tint = mix(vec3(1.0), BiomeTint, tintMask); + FragColor = vec4(texColor.rgb * tint * finalLight, 1.0); } diff --git a/src/shaders/item_model.vert b/src/shaders/item_model.vert index 59911ea..995c525 100644 --- a/src/shaders/item_model.vert +++ b/src/shaders/item_model.vert @@ -3,10 +3,12 @@ layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aUV; layout (location = 2) in float aTileIndex; layout (location = 3) in float aFaceShade; +layout (location = 4) in vec3 aBiomeTint; out vec2 UV; flat out float TileIndex; out float FaceShade; +out vec3 BiomeTint; uniform mat4 transform; @@ -16,4 +18,5 @@ void main() UV = aUV; TileIndex = aTileIndex; FaceShade = aFaceShade; -} \ No newline at end of file + BiomeTint = aBiomeTint; +} diff --git a/src/shaders/particle.frag b/src/shaders/particle.frag index ba638fb..33199ba 100644 --- a/src/shaders/particle.frag +++ b/src/shaders/particle.frag @@ -21,8 +21,9 @@ void main() float skyLightContribution = SkyLight * sunBrightness; float totalLight = max(skyLightContribution, ambientLight); + float tintMask = step(0.998, texColor.a); + vec3 tint = mix(vec3(1.0), ParticleColor.rgb, tintMask); + float fade = smoothstep(0.0, 0.3, Lifetime); - vec4 litColor = vec4(texColor.rgb * totalLight, texColor.a); - FragColor = litColor * ParticleColor; - FragColor.a *= fade; + FragColor = vec4(texColor.rgb * tint * totalLight, fade); } diff --git a/src/shaders/particle.vert b/src/shaders/particle.vert index 492d589..0d39df6 100644 --- a/src/shaders/particle.vert +++ b/src/shaders/particle.vert @@ -25,7 +25,7 @@ void main() + cameraUp * aQuadPos.y * aSize; gl_Position = projection * view * vec4(vertexPos, 1.0); - TexCoord = aQuadPos + 0.5; + TexCoord = vec2(aQuadPos.x + 0.5, 0.5 - aQuadPos.y); TileIndex = aTileIndex; ParticleColor = aColor; Lifetime = aLife; diff --git a/src/ui/DebugUI.cpp b/src/ui/DebugUI.cpp index ea43b79..da787bf 100644 --- a/src/ui/DebugUI.cpp +++ b/src/ui/DebugUI.cpp @@ -183,20 +183,6 @@ void drawDebugUI( ImGui::Begin("Fun Bullshit", nullptr, debugFlags); - if (ImGui::Button("Randomize Block Textures")) - { - randomizeBlockTextures(); - for (auto& pair : chunkManager->chunks) - pair.second->dirtyMesh = true; - } - - if (ImGui::Button("Reset Block Textures")) - { - resetBlockTextures(); - for (auto& pair : chunkManager->chunks) - pair.second->dirtyMesh = true; - } - ImGui::Separator(); ImGui::Text("VISUAL CHAOS"); diff --git a/src/utils/BlockTypes.cpp b/src/utils/BlockTypes.cpp index eff594c..044c71b 100644 --- a/src/utils/BlockTypes.cpp +++ b/src/utils/BlockTypes.cpp @@ -1,9 +1,34 @@ #include "BlockTypes.h" -#include std::array g_blockTypes; std::array g_defaultBlockTypes; +static void setAllFaces(BlockType& b, int tex) +{ + for (int i = 0; i < 6; i++) + { + b.faceTexture[i] = tex; + b.faceRotation[i] = 0; + b.faceTint[i] = false; + } +} + +static void setSolidOpaque(BlockType& b) +{ + b.solid = true; + b.transparent = false; + b.connectsToSame = false; + b.isLiquid = false; +} + +static void setSideRotations(BlockType& b) +{ + b.faceRotation[0] = 1; + b.faceRotation[1] = 1; + b.faceRotation[4] = 2; + b.faceRotation[5] = 2; +} + void initBlockTypes() { for (auto &block : g_blockTypes) @@ -16,122 +41,94 @@ void initBlockTypes() { block.faceTexture[i] = 0; block.faceRotation[i] = 0; + block.faceTint[i] = false; } } - // Block 0 is air + { + BlockType& b = g_blockTypes[1]; + setSolidOpaque(b); + setAllFaces(b, TEX_DIRT); + setSideRotations(b); + } - g_blockTypes[1].solid = true; - g_blockTypes[1].transparent = false; - for (int i = 0; i < 6; i++) { - g_blockTypes[1].faceTexture[i] = 229; // tile 2 = dirt - g_blockTypes[1].faceRotation[i] = 0; - } - g_blockTypes[1].faceRotation[0] = 1; // +X: 180 deg - g_blockTypes[1].faceRotation[1] = 1; // -X: 180 deg - g_blockTypes[1].faceRotation[4] = 2; // +Z: 180 deg - g_blockTypes[1].faceRotation[5] = 2; // -Z: 180 deg - - // Block 2: Grass (different top/side/bottom) - g_blockTypes[2].solid = true; - g_blockTypes[2].transparent = false; - g_blockTypes[2].faceTexture[0] = 78; // +X side (grass side) - g_blockTypes[2].faceTexture[1] = 78; // -X side - g_blockTypes[2].faceTexture[2] = 174; // +Y top (grass top) - g_blockTypes[2].faceTexture[3] = 229; // -Y bottom (dirt) - g_blockTypes[2].faceTexture[4] = 78; // +Z side - g_blockTypes[2].faceTexture[5] = 78; // -Z side - // Flip all side faces upside down (180 deg) - g_blockTypes[2].faceRotation[0] = 1; // +X: 180 deg - g_blockTypes[2].faceRotation[1] = 1; // -X: 180 deg - g_blockTypes[2].faceRotation[4] = 2; // +Z: 180 deg - g_blockTypes[2].faceRotation[5] = 2; // -Z: 180 deg - - // Block 3: Stone - g_blockTypes[3].solid = true; - g_blockTypes[3].transparent = false; - for (int i = 0; i < 6; i++) + BlockType& b = g_blockTypes[2]; + setSolidOpaque(b); + b.faceTexture[0] = TEX_GRASS_SIDE; + b.faceTexture[1] = TEX_GRASS_SIDE; + b.faceTexture[2] = TEX_GRASS_TOP; + b.faceTexture[3] = TEX_DIRT; + b.faceTexture[4] = TEX_GRASS_SIDE; + b.faceTexture[5] = TEX_GRASS_SIDE; + setSideRotations(b); + b.faceTint[0] = true; + b.faceTint[1] = true; + b.faceTint[2] = true; + b.faceTint[3] = false; + b.faceTint[4] = true; + b.faceTint[5] = true; + } + { - g_blockTypes[3].faceTexture[i] = 72; // tile 1 = stone - g_blockTypes[3].faceRotation[i] = 0; + BlockType& b = g_blockTypes[3]; + setSolidOpaque(b); + setAllFaces(b, TEX_STONE); + setSideRotations(b); } - g_blockTypes[3].faceRotation[0] = 1; // +X: 180 deg - g_blockTypes[3].faceRotation[1] = 1; // -X: 180 deg - g_blockTypes[3].faceRotation[4] = 2; // +Z: 180 deg - g_blockTypes[3].faceRotation[5] = 2; // -Z: 180 deg - // Block 4: Sand - g_blockTypes[4].solid = true; - g_blockTypes[4].transparent = false; - for (int i = 0; i < 6; i++) { - g_blockTypes[4].faceTexture[i] = 480; // sand (row 0, around column 18) - g_blockTypes[4].faceRotation[i] = 0; - } - g_blockTypes[4].faceRotation[0] = 1; // +X: 180 deg - g_blockTypes[4].faceRotation[1] = 1; // -X: 180 deg - g_blockTypes[4].faceRotation[4] = 2; // +Z: 180 deg - g_blockTypes[4].faceRotation[5] = 2; // -Z: 180 deg - - // Block 5: Oak Log (different top/bottom vs sides) - g_blockTypes[5].solid = true; - g_blockTypes[5].transparent = false; - g_blockTypes[5].faceTexture[0] = 330; // +X side (bark) - g_blockTypes[5].faceTexture[1] = 330; // -X side (bark) - g_blockTypes[5].faceTexture[2] = 329; // +Y top (log top) - g_blockTypes[5].faceTexture[3] = 329; // -Y bottom (log top) - g_blockTypes[5].faceTexture[4] = 330; // +Z side (bark) - g_blockTypes[5].faceTexture[5] = 330; // -Z side (bark) - g_blockTypes[5].faceRotation[0] = 1; - g_blockTypes[5].faceRotation[1] = 1; - g_blockTypes[5].faceRotation[4] = 2; - g_blockTypes[5].faceRotation[5] = 2; - - // Block 6: Oak Leaves (transparent - don't cull faces) - g_blockTypes[6].solid = true; - g_blockTypes[6].transparent = true; - for (int i = 0; i < 6; i++) + BlockType& b = g_blockTypes[4]; + setSolidOpaque(b); + setAllFaces(b, TEX_SAND); + setSideRotations(b); + } + { - g_blockTypes[6].faceTexture[i] = 328; - g_blockTypes[6].faceRotation[i] = 0; + BlockType& b = g_blockTypes[5]; + setSolidOpaque(b); + setAllFaces(b, TEX_LOG_OAK); + b.faceTexture[2] = TEX_LOG_OAK_TOP; + b.faceTexture[3] = TEX_LOG_OAK_TOP; + setSideRotations(b); } - // Block 7: Glass (transparent for light, but connects to same type like Minecraft) - g_blockTypes[7].solid = true; - g_blockTypes[7].transparent = true; - g_blockTypes[7].connectsToSame = true; - for (int i = 0; i < 6; i++) { - g_blockTypes[7].faceTexture[i] = 205; - g_blockTypes[7].faceRotation[i] = 0; + BlockType& b = g_blockTypes[6]; + b.solid = true; + b.transparent = true; + b.connectsToSame = false; + b.isLiquid = false; + setAllFaces(b, TEX_LEAVES_OAK); + for (int i = 0; i < 6; i++) + b.faceTint[i] = true; } - // Block 8: Oak Planks - g_blockTypes[8].solid = true; - g_blockTypes[8].transparent = false; - for (int i = 0; i < 6; i++) { - g_blockTypes[8].faceTexture[i] = 392; - g_blockTypes[8].faceRotation[i] = 0; + BlockType& b = g_blockTypes[7]; + b.solid = true; + b.transparent = true; + b.connectsToSame = true; + b.isLiquid = false; + setAllFaces(b, TEX_GLASS); + } + + { + BlockType& b = g_blockTypes[8]; + setSolidOpaque(b); + setAllFaces(b, TEX_PLANKS_OAK); + setSideRotations(b); } - g_blockTypes[8].faceRotation[0] = 1; - g_blockTypes[8].faceRotation[1] = 1; - g_blockTypes[8].faceRotation[4] = 2; - g_blockTypes[8].faceRotation[5] = 2; - // Block 9: Water Source - g_blockTypes[9].solid = false; - g_blockTypes[9].transparent = true; - g_blockTypes[9].connectsToSame = true; - g_blockTypes[9].isLiquid = true; - for (int i = 0; i < 6; i++) { - g_blockTypes[9].faceTexture[i] = 863; - g_blockTypes[9].faceRotation[i] = 0; + BlockType& b = g_blockTypes[9]; + b.solid = false; + b.transparent = true; + b.connectsToSame = true; + b.isLiquid = true; + setAllFaces(b, 0); } - // Blocks 10-17: Flowing Water (levels 7 down to 0) for (int level = 0; level < 8; level++) { int blockId = 10 + level; @@ -139,42 +136,63 @@ void initBlockTypes() g_blockTypes[blockId].transparent = true; g_blockTypes[blockId].connectsToSame = true; g_blockTypes[blockId].isLiquid = true; + setAllFaces(g_blockTypes[blockId], 0); + } + + { + BlockType& b = g_blockTypes[18]; + setSolidOpaque(b); + b.faceTexture[0] = TEX_GRASS_SIDE_SNOWED; + b.faceTexture[1] = TEX_GRASS_SIDE_SNOWED; + b.faceTexture[2] = TEX_SNOW; + b.faceTexture[3] = TEX_DIRT; + b.faceTexture[4] = TEX_GRASS_SIDE_SNOWED; + b.faceTexture[5] = TEX_GRASS_SIDE_SNOWED; + setSideRotations(b); for (int i = 0; i < 6; i++) - { - g_blockTypes[blockId].faceTexture[i] = 863; - g_blockTypes[blockId].faceRotation[i] = 0; - } + b.faceTint[i] = false; } - g_defaultBlockTypes = g_blockTypes; -} + { + BlockType& b = g_blockTypes[19]; + setSolidOpaque(b); + setAllFaces(b, TEX_LOG_SPRUCE); + b.faceTexture[2] = TEX_LOG_SPRUCE_TOP; + b.faceTexture[3] = TEX_LOG_SPRUCE_TOP; + setSideRotations(b); + } -void randomizeBlockTextures() -{ - static std::random_device rd; - static std::mt19937 gen(rd()); - std::uniform_int_distribution dist(0, ATLAS_TILES_X * ATLAS_TILES_Y - 1); + { + BlockType& b = g_blockTypes[20]; + setSolidOpaque(b); + setAllFaces(b, TEX_PLANKS_SPRUCE); + setSideRotations(b); + } - for (int blockId = 1; blockId < 256; blockId++) { - if (g_blockTypes[blockId].solid) - { - for (int face = 0; face < 6; face++) - { - g_blockTypes[blockId].faceTexture[face] = dist(gen); - } - } + BlockType& b = g_blockTypes[21]; + b.solid = true; + b.transparent = true; + b.connectsToSame = false; + b.isLiquid = false; + setAllFaces(b, TEX_LEAVES_SPRUCE); + for (int i = 0; i < 6; i++) + b.faceTint[i] = true; } -} -void resetBlockTextures() -{ - g_blockTypes = g_defaultBlockTypes; + { + BlockType& b = g_blockTypes[22]; + setSolidOpaque(b); + setAllFaces(b, TEX_COBBLESTONE); + setSideRotations(b); + } + + g_defaultBlockTypes = g_blockTypes; } float getBlockHardness(uint8_t blockId) { - switch (blockId) + switch (blockId) { case 1: return 1.0f; case 2: return 1.2f; @@ -184,6 +202,11 @@ float getBlockHardness(uint8_t blockId) case 6: return 0.2f; case 7: return 0.6f; case 8: return 1.0f; - default: return 1.0f; + case 18: return 1.2f; + case 19: return 1.0f; + case 20: return 1.0f; + case 21: return 0.2f; + case 22: return 2.0f; + default: return 1.0f; } } diff --git a/src/utils/BlockTypes.h b/src/utils/BlockTypes.h index 83acd41..aaa54cd 100644 --- a/src/utils/BlockTypes.h +++ b/src/utils/BlockTypes.h @@ -3,17 +3,35 @@ #include #include +enum BlockTex : int +{ + TEX_DIRT = 0, + TEX_GRASS_TOP, + TEX_GRASS_SIDE, + TEX_GRASS_SIDE_SNOWED, + TEX_STONE, + TEX_SAND, + TEX_LOG_OAK, + TEX_LOG_OAK_TOP, + TEX_LEAVES_OAK, + TEX_GLASS, + TEX_PLANKS_OAK, + TEX_COBBLESTONE, + TEX_LOG_SPRUCE, + TEX_LOG_SPRUCE_TOP, + TEX_LEAVES_SPRUCE, + TEX_PLANKS_SPRUCE, + TEX_SNOW, + TEX_COUNT +}; -constexpr int ATLAS_TILES_X = 32; // 512 / 16 = 32 tiles -constexpr int ATLAS_TILES_Y = 32; -constexpr float TILE_U = 1.0f / ATLAS_TILES_X; -constexpr float TILE_V = 1.0f / ATLAS_TILES_Y; float getBlockHardness(uint8_t blockId); struct BlockType { int faceTexture[6]; int faceRotation[6]; + bool faceTint[6]; bool solid; bool transparent; bool connectsToSame; @@ -24,8 +42,6 @@ extern std::array g_blockTypes; extern std::array g_defaultBlockTypes; void initBlockTypes(); -void randomizeBlockTextures(); -void resetBlockTextures(); inline bool isBlockSolid(uint8_t blockId) { @@ -44,14 +60,3 @@ inline bool isBlockLiquid(uint8_t blockId) if (blockId == 0) return false; return g_blockTypes[blockId].isLiquid; } - -inline glm::vec2 atlasUV(int tileIndex, float localU, float localV) -{ - int tx = tileIndex % ATLAS_TILES_X; - int ty = tileIndex / ATLAS_TILES_X; - - return glm::vec2( - (tx + localU) * TILE_U, - (ty + localV) * TILE_V - ); -} diff --git a/src/world/Biome.cpp b/src/world/Biome.cpp index 5a3a567..2bc3d21 100644 --- a/src/world/Biome.cpp +++ b/src/world/Biome.cpp @@ -22,7 +22,7 @@ namespace const BiomeDefinition TUNDRA{ BiomeID::Tundra, - 2, + 18, 1, TreeType::Spruce, 0.95f, @@ -69,4 +69,28 @@ BiomeID pickBiomeFromClimate(float temperature, float humidity) } return BiomeID::Plains; +} + +glm::vec3 getBiomeGrassTint(BiomeID biome) +{ + switch (biome) + { + case BiomeID::Desert: return glm::vec3(0.75f, 0.80f, 0.40f); + case BiomeID::Forest: return glm::vec3(0.47f, 0.74f, 0.32f); + case BiomeID::Tundra: return glm::vec3(0.55f, 0.70f, 0.55f); + case BiomeID::Plains: return glm::vec3(0.58f, 0.82f, 0.38f); + default: return glm::vec3(0.55f, 0.78f, 0.35f); + } +} + +glm::vec3 getBiomeFoliageTint(BiomeID biome) +{ + switch (biome) + { + case BiomeID::Desert: return glm::vec3(0.68f, 0.72f, 0.36f); + case BiomeID::Forest: return glm::vec3(0.38f, 0.62f, 0.24f); + case BiomeID::Tundra: return glm::vec3(0.42f, 0.56f, 0.42f); + case BiomeID::Plains: return glm::vec3(0.48f, 0.72f, 0.30f); + default: return glm::vec3(0.45f, 0.66f, 0.28f); + } } \ No newline at end of file diff --git a/src/world/Biome.h b/src/world/Biome.h index 3ba7a34..eae40fd 100644 --- a/src/world/Biome.h +++ b/src/world/Biome.h @@ -1,5 +1,6 @@ #pragma once #include +#include enum class BiomeID : uint8_t { @@ -27,4 +28,6 @@ struct BiomeDefinition }; const BiomeDefinition& getBiomeDefinition(BiomeID biome); -BiomeID pickBiomeFromClimate(float temperature, float humidity); \ No newline at end of file +BiomeID pickBiomeFromClimate(float temperature, float humidity); +glm::vec3 getBiomeGrassTint(BiomeID biome); +glm::vec3 getBiomeFoliageTint(BiomeID biome); \ No newline at end of file diff --git a/src/world/TerrainGenerator.cpp b/src/world/TerrainGenerator.cpp index 286710d..120a1cc 100644 --- a/src/world/TerrainGenerator.cpp +++ b/src/world/TerrainGenerator.cpp @@ -21,9 +21,12 @@ constexpr uint8_t BLOCK_DIRT = 1; constexpr uint8_t BLOCK_GRASS = 2; constexpr uint8_t BLOCK_STONE = 3; constexpr uint8_t BLOCK_SAND = 4; -constexpr uint8_t BLOCK_LOG = 5; -constexpr uint8_t BLOCK_LEAVES = 6; +constexpr uint8_t BLOCK_OAK_LOG = 5; +constexpr uint8_t BLOCK_OAK_LEAVES = 6; constexpr uint8_t BLOCK_WATER = 9; +constexpr uint8_t BLOCK_SNOW_GRASS = 18; +constexpr uint8_t BLOCK_SPRUCE_LOG = 19; +constexpr uint8_t BLOCK_SPRUCE_LEAVES = 21; constexpr int SEA_LEVEL = 116; @@ -236,6 +239,14 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) if (terrainHeight <= SEA_LEVEL + 2) continue; + uint8_t logBlock = BLOCK_OAK_LOG; + uint8_t leafBlock = BLOCK_OAK_LEAVES; + if (biome.treeType == TreeType::Spruce) + { + logBlock = BLOCK_SPRUCE_LOG; + leafBlock = BLOCK_SPRUCE_LEAVES; + } + int treeBaseY = terrainHeight + 1; int trunkHeight = TREE_TRUNK_HEIGHT; if (biome.treeType == TreeType::Spruce) @@ -248,7 +259,7 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) int localX = x; int localY = treeBaseY + ty - worldOffsetY; int localZ = z; - setBlockIfInChunk(blocks, localX, localY, localZ, BLOCK_LOG, true); + setBlockIfInChunk(blocks, localX, localY, localZ, logBlock, true); } int leafCenterY = treeBaseY + trunkHeight - 1; @@ -268,7 +279,7 @@ void generateTerrain(BlockID* blocks, int cx, int cy, int cz) int localX = x + lx; int localY = leafCenterY + ly - worldOffsetY; int localZ = z + lz; - setBlockIfInChunk(blocks, localX, localY, localZ, BLOCK_LEAVES); + setBlockIfInChunk(blocks, localX, localY, localZ, leafBlock); } } }