167 lines
4.3 KiB
GDScript
167 lines
4.3 KiB
GDScript
|
|
# 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
|