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

Add ability to scale terrain #35

Open rossunger opened 1 month ago

rossunger commented 1 month ago

There currently doesn't seem to be any way to scale the terrain. Is this possible?

I have a terrain that I've made, that I like, except it seems that the scale is too small. I would like to be able to scale it both in the Y direction, and in the XZ direction. It doesn't seem like this is possible. Am I missing something?

My current workaround is to export the terrain, re-scale each exr file in an image editing software, and then re-import everything, but this is a terrible user experience. The other workaround is to rescale ALL my other meshes, which seems insane.

p.s. I LOVE this addon. you've done an amazing job.

mohsenph69 commented 1 month ago

Hi there! sorry that I answer this late! There is no way you can scale Terrain mesh! but you can change H_scale (Horizontal scale)! that will scale everything!

For example you have a 512px heightmap image! if your H_scale is one the terrain will be 512 meter! but if you change H_scale 2, the terrain will be 1024 meter! but this way the resolution of terrain will be change!

And please note by doing this you need to change other terrain setting too, so it will adjust itself with new H_scale!

rossunger commented 1 month ago

I've drafted 4 functions that allow you to:

It would be super useful to have these as part of the PAINT tab, or something like that. Let me know what you think!

func shift_terrain_xz(mterrain:MTerrain, offset:Vector2i):      
    var x_count = mterrain.terrain_size.x* mterrain.get_base_size()
    var y_count = mterrain.terrain_size.y* mterrain.get_base_size() 
    for i in x_count:
        for j in y_count:
            var x = i if offset.x > 0 else x_count-i
            var y = j if offset.y > 0 else y_count-j
            var old_y = mterrain.get_height_by_pixel(x+offset.x, y+offset.y)
            #if old_y >0:               
            mterrain.set_height_by_pixel(x,y,old_y)
    mterrain.update_all_dirty_image_texture(false)
    mterrain.save_all_dirty_images()

func scale_terrain_xz(mterrain:MTerrain,xz_multiplier =1):
    if xz_multiplier == 1: return
    if xz_multiplier > 1:                               
        mterrain.terrain_size = ceil( mterrain.terrain_size * xz_multiplier / mterrain.region_size) * mterrain.region_size
    var x_count = mterrain.terrain_size.x* mterrain.get_base_size()
    var y_count = mterrain.terrain_size.y* mterrain.get_base_size()
    for i in x_count:
        for j in y_count:
            var x = i if xz_multiplier < 1 else x_count-i
            var y = j if xz_multiplier < 1 else y_count-j
            var old_y = mterrain.get_height_by_pixel(floor(x/xz_multiplier),floor(y/xz_multiplier))
            mterrain.set_height_by_pixel(x,y,old_y)
    mterrain.update_all_dirty_image_texture(false)
    mterrain.save_all_dirty_images()

func shift_terrain_y(mterrain:MTerrain, offset:float):
    var x_count = mterrain.terrain_size.x* mterrain.get_base_size()
    var y_count = mterrain.terrain_size.y* mterrain.get_base_size() 
    for x in x_count:
        for y in y_count:
            var old_y = mterrain.get_height_by_pixel(x,y)
            mterrain.set_height_by_pixel(x,y,old_y+offset)
    mterrain.update_all_dirty_image_texture(false)
    mterrain.save_all_dirty_images()

func scale_terrain_y(mterrain:MTerrain, height_multiplier): 
    for i in mterrain.terrain_size.x * mterrain.min_size:
        for j in mterrain.terrain_size.y * mterrain.min_size:
            var old_y = mterrain.get_height_by_pixel(i,j)
            mterrain.set_height_by_pixel(i,j,old_y* height_multiplier)
    mterrain.update_all_dirty_image_texture(false)
    mterrain.save_all_dirty_images()
rossunger commented 1 month ago

I've drafted a function that lets you change min_size without destorying the terrain data: note: does not fix mgrass. working on that now.

func multiply_min_size(mterrain:MTerrain, exponent:int = 0):
    if mterrain.min_size - exponent < 0 or mterrain.min_size - exponent > 8:
        push_error("can't multiply min_size, would exceed min/max min_size")
    var arr = []
    for file in DirAccess.get_files_at(mterrain.dataDir):       
        if not ".res" in file: continue
        var path = str(mterrain.dataDir, "/", file)
        var region:MResource = load(path)
        var x = region.get_heightmap_width()
        var img = Image.create(x,x,false, Image.FORMAT_RF)
        img.data.data = region.get_heightmap_rf(false)
        arr.push_back({"path": path, "img": img})           
    mterrain.min_size -= exponent
    mterrain.region_size *= pow(2, exponent)
    mterrain.terrain_size *= pow(2, exponent)
    for img in arr:             
        var region:MResource = load(img.path)               
        region.insert_heightmap_rf( img.img.data.data, 0.02, region.is_compress_qtq(), region.get_file_compress("heightmap"))