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
23 changed files with 142 additions and 126 deletions

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;
}
}