JA
games/doom-jumphp / tools / art / Noise.php · commit 8e1cb9f
<?phpnamespace Tools\Art;/** Deterministic value noise and a seeded random generator (the art is the same at every build). */final class Noise{    private static int $seed = 12345;    public static function seed(int $seed): void    {        self::$seed = $seed & 0x7fffffff;    }    /** 0..1 */    public static function rand(): float    {        self::$seed = (self::$seed * 1103515245 + 12345) & 0x7fffffff;        return self::$seed / 0x7fffffff;    }    public static function hash(int $x, int $y, int $z = 0): float    {        $h = ($x * 374761393 + $y * 668265263 + $z * 1274126177) & 0xffffffff;        $h = (($h ^ ($h >> 13)) * 1274126177) & 0xffffffff;        $h ^= $h >> 16;        return ($h & 0xffff) / 65535;    }    private static function smooth(float $t): float    {        return $t * $t * (3 - 2 * $t);    }    /** Tileable 2D value noise: $period cells across. */    public static function value2(float $x, float $y, int $period = 0): float    {        $x0 = (int)floor($x);        $y0 = (int)floor($y);        $fx = self::smooth($x - $x0);        $fy = self::smooth($y - $y0);        $w = fn (int $a): int => $period > 0 ? (($a % $period) + $period) % $period : $a;        $a = self::hash($w($x0), $w($y0));        $b = self::hash($w($x0 + 1), $w($y0));        $c = self::hash($w($x0), $w($y0 + 1));        $d = self::hash($w($x0 + 1), $w($y0 + 1));        return ($a + ($b - $a) * $fx) + (($c + ($d - $c) * $fx) - ($a + ($b - $a) * $fx)) * $fy;    }    /** Fractal tileable noise 0..1. */    public static function fbm(float $x, float $y, int $period, int $octaves = 4): float    {        $sum = 0.0;        $amp = 0.5;        $norm = 0.0;        for ($o = 0; $o < $octaves; $o++) {            $sum += self::value2($x, $y, $period) * $amp;            $norm += $amp;            $x *= 2; $y *= 2; $period *= 2; $amp *= 0.5;        }        return $sum / $norm;    }    public static function value3(float $x, float $y, float $z): float    {        $x0 = (int)floor($x); $y0 = (int)floor($y); $z0 = (int)floor($z);        $fx = self::smooth($x - $x0); $fy = self::smooth($y - $y0); $fz = self::smooth($z - $z0);        $l = function (int $zz) use ($x0, $y0, $fx, $fy): float {            $a = self::hash($x0, $y0, $zz); $b = self::hash($x0 + 1, $y0, $zz);            $c = self::hash($x0, $y0 + 1, $zz); $d = self::hash($x0 + 1, $y0 + 1, $zz);            $ab = $a + ($b - $a) * $fx;            return $ab + ($c + ($d - $c) * $fx - $ab) * $fy;        };        $p = $l($z0);        return $p + ($l($z0 + 1) - $p) * $fz;    }}