From 78ec3937bfeae25b1966c6e8a7955287890b23a7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 8 Jun 2018 10:13:29 +0100 Subject: [PATCH] BiomeSelector: drastically simplified implementation and made more robust - Doesn't need to be pre-populated with biomes prior to calculating the heatmap - now population of biomes is entirely dependent on the lookup function, improving consistency - Uses an abstract class method for lookup instead of callback (use anonymous class instead) - Faster because the heatmap is directly populated with biomes instead of biome IDs, removing an unnecessary lookup. --- .../level/generator/biome/BiomeSelector.php | 42 +++++----- .../level/generator/normal/Normal.php | 82 ++++++++----------- 2 files changed, 56 insertions(+), 68 deletions(-) diff --git a/src/pocketmine/level/generator/biome/BiomeSelector.php b/src/pocketmine/level/generator/biome/BiomeSelector.php index b5e519575..5d80d607e 100644 --- a/src/pocketmine/level/generator/biome/BiomeSelector.php +++ b/src/pocketmine/level/generator/biome/BiomeSelector.php @@ -24,49 +24,48 @@ declare(strict_types=1); namespace pocketmine\level\generator\biome; use pocketmine\level\biome\Biome; +use pocketmine\level\biome\UnknownBiome; use pocketmine\level\generator\noise\Simplex; use pocketmine\utils\Random; -class BiomeSelector{ - - /** @var Biome */ - private $fallback; - +abstract class BiomeSelector{ /** @var Simplex */ private $temperature; /** @var Simplex */ private $rainfall; - /** @var Biome[] */ - private $biomes = []; - - /** @var \SplFixedArray */ + /** @var Biome[]|\SplFixedArray */ private $map = null; - /** @var callable */ - private $lookup; - - public function __construct(Random $random, callable $lookup, Biome $fallback){ - $this->fallback = $fallback; - $this->lookup = $lookup; + public function __construct(Random $random){ $this->temperature = new Simplex($random, 2, 1 / 16, 1 / 512); $this->rainfall = new Simplex($random, 2, 1 / 16, 1 / 512); } + /** + * Lookup function called by recalculate() to determine the biome to use for this temperature and rainfall. + * + * @param float $temperature + * @param float $rainfall + * + * @return int biome ID 0-255 + */ + abstract protected function lookup(float $temperature, float $rainfall) : int; + public function recalculate(){ $this->map = new \SplFixedArray(64 * 64); for($i = 0; $i < 64; ++$i){ for($j = 0; $j < 64; ++$j){ - $this->map[$i + ($j << 6)] = call_user_func($this->lookup, $i / 63, $j / 63); + $biome = Biome::getBiome($this->lookup($i / 63, $j / 63)); + if($biome instanceof UnknownBiome){ + throw new \RuntimeException("Unknown biome returned by selector with ID " . $biome->getId()); + } + $this->map[$i + ($j << 6)] = $biome; } } } - public function addBiome(Biome $biome){ - $this->biomes[$biome->getId()] = $biome; - } - public function getTemperature($x, $z){ return ($this->temperature->noise2D($x, $z, true) + 1) / 2; } @@ -85,7 +84,6 @@ class BiomeSelector{ $temperature = (int) ($this->getTemperature($x, $z) * 63); $rainfall = (int) ($this->getRainfall($x, $z) * 63); - $biomeId = $this->map[$temperature + ($rainfall << 6)]; - return $this->biomes[$biomeId] ?? $this->fallback; + return $this->map[$temperature + ($rainfall << 6)]; } } diff --git a/src/pocketmine/level/generator/normal/Normal.php b/src/pocketmine/level/generator/normal/Normal.php index 88ddf3619..07cb790d1 100644 --- a/src/pocketmine/level/generator/normal/Normal.php +++ b/src/pocketmine/level/generator/normal/Normal.php @@ -107,56 +107,46 @@ class Normal extends Generator{ $this->random->setSeed($this->level->getSeed()); $this->noiseBase = new Simplex($this->random, 4, 1 / 4, 1 / 32); $this->random->setSeed($this->level->getSeed()); - $this->selector = new BiomeSelector($this->random, function($temperature, $rainfall){ - if($rainfall < 0.25){ - if($temperature < 0.7){ - return Biome::OCEAN; - }elseif($temperature < 0.85){ - return Biome::RIVER; - }else{ - return Biome::SWAMP; - } - }elseif($rainfall < 0.60){ - if($temperature < 0.25){ - return Biome::ICE_PLAINS; - }elseif($temperature < 0.75){ - return Biome::PLAINS; - }else{ - return Biome::DESERT; - } - }elseif($rainfall < 0.80){ - if($temperature < 0.25){ - return Biome::TAIGA; - }elseif($temperature < 0.75){ - return Biome::FOREST; - }else{ - return Biome::BIRCH_FOREST; - } - }else{ - //FIXME: This will always cause River to be used since the rainfall is always greater than 0.8 if we - //reached this branch. However I don't think that substituting temperature for rainfall is correct given - //that mountain biomes are supposed to be pretty cold. + $this->selector = new class($this->random) extends BiomeSelector{ + protected function lookup(float $temperature, float $rainfall) : int{ if($rainfall < 0.25){ - return Biome::MOUNTAINS; - }elseif($rainfall < 0.70){ - return Biome::SMALL_MOUNTAINS; + if($temperature < 0.7){ + return Biome::OCEAN; + }elseif($temperature < 0.85){ + return Biome::RIVER; + }else{ + return Biome::SWAMP; + } + }elseif($rainfall < 0.60){ + if($temperature < 0.25){ + return Biome::ICE_PLAINS; + }elseif($temperature < 0.75){ + return Biome::PLAINS; + }else{ + return Biome::DESERT; + } + }elseif($rainfall < 0.80){ + if($temperature < 0.25){ + return Biome::TAIGA; + }elseif($temperature < 0.75){ + return Biome::FOREST; + }else{ + return Biome::BIRCH_FOREST; + } }else{ - return Biome::RIVER; + //FIXME: This will always cause River to be used since the rainfall is always greater than 0.8 if we + //reached this branch. However I don't think that substituting temperature for rainfall is correct given + //that mountain biomes are supposed to be pretty cold. + if($rainfall < 0.25){ + return Biome::MOUNTAINS; + }elseif($rainfall < 0.70){ + return Biome::SMALL_MOUNTAINS; + }else{ + return Biome::RIVER; + } } } - }, Biome::getBiome(Biome::OCEAN)); - - $this->selector->addBiome(Biome::getBiome(Biome::OCEAN)); - $this->selector->addBiome(Biome::getBiome(Biome::PLAINS)); - $this->selector->addBiome(Biome::getBiome(Biome::DESERT)); - $this->selector->addBiome(Biome::getBiome(Biome::MOUNTAINS)); - $this->selector->addBiome(Biome::getBiome(Biome::FOREST)); - $this->selector->addBiome(Biome::getBiome(Biome::TAIGA)); - $this->selector->addBiome(Biome::getBiome(Biome::SWAMP)); - $this->selector->addBiome(Biome::getBiome(Biome::RIVER)); - $this->selector->addBiome(Biome::getBiome(Biome::ICE_PLAINS)); - $this->selector->addBiome(Biome::getBiome(Biome::SMALL_MOUNTAINS)); - $this->selector->addBiome(Biome::getBiome(Biome::BIRCH_FOREST)); + }; $this->selector->recalculate();