Godot Version
4.5
Question
Hello everyone! I have been making a marching squares terrain authoring plugin for the past couple months and its nearing its completion. However there is one persistent 'bug' having to do with the grass spawning multimeshinstance3d node that I have never been able to fix. Everytime when the project is (re)launched and I try to alter the terrain, the terrain itself terraforms fine but the instances of the multimesh update their information internally instead of also visually. Only when I remove and re-add the whole node after the editor is fully loaded do the new instances update their visual location when I terraform the terrain. Trying to do this in the _ready (or any other) function however does not work. I have to specifically add and remove it by hand.
If someone has any idea what might be causing this then any tips would be greatly appreciated as I have had this problem for 3+ months now! (see screenshots and code below for more clarification)
Project I forked the initial algorithm from:
Visuals on project (re)launch:
Visuals after re-adding the whole node: (just readding the mm doesn’t work)
More clarification:
When the plugin launches it calls several startup function via call_deferred(). These then launch different other startup function in this order → chunk initialization, grass_planter(multimeshinstance3d) setup and grass_planter grass generation.
When checking with print and printerr statements everywhere it doesn’t give me any errors or indication that some part of the code doesn’t get called or is faulty. Also when I check inside the grass_planter script all the set_transform and set_custom calls are valid and go through so the fact that only the visuals don’t update is very weird to me.
Here is some important startup and grassplanter code:
# Called by TerrainSystem parent
func initialize_terrain(should_regenerate_mesh: bool = true):
needs_update = []
# Initally all cells will need to be updated to show the newly loaded height
for z in range(dimensions.z - 1):
needs_update.append([])
for x in range(dimensions.x - 1):
needs_update[z].append(true)
if not grass_planter:
grass_planter = get_node_or_null("GrassPlanter")
if grass_planter:
grass_planter._chunk = self
if Engine.is_editor_hint():
if not height_map:
generate_height_map()
if not color_map_0 or not color_map_1:
generate_color_maps()
if not grass_mask_map:
generate_grass_mask_map()
if not mesh and should_regenerate_mesh:
regenerate_mesh()
for child in get_children():
if child is StaticBody3D:
child.collision_layer = 17 # ground (1) + terrain (16)
grass_planter.setup(self, true)
grass_planter.regenerate_all_cells()
func setup(chunk: MarchingSquaresTerrainChunk, redo: bool = true):
_chunk = chunk
terrain_system = _chunk.terrain_system
if not _chunk or not terrain_system:
printerr("ERROR: SETUP FAILED - no chunk or terrain system found for GrassPlanter")
return
if (redo and multimesh) or !multimesh:
multimesh = MultiMesh.new()
multimesh.instance_count = 0
multimesh.transform_format = MultiMesh.TRANSFORM_3D
multimesh.use_custom_data = true
multimesh.instance_count = (_chunk.dimensions.x-1) * (_chunk.dimensions.z-1) * terrain_system.grass_subdivisions * terrain_system.grass_subdivisions
if terrain_system.grass_mesh:
multimesh.mesh = terrain_system.grass_mesh
else:
multimesh.mesh = QuadMesh.new() # Create a temporary quad
multimesh.mesh.size = terrain_system.grass_size
cast_shadow = SHADOW_CASTING_SETTING_OFF
func regenerate_all_cells() -> void:
# Safety checks:
if not _chunk:
printerr("ERROR: _chunk not set while regenerating cells")
return
if not terrain_system:
printerr("ERROR: terrain_system not set while regenerating cells")
return
if not multimesh:
setup(_chunk)
if not _chunk.cell_geometry:
_chunk.regenerate_mesh()
for z in range(terrain_system.dimensions.z-1):
for x in range(terrain_system.dimensions.x-1):
generate_grass_on_cell(Vector2i(x, z))

