Improved biome generation, get grass color from gradient interpolation, improved performance of generation, try to recreate grass colors from imported chunks, closes #2845, closes #1792

This commit is contained in:
Shoghi Cervantes 2015-06-07 15:17:02 +02:00
parent d881dbf1a2
commit cbb1c55a06
No known key found for this signature in database
GPG Key ID: 78464DB0A7837F89
23 changed files with 142 additions and 126 deletions

View File

@ -1237,8 +1237,8 @@ class Server{
$order = [];
for($X = -4; $X <= 4; ++$X){
for($Z = -4; $Z <= 4; ++$Z){
for($X = -3; $X <= 3; ++$X){
for($Z = -3; $Z <= 3; ++$Z){
$distance = $X ** 2 + $Z ** 2;
$chunkX = $X + $centerX;
$chunkZ = $Z + $centerZ;
@ -1251,7 +1251,7 @@ class Server{
foreach($order as $index => $distance){
Level::getXZ($index, $chunkX, $chunkZ);
$level->generateChunk($chunkX, $chunkZ, true);
$level->populateChunk($chunkX, $chunkZ, true);
}
return true;

View File

@ -94,6 +94,11 @@ class Chunk extends BaseChunk{
parent::__construct($level, (int) $this->nbt["xPos"], (int) $this->nbt["zPos"], $sections, $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue());
if(isset($this->nbt->Biomes)){
$this->checkOldBiomes($this->nbt->Biomes->getValue());
unset($this->nbt->Biomes);
}
unset($this->nbt->Sections);
}

View File

@ -64,7 +64,7 @@ abstract class BaseChunk extends BaseFullChunk implements Chunk{
if(count($biomeColors) === 256){
$this->biomeColors = $biomeColors;
}else{
$this->biomeColors = array_fill(0, 256, Binary::readInt("\x01\x85\xb2\x4a"));
$this->biomeColors = array_fill(0, 256, Binary::readInt("\xff\x00\x00\x00"));
}
if(count($heightMap) === 256){

View File

@ -25,6 +25,7 @@ use pocketmine\block\Block;
use pocketmine\entity\Entity;
use pocketmine\level\format\FullChunk;
use pocketmine\level\format\LevelProvider;
use pocketmine\level\generator\biome\Biome;
use pocketmine\nbt\tag\Compound;
use pocketmine\Player;
use pocketmine\tile\Tile;
@ -94,7 +95,7 @@ abstract class BaseFullChunk implements FullChunk{
if(count($biomeColors) === 256){
$this->biomeColors = $biomeColors;
}else{
$this->biomeColors = array_fill(0, 256, Binary::readInt("\x01\x85\xb2\x4a"));
$this->biomeColors = array_fill(0, 256, Binary::readInt("\xff\x00\x00\x00\x00"));
}
if(count($heightMap) === 256){
@ -107,6 +108,21 @@ abstract class BaseFullChunk implements FullChunk{
$this->NBTentities = $entities;
}
protected function checkOldBiomes($data){
if(strlen($data) !== 256){
return;
}
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){
$biome = Biome::getBiome(ord($data{($z << 4) + $x}));
$this->setBiomeId($x, $z, $biome->getId());
$c = $biome->getColor();
$this->setBiomeColor($x, $z, $c >> 16, ($c >> 8) & 0xff, $c & 0xff);
}
}
}
public function initChunk(){
if($this->getProvider() instanceof LevelProvider and !$this->isInit){
$changed = false;

View File

@ -88,6 +88,12 @@ class Chunk extends BaseFullChunk{
}
parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $this->nbt->Blocks->getValue(), $this->nbt->Data->getValue(), $this->nbt->SkyLight->getValue(), $this->nbt->BlockLight->getValue(), $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue());
if(isset($this->nbt->Biomes)){
$this->checkOldBiomes($this->nbt->Biomes->getValue());
unset($this->nbt->Biomes);
}
unset($this->nbt->Blocks);
unset($this->nbt->Data);
unset($this->nbt->SkyLight);

View File

@ -197,7 +197,7 @@ abstract class Generator{
for($xx = 0; $xx <= $xSize; $xx += $xSamplingRate){
for($zz = 0; $zz <= $zSize; $zz += $zSamplingRate){
for($yy = 0; $yy <= $ySize; $yy += $ySamplingRate){
$noiseArray[$xx][$zz][$yy] = $noise->noise3D($x + $xx, $y + $yy, $z + $zz);
$noiseArray[$xx][$zz][$yy] = $noise->noise3D($x + $xx, $y + $yy, $z + $zz, true);
}
}
}

View File

@ -73,10 +73,12 @@ abstract class Biome{
protected $rainfall = 0.5;
protected $temperature = 0.5;
protected $grassColor = 0;
protected static function register($id, Biome $biome){
self::$biomes[(int) $id] = $biome;
$biome->setId((int) $id);
$biome->grassColor = self::generateBiomeColor($biome->getTemperature(), $biome->getRainfall());
}
public static function init(){
@ -173,9 +175,29 @@ abstract class Biome{
return $this->rainfall;
}
private static function generateBiomeColor($temperature, $rainfall){
$x = (1 - $temperature) * 255;
$z = (1 - $rainfall * $temperature) * 255;
$c = self::interpolateColor(256, $x, $z, [0x47, 0xd0, 0x33], [0x6c, 0xb4, 0x93], [0xbf, 0xb6, 0x55], [0x80, 0xb4, 0x97]);
return ((int) ($c[0] << 16)) | (int) (($c[1] << 8)) | (int) ($c[2]);
}
private static function interpolateColor($size, $x, $z, $c1, $c2, $c3, $c4){
$l1 = self::lerpColor($c1, $c2, $x / $size);
$l2 = self::lerpColor($c3, $c4, $x / $size);
return self::lerpColor($l1, $l2, $z / $size);
}
private static function lerpColor($a, $b, $s){
$invs = 1 - $s;
return [$a[0] * $invs + $b[0] * $s, $a[1] * $invs + $b[1] * $s, $a[2] * $invs + $b[2] * $s];
}
/**
* @return int (randomness|Red|Green|Blue)
* @return int (Red|Green|Blue)
*/
abstract public function getColor();
}

View File

@ -44,8 +44,8 @@ class BiomeSelector{
public function __construct(Random $random, callable $lookup, Biome $fallback){
$this->fallback = $fallback;
$this->lookup = $lookup;
$this->temperature = new Simplex($random, 1, 0.001, 1, 1);
$this->rainfall = new Simplex($random, 1, 0.001, 1, 1);
$this->temperature = new Simplex($random, 2, 1 / 16, 1 / 512);
$this->rainfall = new Simplex($random, 2, 1 / 16, 1 / 512);
}
public function recalculate(){

View File

@ -21,9 +21,6 @@
/**
* Different noise generators for level generation
*
* WARNING: This class is available on the PocketMine-MP Zephir project.
* If this class is modified, remember to modify the PHP C extension.
*/
namespace pocketmine\level\generator\noise;
@ -34,16 +31,15 @@ abstract class Noise{
protected $offsetY = 0;
protected $offsetZ = 0;
protected $octaves = 8;
protected $frequency;
protected $lacunarity;
protected $amplitude;
protected $persistence;
protected $expansion;
public static function floor($x){
return $x >= 0 ? (int) $x : (int) ($x - 1);
}
public static function fade($x){
return $x ** 3 * ($x * ($x * 6 - 15) + 10);
return $x * $x * $x * ($x * ($x * 6 - 15) + 10);
}
public static function lerp($x, $y, $z){
@ -101,18 +97,19 @@ abstract class Noise{
public function noise2D($x, $z, $normalized = false){
$result = 0;
$amp = 1;
$laq = 1;
$freq = 1;
$max = 0;
$x *= $this->frequency;
$z *= $this->frequency;
$x *= $this->expansion;
$z *= $this->expansion;
for($i = 0; $i < $this->octaves; ++$i){
$result += $this->getNoise2D($x * $laq, $z * $laq) * $amp;
$result += $this->getNoise2D($x * $freq, $z * $freq) * $amp;
$max += $amp;
$laq *= $this->lacunarity;
$amp *= $this->amplitude;
$freq *= 2;
$amp *= $this->persistence;
}
if($normalized === true){
$result /= $max;
}
@ -123,19 +120,20 @@ abstract class Noise{
public function noise3D($x, $y, $z, $normalized = false){
$result = 0;
$amp = 1;
$laq = 1;
$freq = 1;
$max = 0;
$x *= $this->frequency;
$y *= $this->frequency;
$z *= $this->frequency;
$x *= $this->expansion;
$y *= $this->expansion;
$z *= $this->expansion;
for($i = 0; $i < $this->octaves; ++$i){
$result += $this->getNoise3D($x * $laq, $y * $laq, $z * $laq) * $amp;
$result += $this->getNoise3D($x * $freq, $y * $freq, $z * $freq) * $amp;
$max += $amp;
$laq *= $this->lacunarity;
$amp *= $this->amplitude;
$freq *= 2;
$amp *= $this->persistence;
}
if($normalized === true){
$result /= $max;
}

View File

@ -31,11 +31,10 @@ class Perlin extends Noise{
];
public function __construct(Random $random, $octaves, $frequency, $amplitude, $lacunarity){
public function __construct(Random $random, $octaves, $persistence, $expansion = 1){
$this->octaves = $octaves;
$this->frequency = $frequency;
$this->lacunarity = $lacunarity;
$this->amplitude = $amplitude;
$this->persistence = $persistence;
$this->expansion = $expansion;
$this->offsetX = $random->nextFloat() * 256;
$this->offsetY = $random->nextFloat() * 256;
$this->offsetZ = $random->nextFloat() * 256;
@ -80,9 +79,9 @@ class Perlin extends Noise{
//$fX = self::fade($x);
//$fY = self::fade($y);
//$fZ = self::fade($z);
$fX = $x ** 3 * ($x * ($x * 6 - 15) + 10);
$fY = $y ** 3 * ($y * ($y * 6 - 15) + 10);
$fZ = $z ** 3 * ($z * ($z * 6 - 15) + 10);
$fX = $x * $x * $x * ($x * ($x * 6 - 15) + 10);
$fY = $y * $y * $y * ($y * ($y * 6 - 15) + 10);
$fZ = $z * $z * $z * ($z * ($z * 6 - 15) + 10);
//Cube corners
$A = $this->perm[$X] + $Y;

View File

@ -63,8 +63,8 @@ class Simplex extends Perlin{
protected $offsetW;
public function __construct(Random $random, $octaves, $frequency, $amplitude, $lacunarity){
parent::__construct($random, $octaves, $frequency, $amplitude, $lacunarity);
public function __construct(Random $random, $octaves, $persistence, $expansion = 1){
parent::__construct($random, $octaves, $persistence, $expansion);
$this->offsetW = $random->nextFloat() * 256;
self::$SQRT_3 = sqrt(3);
self::$SQRT_5 = sqrt(5);
@ -187,28 +187,28 @@ class Simplex extends Perlin{
$n = 0;
// Calculate the contribution from the four corners
$t0 = 0.6 - $x0 ** 2 - $y0 ** 2 - $z0 ** 2;
$t0 = 0.6 - $x0 * $x0 - $y0 * $y0 - $z0 * $z0;
if($t0 > 0){
$gi0 = self::$grad3[$this->perm[$ii + $this->perm[$jj + $this->perm[$kk]]] % 12];
$n += $t0 ** 4 * ($gi0[0] * $x0 + $gi0[1] * $y0 + $gi0[2] * $z0);
$n += $t0 * $t0 * $t0 * $t0 * ($gi0[0] * $x0 + $gi0[1] * $y0 + $gi0[2] * $z0);
}
$t1 = 0.6 - $x1 ** 2 - $y1 ** 2 - $z1 ** 2;
$t1 = 0.6 - $x1 * $x1 - $y1 * $y1 - $z1 * $z1;
if($t1 > 0){
$gi1 = self::$grad3[$this->perm[$ii + $i1 + $this->perm[$jj + $j1 + $this->perm[$kk + $k1]]] % 12];
$n += $t1 ** 4 * ($gi1[0] * $x1 + $gi1[1] * $y1 + $gi1[2] * $z1);
$n += $t1 * $t1 * $t1 * $t1 * ($gi1[0] * $x1 + $gi1[1] * $y1 + $gi1[2] * $z1);
}
$t2 = 0.6 - $x2 ** 2 - $y2 ** 2 - $z2 ** 2;
$t2 = 0.6 - $x2 * $x2 - $y2 * $y2 - $z2 * $z2;
if($t2 > 0){
$gi2 = self::$grad3[$this->perm[$ii + $i2 + $this->perm[$jj + $j2 + $this->perm[$kk + $k2]]] % 12];
$n += $t2 ** 4 * ($gi2[0] * $x2 + $gi2[1] * $y2 + $gi2[2] * $z2);
$n += $t2 * $t2 * $t2 * $t2 * ($gi2[0] * $x2 + $gi2[1] * $y2 + $gi2[2] * $z2);
}
$t3 = 0.6 - $x3 ** 2 - $y3 ** 2 - $z3 ** 2;
$t3 = 0.6 - $x3 * $x3 - $y3 * $y3 - $z3 * $z3;
if($t3 > 0){
$gi3 = self::$grad3[$this->perm[$ii + 1 + $this->perm[$jj + 1 + $this->perm[$kk + 1]]] % 12];
$n += $t3 ** 4 * ($gi3[0] * $x3 + $gi3[1] * $y3 + $gi3[2] * $z3);
$n += $t3 * $t3 * $t3 * $t3 * ($gi3[0] * $x3 + $gi3[1] * $y3 + $gi3[2] * $z3);
}
// Add contributions from each corner to get the noise value.
@ -258,22 +258,22 @@ class Simplex extends Perlin{
$n = 0;
// Calculate the contribution from the three corners
$t0 = 0.5 - $x0 ** 2 - $y0 ** 2;
$t0 = 0.5 - $x0 * $x0 - $y0 * $y0;
if($t0 > 0){
$gi0 = self::$grad3[$this->perm[$ii + $this->perm[$jj]] % 12];
$n += $t0 ** 4 * ($gi0[0] * $x0 + $gi0[1] * $y0); // (x,y) of grad3 used for 2D gradient
$n += $t0 * $t0 * $t0 * $t0 * ($gi0[0] * $x0 + $gi0[1] * $y0); // (x,y) of grad3 used for 2D gradient
}
$t1 = 0.5 - $x1 ** 2 - $y1 ** 2;
$t1 = 0.5 - $x1 * $x1 - $y1 * $y1;
if($t1 > 0){
$gi1 = self::$grad3[$this->perm[$ii + $i1 + $this->perm[$jj + $j1]] % 12];
$n += $t1 ** 4 * ($gi1[0] * $x1 + $gi1[1] * $y1);
$n += $t1 * $t1 * $t1 * $t1 * ($gi1[0] * $x1 + $gi1[1] * $y1);
}
$t2 = 0.5 - $x2 ** 2 - $y2 ** 2;
$t2 = 0.5 - $x2 * $x2 - $y2 * $y2;
if($t2 > 0){
$gi2 = self::$grad3[$this->perm[$ii + 1 + $this->perm[$jj + 1]] % 12];
$n += $t2 ** 4 * ($gi2[0] * $x2 + $gi2[1] * $y2);
$n += $t2 * $t2 * $t2 * $t2 * ($gi2[0] * $x2 + $gi2[1] * $y2);
}
// Add contributions from each corner to get the noise value.

View File

@ -121,21 +121,10 @@ class Normal extends Generator{
$this->level = $level;
$this->random = $random;
$this->random->setSeed($this->level->getSeed());
$this->noiseBase = new Simplex($this->random, 16, 0.01, 0.5, 2);
$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){
$rainfall *= $temperature;
if($temperature < 0.10){
return Biome::ICE_PLAINS;
}elseif($rainfall < 0.20){
if($temperature < 0.50){
return Biome::ICE_PLAINS;
}elseif($temperature < 0.95){
return Biome::PLAINS;
}else{
return Biome::DESERT;
}
}elseif($rainfall > 0.5 and $temperature < 0.7){
if($rainfall < 0.25){
if($rainfall < 0.7){
return Biome::OCEAN;
}elseif($rainfall < 0.85){
@ -143,24 +132,30 @@ class Normal extends Generator{
}else{
return Biome::SWAMP;
}
}elseif($temperature < 0.50){
return Biome::TAIGA;
}elseif($temperature < 0.97){
if($rainfall < 0.25){
return Biome::MOUNTAINS;
}elseif($rainfall < 0.35){
return Biome::SMALL_MOUNTAINS;
}elseif($rainfall < 0.60){
if($temperature < 0.25){
return Biome::ICE_PLAINS;
}elseif($temperature < 0.75){
return Biome::PLAINS;
}else{
return Biome::PLAINS;
return Biome::DESERT;
}
}else{
if($rainfall < 0.45){
return Biome::PLAINS;
}elseif($rainfall < 0.90){
}elseif($rainfall < 0.80){
if($temperature < 0.25){
return Biome::TAIGA;
}elseif($temperature < 0.75){
return Biome::FOREST;
}else{
return Biome::BIRCH_FOREST;
}
}else{
if($rainfall < 0.25){
return Biome::MOUNTAINS;
}elseif($rainfall < 0.70){
return Biome::SMALL_MOUNTAINS;
}else{
return Biome::RIVER;
}
}
}, Biome::getBiome(Biome::OCEAN));
@ -212,8 +207,7 @@ class Normal extends Generator{
$biome = $this->pickBiome($chunkX * 16 + $x, $chunkZ * 16 + $z);
$chunk->setBiomeId($x, $z, $biome->getId());
$color = $biome->getColor();
$chunk->setBiomeColor($x, $z, $color >> 16, ($color >> 8) & 0xff, $color & 0xff);
$color = [0, 0, 0];
for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){
for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){
@ -231,8 +225,13 @@ class Normal extends Generator{
}
}
$minSum += $adjacent->getMinElevation() * $weight;
$minSum += ($adjacent->getMinElevation() - 1) * $weight;
$maxSum += $adjacent->getMaxElevation() * $weight;
$bColor = $adjacent->getColor();
$color[0] += (($bColor >> 16) ** 2) * $weight;
$color[1] += ((($bColor >> 8) & 0xff) ** 2) * $weight;
$color[2] += (($bColor & 0xff) ** 2) * $weight;
$weightSum += $weight;
}
}
@ -240,6 +239,8 @@ class Normal extends Generator{
$minSum /= $weightSum;
$maxSum /= $weightSum;
$chunk->setBiomeColor($x, $z, sqrt($color[0] / $weightSum), sqrt($color[1] / $weightSum), sqrt($color[2] / $weightSum));
$smoothHeight = ($maxSum - $minSum) / 2;
for($y = 0; $y < 128; ++$y){
@ -249,19 +250,11 @@ class Normal extends Generator{
}
$noiseValue = $noise[$x][$z][$y] - 1 / $smoothHeight * ($y - $smoothHeight - $minSum);
if($noiseValue >= 0){
if($noiseValue > 0){
$chunk->setBlockId($x, $y, $z, Block::STONE);
}/*else{
if($y <= $this->waterHeight){
$chunk->setBlockId($x, $y, $z, Block::STILL_WATER);
$lightValue = 15 - ($this->waterHeight - $y) * 2;
if($lightValue > 0){
$chunk->setBlockSkyLight($x, $y, $z, $lightValue);
}
}else{
$chunk->setBlockSkyLight($x, $y, $z, 15);
}
}*/
}elseif($y <= $this->waterHeight){
$chunk->setBlockId($x, $y, $z, Block::STILL_WATER);
}
}
}
}

View File

@ -60,8 +60,4 @@ class ForestBiome extends GrassyBiome{
public function getName(){
return $this->type === self::TYPE_BIRCH ? "Birch Forest" : "Forest";
}
public function getColor(){
return 0x056621;
}
}

View File

@ -42,8 +42,4 @@ class IcePlainsBiome extends SnowyBiome{
public function getName(){
return "Ice Plains";
}
public function getColor(){
return 0x163933;
}
}

View File

@ -24,7 +24,8 @@ namespace pocketmine\level\generator\normal\biome;
use pocketmine\level\generator\biome\Biome;
abstract class NormalBiome extends Biome{
public function getColor(){
return 0xffb360; //Detect wrong biomes
return $this->grassColor;
}
}

View File

@ -42,8 +42,4 @@ class OceanBiome extends GrassyBiome{
public function getName(){
return "Ocean";
}
public function getColor(){
return 0x8da360;
}
}

View File

@ -42,8 +42,4 @@ class PlainBiome extends GrassyBiome{
public function getName(){
return "Plains";
}
public function getColor(){
return 0x8db360;
}
}

View File

@ -42,8 +42,4 @@ class RiverBiome extends GrassyBiome{
public function getName(){
return "River";
}
public function getColor(){
return 0x8dc360;
}
}

View File

@ -37,6 +37,6 @@ class SwampBiome extends GrassyBiome{
}
public function getColor(){
return 0x07f9b2;
return 0x6a7039;
}
}

View File

@ -48,8 +48,4 @@ class TaigaBiome extends SnowyBiome{
public function getName(){
return "Taiga";
}
public function getColor(){
return 0x0b6659;
}
}

View File

@ -67,17 +67,17 @@ class Ore{
for($x = $startX; $x <= $endX; ++$x){
$sizeX = ($x + 0.5 - $seedX) / $size;
$sizeX **= 2;
$sizeX *= $sizeX;
if($sizeX < 1){
for($y = $startY; $y <= $endY; ++$y){
$sizeY = ($y + 0.5 - $seedY) / $size;
$sizeY **= 2;
$sizeY *= $sizeY;
if($y > 0 and ($sizeX + $sizeY) < 1){
for($z = $startZ; $z <= $endZ; ++$z){
$sizeZ = ($z + 0.5 - $seedZ) / $size;
$sizeZ **= 2;
$sizeZ *= $sizeZ;
if(($sizeX + $sizeY + $sizeZ) < 1 and $level->getBlockIdAt($x, $y, $z) === 1){
$level->setBlockIdAt($x, $y, $z, $this->type->material->getId());

View File

@ -62,7 +62,7 @@ class TallGrass extends Populator{
private function getHighestWorkableBlock($x, $z){
for($y = 127; $y >= 0; --$y){
$b = $this->level->getBlockIdAt($x, $y, $z);
if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::SNOW_LAYER){
if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2 and $b !== Block::SNOW_LAYER){
break;
}
}

View File

@ -199,7 +199,7 @@ class Vector3{
}
public function lengthSquared(){
return $this->x ** 2 + $this->y ** 2 + $this->z ** 2;
return $this->x * $this->x + $this->y * $this->y + $this->z * $this->z;
}
/**
@ -244,7 +244,7 @@ class Vector3{
$yDiff = $v->y - $this->y;
$zDiff = $v->z - $this->z;
if(($xDiff ** 2) < 0.0000001){
if(($xDiff * $xDiff) < 0.0000001){
return null;
}
@ -271,7 +271,7 @@ class Vector3{
$yDiff = $v->y - $this->y;
$zDiff = $v->z - $this->z;
if(($yDiff ** 2) < 0.0000001){
if(($yDiff * $yDiff) < 0.0000001){
return null;
}
@ -298,7 +298,7 @@ class Vector3{
$yDiff = $v->y - $this->y;
$zDiff = $v->z - $this->z;
if(($zDiff ** 2) < 0.0000001){
if(($zDiff * $zDiff) < 0.0000001){
return null;
}