aboutsummaryrefslogtreecommitdiff
path: root/util/pnoise.py
blob: fcab5ac15cb28f38fbb89f9b7ad8a347401bf4ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#
# A python perlin noise implementation, from
# http://www.fundza.com/c4serious/noise/perlin/perlin.html
#
# This is used for testing how to create maps with a python script.
#

import math
p = (
151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,
30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,
62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,
125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,
83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,
143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,
196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,
58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,
221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,
224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,
12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,
199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,
205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,
30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,
62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,
125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,
83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,
143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,
196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,
58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,
221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,
224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,
12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,
199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,
205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180)
  
def lerp(t, a, b):
    return a + t * (b - a)
  
def fade(t):
    return t * t * t * (t * (t * 6 - 15) + 10)
  
def grad(hash, x, y, z):
    h = hash & 15
    if h < 8:
        u = x
    else:
        u = y
    if h < 4:
        v = y
    elif h == 12 or h == 14:
        v = x
    else:
        v = z
    if h & 1 != 0:
        u = -u
    if h & 2 != 0:
        v = -v
    return u + v
  
def pnoise(x, y, z):
    global p
    X = int(math.floor(x)) & 255
    Y = int(math.floor(y)) & 255
    Z = int(math.floor(z)) & 255
    x -= math.floor(x)
    y -= math.floor(y)
    z -= math.floor(z)
    
    u = fade(x)
    v = fade(y)
    w = fade(z)
    
    A =  p[X] + Y
    AA = p[A] + Z
    AB = p[A + 1] + Z
    B =  p[X + 1] + Y
    BA = p[B] + Z
    BB = p[B + 1] + Z
    
    pAA = p[AA]
    pAB = p[AB]
    pBA = p[BA]
    pBB = p[BB]
    pAA1 = p[AA + 1]
    pBA1 = p[BA + 1]
    pAB1 = p[AB + 1]
    pBB1 = p[BB + 1]
    
    gradAA =  grad(pAA, x,   y,   z)
    gradBA =  grad(pBA, x-1, y,   z)
    gradAB =  grad(pAB, x,   y-1, z)
    gradBB =  grad(pBB, x-1, y-1, z)
    gradAA1 = grad(pAA1,x,   y,   z-1)
    gradBA1 = grad(pBA1,x-1, y,   z-1)
    gradAB1 = grad(pAB1,x,   y-1, z-1)
    gradBB1 = grad(pBB1,x-1, y-1, z-1)
    return lerp(w, 
    lerp(v, lerp(u, gradAA, gradBA), lerp(u, gradAB, gradBB)),
    lerp(v, lerp(u, gradAA1,gradBA1),lerp(u, gradAB1,gradBB1)))    
m">1 << GENNOTIFY_DUNGEON}, {"temple", 1 << GENNOTIFY_TEMPLE}, {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN}, {"cave_end", 1 << GENNOTIFY_CAVE_END}, {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN}, {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END}, {"decoration", 1 << GENNOTIFY_DECORATION}, {NULL, 0} }; struct MapgenDesc { const char *name; bool is_user_visible; }; //// //// Built-in mapgens //// // Order used here defines the order of appearence in mainmenu. // v6 always last to discourage selection. // Special mapgens flat, fractal, singlenode, next to last. Of these, singlenode // last to discourage selection. // Of the remaining, v5 last due to age, v7 first due to being the default. // The order of 'enum MapgenType' in mapgen.h must match this order. static MapgenDesc g_reg_mapgens[] = { {"v7", true}, {"valleys", true}, {"carpathian", true}, {"v5", true}, {"flat", true}, {"fractal", true}, {"singlenode", true}, {"v6", true}, }; STATIC_ASSERT( ARRLEN(g_reg_mapgens) == MAPGEN_INVALID, registered_mapgens_is_wrong_size); //// //// Mapgen //// Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeParams *emerge) : gennotify(emerge->gen_notify_on, emerge->gen_notify_on_deco_ids) { id = mapgenid; water_level = params->water_level; mapgen_limit = params->mapgen_limit; flags = params->flags; csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE); /* We are losing half our entropy by doing this, but it is necessary to preserve reverse compatibility. If the top half of our current 64 bit seeds ever starts getting used, existing worlds will break due to a different hash outcome and no way to differentiate between versions. A solution could be to add a new bit to designate that the top half of the seed value should be used, essentially a 1-bit version code, but this would require increasing the total size of a seed to 9 bytes (yuck) It's probably okay if this never gets fixed. 4.2 billion possibilities ought to be enough for anyone. */ seed = (s32)params->seed; ndef = emerge->ndef; } MapgenType Mapgen::getMapgenType(const std::string &mgname) { for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) { if (mgname == g_reg_mapgens[i].name) return (MapgenType)i; } return MAPGEN_INVALID; } const char *Mapgen::getMapgenName(MapgenType mgtype) { size_t index = (size_t)mgtype; if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens)) return "invalid"; return g_reg_mapgens[index].name; } Mapgen *Mapgen::createMapgen(MapgenType mgtype, MapgenParams *params, EmergeParams *emerge) { switch (mgtype) { case MAPGEN_CARPATHIAN: return new MapgenCarpathian((MapgenCarpathianParams *)params, emerge); case MAPGEN_FLAT: return new MapgenFlat((MapgenFlatParams *)params, emerge); case MAPGEN_FRACTAL: return new MapgenFractal((MapgenFractalParams *)params, emerge); case MAPGEN_SINGLENODE: return new MapgenSinglenode((MapgenSinglenodeParams *)params, emerge); case MAPGEN_V5: return new MapgenV5((MapgenV5Params *)params, emerge); case MAPGEN_V6: return new MapgenV6((MapgenV6Params *)params, emerge); case MAPGEN_V7: return new MapgenV7((MapgenV7Params *)params, emerge); case MAPGEN_VALLEYS: return new MapgenValleys((MapgenValleysParams *)params, emerge); default: return nullptr; } } MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype) { switch (mgtype) { case MAPGEN_CARPATHIAN: return new MapgenCarpathianParams; case MAPGEN_FLAT: return new MapgenFlatParams; case MAPGEN_FRACTAL: return new MapgenFractalParams; case MAPGEN_SINGLENODE: return new MapgenSinglenodeParams; case MAPGEN_V5: return new MapgenV5Params; case MAPGEN_V6: return new MapgenV6Params; case MAPGEN_V7: return new MapgenV7Params; case MAPGEN_VALLEYS: return new MapgenValleysParams; default: return nullptr; } } void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden) { for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) { if (include_hidden || g_reg_mapgens[i].is_user_visible) mgnames->push_back(g_reg_mapgens[i].name); } } void Mapgen::setDefaultSettings(Settings *settings) { settings->setDefault("mg_flags", flagdesc_mapgen, MG_CAVES | MG_DUNGEONS | MG_LIGHT | MG_DECORATIONS | MG_BIOMES | MG_ORES); for (int i = 0; i < (int)MAPGEN_INVALID; ++i) { MapgenParams *params = createMapgenParams((MapgenType)i); params->setDefaultSettings(settings); delete params; } } u32 Mapgen::getBlockSeed(v3s16 p, s32 seed) { return (u32)seed + p.Z * 38134234 + p.Y * 42123 + p.X * 23; } u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed) { u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed; n = (n >> 13) ^ n; return (n * (n * n * 60493 + 19990303) + 1376312589); } // Returns -MAX_MAP_GENERATION_LIMIT if not found s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax) { const v3s16 &em = vm->m_area.getExtent(); u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y); s16 y; for (y = ymax; y >= ymin; y--) { MapNode &n = vm->m_data[i]; if (ndef->get(n).walkable) break; VoxelArea::add_y(em, i, -1); } return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT; } // Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax) { const v3s16 &em = vm->m_area.getExtent(); u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y); s16 y; for (y = ymax; y >= ymin; y--) { MapNode &n = vm->m_data[i]; if (ndef->get(n).walkable) return -MAX_MAP_GENERATION_LIMIT; if (ndef->get(n).isLiquid()) break; VoxelArea::add_y(em, i, -1); } return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT; } void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax) { if (!heightmap) return; //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO); int index = 0; for (s16 z = nmin.Z; z <= nmax.Z; z++) { for (s16 x = nmin.X; x <= nmax.X; x++, index++) { s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y); heightmap[index] = y; } } } void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax, std::vector<s16> &floors, std::vector<s16> &ceilings) { const v3s16 &em = vm->m_area.getExtent(); bool is_walkable = false; u32 vi = vm->m_area.index(p2d.X, ymax, p2d.Y); MapNode mn_max = vm->m_data[vi]; bool walkable_above = ndef->get(mn_max).walkable; VoxelArea::add_y(em, vi, -1); for (s16 y = ymax - 1; y >= ymin; y--) { MapNode mn = vm->m_data[vi]; is_walkable = ndef->get(mn).walkable; if (is_walkable && !walkable_above) { floors.push_back(y); } else if (!is_walkable && walkable_above) { ceilings.push_back(y + 1); } VoxelArea::add_y(em, vi, -1); walkable_above = is_walkable; } } inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em) { u32 vi_neg_x = vi; VoxelArea::add_x(em, vi_neg_x, -1); if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) { const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]); if (c_nx.floodable && !c_nx.isLiquid()) return true; } u32 vi_pos_x = vi; VoxelArea::add_x(em, vi_pos_x, +1); if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) { const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]); if (c_px.floodable && !c_px.isLiquid()) return true; } u32 vi_neg_z = vi; VoxelArea::add_z(em, vi_neg_z, -1); if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) { const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]); if (c_nz.floodable && !c_nz.isLiquid()) return true; } u32 vi_pos_z = vi; VoxelArea::add_z(em, vi_pos_z, +1); if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) { const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]); if (c_pz.floodable && !c_pz.isLiquid()) return true; } return false; } void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax) { bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;