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

Wiki is missing important information #37

Open rossunger opened 1 month ago

rossunger commented 1 month ago

I love this plugin, but noticed that the wiki is missing a lot of information about how to use this plugin. Some of the information is covered in your videos, but not in the wiki

Would it be helpful if I drafted some text explaining some of the features?

I've had a lot of situations where I misunderstood how some aspect of the plugin worked, and had to rewatch the videos several times to realise what my mistake was. MGrass currently has no write-up. (unless I'm mistaken?)

Also, I still get confused with Region Size and Terrain size.

Also, it would be SUPER helpful to see descriptions of what each property does in the editor. I believe this will now possible in godot 4.3 with this new gdextension feature: https://github.com/godotengine/godot/pull/83747.

mohsenph69 commented 1 month ago

Hello There! You are right my wiki page does not cover a bunch of things! But also I you want to contribute to this thing I will really appreciate that! You can send me some text so I can add that to wiki page! also if some part of already existing wiki page is not explained well or it lack some explanation you can tell me so I can fix that!

Thanks for telling me that by the way! :)

rossunger commented 1 month ago

I've drafted some text... Let me know what you think! Also, is there a good way to chat with you directly? maybe email, or messenger? I'd love to get better understandings of how some of the things in this plugin work. I would be happy to draft more text for the wiki if you need

I've also included some bugfix and feature requests. also, it would be super helpful if you clarified the connection between "chunks" and "grid units" in the base concept wiki page.

I've also added a little terrain LUT shader example that I think could be helpful for people. Maybe people can contribute open source example shaders that you can include with the plugin?

Quick Start Guide

  1. Install and activate plugin
  2. Create new 3D scene. Add an MTerrain node. Select the node and look at the inspector for the next steps.
  3. Set a path to the Data Folder. This should be an empty folder where MTerrain will save all the terrain data.
  4. Set terrain_material to an MTerrainMaterial. You can use the example in the addon folder as a starting point.
  5. Choose region_size and min_size. These are the most important settings because they define the grid units, and you cannot change later. To make good choices it is helpful to understand what regions and chunks are. (see below for a quick summary, or read the wiki for more details)

min_size defines the smallest possible grid unit in meters. Lower numbers allow the terrain to be chunked more efficiently

region_size defines how big each region will be. This setting is in grid units. min_size * region_size = region size in meters. Larger regions require more RAM, but are chunked more efficiently which means faster rendering times.

e.g. if you want a terrain that is 8192x8192km, and you want to divide that into 16 x 16 regions for a total of 256 regions, you could try the following settings: option a) min_size of 64 region size of 8 which makes regions that are 512x512m, and use a terrain size of 128 option b) min_size of 32 region size of 16 which makes regions that are 512x512m, and use a terrain size of 256

Formulas:

sqrt(Terrain size in meters) = min_size * terrain_size
sqrt(Region size in meters) = min_size * region size
sqrt(Number of regions) = sqrt(terrain_size in meters) / sqrt(region_size in meters)

NOTE: once you've started sculpting your terrain you cannot change these settings.

  1. Choose other settings: a) terrain_size x min_size = Terrain size in meters. e.g. if you want a 8192x8192m terrain, and your min_size is 64, you should make your terrain size be 8192/64, which is 128. Increase this number to add more regions b) region_limit controls how many regions can be loaded at one time. lower this number for faster load times. c) layers_data_dir is a path to a folder where MTerrain can store layer data. This is needed if you intend to use layers for non-destructive terrain sculpting.

  2. Press Create in the inspector to see your terrain.

  3. Press the Paint button in the toolbar (include pic) to start sculpting your terrain. Switch to the Paint tab that appears in the top right dock (image). Select a brush, brush size, and brush mode. You can now sculpt your terrain. You can use Layers for non-destructive editing.

NOTE: Remember to save regularly - the painted data isn't automatically saved. You can do this by saving the scene

Importing Heightmap

  1. Go to Project -> Tools -> Import MTerrain data
  2. Select the file to import.
  3. Set region_size. This controls how your heightmap will be divided into regions. e.g if your heightmap is 512x512, and you choose 256, then your heightmap will be divided into 2x2 regions of 256x256 squares. NOTE: there is no way to scale your heightmap in the xz plane during this import process
  4. Toggle the "heightmap" button
  5. Set the height scale for the import. ?????
  6. Choose the output directory. This should be the same as your DATA folder for your MTerrain node.
  7. Import. This can take some time and your computer may become unresponsive. When complete you should have .res files in your DATA folder.

MGrass Quick Start Guide

  1. Setup an MTerrain
  2. Add an MGrass node as child of MTerrain
  3. Create MGrassData and save it as a .res resource
  4. Select density for this MGrass. This controls how small the smallest grid square of grass will be.

IMPORTANT: You can not change this setting after you start painting grass.

E.g. for grass, 0.25 or 0.5 density is good. For trees, 2 or 4 density is good. For very large rocks, you might use 8. To make this choise, create a test terrain, play around with the other settings, paint some mgrass and decide if you need to start again.

  1. Add a Mesh. Different LODs are Optional. If some LOD is missing, mgrass will fall back to a different LOD
  2. Add a Material. Different LOD are optional. You cannot have more than one material per mesh
  3. Add grass_lod_settings. Here you can control the density of meshes, random rotation, scale, position.

The density is controlled by "grass_in_cell" and must be a whole number, no fractions. This means that the lowest density is 1. the maximum density is 256. This is the reason for setting an appropriate density in MGrassData.

  1. Click on MGrass node and in press the paint button in the toolbar (include picture). Click the Paint tab that appears in the inspector dock. Select a brush size, and a brush (null is just a round brush). Now you can paint your grass on the terrain.

Regions, Chunk, and Grid units

To work with MTerrain it is helpful to understand what Regions, Chunk, and Grid units are.

If you zoom out all the way, you have a terrain. This terrain is broken down into regions. Regions are like stand-alone mini-terrains that have been stitched at the edges. MTerrain loads and unloads regions in their entirety. You cannot load part of a region only.

If you zoom into a region, this is where the efficiency magic happens. MTerrain breaks a region into chunks, and does some magic to make chunks that are far away use less resources for rendering.

The smallest possible chunk is called a grid unit. Most of the settings are measured in grid units

Troubleshooting:

1) I'm painting but my grass isn't showing up. Why? Firstly, select MTerrain and toggled Create on and off again. Some setting in MGrass require you to recreate the terrain to appear

Another issue might be that your density * grass_in_cell is lower than the size of your brush. Try increasing grass_in_cell or using a bigger brush

Another issues could be that you've accidentally deselected the MGrass node. Try turning paint mode of and on again.

2) I'm sculpting the terrain, but my work is dissapearing. MTerrain doesn't auto save you sculpt data. If you move in the viewport and MTerrain loads a different region before you've saved, the data for the unloaded region will be lost.

FEATURE REQUESTS

1) A tool to rescale the current heightmap data, both in XZ plane and in the Y direction. 2) Autosave when painting. or at least warn when an unsaved region is being unloaded? or autosave when unloading a region 3) A calculator for deciding region size and terrain size. The MTerrainInfo tool seems like a good place for this 4) Ability to export entire heightmap as a single exr file 5) a button to recreate terrain that's clickable when an MGrass node is selected. 6) An option to keep terrain on when switching scene, or at least an option to automatically create terrain when you switch back to an mterrain scene. Often when I'm working on my terrain, I need to switch to a different scene for a moment, and when I come back, MTerrain is off again. This would be partially solved by a permanent "create" button somewhere in the UI, but would be better if automatic. 7) Please switch Region_Size from grid units, to meters. This would solve a LOT of confusion about what this setting does. I think there's an easy way to fake this in the inspector, so that it is displayed to the user as region size in meters without having to change the underlying code. Or at least have a label that also tells you the region size in meters. 8) Some way to visualise the whole terrain when editing. Maybe a minimap? could be just an image of the whole heightmap, with an outline showing which regions are loaded, and what the editor camera's XZ coordinates are on the minimap? or a super_low_resolution rendering off all regions at once? . I often get lost trying to navigate my terrain. I might be able to draft something using gdscript, but I need help working with the heightmap data, stitching it into one big image. 9) A tool that allows you to change min_size after you've created the terrain. I can help draft this if I can understand bit more about how the data is stored in the .res files. I understand that all the data would need to be converted and it would be a bit of a heavy operation, but it would be SUPER useful. 10) A tool to export MGrass data as an image for use in other software e.g. Blender.

BUGFIXES

1) In PAINT mode, sometimes the mgrass or mterrain node gets deselected. Maybe the plugin can detect when this has happened and automatically disable paint mode? or have an option to lock the selected node so that godot doesn't allow you to deselct the current node being painted

2) Painting holes in terrain creates problems for future painting because the brush gets stuck at the edges of the hole, and it's very difficult to "Unpaint" a hole. Also, the brushes "collision" uses the whole brush, not just the center of the brush. I should be able to paint up to the edge of the hole, but my mouse gets stopped by the collision (i think).

Questions

1) What is the downside to smaller min_size?

MTerrainMaterial shader with LUT

1) Create a new MTerrainMaterial and add a new shader with the following code:

shader_type spatial;

uniform sampler2D mterrain_heightmap:filter_nearest;
uniform sampler2D mterrain_normals:filter_nearest;

uniform vec3 region_world_position;
uniform float region_size;
uniform float region_a;
uniform float region_b;
uniform int min_lod;
varying vec3 world_pos;
varying vec2 region_uv;
global uniform sampler2D TerrainLUT;
varying float y;
uniform float terrain_max_height = 740.4;

void vertex(){
    world_pos = (NODE_POSITION_WORLD + VERTEX);
    region_uv = (world_pos.xz - region_world_position.xz)/region_size;
    region_uv = region_uv*region_a + region_b;
    VERTEX.y = texture(mterrain_heightmap, region_uv).r;
    world_pos.y = VERTEX.y;
    vec3 n = texture(mterrain_normals, region_uv ).rgb; 
    n.rb=fma(n.rb,vec2(2.0),vec2(-1.0));
    NORMAL = n;
    //Scale world uv for terrain texture
    world_pos /= 10.0;
    y = VERTEX.y;

}

void fragment(){    
    ALBEDO = texture(TerrainLUT, vec2(y/terrain_max_height,0.)).rgb *0.1;           
}

2) For the shader_parameter TerrainLUT, create a new GradientTexture1d. Fill it with random (or not random) colors.

mohsenph69 commented 1 month ago

Thank you very much rossunger!

I just modified some of these and I added this page: page , see if it is good or we can something more!

Really good work and many good suggestion! Thank you! You can join on my discord channel

rossunger commented 1 month ago

This is great!

rossunger commented 1 month ago

I edited the addon to add a button RELOAD which appears in the MTools panel. In the MTools.tscn I added a button and connected it to the following :

in mtools.gd:

signal request_recreate

func _on_button_pressed() -> void:
    request_recreate.emit()

and in m_terrain.gd, in the enter_tree function I added:

tools.request_recreate.connect(request_recreate)

func request_recreate():
    if active_terrain:
        active_terrain.create = false
        active_terrain.create = true

this solves my problem with reloading the terrain while working on mgrass. Might help someone else

rossunger commented 1 month ago

I wrote a editor script to export grass data:

@tool extends EditorScript

func _run():
    var selection = get_editor_interface().get_selection().get_selected_nodes()[0]  
    if selection is MGrass: 
        export_grass_data(selection)

func export_grass_data(mgrass:MGrass):      
    var img = Image.create(mgrass.get_width(),mgrass.get_height(),false,Image.FORMAT_L8)
    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)
    img.save_png("res://TO_DELETE/MTerrain scale tool/Test.png")
rossunger commented 1 month ago

and here's one to export the whole heightmap as a single exr file

@tool extends EditorScript

func _run():
    var selection = get_editor_interface().get_selection().get_selected_nodes()[0]  
    if selection is MTerrain:
        export_heightmap(selection)

func export_heightmap(mterrain:MTerrain):
    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) / 164            
            img.set_pixel(x,y, Color(h,h,h,1))  
    img.save_exr("res://TO_DELETE/MTerrain scale tool/heightmap.exr", true)