Jason Crosby
Jason Crosby

Reputation: 3583

How to add water material below sea level in procedural terrain?

I'm working on procedural landscape in Godot 4. It works but at the moment it's just using simple textures. Below sea level I want to add a water material instead of the mud texture. How can I accomplish this?

class_name HTerrainGenerator

# Import classes
const HTerrain: Resource = preload("res://addons/zylann.hterrain/hterrain.gd")
const HTerrainData: Resource = preload("res://addons/zylann.hterrain/hterrain_data.gd")
const HTerrainTextureSet: Resource = preload("res://addons/zylann.hterrain/hterrain_texture_set.gd")


var grass_texture: Resource =  load("res://Levels/Textures/T_ground_forest_grass_03_BC_H.PNG")
var sand_texture: Resource = load("res://Levels/Textures/T_ground_sand_03_BC_H.PNG")
var mud_texture: Resource = load("res://Levels/Textures/t_mud_01_d_1k.PNG")

var terrian_seed: int = randi_range(0, 999999999999999999)

func generate_terrain() -> HTerrain:
    # Create terrain resource and give it a size.
    # It must be either 513, 1025, 2049 or 4097.
    var terrain_data: HTerrainData = HTerrainData.new()
    terrain_data.resize(513)

    var noise: FastNoiseLite = FastNoiseLite.new()
    noise.seed = terrian_seed
    var noise_multiplier = 10.0 # affects the height of the terrain

    # Get access to terrain maps
    var heightmap: Image = terrain_data.get_image(HTerrainData.CHANNEL_HEIGHT)
    var normalmap: Image = terrain_data.get_image(HTerrainData.CHANNEL_NORMAL)
    var splatmap: Image = terrain_data.get_image(HTerrainData.CHANNEL_SPLAT)

    # Generate terrain maps
    for z in heightmap.get_height():
        for x in heightmap.get_width():
            # Generate height
            var h = noise_multiplier * noise.get_noise_2d(x, z)

            # Getting normal by generating extra heights directly from noise,
            # so map borders won't have seams in case you stitch them
            var h_right = noise_multiplier * noise.get_noise_2d(x + 0.1, z)
            var h_forward = noise_multiplier * noise.get_noise_2d(x, z + 0.1)
            var normal: Vector3 = Vector3(h - h_right, 0.1, h_forward - h).normalized()

            # Generate texture amounts
            var splat: Color = splatmap.get_pixel(x, z)
            var slope: float = 4.0 * normal.dot(Vector3.UP) - 2.0
            # Sand on the slopes the higher value the more sand shows
            var sand_amount: float = clamp(2.1 - slope, 0.0, 1.0)
            # Mud below sea level
            var mud_amount: float = clamp(0.0 - h, 0.0, 1.0)
            splat = splat.lerp(Color(0,1,0,0), sand_amount)
            splat = splat.lerp(Color(0,0,1,0), mud_amount)

            heightmap.set_pixel(x, z, Color(h, 0, 0))
            normalmap.set_pixel(x, z, HTerrainData.encode_normal(normal))
            splatmap.set_pixel(x, z, splat)

    # Commit modifications so they get uploaded to the graphics card
    var modified_region: Rect2 = Rect2(Vector2(), heightmap.get_size())
    terrain_data.notify_region_change(modified_region, HTerrainData.CHANNEL_HEIGHT)
    terrain_data.notify_region_change(modified_region, HTerrainData.CHANNEL_NORMAL)
    terrain_data.notify_region_change(modified_region, HTerrainData.CHANNEL_SPLAT)

    # Create texture set
    var texture_set: HTerrainTextureSet = HTerrainTextureSet.new()
    texture_set.set_mode(HTerrainTextureSet.MODE_TEXTURES)
    texture_set.insert_slot(-1)
    texture_set.set_texture(0, HTerrainTextureSet.TYPE_ALBEDO_BUMP, grass_texture)
    texture_set.insert_slot(-1)
    texture_set.set_texture(1, HTerrainTextureSet.TYPE_ALBEDO_BUMP, sand_texture)
    texture_set.insert_slot(-1)
    texture_set.set_texture(2, HTerrainTextureSet.TYPE_ALBEDO_BUMP, mud_texture)

    # Create terrain node
    var terrain: HTerrain = HTerrain.new()
    terrain.set_shader_type(HTerrain.SHADER_CLASSIC4_LITE)
    terrain.set_data(terrain_data)
    terrain.set_texture_set(texture_set)
    terrain.position = Vector3(0, 0, 0)

    return terrain

Upvotes: 0

Views: 41

Answers (0)

Related Questions