mohsenph69 / Godot-MTerrain-plugin

A GDExtension plugin which give godot the ability to produce highly optimized Terrain for Open world games
MIT License
372 stars 19 forks source link

Scripts for importing/exporting heightmap and grass data as images #39

Open rossunger opened 2 weeks ago

rossunger commented 2 weeks ago

I've written some editor scripts that allow you to import export grass data and the entire heightmap as one image. Just uncomment the function you want to use.

Some of these take a few minutes to run if you have a reasonably large terrain and godot will freeze while processing them. I had a 8192x8192 terrain with a 0.25density grass and that took 2 or 3 hours, so be warned :)

@tool extends EditorScript
func _run():                        
    var selection = get_editor_interface().get_selection().get_selected_nodes()
    var mgrass
    var mterrain

    for node in selection:
        if node is MGrass: mgrass = node
        if node is MTerrain: mterrain = node
    if is_instance_valid(mgrass):
        #import_grass_data_from_image(mgrass, "PATH_TO_GRASS_IMAGE")        
        #export_grass_data_to_image(mgrass, "PATH_TO_GRASS_IMAGE")      
        pass
    if is_instance_valid(mterrain):                     
        #export_heightmap(mterrain)
        pass
    if is_instance_valid(mterrain) and is_instance_valid(mgrass):               
        #fill_mgrass_data(mterrain, mgrass, false)  # This clears all the grass
        #fill_mgrass_data(mterrain, mgrass, true)   # This paints grass in all the cells.
        pass

func export_heightmap(mterrain:MTerrain, height_scale=164):
    var x_count = mterrain.terrain_size.x* mterrain.get_base_size()
    var y_count = mterrain.terrain_size.y* mterrain.get_base_size()     
    var img = Image.create(x_count,y_count,false,Image.FORMAT_RF)   
    for x in x_count:
        for y in y_count:           
            var h = mterrain.get_height_by_pixel(x, y) / height_scale           
            img.set_pixel(x,y, Color(h,h,h,1))  
    img.save_exr("res://TO_DELETE/MTerrain scale tool/heightmap.exr", true)

func import_heightmap(mterrain:MTerrain, path, height_scale):
    var x_count = mterrain.terrain_size.x* mterrain.get_base_size()
    var y_count = mterrain.terrain_size.y* mterrain.get_base_size()     
    var img = Image.load_from_file(path)
    for x in x_count:
        for y in y_count:           
            var value = img.get_pixel(x,y).r
            mterrain.set_height_by_pixel(x, y, value * height_scale)            
    mterrain.update_all_dirty_image_texture(false)
    mterrain.save_all_dirty_images()

func import_grass_data_from_image(mgrass:MGrass, path):             
    var img = Image.load_from_file(path)
    var scale = mgrass.get_width() / img.get_width()    
    for y in img.get_height() * scale:
        for x in img.get_width() * scale:
            mgrass.set_grass_by_pixel(x,y,img.get_pixel(floor(x/scale),floor(y/scale))[0] >= 0.8)
    mgrass.update_dirty_chunks()    
    mgrass.save_grass_data()    
    print(mgrass.name, " loaded from ", path)

func export_grass_data_to_image(mgrass:MGrass, path):           
    var img = Image.create(mgrass.get_width(),mgrass.get_height(),false,Image.FORMAT_RGB8)
    for x in mgrass.get_width():
        for y in mgrass.get_height():
            if mgrass.get_grass_by_pixel(x,y):
                img.set_pixel(x,y, Color.WHITE)
    print("saving ", mgrass.name, " to ", path)
    img.save_png(path)

func fill_mgrass_data(mterrain:MTerrain, mgrass:MGrass, fill=false):
    var region_size_meter = mterrain.get_base_size()*mterrain.region_size;  
    var grass_region_pixel_width = region_size_meter/ pow(2, mgrass.grass_data.density-2);
    var grass_region_pixel_size = grass_region_pixel_width*grass_region_pixel_width;
    var region_count = mterrain.terrain_size.x / mterrain.region_size * mterrain.terrain_size.y / mterrain.region_size 
    var data_size = (grass_region_pixel_size*region_count)/8;   
    var data:PackedByteArray = []
    data.resize(data_size)
    if fill:
        data.fill(255)
    mgrass.grass_data.data = data
    mgrass.update_dirty_chunks()    
    mgrass.save_grass_data()    
rossunger commented 2 weeks ago

Also, I made a script that takes an array of positions and removes the grass near those positions. I use to remove grass near roads and rivers and buildings and other objects.

func clear_mgrass_near_positions(mgrass:MGrass, positions, radius):
    for position in positions:      
        for i in radius*2:
            for j in radius*2:
                mgrass.set_grass_by_pixel(position.x + i - radius, position.z+j-radius, false)