Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More Mountain Levels #188

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 34 additions & 31 deletions worldengine/World.proto
Original file line number Diff line number Diff line change
Expand Up @@ -57,62 +57,65 @@ message World {
required int32 height = 5;

// Elevation
required DoubleMatrix heightMapData = 6;
required double heightMapTh_sea = 7;
required double heightMapTh_plain = 8;
required double heightMapTh_hill = 9;
required DoubleMatrix heightMapData = 6;
required double heightMapTh_sea = 7;
required double heightMapTh_plain = 8;
required double heightMapTh_hill = 9;
required double heightMapTh_low_mountain = 10;
required double heightMapTh_med_mountain = 11;
required double heightMapTh_high_mountain = 12;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it problematic to add the new variables in the "middle" instead of the end?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, that breaks compatibility but in this case I would say it is intended


// Plates
required IntegerMatrix plates = 10;
required IntegerMatrix plates = 13;

// Ocean
required BooleanMatrix ocean = 11;
required DoubleMatrix sea_depth = 12;
required BooleanMatrix ocean = 14;
required DoubleMatrix sea_depth = 15;

// Biome
optional IntegerMatrix biome = 13;
optional IntegerMatrix biome = 16;

// Humidity
optional DoubleMatrixWithQuantiles humidity = 14;
optional DoubleMatrixWithQuantiles humidity = 17;

// Irrigation
optional DoubleMatrix irrigation = 15;
optional DoubleMatrix irrigation = 18;

// Permeability
optional DoubleMatrix permeabilityData = 16;
optional double permeability_low = 17;
optional double permeability_med = 18;
optional DoubleMatrix permeabilityData = 19;
optional double permeability_low = 20;
optional double permeability_med = 21;

// Watermap
optional DoubleMatrix watermapData = 19;
optional double watermap_creek = 20;
optional double watermap_river = 21;
optional double watermap_mainriver = 22;
optional DoubleMatrix watermapData = 22;
optional double watermap_creek = 23;
optional double watermap_river = 24;
optional double watermap_mainriver = 25;

// Precipitation
optional DoubleMatrix precipitationData = 23;
optional double precipitation_low = 24;
optional double precipitation_med = 25;
optional DoubleMatrix precipitationData = 26;
optional double precipitation_low = 27;
optional double precipitation_med = 28;

// Temperature
optional DoubleMatrix temperatureData = 26;
optional double temperature_polar = 27;
optional double temperature_alpine = 28;
optional double temperature_boreal = 29;
optional double temperature_cool = 30;
optional double temperature_warm = 31;
optional double temperature_subtropical = 32;
optional DoubleMatrix temperatureData = 29;
optional double temperature_polar = 30;
optional double temperature_alpine = 31;
optional double temperature_boreal = 32;
optional double temperature_cool = 33;
optional double temperature_warm = 34;
optional double temperature_subtropical = 35;

// Data about generation:
// introduced in v0.5.3
// this is optional for backward compatibility reasons
optional GenerationData generationData = 33;
optional GenerationData generationData = 36;

optional DoubleMatrix lakemap = 34;
optional DoubleMatrix rivermap = 35;
optional DoubleMatrix lakemap = 37;
optional DoubleMatrix rivermap = 38;

// Ice-caps
optional DoubleMatrix icecap = 36;
optional DoubleMatrix icecap = 39;
}


50 changes: 21 additions & 29 deletions worldengine/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@
### For draw_satellite ###
NOISE_RANGE = 15 # a random value between -NOISE_RANGE and NOISE_RANGE will be added to the rgb of each pixel

# These are arbitrarily-chosen elevation cutoffs for 4 different height levels.
# Some color modifiers will be applied at each level
HIGH_MOUNTAIN_ELEV = 215
MOUNTAIN_ELEV = 175
HIGH_HILL_ELEV = 160
HILL_ELEV = 145

# These are rgb color values which will be added to the noise, if the elevation is greater than the height specified
# These are not cumulative
HIGH_MOUNTAIN_NOISE_MODIFIER = (10, 6, 10)
Expand Down Expand Up @@ -279,30 +272,29 @@ def get_biome_color_based_on_elevation(world, elev, x, y, rng):
## Generate some random noise to apply to this pixel
# There is noise for each element of the rgb value
# This noise will be further modified by the height of this tile

noise = rng.randint(-NOISE_RANGE, NOISE_RANGE, size=3) # draw three random numbers at once

####### Case 1 - elevation is very high ########
if elev > HIGH_MOUNTAIN_ELEV:
# Modify the noise to make the area slightly brighter to simulate snow-topped mountains.
noise = add_colors(noise, HIGH_MOUNTAIN_NOISE_MODIFIER)
# Average the biome's color with the MOUNTAIN_COLOR to tint the terrain
biome_color = average_colors(biome_color, MOUNTAIN_COLOR)

####### Case 2 - elevation is high ########
elif elev > MOUNTAIN_ELEV:
# Modify the noise to make this tile slightly darker, especially draining the green
noise = add_colors(noise, MOUNTAIN_NOISE_MODIFIER)
# Average the biome's color with the MOUNTAIN_COLOR to tint the terrain
biome_color = average_colors(biome_color, MOUNTAIN_COLOR)

####### Case 3 - elevation is somewhat high ########
elif elev > HIGH_HILL_ELEV:
noise = add_colors(noise, HIGH_HILL_NOISE_MODIFIER)

####### Case 4 - elevation is a little bit high ########
elif elev > HILL_ELEV:
noise = add_colors(noise, HILL_NOISE_MODIFIER)
####### Case 1 - elevation is very high ########
if world.is_high_mountain((x, y)):
# Modify the noise to make the area slightly brighter to simulate snow-topped mountains.
noise = add_colors(noise, HIGH_MOUNTAIN_NOISE_MODIFIER)
# Average the biome's color with the MOUNTAIN_COLOR to tint the terrain
biome_color = average_colors(biome_color, MOUNTAIN_COLOR)

####### Case 2 - elevation is high ########
elif world.is_medium_mountain((x, y)):
# Modify the noise to make this tile slightly darker, especially draining the green
noise = add_colors(noise, MOUNTAIN_NOISE_MODIFIER)
# Average the biome's color with the MOUNTAIN_COLOR to tint the terrain
biome_color = average_colors(biome_color, MOUNTAIN_COLOR)

####### Case 3 - elevation is somewhat high ########
elif world.is_low_mountain((x, y)):
noise = add_colors(noise, HIGH_HILL_NOISE_MODIFIER)

####### Case 4 - elevation is a little bit high ########
elif world.is_hill((x, y)):
noise = add_colors(noise, HILL_NOISE_MODIFIER)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the static height-checks have been replaced by calls to the appropriate functions; output looks slightly different than before, but that is to be expected with the changed parameters in generation.py.

Here the output of a test-world:
satellite_28070


# There is also a minor base modifier to the pixel's rgb value based on height
modification_amount = int(elev / BASE_ELEVATION_INTENSITY_MODIFIER)
Expand Down
4 changes: 2 additions & 2 deletions worldengine/drawing_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ def _find_mountains_mask(world, factor):
_mask = numpy.zeros((factor * world.height, factor * world.width), dtype=float)
for y in range(factor * world.height):
for x in range(factor * world.width):
if world.is_mountain((int(x / factor), int(y / factor))):
if world.is_low_mountain((int(x / factor), int(y / factor)), include_higher=True): # maybe use is_medium_mountain()?
v = len(world.tiles_around((int(x / factor), int(y / factor)),
radius=3,
predicate=world.is_mountain))
predicate=lambda pos: world.is_low_mountain(pos, include_higher=True)))
if v > 32:
_mask[y, x] = v / 4.0 # force conversion to float, Python 2 will *not* do it automatically
return _mask
Expand Down
18 changes: 13 additions & 5 deletions worldengine/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,20 @@ def initialize_ocean_and_thresholds(world, ocean_level=1.0):
"""
e = world.elevation['data']
ocean = fill_ocean(e, ocean_level)
hl = find_threshold_f(e, 0.10) # the highest 10% of all (!) land are declared hills
ml = find_threshold_f(e, 0.03) # the highest 3% are declared mountains

# values chosen to emulate the exponential behavior seen here:
# http://www.ngdc.noaa.gov/mgg/global/etopo1_surface_histogram.html
pl = find_threshold_f(e, 0.80, ocean=ocean) # the highest x% of land are declared plains; save some for beaches (?)
hl = find_threshold_f(e, 0.40, ocean=ocean) # the highest x% are declared hills
ml = find_threshold_f(e, 0.20, ocean=ocean) # the highest x% are declared low mountains
mml = find_threshold_f(e, 0.10, ocean=ocean) # the highest x% are declared medium mountains
hml = find_threshold_f(e, 0.05, ocean=ocean) # the highest x% are declared high mountains
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the values a bit, created new plots and want to collect all the important points in a single post:
The former "hills"-level was assigned to the highest 3% of the world, the following 7% (for a total of 10%) was declared plains.

The new values declare the following values, in relation to total land (in the code the values are relative to non-submerged land):
plains ~28%, hills 14%, low mountains 7%, medium mountains 3.5% and high mountains 1.8%.

So the former "hills" roughly correspond to "medium mountains" now. Thus the changes to river-, temperature- and ancient map-generation. The choices in there made sense, but the hill-level was very high.

The following two plots show the old and new height-levels:
seed_13051_hypsographicseed_13051_hypsographic

The plains are set to 80% (of non-submerged land) since due to possible depressions in the land 100% would end up below sea-level. 80% leaves a little wiggle-room.
Everything else is chosen to follow a rough 2^x shape, as I estimated it from here (supposedly real data): hypsographic curve Earth

If anybody has more accurate data, please point me to it (other criticism is welcome, too). For now the values seem reasonable and the results of world-generation look good to me.

e_th = [('sea', ocean_level),
('plain', hl),
('hill', ml),
('mountain', None)]
('plain', pl),
('hill', hl),
('mountain', ml),
('med_mountain', mml),
('high_mountain', hml)]
harmonize_ocean(ocean, e, ocean_level)
world.set_ocean(ocean)
world.set_elevation(e, e_th)
Expand Down
7 changes: 6 additions & 1 deletion worldengine/hdf5_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def save_world_to_hdf5(world, filename):
elevation_ths_grp["sea"] = world.elevation['thresholds'][0][1]
elevation_ths_grp["plain"] = world.elevation['thresholds'][1][1]
elevation_ths_grp["hill"] = world.elevation['thresholds'][2][1]
elevation_ths_grp["low_mountain"] = world.elevation['thresholds'][3][1]
elevation_ths_grp["med_mountain"] = world.elevation['thresholds'][4][1]
elevation_ths_grp["high_mountain"] = world.elevation['thresholds'][5][1]
elevation_data = elevation_grp.create_dataset("data", (world.height, world.width), dtype=numpy.float)
elevation_data.write_direct(world.elevation['data'])

Expand Down Expand Up @@ -137,7 +140,9 @@ def load_world_to_hdf5(filename):
e_th = [('sea', f['elevation/thresholds/sea'].value),
('plain', f['elevation/thresholds/plain'].value),
('hill', f['elevation/thresholds/hill'].value),
('mountain', None)]
('mountain', f['elevation/thresholds/low_mountain'].value),
('med_mountain', f['elevation/thresholds/med_mountain'].value),
('high_mountain', f['elevation/thresholds/high_mountain'].value)]
w.set_elevation(e, e_th)

# Plates
Expand Down
Loading