/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * * Deliantra is free software: you can redistribute it and/or modify it under * the terms of the Affero GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the Affero GNU General Public License * and the GNU General Public License along with this program. If not, see * . * * The authors can be reached via e-mail to */ #include "noise.h" ///////////////////////////////////////////////////////////////////////////// noise2d::noise2d (uint32_t seed) { seedable_rand_gen rng (seed); for (int i = 0; i < 256; ++i) { for (int j = 0; j < 2; ++j) rvec [i][j] = rng () - 0.5f; // normalise float mag = 1.f / sqrtf (rvec [i][0] * rvec [i][0] + rvec [i][1] * rvec [i][1]); rvec [i][0] *= mag; rvec [i][1] *= mag; } rvmap [0].seed (rng); rvmap [1].seed (rng); } float noise2d::noise (float x, float y) { int ix = floorf (x); float fx = x - ix; int iy = floorf (y); float fy = y - iy; float v = 0; for (int j = -1; j <= 2; ++j) for (int i = -1; i <= 2; ++i) { float Ax = fx - i; float Ay = fy - j; float d = Ax * Ax + Ay * Ay; if (d < 4) { int h = rvmap [0](ix + i) ^ rvmap [1](iy + j); float *G = rvec [h & 0xff]; float t1 = 1 - d / 4; float t2 = t1 * t1; float t4 = t2 * t2; float p = (4 * t1 - 3) * t4; v += (Ax * G[0] + Ay * G[1]) * p; } } return clamp (v * 2, -.9999999f, .9999999f); } #if 0||ABSTRACTION_PENALTY_IS_GONE float noise2d::noise (float x, float y) { vec2d X = vec2d (x, y); gen_vec<2,int> I = floor (X); vec2d F = X - I; float v = 0; for (int j = -1; j <= 2; ++j) for (int i = -1; i <= 2; ++i) { vec2d A = F - vec2d (i, j); float d = A * A; if (d < 4) { int h = rvmap [0](I[0] + i) ^ rvmap [1](I[1] + j); float *G = rvec [h & 0xff]; float t1 = 1 - d / 4; float t2 = t1 * t1; float t4 = t2 * t2; float p = (4 * t1 - 3) * t4; v += A * vec2d (G[0], G[1]) * p; } } return clamp (v * 2, -.9999999f, .9999999f); } #endif ///////////////////////////////////////////////////////////////////////////// noise3d::noise3d (uint32_t seed) { seedable_rand_gen rng (seed); for (int i = 0; i < 256; ++i) { for (int j = 0; j < 3; ++j) rvec [i][j] = rng () - 0.5f; // normalise float mag = 1.f / sqrtf (rvec [i][0] * rvec [i][0] + rvec [i][1] * rvec [i][1] + rvec [i][2] * rvec [i][2]); rvec [i][0] *= mag; rvec [i][1] *= mag; rvec [i][2] *= mag; } rvmap [0].seed (rng); rvmap [1].seed (rng); rvmap [2].seed (rng); } float noise3d::noise (float x, float y, float z) { int ix = floorf (x); float fx = x - ix; int iy = floorf (y); float fy = y - iy; int iz = floorf (z); float fz = z - iz; float v = 0; for (int j = -1; j <= 2; ++j) for (int i = -1; i <= 2; ++i) for (int k = -1; k <= 2; ++k) { float Ax = fx - i; float Ay = fy - j; float Az = fz - k; float d = Ax * Ax + Ay * Ay + Az * Az; if (d < 4) { int h = rvmap [0](ix + i) ^ rvmap [1](iy + j) ^ rvmap [2](iz + k); float *G = rvec [h & 0xff]; float t1 = 1 - d / 4; float t2 = t1 * t1; float t4 = t2 * t2; // (4t⁵ - 3t⁴) float p = (4 * t1 - 3) * t4; v += (Ax * G[0] + Ay * G[1] + Az * G[2]) * p; } } return clamp (v * 2, -.9999999f, .9999999f); } float noise3d::noise (float x, float y, float z, float nx, float ny, float nz) { int ix = floorf (x); float fx = x - ix; int iy = floorf (y); float fy = y - iy; int iz = floorf (z); float fz = z - iz; float v = 0; for (int j = -1; j <= 2; ++j) for (int i = -1; i <= 2; ++i) for (int k = -1; k <= 2; ++k) { float Dx = fx - i; float Dy = fy - j; float Dz = fz - k; float e = Dx * nx + Dy * ny + Dz * nz; float o = 1 - abs (e); if (o > 0) { float Ax = Dx - e * nx; float Ay = Dy - e * ny; float Az = Dz - e * nz; float d = Ax * Ax + Ay * Ay + Az * Az; if (d < 4) { int h = rvmap [0](ix + i) ^ rvmap [1](iy + j) ^ rvmap [2](iz + k); float *G = rvec [h & 0xff]; float t1 = 1 - d / 4; float t2 = t1 * t1; float t4 = t2 * t2; float o2 = o * o; // (4t⁵ - 3t⁴) * (3o³ - 2o²) float p = (4 * t1 - 3) * t4 * (3 * o2 - 2 * o2 * o); v += (Ax * G[0] + Ay * G[1] + Az * G[2]) * p; } } } return clamp (v * 2, -.9999999f, .9999999f); } frac_gen::frac_gen (float hurst_expo, float lacunarity) : h (hurst_expo), lac (lacunarity), noise2d (0) { float exsum = 0; for (int i = 0; i < MAX_OCTAVES; ++i) { ex [i] = powf (lac, -h * i); exsum += ex [i]; fbm_mul [i] = 0.5f / (exsum * 0.75f); // .75f is a heuristic rot [i].set (0.4488f * i); } } float frac_gen::fBm (float x, float y, int octaves) { float v = 0.f; for (int i = 0; i < octaves; ++i) { rot [i](x, y); v += noise (x + i, y) * ex [i]; x *= lac; y *= lac; } return clamp (v * fbm_mul [octaves - 1] + 0.5f, 0.f, .9999999f); } float frac_gen::turbulence (float x, float y, int octaves) { float v = 0.f; for (int i = 0; i < octaves; ++i) { rot [i](x, y); v += fabsf (noise (x + i, y)) * ex [i]; x *= lac; y *= lac; } return clamp (v * fbm_mul [octaves - 1] * 2.f, 0.f, .9999999f); } float frac_gen::multifractal (float x, float y, int octaves, float offset) { float v = 1.f; for (int i = 0; i < octaves; ++i) { rot [i](x, y); v *= noise (x, y) * ex [i] + offset; x *= lac; y *= lac; } return clamp (v * 0.5f, 0.f, .9999999f); } float frac_gen::heterofractal (float x, float y, int octaves, float offset) { float v = noise (x, y) + offset; int i; for (i = 1; i < octaves; ++i) { rot [i](x, y); x *= lac; y *= lac; v += v * (noise (x, y) * ex [i] + offset); } return v / powf (2., octaves); } float frac_gen::hybridfractal (float x, float y, int octaves, float offset, float gain) { float v = (noise (x, y) + offset) * ex [0]; float weight = v; int i; for (i = 1; i < octaves; ++i) { rot [i](x, y); x *= lac; y *= lac; min_it (weight, 1.f); float sig = (noise (x, y) + offset) * ex [i]; v += weight * sig; weight *= gain * sig; } return clamp (v * 0.5f + 0.5f, 0.f, .9999999f); } // http://www.gamasutra.com/view/feature/3098/a_realtime_procedural_universe_.php?page=2 float frac_gen::terrain (float x, float y, int octaves) { float v = 0.f; for (int i = 0; i < octaves; ++i) { rot[i] (x, y); v += noise (x + i, y) * ex [i]; x *= lac; y *= lac; } v *= fbm_mul [octaves - 1]; return v <= 0.f ? - powf (-v, 0.7f) : powf (v, 1. + noise (x, y) * v); } float frac_gen::terrain2 (float x, float y, int octaves) { float a = fBm (x, y, octaves); float b = ridgedmultifractal (x, y, octaves, 1, 8); float fade = fBm (x + 10.3, y + 10.3, octaves); const float width = 0.05f; if (fade > 0.5f + width) return a; else if (fade < 0.5f - width) return b; fade = (fade - 0.5f + width) / (width * 2); // sigmoidal curve fade = fade * fade * 2 * (1.5f - fade); return a * fade + b * (1 - fade); } float frac_gen::ridgedmultifractal (float x, float y, int octaves, float offset, float gain) { float sig = offset - fabsf (noise (x, y)); sig *= sig; float v = sig; for (int i = 1; i < octaves; ++i) { rot [i](x, y); x *= lac; y *= lac; float w = clamp (sig * gain, 0.f, 1.f); sig = offset - fabsf (noise (x, y)); sig *= sig; sig *= w; v += sig * ex [i]; } return clamp (v * 0.25f, 0.f, .9999999f); } #if 0 void hack() { frac_gen gen (0.5, 2); noise3d n(0); #if 0 int N = 1024; printf ("P6 %d %d 255\n", N, N); // pmake&&server/deliantra-server >x&&convert -depth 8 -size 512xx512 gray:x x.ppm&& cv x.ppm for (int y = 0; y < N; ++y) { if (!(y&63))fprintf (stderr, "y %d\n", y);//D for (int x = 0; x < N; ++x) { putc (128 + 128 * n.noise (x * 0.04 + 0.2, y * 0.04 + 0.2), stdout); putc (128 + 128 * n.noise (x * 0.04 + 0.2, y * 0.08 + 0.2), stdout); putc (128 + 128 * n.noise (x * 0.08 + 0.2, y * 0.04 + 0.2), stdout); //putc (256 * gen.terrain2 (x * 0.004, y * 0.004, 8), stdout); //putc (256 * gen.fBm (x * 0.001, y * 0.001, 3), stdout); //putc (256 * gen.turbulence (x * 0.004 - 1, y * 0.004 - 1, 8), stdout); //putc (256 * gen.heterofractal (x * 0.008, y * 0.008, 8, -1.1, 2.0), stdout); //putc (256 * gen.ridgedmultifractal (x * 0.001, y * 0.001, 32, 1.001, 32), stdout); } } #else int N = 128; //printf ("P6 %d %d 255\n", N, N); // pmake&&server/deliantra-server >x&&convert -depth 8 -size 512xx512 gray:x x.ppm&& cv x.ppm for (int z = 0; z < N; ++z) { if (!(z&4))fprintf (stderr, "z %d\n", z);//D for (int y = 0; y < N; ++y) for (int x = 0; x < N; ++x) { float v = n.noise (x * 0.06 + 0.2, y * 0.06 + 0.2, z * 0.06 + 0.2) * 0.5 + 0.5; if (z < 64) v = v * (z * z) / (64 * 64); if (v <= 0.1) continue; float r[4]; int i[4]; r[0] = x; r[1] = y; r[2] = z; r[3] = v; memcpy (i, r, 16); i[0] = htonl (i[0]); i[1] = htonl (i[1]); i[2] = htonl (i[2]); i[3] = htonl (i[3]); fwrite (i, 4*4, 1, stdout); } } #endif exit (0); } #endif