From 1bc54dbc44cd6e05f435283c450ddd3581b813af Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Wed, 12 Feb 2014 09:45:38 +0100 Subject: [PATCH] Such noise --- src/API/LevelAPI.php | 2 +- src/utils/Random.php | 33 ++-- src/world/generator/NormalGenerator.php | 136 ++++++++++++++ src/world/generator/SuperflatGenerator.php | 2 +- src/world/generator/noise/NoiseGenerator.php | 43 +++++ .../generator/noise/NoiseGeneratorOctaves.php | 68 ------- .../generator/noise/NoiseGeneratorPerlin.php | 169 ------------------ src/world/generator/noise/OctaveGenerator.php | 108 +++++++++++ .../generator/noise/PerlinNoiseGenerator.php | 96 ++++++++++ .../generator/noise/PerlinOctaveGenerator.php | 35 ++++ 10 files changed, 430 insertions(+), 262 deletions(-) create mode 100644 src/world/generator/NormalGenerator.php delete mode 100644 src/world/generator/noise/NoiseGeneratorOctaves.php delete mode 100644 src/world/generator/noise/NoiseGeneratorPerlin.php create mode 100644 src/world/generator/noise/OctaveGenerator.php create mode 100644 src/world/generator/noise/PerlinNoiseGenerator.php create mode 100644 src/world/generator/noise/PerlinOctaveGenerator.php diff --git a/src/API/LevelAPI.php b/src/API/LevelAPI.php index 07726c77f..56d96ec17 100644 --- a/src/API/LevelAPI.php +++ b/src/API/LevelAPI.php @@ -94,7 +94,7 @@ class LevelAPI{ if(strtoupper($this->server->api->getProperty("level-type")) == "FLAT"){ $generator = new SuperflatGenerator($options); }else{ - $generator = new TemporalGenerator($options); + $generator = new NormalGenerator($options); } } $gen = new WorldGenerator($generator, $name, $seed === false ? Utils::readInt(Utils::getRandomBytes(4, false)):(int) $seed); diff --git a/src/utils/Random.php b/src/utils/Random.php index fbd00397f..da7e9bf92 100644 --- a/src/utils/Random.php +++ b/src/utils/Random.php @@ -22,25 +22,15 @@ //Unsecure, not used for "Real Randomness" class Random{ - private $x, $y, $z, $w; + private $z, $w; public function __construct($seed = false){ $this->setSeed($seed); } public function setSeed($seed = false){ - $seed = $seed !== false ? Utils::writeInt((int) $seed):Utils::getRandomBytes(4, false); - $state = array(); - for($i = 0; $i < 256; ++$i){ - $state[] = $i; - } - for($i = $j = 0; $i < 256; ++$i){ - $j = ($j + ord($seed{$i & 0x03}) + $state[$i]) & 0xFF; - $state[$i] ^= $state[$j]; - $state[$j] ^= $state[$i]; - $state[$i] ^= $state[$j]; - } - $this->state = $state; - $this->i = $this->j = 0; + $seed = $seed !== false ? (int) $seed:Utils::readInt(Utils::getRandomBytes(4, false)); + $this->z = $seed ^ 0xdeadbeef; + $this->w = $seed ^ 0xc0de1337; } public function nextInt(){ @@ -61,19 +51,16 @@ class Random{ public function nextBytes($byteCount){ $bytes = ""; - for($i = 0; $i < $byteCount; ++$i){ - $this->i = ($this->i + 1) & 0xFF; - $this->j = ($this->j + $this->state[$this->i]) & 0xFF; - $this->state[$this->i] ^= $this->state[$this->j]; - $this->state[$this->j] ^= $this->state[$this->i]; - $this->state[$this->i] ^= $this->state[$this->j]; - $bytes .= chr($this->state[($this->state[$this->i] + $this->state[$this->j]) & 0xFF]); + while(strlen($bytes) < $byteCount){ + $this->z = 36969 * ($this->z & 65535) + ($this->z >> 16); + $this->w = 18000 * ($this->w & 65535) + ($this->w >> 16); + $bytes .= pack("N", ($this->z << 16) + $this->w); } - return $bytes; + return substr($bytes, 0, $byteCount); } public function nextBoolean(){ - return ($this->nextBytes(1) & 0x01) == 0; + return ($this->nextSignedInt() & 0x01) === 0; } public function nextRange($start = 0, $end = PHP_INT_MAX){ diff --git a/src/world/generator/NormalGenerator.php b/src/world/generator/NormalGenerator.php new file mode 100644 index 000000000..9ea8967cb --- /dev/null +++ b/src/world/generator/NormalGenerator.php @@ -0,0 +1,136 @@ +noiseGen1 = new PerlinOctaveGenerator($rand, 16); + $this->noiseGen2 = new PerlinOctaveGenerator($rand, 16); + $this->noiseGen3 = new PerlinOctaveGenerator($rand, 8); + $this->noiseGen4 = new PerlinOctaveGenerator($rand, 4); + $this->noiseGen5 = new PerlinOctaveGenerator($rand, 10); + $this->noiseGen6 = new PerlinOctaveGenerator($rand, 16); + } + + public function init(Level $level, Random $random){ + $this->level = $level; + $this->random = $random; + $this->random->setSeed($this->level->getSeed()); + $perlin = new PerlinNoiseGenerator($this->random, array( + "frequency" => 1, + "octaves" => 8, + "persistence" => 0.3, + "scale" => 1, + )); + $this->deviations = $perlin->fillNoiseArray(256, 256); + + $ores = new OrePopulator(); + $ores->setOreTypes(array( + new OreType(new CoalOreBlock(), 20, 16, 0, 128), + new OreType(New IronOreBlock(), 20, 8, 0, 64), + new OreType(new RedstoneOreBlock(), 8, 7, 0, 16), + new OreType(new LapisOreBlock(), 1, 6, 0, 32), + new OreType(new GoldOreBlock(), 2, 8, 0, 32), + new OreType(new DiamondOreBlock(), 1, 7, 0, 16), + new OreType(new DirtBlock(), 20, 32, 0, 128), + new OreType(new GravelBlock(), 10, 16, 0, 128), + )); + $this->populators[] = $ores; + } + + public function generateChunk($chunkX, $chunkZ){ + //$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); + $byte0 = 4; + $this->noiseArray = $this->initializeNoiseArray($chunkX * $byte0, 0, $chunkZ * $byte0, $byte0 + 1, ($this->worldHeight / 8) + 1, $byte0 + 1); + + $chunks = array(); + for($chunkY = 0; $chunkY < 8; ++$chunkY){ + $chunk = ""; + $startY = $chunkY << 4; + $endY = $startY + 16; + for($z = 0; $z < 16; ++$z){ + for($x = 0; $x < 16; ++$x){ + $height = (int) ($this->baseHeight + $this->deviations[$z + ($chunkZ << 4)][$x + ($chunkX << 4)]); + for($y = $startY; $y < $endY; ++$y){ + $diff = $height - $y; + if($y <= 4 and ($y === 0 or $this->random->nextFloat() < 0.75)){ + $chunk .= "\x07"; //bedrock + }elseif($diff > 3){ + $chunk .= "\x01"; //stone + }elseif($diff > 0){ + $chunk .= "\x03"; //dirt + }elseif($y <= $this->waterHeight){ + $chunk .= "\x09"; //still_water + }elseif($diff === 0){ + $chunk .= "\x02"; //grass + }else{ + $chunk .= "\x00"; + } + } + $chunk .= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + } + } + $this->level->setMiniChunk($chunkX, $chunkZ, $chunkY, $chunk); + } + + } + + private function initializeNoiseArray($x, $y, $z, $sizeX, $sizeY, $sizeZ){ + $noiseArray = array_fill(0, $sizeX * $sizeY * $sizeZ, 0.0); + + $noise5 = $this->noiseGen5->generateNoiseOctaves($x, $y, $z); + + } + + public function populateChunk($chunkX, $chunkZ){ + foreach($this->populators as $populator){ + $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); + $populator->populate($this->level, $chunkX, $chunkZ, $this->random); + } + } + + public function populateLevel(){ + + } + + public function getSpawn(){ + return $this->level->getSafeSpawn(new Vector3(127.5, 128, 127.5)); + } + +} \ No newline at end of file diff --git a/src/world/generator/SuperflatGenerator.php b/src/world/generator/SuperflatGenerator.php index b7235694f..731c304df 100644 --- a/src/world/generator/SuperflatGenerator.php +++ b/src/world/generator/SuperflatGenerator.php @@ -127,7 +127,7 @@ class SuperflatGenerator implements LevelGenerator{ public function populateChunk($chunkX, $chunkZ){ foreach($this->populators as $populator){ - $this->random->setSeed((int) ($chunkX * 0xdead + $chunkZ * 0xbeef) ^ $this->level->getSeed()); + $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); $populator->populate($this->level, $chunkX, $chunkZ, $this->random); } } diff --git a/src/world/generator/noise/NoiseGenerator.php b/src/world/generator/noise/NoiseGenerator.php index b6d24472c..2bfbf1c8d 100644 --- a/src/world/generator/noise/NoiseGenerator.php +++ b/src/world/generator/noise/NoiseGenerator.php @@ -19,6 +19,49 @@ * */ + abstract class NoiseGenerator{ + protected $perm = array(); + protected $offsetX; + protected $offsetY; + protected $offsetZ; + public static function floor($x){ + return $x >= 0 ? (int) $x : (int) $x - 1; + } + + public static function fade($x){ + return $x * $x * $x * ($x * ($x * 6 - 15) + 10); + } + + public static function lerp($x, $y, $z){ + return $y + $x * ($z - $y); + } + + public static function grad($hash, $x, $y, $z){ + $hash &= 15; + $u = $hash < 8 ? $x : $y; + $v = $hash < 4 ? $y : (($hash === 12 or $hash === 14) ? $x : $z); + return (($hash & 1) === 0 ? $u : -$u) + (($hash & 2) === 0 ? $v : -$v); + } + + abstract public function noise($x, $y, $z); + + public function noise3D($x, $y, $z, $octaves, $frequency, $amplitude, $normalized = false){ + $result = 0; + $amp = 1; + $freq = 1; + $max = 0; + + for($i = 0; $i < $octaves; ++$i){ + $result += $this->noise($x * $freq, $y * $freq, $z * $freq) * $amp; + $max += $amp; + $freq *= $frequency; + $amp *= $amplitude; + } + if($normalized === true){ + $result /= $max; + } + + return $result; } \ No newline at end of file diff --git a/src/world/generator/noise/NoiseGeneratorOctaves.php b/src/world/generator/noise/NoiseGeneratorOctaves.php deleted file mode 100644 index ac5661432..000000000 --- a/src/world/generator/noise/NoiseGeneratorOctaves.php +++ /dev/null @@ -1,68 +0,0 @@ -generatorCollection = array(); - $this->octaves = (int) $octaves; - for($o = 0; $o < $this->octaves; ++$o){ - $this->generatorCollection[$o] = new NoiseGeneratorPerlin($random); - } - } - - public function generateNoiseOctaves($int1, $int2, $int3, $int4, $int5, $int6, $par1 = false, $par2 = false, $par3 = false){ - if($par1 === false or $par2 === false or $par3 === false){ - return $this->generateNoiseOctaves($int1, 10, $int2, $int3, 1, $int4, $int5, 1, $int6); - } - - $floats = array(); - $cnt = $int4 * $int5 * $int6; - for($i = 0; $i < $cnt; ++$i){ - $floats[$i] = 0; - } - - $d1 = 1; - - for($j = 0; $j < $this->octaves; ++$j){ - $d2 = $int1 * $d1 * $par1; - $d3 = $int2 * $d1 * $par2; - $d4 = $int3 * $d1 * $par3; - $l1 = floor($d2); - $l2 = floor($d4); - $d2 -= $l1; - $d4 -= $l2; - $l1 %= 16777216; - $l2 %= 16777216; - - $d2 += $l1; - $d4 += $l2; - $this->generatorCollection[$j]->populateNoiseArray($floats, $d2, $d3, $d4, $int4, $int5, $int6, $par1 * $d1, $par2 * $d1, $par3 * $d1, $d1); - $d1 /= 2; - } - return $floats; - } -} \ No newline at end of file diff --git a/src/world/generator/noise/NoiseGeneratorPerlin.php b/src/world/generator/noise/NoiseGeneratorPerlin.php deleted file mode 100644 index 079bda1f1..000000000 --- a/src/world/generator/noise/NoiseGeneratorPerlin.php +++ /dev/null @@ -1,169 +0,0 @@ -xCoord = $random->nextFloat() * 256; - $this->yCoord = $random->nextFloat() * 256; - $this->zCoord = $random->nextFloat() * 256; - - for($i = 0; $i < 512; ++$i){ - $this->permutations[$i] = 0; - } - for($i = 0; $i < 256; ++$i){ - $this->permutations[$i] = $i; - } - - for($i = 0; $i < 256; ++$i){ - $j = $random->nextRange(0, 256 - $i) + $i; - $k = $this->permutations[$i]; - $this->permutations[$i] = $this->permutations[$j]; - $this->permutations[$j] = $k; - $this->permutations[$i + 256] = $this->permutations[$i]; - } - - } - - public final function curve($par1, $par2, $par3){ - return $par2 + $par1 * ($par3 - $par2); - } - - public function grad2D($int, $par1, $par2){ - $i = $int & 0x0F; - $d1 = (1 - (($i & 0x08) >> 3)) * $par1; - $d2 = ($i === 12 or $i === 14) ? $par1:($i < 4 ? 0:$par2); - - return (($i & 0x01) === 0 ? $d1:-$d1) + (($i & 0x02) === 0 ? $d2:-$d2); - } - - public function grad3D($int, $par1, $par2, $par3){ - $i = $int & 0x0F; - $d1 = $i < 8 ? $par1 : $par2; - $d2 = ($i === 12 or $i === 14) ? $par1:($i < 4 ? $par2:$par3); - - return (($i & 0x01) === 0 ? $d1:-$d1) + (($i & 0x02) === 0 ? $d2:-$d2); - } - - public function populateNoiseArray(&$floats, $par1, $par2, $par3, $int1, $int2, $int3, $par4, $par5, $par6, $par7){ - if($int2 === 1){ - $n = 0; - $d3 = 1 / $par7; - for($i1 = 0; $i1 < $int1; ++$i1){ - $d4 = $par1 + $i1 * $par4 + $this->xCoord; - $i2 = (int) $d4; - if($d4 < $i2){ - --$i2; - } - $i3 = $i2 & 0xFF; - $d4 -= $i2; - $d5 = $d4 * $d4 * $d4 * ($d4 * ($d4 * 6 - 15) + 10); - - for($i4 = 0; $i4 < $int3; ++$i4){ - $d6 = $par3 + $i4 * $par6 + $this->zCoord; - $i5 = (int) $d6; - if($d6 < $i5){ - --$i5; - } - $i6 = $i5 & 0xFF; - $d6 -= $i5; - $d7 = $d6 * $d6 * $d6 * ($d6 * ($d6 * 6 - 15) + 10); - - $i = $this->permutations[$i3]; - $j = $this->permutations[$i] + $i6; - $k = $this->permutations[$i3 + 1]; - $m = $this->permutations[$k] + $i6; - $d1 = $this->curve($d5, $this->grad2D($this->permutations[$j], $d4, $d6), $this->grad3D($this->permutations[$m], $d4 - 1, 0, $d6)); - $d2 = $this->curve($d5, $this->grad3D($this->permutations[$j + 1], $d4, 0, $d6 - 1), $this->grad3D($this->permutations[$m + 1], $d4 - 1, 0, $d6 - 1)); - - $d8 = $this->curve($d7, $d1, $d2); - $floats[$n++] += $d8 * $d3; - } - } - return; - } - - $d9 = 1 / $par7; - $m = -1; - $n = 0; - $i = 0; - - for($i4 = 0; $i4 < $int1; ++$i4){ - $d6 = $par1 + $i4 * $par4 + $this->xCoord; - $i5 = (int) $d6; - if($d6 < $i5){ - --$i5; - } - $i6 = $i5 & 0xFF; - $d6 -= $i5; - $d7 = $d6 * $d6 * $d6 * ($d6 * ($d6 * 6 - 15) + 10); - - for($i12 = 0; $i12 < $int3; ++$i12){ - $d12 = $par3 + $i12 * $par6 + $this->zCoord; - $i13 = (int) $d12; - if($d12 < $i13){ - --$i13; - } - $i14 = $i13 & 0xFF; - $d12 -= $i13; - $d13 = $d12 * $d12 * $d12 * ($d12 * ($d12 * 6 - 15) + 10); - - for($i15 = 0; $i15 < $int2; ++$i15){ - $d14 = $par2 + $i15 * $par5 + $this->yCoord; - $i16 = (int) $d14; - if($d14 < $i16){ - --$i16; - } - $d14 -= $i16; - $d15 = $d14 * $d14 * $d14 * ($d14 * ($d14 * 6 - 15) + 10); - - if($i15 === 0 or $i17 !== $m){ - $m = $i17; - $i7 = $this->permutations[$i6] + $i17; - $i8 = $this->permutations[$i7] + $i14; - $i9 = $this->permutations[$i7 + 1] + $i14; - $i10 = $this->permutations[$i6 + 1] + $i17; - $n = $this->permutations[$i10] + $i14; - $i11 = $this->permutations[$i10 + 1] + $i14; - $d10 = $this->curve($d7, $this->grad3D($this->permutations[$i8], $d6, $d14, $d12), $this->grad3D($this->permutations[$n], $d6 - 1, $d14, $d12)); - $d4 = $this->curve($d7, $this->grad3D($this->permutations[$i9], $d6, $d14 - 1, $d12), $this->grad3D($this->permutations[$i11], $d6 - 1, $d14 - 1, $d12)); - $d11 = $this->curve($d7, $this->grad3D($this->permutations[$i8 + 1], $d6, $d14, $d12 - 1), $this->grad3D($this->permutations[$n + 1], $d6 - 1, $d14, $d12 - 1)); - $d5 = $this->curve($d7, $this->grad3D($this->permutations[$i9 + 1], $d6, $d14 - 1, $d12 - 1), $this->grad3D($this->permutations[$i11 + 1], $d6 - 1, $d14 - 1, $d12 - 1)); - } - - $d16 = $this->curve($d15, $d10, $d4); - $d17 = $this->curve($d15, $d11, $d5); - $d18 = $this->curve($d13, $d16, $d17); - $floats[$i++] += $d18 * $d9; - } - } - } - } -} \ No newline at end of file diff --git a/src/world/generator/noise/OctaveGenerator.php b/src/world/generator/noise/OctaveGenerator.php new file mode 100644 index 000000000..4bda3e720 --- /dev/null +++ b/src/world/generator/noise/OctaveGenerator.php @@ -0,0 +1,108 @@ +octaves = $octaves; + } + + public function setScale($scale){ + $this->setXScale($scale); + $this->setYScale($scale); + $this->setZScale($scale); + } + + public function getXScale(){ + return $this->xScale; + } + + public function setXScale($scale){ + $this->xScale = $scale; + } + + public function getYScale(){ + return $this->yScale; + } + + public function setYScale($scale){ + $this->yScale = $scale; + } + + public function getZScale(){ + return $this->zScale; + } + + public function setZScale($scale){ + $this->zScale = $scale; + } + + public function getOctaves(){ + $array = array(); + foreach($this->octaves as $index => $value){ + $array[$index] = clone $value; + } + return $array; + } + + //1D-noise + public function noise1D($x, $frequency, $amplitude, $normalized = false){ + return $this->noise3D($x, 0, 0, $frequency, $amplitude, $normalized); + } + + //2D-noise + public function noise2D($x, $y, $frequency, $amplitude, $normalized = false){ + return $this->noise3D($x, $y, 0, $frequency, $amplitude, $normalized); + } + + //3D-noise + public function noise3D($x, $y, $z, $frequency, $amplitude, $normalized = false){ + $result = 0; + $amp = 1; + $freq = 1; + $max = 0; + + $x *= $this->xScale; + $y *= $this->yScale; + $z *= $this->zScale; + + foreach($this->octaves as $noiseGenerator){ + $result += $octave->noise($x * $freq, $y * $freq, $z * $freq) * $amp; + $max += $amp; + $freq *= $frequency; + $amp *= $amplitude; + } + if($normalized === true){ + $result /= $max; + } + + return $result; + } + + public function generateNoiseOctaves($x, $y, $z, $frequency, $amplitude){ + + } +} \ No newline at end of file diff --git a/src/world/generator/noise/PerlinNoiseGenerator.php b/src/world/generator/noise/PerlinNoiseGenerator.php new file mode 100644 index 000000000..640094aa1 --- /dev/null +++ b/src/world/generator/noise/PerlinNoiseGenerator.php @@ -0,0 +1,96 @@ +offsetX = $random->nextFloat() * 256; + $this->offsetY = $random->nextFloat() * 256; + $this->offsetZ = $random->nextFloat() * 256; + + for($i = 0; $i < 512; ++$i){ + $this->perm[$i} = 0; + } + + for($i = 0; $i < 256; ++$i){ + $this->perm[$i} = $random->nextRange(0, 255); + } + for($i = 0; $i < 256; ++$i)[ + $pos = $random->nextRange(0, 255 - $i) + $i; + $old = $this->perm[$i}; + + $this->perm[$i} = $this->perm[$pos}; + $this->perm[$pos} = $old; + $this->perm[$i + 256} = $this->perm[$i}; + } + + } + + public function noise($x, $y, $z){ + $x += $this->offsetX; + $y += $this->offsetY; + $z += $this->offsetZ; + + $floorX = self::floor($x); + $floorY = self::floor($y); + $floorZ = self::floor($z); + + $X = $floorX & 0xFF; + $Y = $floorY & 0xFF; + $Z = $floorZ & 0xFF; + + $x -= $floorX; + $y -= $floorY; + $z -= $floorZ; + + //Fade curves + $fX = self::fade($x); + $fY = self::fade($y); + $fZ = self::fade($z); + + //Cube corners + $A = $this->perm[$X] + $Y; + $AA = $this->perm[$A] + $Z; + $AB = $this->perm[$A + 1] + $Z; + $B = $this->perm[$X + 1] + $Y; + $BA = $this->perm[$B] + $Z; + $BB = $this->perm[$B + 1] + $Z; + + return self::lerp($fZ, self::lerp($fY, self::lerp($fX, self::grad($this->perm[$AA]), $x, $y, $z), + self::grad($this->perm[$BA], $x - 1, $y, $z), + self::lerp($fX, self::grad($this->perm[$AB], $x, $y - 1, $z), + self::grad($this->perm[$BB], $x - 1, $y - 1, $z))), + self::lerp($fY, self::lerp($fX, self::grad($this->perm[$AA + 1], $x, $y, $z - 1), + self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1)), + self::lerp($fX, self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1), + self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1)))); + } +} \ No newline at end of file diff --git a/src/world/generator/noise/PerlinOctaveGenerator.php b/src/world/generator/noise/PerlinOctaveGenerator.php new file mode 100644 index 000000000..857677285 --- /dev/null +++ b/src/world/generator/noise/PerlinOctaveGenerator.php @@ -0,0 +1,35 @@ +octaves = array(); + for($o = 0; $o < $octaves; ++$o){ + $this->octaves[$o] = new NoiseGeneratorPerlin($random); + } + } + + public function +} \ No newline at end of file