diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 69da40c92..e4c7af760 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -54,11 +54,12 @@ use pocketmine\level\format\anvil\Anvil; use pocketmine\level\format\leveldb\LevelDB; use pocketmine\level\format\LevelProviderManager; use pocketmine\level\format\mcregion\McRegion; +use pocketmine\level\generator\biome\Biome; use pocketmine\level\generator\Flat; use pocketmine\level\generator\GenerationInstanceManager; use pocketmine\level\generator\GenerationRequestManager; use pocketmine\level\generator\Generator; -use pocketmine\level\generator\Normal; +use pocketmine\level\generator\normal\Normal; use pocketmine\level\Level; use pocketmine\metadata\EntityMetadataStore; use pocketmine\metadata\LevelMetadataStore; @@ -1603,6 +1604,7 @@ class Server{ InventoryType::init(); Block::init(); Item::init(); + Biome::init(); TextWrapper::init(); $this->craftingManager = new CraftingManager(); diff --git a/src/pocketmine/level/generator/Generator.php b/src/pocketmine/level/generator/Generator.php index 2da87339c..8e8a07ebd 100644 --- a/src/pocketmine/level/generator/Generator.php +++ b/src/pocketmine/level/generator/Generator.php @@ -25,6 +25,7 @@ namespace pocketmine\level\generator; use pocketmine\level\generator\noise\Noise; +use pocketmine\level\generator\normal\Normal; use pocketmine\utils\Random; abstract class Generator{ @@ -196,12 +197,12 @@ abstract class Generator{ $nz = (int) ($zz / $zSamplingRate) * $zSamplingRate; $ny = (int) ($yy / $ySamplingRate) * $ySamplingRate; $noiseArray[$xx][$zz][$yy] = Noise::trilinearLerp( - $xx, $yy, $zz, $noiseArray[$nx][$nz][$ny], $noiseArray[$nx][$nz][$ny + $ySamplingRate], - $noiseArray[$nx][$nz + $zSamplingRate][$ny], $noiseArray[$nx][$nz + $zSamplingRate][$ny + $ySamplingRate], - $noiseArray[$nx + $xSamplingRate][$nz][$ny], $noiseArray[$nx + $xSamplingRate][$nz][$ny + $ySamplingRate], - $noiseArray[$nx + $xSamplingRate][$nz + $zSamplingRate][$ny], - $noiseArray[$nx + $xSamplingRate][$nz + $zSamplingRate][$ny + $ySamplingRate], - $nx, $nx + $zSamplingRate, $ny, $ny + $ySamplingRate, $nz, $nz + $zSamplingRate + $xx, $yy, $zz, $noiseArray[$nx][$nz][$ny], $noiseArray[$nx][$nz][$nny = $ny + $ySamplingRate], + $noiseArray[$nx][$nnz = $nz + $zSamplingRate][$ny], $noiseArray[$nx][$nnz][$nny], + $noiseArray[$nnx = $nx + $xSamplingRate][$nz][$ny], $noiseArray[$nnx][$nz][$nny], + $noiseArray[$nnx][$nnz][$ny], + $noiseArray[$nnx][$nnz][$nny], + $nx, $nnx, $ny, $nny, $nz, $nnz ); } } diff --git a/src/pocketmine/level/generator/biome/Biome.php b/src/pocketmine/level/generator/biome/Biome.php new file mode 100644 index 000000000..a29b35b65 --- /dev/null +++ b/src/pocketmine/level/generator/biome/Biome.php @@ -0,0 +1,123 @@ +setId((int) $id); + } + + public static function init(){ + if(self::$setup === false){ + self::$setup = true; + self::register(self::OCEAN, new OceanBiome()); + self::register(self::PLAINS, new PlainBiome()); + self::register(self::DESERT, new DesertBiome()); + self::register(self::MOUNTAINS, new MountainsBiome()); + self::register(self::FOREST, new ForestBiome()); + + self::register(self::RIVER, new RiverBiome()); + + self::register(self::BEACH, new BeachBiome()); + + self::register(self::SMALL_MOUNTAINS, new SmallMountainsBiome()); + } + } + + /** + * @param $id + * + * @return Biome + */ + public static function getBiome($id){ + return isset(self::$biomes[$id]) ? self::$biomes[$id] : null; + } + + public function clearPopulators(){ + $this->populators = []; + } + + public function addPopulator(Populator $populator){ + $this->populators[] = $populator; + } + + public function populateChunk(ChunkManager $level, $chunkX, $chunkZ, Random $random){ + foreach($this->populators as $populator){ + $populator->populate($level, $chunkX, $chunkZ, $random); + } + } + + public function getPopulators(){ + return $this->populators; + } + + public function setId($id){ + if(!$this->registered){ + $this->registered = true; + $this->id = $id; + } + } + + public function getId(){ + return $this->id; + } + + public abstract function getName(); +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/biome/BiomeSelector.php b/src/pocketmine/level/generator/biome/BiomeSelector.php new file mode 100644 index 000000000..70ce44fd6 --- /dev/null +++ b/src/pocketmine/level/generator/biome/BiomeSelector.php @@ -0,0 +1,78 @@ +fallback = $fallback; + $this->temperature = new Simplex($random, 1, 0.004, 0.5, 2); + $this->rainfall = new Simplex($random, 1, 0.004, 0.5, 2); + } + + public function addBiome(Biome $biome, $start, $end){ + $this->biomes[$biome->getId()] = $biome; + $this->select[$biome->getId()] = [$biome->getId(), $start, $end]; + } + + /** + * @param $x + * @param $z + * + * @return Biome + */ + public function pickBiome($x, $z){ + //$temperature = $this->temperature->noise2D($x, $z); + $rainfall = $this->rainfall->noise2D($x, $z); + + if($rainfall > 0.9){ + return Biome::getBiome(Biome::OCEAN); + }elseif($rainfall > 0.7){ + return Biome::getBiome(Biome::RIVER); + }elseif($rainfall > 0.6){ + return Biome::getBiome(Biome::BEACH); + }elseif($rainfall > 0.2){ + return Biome::getBiome(Biome::PLAINS); + }elseif($rainfall > -0.3){ + return Biome::getBiome(Biome::FOREST); + }else{ + return Biome::getBiome(Biome::DESERT); + } + + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/noise/Noise.php b/src/pocketmine/level/generator/noise/Noise.php index 1d0fab140..2c5a36483 100644 --- a/src/pocketmine/level/generator/noise/Noise.php +++ b/src/pocketmine/level/generator/noise/Noise.php @@ -55,19 +55,35 @@ abstract class Noise{ } public static function bilinearLerp($x, $y, $q00, $q01, $q10, $q11, $x1, $x2, $y1, $y2){ - $q0 = self::linearLerp($x, $x1, $x2, $q00, $q10); - $q1 = self::linearLerp($x, $x1, $x2, $q01, $q11); - return self::linearLerp($y, $y1, $y2, $q0, $q1); + $dx1 = (($x2 - $x) / ($x2 - $x1)); + $dx2 = (($x - $x1) / ($x2 - $x1)); + + return (($y2 - $y) / ($y2 - $y1)) * ( + $dx1 * $q00 + $dx2 * $q10 + ) + (($y - $y1) / ($y2 - $y1)) * ( + $dx1 * $q01 + $dx2 * $q11 + ); } public static function trilinearLerp($x, $y, $z, $q000, $q001, $q010, $q011, $q100, $q101, $q110, $q111, $x1, $x2, $y1, $y2, $z1, $z2) { - $q00 = self::linearLerp($x, $x1, $x2, $q000, $q100); - $q01 = self::linearLerp($x, $x1, $x2, $q010, $q110); - $q10 = self::linearLerp($x, $x1, $x2, $q001, $q101); - $q11 = self::linearLerp($x, $x1, $x2, $q011, $q111); - $q0 = self::linearLerp($y, $y1, $y2, $q00, $q10); - $q1 = self::linearLerp($y, $y1, $y2, $q01, $q11); - return self::linearLerp($z, $z1, $z2, $q0, $q1); + $dx1 = (($x2 - $x) / ($x2 - $x1)); + $dx2 = (($x - $x1) / ($x2 - $x1)); + $dy1 = (($y2 - $y) / ($y2 - $y1)); + $dy2 = (($y - $y1) / ($y2 - $y1)); + + return (($z2 - $z) / ($z2 - $z1)) * ( + $dy1 * ( + $dx1 * $q000 + $dx2 * $q100 + ) + $dy2 * ( + $dx1 * $q001 + $dx2 * $q101 + ) + ) + (($z - $z1) / ($z2 - $z1)) * ( + $dy1 * ( + $dx1 * $q010 + $dx2 * $q110 + ) + $dy2 * ( + $dx1 * $q011 + $dx2 * $q111 + ) + ); } public static function grad($hash, $x, $y, $z){ diff --git a/src/pocketmine/level/generator/Normal.php b/src/pocketmine/level/generator/normal/Normal.php similarity index 77% rename from src/pocketmine/level/generator/Normal.php rename to src/pocketmine/level/generator/normal/Normal.php index c24da6988..6bd5ab793 100644 --- a/src/pocketmine/level/generator/Normal.php +++ b/src/pocketmine/level/generator/normal/Normal.php @@ -19,7 +19,7 @@ * */ -namespace pocketmine\level\generator; +namespace pocketmine\level\generator\normal; use pocketmine\block\Block; use pocketmine\block\CoalOre; @@ -30,13 +30,19 @@ use pocketmine\block\Gravel; use pocketmine\block\IronOre; use pocketmine\block\LapisOre; use pocketmine\block\RedstoneOre; +use pocketmine\level\generator\biome\Biome; +use pocketmine\level\generator\biome\BiomeSelector; +use pocketmine\level\generator\GenerationChunkManager; +use pocketmine\level\generator\Generator; use pocketmine\level\generator\noise\Perlin; use pocketmine\level\generator\noise\Simplex; +use pocketmine\level\generator\normal\biome\NormalBiome; use pocketmine\level\generator\object\OreType; use pocketmine\level\generator\populator\Ore; use pocketmine\level\generator\populator\Populator; use pocketmine\level\generator\populator\TallGrass; use pocketmine\level\generator\populator\Tree; +use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; @@ -53,6 +59,9 @@ class Normal extends Generator{ /** @var Simplex */ private $noiseBase; + /** @var BiomeSelector */ + private $selector; + private static $GAUSSIAN_KERNEL = null; private static $SMOOTH_SIZE = 2; @@ -66,8 +75,8 @@ class Normal extends Generator{ self::$GAUSSIAN_KERNEL = []; $bellSize = 1 / self::$SMOOTH_SIZE; $bellHeight = 2 * self::$SMOOTH_SIZE; - for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx) { - for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz) { + for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){ + for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){ $bx = $bellSize * $sx; $bz = $bellSize * $sz; self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE] = $bellHeight * exp(-($bx * $bx + $bz * $bz) / 2); @@ -88,6 +97,8 @@ class Normal extends Generator{ $this->random = $random; $this->random->setSeed($this->level->getSeed()); $this->noiseBase = new Simplex($this->random, 16, 0.012, 0.5, 2); + $this->random->setSeed($this->level->getSeed()); + $this->selector = new BiomeSelector($this->random, Biome::getBiome(Biome::OCEAN)); $ores = new Ore(); @@ -121,11 +132,11 @@ class Normal extends Generator{ $start = microtime(true); - $noise = Generator::getFastNoise3D($this->noiseBase, 16, 128, 16, 4, 16, 4, $chunkX * 16, 0, $chunkZ * 16); + $noise = Generator::getFastNoise3D($this->noiseBase, 16, 128, 16, 8, 8, 8, $chunkX * 16, 0, $chunkZ * 16); $chunk = $this->level->getChunk($chunkX, $chunkZ); - var_dump((microtime(true) - $start) * 1000); + $biomeCache = []; for($x = 0; $x < 16; ++$x){ for($z = 0; $z < 16; ++$z){ @@ -133,10 +144,21 @@ class Normal extends Generator{ $maxSum = 0; $weightSum = 0; - //TODO: biome things - $minSum = 63; - $maxSum = 127; - $weightSum += 1; + for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){ + for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){ + if(isset($biomeCache[$index = (($chunkX * 16 + $x + $sx * 16) >> 2) . ":" . (($chunkZ * 16 + $z + $sz * 16) >> 2)])){ + $adjacent = $biomeCache[$index]; + }else{ + $adjacent = $this->selector->pickBiome($chunkX * 16 + $x + $sx * 16, $chunkZ * 16 + $z + $sz * 16); + $biomeCache[$index] = $adjacent; + } + /** @var NormalBiome $adjacent */ + $weight = self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE]; + $minSum += $adjacent->getMinElevation() * $weight; + $maxSum += $adjacent->getMaxElevation() * $weight; + $weightSum += $weight; + } + } $minElevation = $minSum / $weightSum; $smoothHeight = ($maxSum / $weightSum - $minElevation) / 2; @@ -154,10 +176,14 @@ class Normal extends Generator{ if($y <= $this->waterHeight){ $chunk->setBlockId($x, $y, $z, Block::STILL_WATER); } + $chunk->setBlockSkyLight($x, $y, $z, 15); } } } } + + + var_dump((microtime(true) - $start) * 1000); } public function populateChunk($chunkX, $chunkZ){ diff --git a/src/pocketmine/level/generator/normal/biome/BeachBiome.php b/src/pocketmine/level/generator/normal/biome/BeachBiome.php new file mode 100644 index 000000000..2c67883da --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/BeachBiome.php @@ -0,0 +1,35 @@ +setElevation(62, 65); + } + + public function getName(){ + return "Beach"; + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/biome/DesertBiome.php b/src/pocketmine/level/generator/normal/biome/DesertBiome.php new file mode 100644 index 000000000..180db1fa7 --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/DesertBiome.php @@ -0,0 +1,35 @@ +setElevation(63, 74); + } + + public function getName(){ + return "Desert"; + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/biome/ForestBiome.php b/src/pocketmine/level/generator/normal/biome/ForestBiome.php new file mode 100644 index 000000000..be18d7498 --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/ForestBiome.php @@ -0,0 +1,47 @@ +setBaseAmount(5); + $this->addPopulator($trees); + + $tallGrass = new TallGrass(); + $tallGrass->setBaseAmount(3); + + $this->addPopulator($tallGrass); + + $this->setElevation(63, 81); + } + + public function getName(){ + return "Forest"; + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/biome/GrassyBiome.php b/src/pocketmine/level/generator/normal/biome/GrassyBiome.php new file mode 100644 index 000000000..6431cd8a0 --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/GrassyBiome.php @@ -0,0 +1,37 @@ +setGroundCover([ + Block::get(Block::GRASS, 0), + Block::get(Block::DIRT, 0), + Block::get(Block::DIRT, 0), + Block::get(Block::DIRT, 0), + Block::get(Block::DIRT, 0), + ]); + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/biome/MountainsBiome.php b/src/pocketmine/level/generator/normal/biome/MountainsBiome.php new file mode 100644 index 000000000..f0854ab1f --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/MountainsBiome.php @@ -0,0 +1,49 @@ +setBaseAmount(1); + $this->addPopulator($trees); + + $tallGrass = new TallGrass(); + $tallGrass->setBaseAmount(1); + + $this->addPopulator($tallGrass); + + //TODO: add emerald + + $this->setElevation(63, 127); + } + + public function getName(){ + return "Mountains"; + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/biome/NormalBiome.php b/src/pocketmine/level/generator/normal/biome/NormalBiome.php new file mode 100644 index 000000000..581bc6795 --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/NormalBiome.php @@ -0,0 +1,53 @@ +minElevation; + } + + public function getMaxElevation(){ + return $this->maxElevation; + } + + public function setElevation($min, $max){ + $this->minElevation = $min; + $this->maxElevation = $max; + } + + public function getGroundCover(){ + return $this->groundCover; + } + + public function setGroundCover(array $covers){ + $this->groundCover = $covers; + } +} diff --git a/src/pocketmine/level/generator/normal/biome/OceanBiome.php b/src/pocketmine/level/generator/normal/biome/OceanBiome.php new file mode 100644 index 000000000..92bb99d2a --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/OceanBiome.php @@ -0,0 +1,42 @@ +setBaseAmount(5); + + $this->addPopulator($tallGrass); + + $this->setElevation(46, 58); + } + + public function getName(){ + return "Ocean"; + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/biome/PlainBiome.php b/src/pocketmine/level/generator/normal/biome/PlainBiome.php new file mode 100644 index 000000000..e48a6eee8 --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/PlainBiome.php @@ -0,0 +1,42 @@ +setBaseAmount(5); + + $this->addPopulator($tallGrass); + + $this->setElevation(63, 74); + } + + public function getName(){ + return "Plains"; + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/biome/RiverBiome.php b/src/pocketmine/level/generator/normal/biome/RiverBiome.php new file mode 100644 index 000000000..fcedca423 --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/RiverBiome.php @@ -0,0 +1,42 @@ +setBaseAmount(5); + + $this->addPopulator($tallGrass); + + $this->setElevation(58, 62); + } + + public function getName(){ + return "River"; + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/biome/SandyBiome.php b/src/pocketmine/level/generator/normal/biome/SandyBiome.php new file mode 100644 index 000000000..cb3b41021 --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/SandyBiome.php @@ -0,0 +1,36 @@ +setGroundCover([ + Block::get(Block::SAND, 0), + Block::get(Block::SANDSTONE, 0), + Block::get(Block::SANDSTONE, 0), + Block::get(Block::SANDSTONE, 0), + ]); + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/biome/SmallMountainsBiome.php b/src/pocketmine/level/generator/normal/biome/SmallMountainsBiome.php new file mode 100644 index 000000000..9321d19a4 --- /dev/null +++ b/src/pocketmine/level/generator/normal/biome/SmallMountainsBiome.php @@ -0,0 +1,38 @@ +setElevation(63, 97); + } + + public function getName(){ + return "Small Mountains"; + } +} \ No newline at end of file