From 6f725b835509267943946867894110bce5e60a0e Mon Sep 17 00:00:00 2001 From: NSDeathman Date: Wed, 11 Mar 2026 20:46:50 +0300 Subject: [PATCH] Simple climate based biome scattering --- Minecraft.World/BiomeSource.cpp | 51 +++++++++++++------- Minecraft.World/BiomeSource.h | 2 + Minecraft.World/ClimateLayer.cpp | 81 ++++++++++++++++++++++++++++++++ Minecraft.World/ClimateLayer.h | 29 ++++++++++++ 4 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 Minecraft.World/ClimateLayer.cpp create mode 100644 Minecraft.World/ClimateLayer.h diff --git a/Minecraft.World/BiomeSource.cpp b/Minecraft.World/BiomeSource.cpp index 894226775..3be87c9a1 100644 --- a/Minecraft.World/BiomeSource.cpp +++ b/Minecraft.World/BiomeSource.cpp @@ -5,6 +5,7 @@ #include "net.minecraft.world.level.newbiome.layer.h" #include "System.h" #include "BiomeSource.h" +#include "ClimateLayer.h" #include "..\Minecraft.Client\Minecraft.h" #include "..\Minecraft.Client\ProgressRenderer.h" @@ -16,12 +17,12 @@ void BiomeSource::_init() cache = new BiomeCache(this); - playerSpawnBiomes.push_back(Biome::forest); - playerSpawnBiomes.push_back(Biome::taiga); // 4J-PB - Moving forward plains as a spawnable biome (mainly for the Superflat world) playerSpawnBiomes.push_back(Biome::plains); - playerSpawnBiomes.push_back(Biome::taigaHills); + playerSpawnBiomes.push_back(Biome::forest); playerSpawnBiomes.push_back(Biome::forestHills); + playerSpawnBiomes.push_back(Biome::taiga); + playerSpawnBiomes.push_back(Biome::taigaHills); playerSpawnBiomes.push_back(Biome::jungle); playerSpawnBiomes.push_back(Biome::jungleHills); } @@ -30,7 +31,8 @@ void BiomeSource::_init(int64_t seed, LevelType *generator) { _init(); - LayerArray layers = Layer::getDefaultLayers(seed, generator); + // LayerArray layers = Layer::getDefaultLayers(seed, generator); + LayerArray layers = getClimateLayers(seed, generator); layer = layers[0]; zoomedLayer = layers[1]; @@ -147,6 +149,16 @@ void BiomeSource::getTemperatureBlock(floatArray& temperatures, int x, int z, in } } +LayerArray BiomeSource::getClimateLayers(int64_t seed, LevelType* generator) +{ + LayerArray layers(2); + // Грубый слой (масштаб 0.04) — для предварительных проверок, если нужен + layers[0] = std::make_shared(seed, 4, 0.04); + // Детальный слой (масштаб 0.01) — для финальной карты биомов + layers[1] = std::make_shared(seed, 4, 0.01); + return layers; +} + BiomeArray BiomeSource::getRawBiomeBlock(int x, int z, int w, int h) const { BiomeArray biomes; @@ -408,6 +420,7 @@ void BiomeSource::update() } //#define DEBUG_SEEDS 50 +const int MAX_SEED_ATTEMPTS = 100; // 4J added - find a seed for this biomesource that matches certain criteria #ifdef __PSVITA__ @@ -422,7 +435,7 @@ int64_t BiomeSource::findSeed(LevelType *generator) ProgressRenderer *mcprogress = Minecraft::GetInstance()->progressRenderer; mcprogress->progressStage(IDS_PROGRESS_NEW_WORLD_SEED); -#ifndef _CONTENT_PACKAGE +#if 0//ndef _CONTENT_PACKAGE if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<progressStagePercentage( tryCount % 100 ); #ifdef __PSVITA__ - } while (!matchFound && *pServerRunning); + } while (!matchFound && *pServerRunning && tryCount < MAX_SEED_ATTEMPTS); #else - } while (!matchFound); + } while (!matchFound && tryCount < MAX_SEED_ATTEMPTS); #endif // Clean up delete pr; delete indices.data; -#ifdef DEBUG_SEEDS - app.DebugPrintf("%d: %d tries taken, seed used is %lld\n", k, tryCount, bestSeed); +#if 1//def DEBUG_SEEDS + //app.DebugPrintf("%d: %d tries taken, seed used is %lld\n", k, tryCount, bestSeed); - BiomeSource *biomeSource = new BiomeSource(bestSeed); - BiomeArray biomes = biomeSource->getBiomeBlock(-27 * 16, -27 * 16, 54 * 16, 54 * 16); + int size = 64 * 16; - unsigned int *pixels = new unsigned int[54 * 16 * 54 * 16]; - for(int i = 0; i < 54 * 16 * 54 * 16; i++ ) + BiomeSource *biomeSource = new BiomeSource(bestSeed, generator); + BiomeArray biomes = biomeSource->getBiomeBlock(-27 * 16, -27 * 16, size, size); + + unsigned int *pixels = new unsigned int[size * size]; + for(int i = 0; i < size * size; i++ ) { int id = biomes[i]->id; @@ -519,13 +534,13 @@ int64_t BiomeSource::findSeed(LevelType *generator) if( id & 8 ) pixels[i] |= 0x00808080; } D3DXIMAGE_INFO srcInfo; - srcInfo.Format = D3DFMT_LIN_A8R8G8B8; - srcInfo.ImageFileFormat = D3DXIFF_BMP; - srcInfo.Width = 54 * 16; - srcInfo.Height = 54 * 16; + //srcInfo.Format = D3DFMT_LIN_A8R8G8B8; + //srcInfo.ImageFileFormat = D3DXIFF_BMP; + srcInfo.Width = size; + srcInfo.Height = size; char buf[256]; - sprintf(buf,"GAME:\\BiomeTest%d.bmp",k); + sprintf(buf,"GAME:\\BiomeTest%d.bmp", bestSeed); RenderManager.SaveTextureData(buf, &srcInfo, (int *)pixels); delete [] pixels; diff --git a/Minecraft.World/BiomeSource.h b/Minecraft.World/BiomeSource.h index a1e8a50ba..a8b9985e9 100644 --- a/Minecraft.World/BiomeSource.h +++ b/Minecraft.World/BiomeSource.h @@ -47,6 +47,8 @@ public: virtual Biome *getBiome(ChunkPos *cp); virtual Biome *getBiome(int x, int z); + static LayerArray getClimateLayers(int64_t seed, LevelType* generator); + // 4J - changed the interface for these methods, mainly for thread safety virtual float getDownfall(int x, int z) const; virtual floatArray getDownfallBlock(int x, int z, int w, int h) const; diff --git a/Minecraft.World/ClimateLayer.cpp b/Minecraft.World/ClimateLayer.cpp new file mode 100644 index 000000000..33cbe2789 --- /dev/null +++ b/Minecraft.World/ClimateLayer.cpp @@ -0,0 +1,81 @@ +#include "stdafx.h" +#include "ClimateLayer.h" +#include "Random.h" +#include "Mth.h" +#include "IntCache.h" + +ClimateLayer::ClimateLayer(int64_t seed, int octaves, double scale) + : Layer(seed), scale(scale) +{ + Random random(seed); + temperatureNoise = new PerlinNoise(&random, octaves); + humidityNoise = new PerlinNoise(&random, octaves); + initBiomeCenters(); +} + +ClimateLayer::~ClimateLayer() +{ + delete temperatureNoise; + delete humidityNoise; +} + +void ClimateLayer::initBiomeCenters() +{ + // Задаём центры основных биомов в пространстве (температура, влажность) + // Значения подобраны на основе setTemperatureAndDownfall() из Biome.cpp + centers.push_back({ 0.00f, 0.25f, Biome::iceFlats }); // холодные равнины + centers.push_back({ 0.10f, 0.80f, Biome::taiga }); // тайга + centers.push_back({ 0.30f, 0.30f, Biome::extremeHills }); // экстремальные холмы + centers.push_back({ 0.70f, 0.40f, Biome::plains }); // равнины + centers.push_back({ 0.70f, 0.70f, Biome::forest }); // лес + centers.push_back({ 0.80f, 0.95f, Biome::swampland }); // болото + centers.push_back({ 0.95f, 0.05f, Biome::desert }); // пустыня + centers.push_back({ 0.95f, 0.85f, Biome::jungle }); // джунгли + + // Примечание: океанические биомы не включены — они будут определяться по высоте отдельно. + // Холмистые варианты (desertHills, forestHills и т.д.) можно добавить позже, + // используя дополнительный шум высоты или второй проход. +} + +Biome* ClimateLayer::getBiomeForClimate(float temp, float hum) const +{ + float bestDist = 1e9f; + Biome* bestBiome = Biome::plains; // запасной вариант + + for (const auto& c : centers) { + float dx = temp - c.temp; + float dy = hum - c.hum; + float dist = dx * dx + dy * dy; // квадрат евклидова расстояния + if (dist < bestDist) { + bestDist = dist; + bestBiome = c.biome; + } + } + return bestBiome; +} + +intArray ClimateLayer::getArea(int xo, int yo, int w, int h) +{ + IntCache::releaseAll(); + intArray result = IntCache::allocate(w * h); + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + int worldX = xo + x; + int worldY = yo + y; + + // Получаем значения шума в диапазоне примерно [-1, 1] + double tempNoise = temperatureNoise->getValue(worldX * scale, worldY * scale); + double humNoise = humidityNoise->getValue(worldX * scale, worldY * scale); + + // Нормализуем в [0, 1] (логистическая функция или просто clamp) + float normTemp = Mth::clamp((float)((tempNoise + 1.0) * 0.5), 0.0f, 1.0f); + float normHum = Mth::clamp((float)((humNoise + 1.0) * 0.5), 0.0f, 1.0f); + + Biome* biome = getBiomeForClimate(normTemp, normHum); + result[x + y * w] = biome->id; + } + } + + return result; +} diff --git a/Minecraft.World/ClimateLayer.h b/Minecraft.World/ClimateLayer.h new file mode 100644 index 000000000..c97bf5772 --- /dev/null +++ b/Minecraft.World/ClimateLayer.h @@ -0,0 +1,29 @@ +#pragma once +#include "Layer.h" +#include "PerlinNoise.h" +#include "Biome.h" +#include + +class ClimateLayer : public Layer +{ +private: + PerlinNoise* temperatureNoise; + PerlinNoise* humidityNoise; + double scale; // масштаб шума (чем меньше, тем крупнее биомы) + + struct BiomeCenter { + float temp; + float hum; + Biome* biome; + }; + std::vector centers; + + void initBiomeCenters(); // заполнить центры биомов + Biome* getBiomeForClimate(float temp, float hum) const; + +public: + ClimateLayer(int64_t seed, int octaves = 4, double scale = 0.01); + virtual ~ClimateLayer(); + + virtual intArray getArea(int xo, int yo, int w, int h) override; +};