1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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
|