28f576d5创建于 2025年11月11日历史提交
<?php

/*
 *
 *  ____            _        _   __  __ _                  __  __ ____
 * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
 * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
 * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * @author PocketMine Team
 * @link http://www.pocketmine.net/
 *
 *
 */

declare(strict_types=1);

namespace pocketmine\world\generator\noise;

use pocketmine\utils\Random;
use const M_SQRT3;

/**
 * Generates simplex-based noise.
 *
 * This is a modified version of the freely published version in the paper by
 * Stefan Gustavson at
 * http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
 */
class Simplex extends Noise{
	protected const grad3 = [
		[1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0],
		[1, 0, 1], [-1, 0, 1], [1, 0, -1], [-1, 0, -1],
		[0, 1, 1], [0, -1, 1], [0, 1, -1], [0, -1, -1]
	];

	protected const F2 = 0.5 * (M_SQRT3 - 1);
	protected const G2 = (3 - M_SQRT3) / 6;
	protected const G22 = self::G2 * 2.0 - 1;
	protected const F3 = 1.0 / 3.0;
	protected const G3 = 1.0 / 6.0;

	protected float $offsetX;
	protected float $offsetZ;
	protected float $offsetY;
	/** @var int[] */
	protected array $perm = [];

	public function __construct(Random $random, int $octaves, float $persistence, float $expansion){
		parent::__construct($octaves, $persistence, $expansion);

		$this->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->nextBoundedInt(256);
		}

		for($i = 0; $i < 256; ++$i){
			$pos = $random->nextBoundedInt(256 - $i) + $i;
			$old = $this->perm[$i];

			$this->perm[$i] = $this->perm[$pos];
			$this->perm[$pos] = $old;
			$this->perm[$i + 256] = $this->perm[$i];
		}

		//this dummy call is necessary to produce the same RNG state as before latest refactors to this file
		//previously this value would be used for offsetW
		//TODO: this really needs to reset the RNG seed to avoid future RNG contamination
		$random->nextSignedInt();
	}

	public function getNoise3D($x, $y, $z){
		$x += $this->offsetX;
		$y += $this->offsetY;
		$z += $this->offsetZ;

		// Skew the input space to determine which simplex cell we're in
		$s = ($x + $y + $z) * self::F3; // Very nice and simple skew factor for 3D
		$i = (int) ($x + $s);
		$j = (int) ($y + $s);
		$k = (int) ($z + $s);
		$t = ($i + $j + $k) * self::G3;
		// Unskew the cell origin back to (x,y,z) space
		$x0 = $x - ($i - $t); // The x,y,z distances from the cell origin
		$y0 = $y - ($j - $t);
		$z0 = $z - ($k - $t);

		// For the 3D case, the simplex shape is a slightly irregular tetrahedron.

		// Determine which simplex we are in.
		if($x0 >= $y0){
			if($y0 >= $z0){
				$i1 = 1;
				$j1 = 0;
				$k1 = 0;
				$i2 = 1;
				$j2 = 1;
				$k2 = 0;
			} // X Y Z order
			elseif($x0 >= $z0){
				$i1 = 1;
				$j1 = 0;
				$k1 = 0;
				$i2 = 1;
				$j2 = 0;
				$k2 = 1;
			} // X Z Y order
			else{
				$i1 = 0;
				$j1 = 0;
				$k1 = 1;
				$i2 = 1;
				$j2 = 0;
				$k2 = 1;
			}
			// Z X Y order
		}else{ // x0<y0
			if($y0 < $z0){
				$i1 = 0;
				$j1 = 0;
				$k1 = 1;
				$i2 = 0;
				$j2 = 1;
				$k2 = 1;
			} // Z Y X order
			elseif($x0 < $z0){
				$i1 = 0;
				$j1 = 1;
				$k1 = 0;
				$i2 = 0;
				$j2 = 1;
				$k2 = 1;
			} // Y Z X order
			else{
				$i1 = 0;
				$j1 = 1;
				$k1 = 0;
				$i2 = 1;
				$j2 = 1;
				$k2 = 0;
			}
			// Y X Z order
		}

		// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
		// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
		// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
		// c = 1/6.
		$x1 = $x0 - $i1 + self::G3; // Offsets for second corner in (x,y,z) coords
		$y1 = $y0 - $j1 + self::G3;
		$z1 = $z0 - $k1 + self::G3;
		$x2 = $x0 - $i2 + 2.0 * self::G3; // Offsets for third corner in (x,y,z) coords
		$y2 = $y0 - $j2 + 2.0 * self::G3;
		$z2 = $z0 - $k2 + 2.0 * self::G3;
		$x3 = $x0 - 1.0 + 3.0 * self::G3; // Offsets for last corner in (x,y,z) coords
		$y3 = $y0 - 1.0 + 3.0 * self::G3;
		$z3 = $z0 - 1.0 + 3.0 * self::G3;

		// Work out the hashed gradient indices of the four simplex corners
		$ii = $i & 255;
		$jj = $j & 255;
		$kk = $k & 255;

		$n = 0;

		// Calculate the contribution from the four corners
		$t0 = 0.6 - $x0 * $x0 - $y0 * $y0 - $z0 * $z0;
		$perm = $this->perm;
		if($t0 > 0){
			$gi0 = self::grad3[$perm[$ii + $perm[$jj + $perm[$kk]]] % 12];
			$n += $t0 * $t0 * $t0 * $t0 * ($gi0[0] * $x0 + $gi0[1] * $y0 + $gi0[2] * $z0);
		}

		$t1 = 0.6 - $x1 * $x1 - $y1 * $y1 - $z1 * $z1;
		if($t1 > 0){
			$gi1 = self::grad3[$perm[$ii + $i1 + $perm[$jj + $j1 + $perm[$kk + $k1]]] % 12];
			$n += $t1 * $t1 * $t1 * $t1 * ($gi1[0] * $x1 + $gi1[1] * $y1 + $gi1[2] * $z1);
		}

		$t2 = 0.6 - $x2 * $x2 - $y2 * $y2 - $z2 * $z2;
		if($t2 > 0){
			$gi2 = self::grad3[$perm[$ii + $i2 + $perm[$jj + $j2 + $perm[$kk + $k2]]] % 12];
			$n += $t2 * $t2 * $t2 * $t2 * ($gi2[0] * $x2 + $gi2[1] * $y2 + $gi2[2] * $z2);
		}

		$t3 = 0.6 - $x3 * $x3 - $y3 * $y3 - $z3 * $z3;
		if($t3 > 0){
			$gi3 = self::grad3[$perm[$ii + 1 + $perm[$jj + 1 + $perm[$kk + 1]]] % 12];
			$n += $t3 * $t3 * $t3 * $t3 * ($gi3[0] * $x3 + $gi3[1] * $y3 + $gi3[2] * $z3);
		}

		// Add contributions from each corner to get the noise value.
		// The result is scaled to stay just inside [-1,1]
		return 32.0 * $n;
	}

	/**
	 * @param float $x
	 * @param float $y
	 *
	 * @return float
	 */
	public function getNoise2D($x, $y){
		$x += $this->offsetX;
		$y += $this->offsetY;

		// Skew the input space to determine which simplex cell we're in
		$s = ($x + $y) * self::F2; // Hairy factor for 2D
		$i = (int) ($x + $s);
		$j = (int) ($y + $s);
		$t = ($i + $j) * self::G2;
		// Unskew the cell origin back to (x,y) space
		$x0 = $x - ($i - $t); // The x,y distances from the cell origin
		$y0 = $y - ($j - $t);

		// For the 2D case, the simplex shape is an equilateral triangle.

		// Determine which simplex we are in.
		if($x0 > $y0){
			$i1 = 1;
			$j1 = 0;
		} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
		else{
			$i1 = 0;
			$j1 = 1;
		}
		// upper triangle, YX order: (0,0)->(0,1)->(1,1)

		// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
		// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
		// c = (3-sqrt(3))/6

		$x1 = $x0 - $i1 + self::G2; // Offsets for middle corner in (x,y) unskewed coords
		$y1 = $y0 - $j1 + self::G2;
		$x2 = $x0 + self::G22; // Offsets for last corner in (x,y) unskewed coords
		$y2 = $y0 + self::G22;

		// Work out the hashed gradient indices of the three simplex corners
		$ii = $i & 255;
		$jj = $j & 255;

		$n = 0;

		// Calculate the contribution from the three corners
		$t0 = 0.5 - $x0 * $x0 - $y0 * $y0;
		if($t0 > 0){
			$gi0 = self::grad3[$this->perm[$ii + $this->perm[$jj]] % 12];
			$n += $t0 * $t0 * $t0 * $t0 * ($gi0[0] * $x0 + $gi0[1] * $y0); // (x,y) of grad3 used for 2D gradient
		}

		$t1 = 0.5 - $x1 * $x1 - $y1 * $y1;
		if($t1 > 0){
			$gi1 = self::grad3[$this->perm[$ii + $i1 + $this->perm[$jj + $j1]] % 12];
			$n += $t1 * $t1 * $t1 * $t1 * ($gi1[0] * $x1 + $gi1[1] * $y1);
		}

		$t2 = 0.5 - $x2 * $x2 - $y2 * $y2;
		if($t2 > 0){
			$gi2 = self::grad3[$this->perm[$ii + 1 + $this->perm[$jj + 1]] % 12];
			$n += $t2 * $t2 * $t2 * $t2 * ($gi2[0] * $x2 + $gi2[1] * $y2);
		}

		// Add contributions from each corner to get the noise value.
		// The result is scaled to return values in the interval [-1,1].
		return 70.0 * $n;
	}
}