mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-07 12:18:46 +00:00
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.
This commit is contained in:
parent
4e3e807741
commit
78ec3937bf
@ -24,49 +24,48 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\level\generator\biome;
|
namespace pocketmine\level\generator\biome;
|
||||||
|
|
||||||
use pocketmine\level\biome\Biome;
|
use pocketmine\level\biome\Biome;
|
||||||
|
use pocketmine\level\biome\UnknownBiome;
|
||||||
use pocketmine\level\generator\noise\Simplex;
|
use pocketmine\level\generator\noise\Simplex;
|
||||||
use pocketmine\utils\Random;
|
use pocketmine\utils\Random;
|
||||||
|
|
||||||
class BiomeSelector{
|
abstract class BiomeSelector{
|
||||||
|
|
||||||
/** @var Biome */
|
|
||||||
private $fallback;
|
|
||||||
|
|
||||||
/** @var Simplex */
|
/** @var Simplex */
|
||||||
private $temperature;
|
private $temperature;
|
||||||
/** @var Simplex */
|
/** @var Simplex */
|
||||||
private $rainfall;
|
private $rainfall;
|
||||||
|
|
||||||
/** @var Biome[] */
|
/** @var Biome[]|\SplFixedArray */
|
||||||
private $biomes = [];
|
|
||||||
|
|
||||||
/** @var \SplFixedArray */
|
|
||||||
private $map = null;
|
private $map = null;
|
||||||
|
|
||||||
/** @var callable */
|
public function __construct(Random $random){
|
||||||
private $lookup;
|
|
||||||
|
|
||||||
public function __construct(Random $random, callable $lookup, Biome $fallback){
|
|
||||||
$this->fallback = $fallback;
|
|
||||||
$this->lookup = $lookup;
|
|
||||||
$this->temperature = new Simplex($random, 2, 1 / 16, 1 / 512);
|
$this->temperature = new Simplex($random, 2, 1 / 16, 1 / 512);
|
||||||
$this->rainfall = 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(){
|
public function recalculate(){
|
||||||
$this->map = new \SplFixedArray(64 * 64);
|
$this->map = new \SplFixedArray(64 * 64);
|
||||||
|
|
||||||
for($i = 0; $i < 64; ++$i){
|
for($i = 0; $i < 64; ++$i){
|
||||||
for($j = 0; $j < 64; ++$j){
|
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){
|
public function getTemperature($x, $z){
|
||||||
return ($this->temperature->noise2D($x, $z, true) + 1) / 2;
|
return ($this->temperature->noise2D($x, $z, true) + 1) / 2;
|
||||||
}
|
}
|
||||||
@ -85,7 +84,6 @@ class BiomeSelector{
|
|||||||
$temperature = (int) ($this->getTemperature($x, $z) * 63);
|
$temperature = (int) ($this->getTemperature($x, $z) * 63);
|
||||||
$rainfall = (int) ($this->getRainfall($x, $z) * 63);
|
$rainfall = (int) ($this->getRainfall($x, $z) * 63);
|
||||||
|
|
||||||
$biomeId = $this->map[$temperature + ($rainfall << 6)];
|
return $this->map[$temperature + ($rainfall << 6)];
|
||||||
return $this->biomes[$biomeId] ?? $this->fallback;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,56 +107,46 @@ class Normal extends Generator{
|
|||||||
$this->random->setSeed($this->level->getSeed());
|
$this->random->setSeed($this->level->getSeed());
|
||||||
$this->noiseBase = new Simplex($this->random, 4, 1 / 4, 1 / 32);
|
$this->noiseBase = new Simplex($this->random, 4, 1 / 4, 1 / 32);
|
||||||
$this->random->setSeed($this->level->getSeed());
|
$this->random->setSeed($this->level->getSeed());
|
||||||
$this->selector = new BiomeSelector($this->random, function($temperature, $rainfall){
|
$this->selector = new class($this->random) extends BiomeSelector{
|
||||||
if($rainfall < 0.25){
|
protected function lookup(float $temperature, float $rainfall) : int{
|
||||||
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.
|
|
||||||
if($rainfall < 0.25){
|
if($rainfall < 0.25){
|
||||||
return Biome::MOUNTAINS;
|
if($temperature < 0.7){
|
||||||
}elseif($rainfall < 0.70){
|
return Biome::OCEAN;
|
||||||
return Biome::SMALL_MOUNTAINS;
|
}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{
|
}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();
|
$this->selector->recalculate();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user