diff options
Diffstat (limited to 'game/addons/zylann.hterrain/tools/globalmap_baker.gd')
-rw-r--r-- | game/addons/zylann.hterrain/tools/globalmap_baker.gd | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/game/addons/zylann.hterrain/tools/globalmap_baker.gd b/game/addons/zylann.hterrain/tools/globalmap_baker.gd new file mode 100644 index 0000000..6faa0eb --- /dev/null +++ b/game/addons/zylann.hterrain/tools/globalmap_baker.gd @@ -0,0 +1,168 @@ + +# Bakes a global albedo map using the same shader the terrain uses, +# but renders top-down in orthographic mode. + +@tool +extends Node + +const HTerrain = preload("../hterrain.gd") +const HTerrainData = preload("../hterrain_data.gd") +const HTerrainMesher = preload("../hterrain_mesher.gd") + +# Must be power of two +const DEFAULT_VIEWPORT_SIZE = 512 + +signal progress_notified(info) +signal permanent_change_performed(message) + +var _terrain : HTerrain = null +var _viewport : SubViewport = null +var _viewport_size := DEFAULT_VIEWPORT_SIZE +var _plane : MeshInstance3D = null +var _camera : Camera3D = null +var _sectors := [] +var _sector_index := 0 + + +func _ready(): + set_process(false) + + +func bake(terrain: HTerrain): + assert(terrain != null) + var data := terrain.get_data() + assert(data != null) + _terrain = terrain + + var splatmap := data.get_texture(HTerrainData.CHANNEL_SPLAT) + var colormap := data.get_texture(HTerrainData.CHANNEL_COLOR) + + var terrain_size := data.get_resolution() + + if _viewport == null: + _setup_scene(terrain_size) + + var cw := terrain_size / _viewport_size + var ch := terrain_size / _viewport_size + for y in ch: + for x in cw: + _sectors.append(Vector2(x, y)) + + var mat := _plane.material_override + _terrain.setup_globalmap_material(mat) + + _sector_index = 0 + set_process(true) + + +func _setup_scene(terrain_size: int): + assert(_viewport == null) + + _viewport_size = DEFAULT_VIEWPORT_SIZE + while _viewport_size > terrain_size: + _viewport_size /= 2 + + _viewport = SubViewport.new() + _viewport.size = Vector2(_viewport_size + 1, _viewport_size + 1) + _viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS + _viewport.render_target_clear_mode = SubViewport.CLEAR_MODE_ALWAYS + # _viewport.render_target_v_flip = true + _viewport.world_3d = World3D.new() + _viewport.own_world_3d = true + _viewport.debug_draw = Viewport.DEBUG_DRAW_UNSHADED + + var mat := ShaderMaterial.new() + + _plane = MeshInstance3D.new() + # Make a very small mesh, vertex precision isn't required + var plane_res := 4 + _plane.mesh = \ + HTerrainMesher.make_flat_chunk(plane_res, plane_res, _viewport_size / plane_res, 0) + _plane.material_override = mat + _viewport.add_child(_plane) + + _camera = Camera3D.new() + _camera.projection = Camera3D.PROJECTION_ORTHOGONAL + _camera.size = _viewport.size.x + _camera.near = 0.1 + _camera.far = 10.0 + _camera.current = true + _camera.rotation = Vector3(deg_to_rad(-90), 0, 0) + _viewport.add_child(_camera) + + add_child(_viewport) + + +func _cleanup_scene(): + _viewport.queue_free() + _viewport = null + _plane = null + _camera = null + + +func _process(delta): + if not is_processing(): + return + + if _sector_index > 0: + _grab_image(_sectors[_sector_index - 1]) + + if _sector_index >= len(_sectors): + set_process(false) + _finish() + progress_notified.emit({ "finished": true }) + else: + _setup_pass(_sectors[_sector_index]) + _report_progress() + _sector_index += 1 + + +func _report_progress(): + var sector = _sectors[_sector_index] + progress_notified.emit({ + "progress": float(_sector_index) / len(_sectors), + "message": "Calculating sector (" + str(sector.x) + ", " + str(sector.y) + ")" + }) + + +func _setup_pass(sector: Vector2): + # Note: we implicitely take off-by-one pixels into account + var origin := sector * _viewport_size + var center := origin + 0.5 * Vector2(_viewport.size) + + # The heightmap is left empty, so will default to white, which is a height of 1. + # The camera must be placed above the terrain to see it. + _camera.position = Vector3(center.x, 2.0, center.y) + _plane.position = Vector3(origin.x, 0.0, origin.y) + + +func _grab_image(sector: Vector2): + var tex := _viewport.get_texture() + var src := tex.get_image() + + assert(_terrain != null) + var data := _terrain.get_data() + assert(data != null) + + if data.get_map_count(HTerrainData.CHANNEL_GLOBAL_ALBEDO) == 0: + data._edit_add_map(HTerrainData.CHANNEL_GLOBAL_ALBEDO) + + var dst := data.get_image(HTerrainData.CHANNEL_GLOBAL_ALBEDO) + + src.convert(dst.get_format()) + var origin = sector * _viewport_size + dst.blit_rect(src, Rect2i(0, 0, src.get_width(), src.get_height()), origin) + + +func _finish(): + assert(_terrain != null) + var data := _terrain.get_data() as HTerrainData + assert(data != null) + var dst := data.get_image(HTerrainData.CHANNEL_GLOBAL_ALBEDO) + + data.notify_region_change(Rect2(0, 0, dst.get_width(), dst.get_height()), + HTerrainData.CHANNEL_GLOBAL_ALBEDO) + permanent_change_performed.emit("Bake globalmap") + + _cleanup_scene() + _terrain = null |