β€Ί
This is the testing Godot forums! All forum posts unique to this forum will be deleted! Please use the main forums here for any posts you want to keep. All forum rules still apply.

OpenSimplexNoise tool for procedural generation

traquilatraquila Posts: 2Member

Hello,

Beginning on Godot, I started to create a world based on procedural generation.

I use OpenSimplexNoise to generate the relief but when I wanted to use it to generate the elements of nature (tree, plants, rocks) I realized that it was not so easy to configure or visualize the result.

The need is to have a way to give me the biome, the type of ground or vegetation that I have to use for a given coordinate.

So I created a tool to find the right values ​​to use according to my needs.

How to start this tool:
1. Create a new 3D scene
2. Add the following script to the spacial node
3. Play

How to use it:
* A color representant an element (biome, ground, population .. etc) you can add/remove colors in the node configuration.
* Select the quality (1-10) according to your computer speed (use 10 for screenshot)
* Mode 0: provide the result of the OpenSimplexNoise as is. As you can see all colors are not displayed.
* Mode 1: Use an exponential function to evenly distribute colors
* Mode 2: Use multiple layers (one by color) and keep the highest value. This avoids the "ordered color in circle" effect.
* Play with OpenSimplexNoise parameters


extends Spatial

enum eModeList {SimpleNoise, SimpleNoiseExp, MultipleNoise}
export(eModeList) var Mode = eModeList.MultipleNoise
export var map_size = Vector2(2500, 1500)
export(int, 1, 10) var quality = 2
export var noiseParam = {"seed":0, "period":400, "octaves":7, "persistence":0.3, "lacunarity":2.0}
export(int, 2, 20) var exp_coef = 5 # exponential coefficient used for SimpleNoiseExp mode
export var colors = [Color(0,0,0), Color(0,0,1), Color(0,1,0),Color(0,1,1), Color(1,0,0), Color(1,0,1), Color(1,1,0), Color(1,1,1)]

const CAMERA_HEIGH_MAP_RATIO = 2.4
const NOISE_OFFSET = 1009

var g_spinBoxCount = 0
var g_noiseColor

func _ready():
    g_noiseColor = OpenSimplexNoise.new()
    g_noiseColor.seed = noiseParam.seed
    g_noiseColor.octaves = noiseParam.octaves
    g_noiseColor.period = noiseParam.period
    g_noiseColor.persistence = noiseParam.persistence
    g_noiseColor.lacunarity = noiseParam.lacunarity
    add_camera()
    add_gui()
    genSurface(false)

func genSurface(update):    
    var plane_mesh = PlaneMesh.new()
    plane_mesh.size = map_size
    plane_mesh.subdivide_depth = quality * 100
    plane_mesh.subdivide_width = quality * 100
    plane_mesh.material = shaderMat()

    var surface_tool = SurfaceTool.new()
    surface_tool.create_from(plane_mesh, 0)

    var mesh_instance = MeshInstance.new()
    mesh_instance.mesh = surface_tool.commit()
    mesh_instance.name = "meshInst"

    if update:
        $meshInst.free()

    add_child(mesh_instance)
    updateVertex()

func updateVertex():
    var colCount = []
    for i in range(colors.size()):
        colCount += [0]

    var mdt = MeshDataTool.new()
    mdt.create_from_surface($meshInst.mesh, 0)

    for i in range(mdt.get_vertex_count()):
        var colPos = getColor(mdt.get_vertex(i), g_noiseColor)
        mdt.set_vertex_color(i, colors[colPos])
        colCount[colPos] += 1

    update_stats(colCount, mdt.get_vertex_count())

    $meshInst.mesh.surface_remove(0)
    mdt.commit_to_surface($meshInst.mesh)
    mdt.clear()

func getColor(vertex, g_noiseColor):
    # This is the default SimpleNoise usage
    if Mode == eModeList.SimpleNoise:
        var color = g_noiseColor.get_noise_2d(vertex.x, vertex.z)
        color = .5 * (color+1) # convert [-1,1] range to [0,1]
        return int((colors.size())*color)

    # Use an exponential function to evenly distribute colors
    if Mode == eModeList.SimpleNoiseExp:
        var color = g_noiseColor.get_noise_2d(vertex.x, vertex.z)
        color = 1/(1+exp(-color*exp_coef))
        return int((colors.size())*color)

    # Computes a noise for each color and keeps the highest value
    if Mode == eModeList.MultipleNoise:
        var colPos = 0
        var best = -1
        for i in range(colors.size()):
            var cur = g_noiseColor.get_noise_3d(vertex.x, vertex.z, i*NOISE_OFFSET)
            if (best < cur):
                best = cur
                colPos = i
        return colPos

func shaderMat():
    var mat = ShaderMaterial.new()
    mat.shader = Shader.new()
    mat.shader.code = "shader_type spatial; void fragment() {ALBEDO.r=COLOR.r;ALBEDO.g=COLOR.g;ALBEDO.b=COLOR.b;}"
    return mat

func add_camera():
    var cam = Camera.new()
    cam.far = 0
    cam.translation.y = max(map_size.x, map_size.y) / CAMERA_HEIGH_MAP_RATIO
    cam.rotation.x = -PI/2
    cam.current = true
    add_child(cam)

func add_gui():
    addSpinBox("quality",     quality, 1, 10, 1, "onQualityChanged")
    addSpinBox("seed",        noiseParam.seed, 0, 0, 1, "onSeedChanged")
    addSpinBox("period",      noiseParam.period, 10, 0, 10, "onPeriodChanged")
    addSpinBox("octaves",     noiseParam.octaves, 1, 0, 1, "onOctavesChanged")
    addSpinBox("persistence", noiseParam.persistence, 0, 0, 0.1, "onPersistenceChanged")
    addSpinBox("lacunarity",  noiseParam.lacunarity, 0, 0, 0.1, "onLacunarityChanged")
    addSpinBox("Mode",        Mode, 0, 2, 1, "onModeChanged")
    addSpinBox("ExpCoef",     exp_coef, 1, 100, 1, "onExpCoefChanged")

    $ExpCoef.visible = (Mode==1)

    var label = Label.new()
    label.name = "label"
    label.margin_left = 300
    add_child(label)

func addSpinBox(prefix, value, vMin, vMax, step, cbk):
    var spinBox = SpinBox.new()
    spinBox.margin_top = g_spinBoxCount* 28
    spinBox.margin_right = 200
    spinBox.prefix = prefix
    spinBox.min_value = vMin
    spinBox.max_value = vMax
    spinBox.allow_greater = (vMax==0)
    spinBox.step = step
    spinBox.value = value
    spinBox.name = prefix
    spinBox.connect("value_changed", self, cbk)
    add_child(spinBox)
    g_spinBoxCount += 1

func update_stats(colCount, total):
    for i in range(colCount.size()):
        colCount[i] = str(100 * colCount[i] / total) + "%"
    $label.text = str(colCount)

func onQualityChanged(value):
    quality = value
    genSurface(true)

func onSeedChanged(value):
    g_noiseColor.seed = value
    updateVertex()

func onPeriodChanged(value):
    g_noiseColor.period = value
    updateVertex()

func onOctavesChanged(value):
    g_noiseColor.octaves = value
    updateVertex()

func onPersistenceChanged(value):
    g_noiseColor.persistence = value
    updateVertex()

func onLacunarityChanged(value):
    g_noiseColor.lacunarity = value
    updateVertex()

func onModeChanged(value):
    $ExpCoef.visible = (value==1)
    Mode = value
    updateVertex()

func onExpCoefChanged(value):
    exp_coef = value
    updateVertex()

Here some results:


Have fun in godot!
Traquila


Tags :

Comments

  • SIsilicon28SIsilicon28 Posts: 693Moderator

    It looks great, but dang! That's a huge wall of text. 😲<-(on mobile) Do you think you could put it in like a repository, like GitHub, for convenience?

Sign In or Register to comment.