Mindwerks / worldengine

World generator using simulation of plates, rain shadow, erosion, etc.
MIT License
982 stars 128 forks source link

Armonize ocean floor #150

Closed ftomassetti closed 8 years ago

ftomassetti commented 8 years ago

This is a simple approach to solve the issue of noisy ocean floors (issue #60)

ftomassetti commented 8 years ago

@tcld I fixed the PR according to your suggestion

tcld commented 8 years ago

I just noticed that the ocean-map is not used, I think the function works on the whole generated world. Is that intended?

psi29a commented 8 years ago

Is armonize missing an h? ;)

ftomassetti commented 8 years ago

@psi29a I was wondering if that was the case... I just saw the someone over the internet used the word "armonize" like I did but perhaps he was a creative foreigner like me :)

tcld commented 8 years ago

If you could add a #TODO: numpy or similar, I would say this is fine. And maybe add an 'h' in front of 'armonize' unless that actually is a word I don't know. (As psi said.^^)

ftomassetti commented 8 years ago

renamed

ftomassetti commented 8 years ago

TODO added

tcld commented 8 years ago

I have to admit that I don't fully understand what the loop body does. It looks to me like it isn't as much smoothing the ocean floor as it is making it shallower (below midpoint) respectively deeper (above midpoint).

ftomassetti commented 8 years ago

@tcld it affects only ocean points which are deep enough (it ignores point above shallow_sea) and what it does it make the elevation very close to midpoint. In this way we have no huge variations in the elevation for ocean tiles. You can someone look at it as a cheap way to perform antialias.

tcld commented 8 years ago

But it does make the ocean much more shallow where it was deep before, doesn't it? Maybe we should instead make use of the anti_alias()-method worldengine already heavily uses? It is a very expensive method but it is kind of made for this job.

ftomassetti commented 8 years ago

Well we would need to adapt it to use a mask. With this method the deepest points should remain deeper than midpoint and the most shallow should remain higher than midpoint, we just reduce the variation. We could rescale it after to have a lower absolute value but I am not sure that would be helpful.

rbb commented 8 years ago

Looks like numpy.logical_and() is the key...

import numpy as np

def harmonize_ocean(ocean, elevation, ocean_level):
    """
    The goal of this function is to make the ocean floor less noisy.
    The underwater erosion should cause the ocean floor to be more uniform
    """
    height, width = elevation.shape

    shallow_sea = ocean_level * 0.85
    midpoint = shallow_sea / 2.0

    for y in range(height):
        for x in range(width):
            e = elevation[y, x]
            if ocean[y, x] and e < shallow_sea:
                # TODO: use numpy to avoid the loop
                if e < midpoint:
                    e = midpoint - ((midpoint - e) / 5.0)
                else:
                    e = midpoint + ((e - midpoint) / 5.0)
                elevation[y, x] = e

def harmonize_ocean_np(ocean, elevation, ocean_level):
    """
    The goal of this function is to make the ocean floor less noisy.
    The underwater erosion should cause the ocean floor to be more uniform
    """

    shallow_sea = ocean_level * 0.85
    midpoint = shallow_sea / 2.0

    lt = elevation_np < shallow_sea
    a = np.logical_and(lt, ocean)
    elevation[a] = midpoint - ((midpoint - elevation[a]) / 5.0)

    gt = elevation_np > shallow_sea
    a = np.logical_and(lt, ocean)
    elevation[a] = midpoint - ((midpoint - elevation[a]) / 5.0)

ocean = np.random.rand(2,2) > 0.5;
elevation = np.random.rand(2,2);
elevation_np = elevation
ocean_level = float(np.random.rand(1,1));

print "ocean: "
print ocean
print "elevation: "
print elevation
print "ocean_level = " +str(ocean_level)

harmonize_ocean(ocean, elevation, ocean_level);
print "--------------------------------"
print "elevation: "
print elevation

harmonize_ocean_np(ocean, elevation_np, ocean_level);
print "--------------------------------"
print "elevation_np: "
print elevation_np

So, on this very small sample data, I got the same result between the two versions of code:

In [33]: execfile('harm.py')
ocean: 
[[ True False]
 [False False]]
elevation: 
[[ 0.78725756  0.86435022]
 [ 0.69927748  0.13072007]]
ocean_level = 0.345082907826
--------------------------------
elevation: 
[[ 0.78725756  0.86435022]
 [ 0.69927748  0.13072007]]
--------------------------------
elevation_np: 
[[ 0.78725756  0.86435022]
 [ 0.69927748  0.13072007]]
tcld commented 8 years ago

Ah, this is a great idea. Although I think the heart of your code (and the parts @ftomassetti would be interested in) should be slightly altered:

    valid = numpy.logical_and(elevation_np < shallow_sea, ocean)  # all tiles that need to be worked on

    lt = numpy.logical_and(elevation_np < midpoint, valid)
    elevation[lt] = midpoint - ((midpoint - elevation[lt]) / 5.0)

    gt = numpy.logical_and(elevation_np >= midpoint, valid)
    elevation[gt] = midpoint + ((elevation[gt] - midpoint) / 5.0)

There were some mistakes in your version. I also renamed the variables and tried to make clear what is being done.

Your small example passed since you didn't move below midpoint. The shallower waters were handled correctly by your code.

rbb commented 8 years ago

I hadn't caught the elevation < shallow_sea part, but I realize the missing >= part.

Good catches!

ftomassetti commented 8 years ago

Thank you guys, I tried incorporating your suggestions (a.k.a. copying and pasting the code you gently provided...)

ftomassetti commented 8 years ago

Ok, blessed images fixed, script for generating blessed images corrected. Time to merge (also because otherwise tests would fail given I have updated the blessed images)

psi29a commented 8 years ago

Awesome work guys! :)