1 | /**************************************************************************/ |
2 | /* lightmap_gi.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "lightmap_gi.h" |
32 | |
33 | #include "core/config/project_settings.h" |
34 | #include "core/io/config_file.h" |
35 | #include "core/math/delaunay_3d.h" |
36 | #include "lightmap_probe.h" |
37 | #include "scene/3d/mesh_instance_3d.h" |
38 | #include "scene/resources/camera_attributes.h" |
39 | #include "scene/resources/environment.h" |
40 | #include "scene/resources/image_texture.h" |
41 | #include "scene/resources/sky.h" |
42 | |
43 | void LightmapGIData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance) { |
44 | User user; |
45 | user.path = p_path; |
46 | user.uv_scale = p_uv_scale; |
47 | user.slice_index = p_slice_index; |
48 | user.sub_instance = p_sub_instance; |
49 | users.push_back(user); |
50 | } |
51 | |
52 | int LightmapGIData::get_user_count() const { |
53 | return users.size(); |
54 | } |
55 | |
56 | NodePath LightmapGIData::get_user_path(int p_user) const { |
57 | ERR_FAIL_INDEX_V(p_user, users.size(), NodePath()); |
58 | return users[p_user].path; |
59 | } |
60 | |
61 | int32_t LightmapGIData::get_user_sub_instance(int p_user) const { |
62 | ERR_FAIL_INDEX_V(p_user, users.size(), -1); |
63 | return users[p_user].sub_instance; |
64 | } |
65 | |
66 | Rect2 LightmapGIData::get_user_lightmap_uv_scale(int p_user) const { |
67 | ERR_FAIL_INDEX_V(p_user, users.size(), Rect2()); |
68 | return users[p_user].uv_scale; |
69 | } |
70 | |
71 | int LightmapGIData::get_user_lightmap_slice_index(int p_user) const { |
72 | ERR_FAIL_INDEX_V(p_user, users.size(), -1); |
73 | return users[p_user].slice_index; |
74 | } |
75 | |
76 | void LightmapGIData::clear_users() { |
77 | users.clear(); |
78 | } |
79 | |
80 | void LightmapGIData::_set_user_data(const Array &p_data) { |
81 | ERR_FAIL_COND(p_data.is_empty()); |
82 | ERR_FAIL_COND((p_data.size() % 4) != 0); |
83 | |
84 | for (int i = 0; i < p_data.size(); i += 4) { |
85 | add_user(p_data[i + 0], p_data[i + 1], p_data[i + 2], p_data[i + 3]); |
86 | } |
87 | } |
88 | |
89 | Array LightmapGIData::_get_user_data() const { |
90 | Array ret; |
91 | for (int i = 0; i < users.size(); i++) { |
92 | ret.push_back(users[i].path); |
93 | ret.push_back(users[i].uv_scale); |
94 | ret.push_back(users[i].slice_index); |
95 | ret.push_back(users[i].sub_instance); |
96 | } |
97 | return ret; |
98 | } |
99 | |
100 | void LightmapGIData::_set_light_textures_data(const Array &p_data) { |
101 | ERR_FAIL_COND(p_data.is_empty()); |
102 | |
103 | if (p_data.size() == 1) { |
104 | set_light_texture(p_data[0]); |
105 | } else { |
106 | Vector<Ref<Image>> images; |
107 | for (int i = 0; i < p_data.size(); i++) { |
108 | Ref<TextureLayered> texture = p_data[i]; |
109 | ERR_FAIL_COND_MSG(texture.is_null(), vformat("Invalid TextureLayered at index %d." , i)); |
110 | for (int j = 0; j < texture->get_layers(); j++) { |
111 | images.push_back(texture->get_layer_data(j)); |
112 | } |
113 | } |
114 | |
115 | Ref<Texture2DArray> combined_texture; |
116 | combined_texture.instantiate(); |
117 | |
118 | combined_texture->create_from_images(images); |
119 | set_light_texture(combined_texture); |
120 | } |
121 | } |
122 | |
123 | Array LightmapGIData::_get_light_textures_data() const { |
124 | Array ret; |
125 | if (light_texture.is_null() || light_texture->get_layers() == 0) { |
126 | return ret; |
127 | } |
128 | |
129 | Vector<Ref<Image>> images; |
130 | for (int i = 0; i < light_texture->get_layers(); i++) { |
131 | images.push_back(light_texture->get_layer_data(i)); |
132 | } |
133 | |
134 | int slice_count = images.size(); |
135 | int slice_width = images[0]->get_width(); |
136 | int slice_height = images[0]->get_height(); |
137 | |
138 | int slices_per_texture = Image::MAX_HEIGHT / slice_height; |
139 | int texture_count = Math::ceil(slice_count / (float)slices_per_texture); |
140 | |
141 | ret.resize(texture_count); |
142 | |
143 | String base_name = get_path().get_basename(); |
144 | |
145 | int last_count = slice_count % slices_per_texture; |
146 | for (int i = 0; i < texture_count; i++) { |
147 | int texture_slice_count = (i == texture_count - 1 && last_count != 0) ? last_count : slices_per_texture; |
148 | |
149 | Ref<Image> texture_image = Image::create_empty(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); |
150 | |
151 | for (int j = 0; j < texture_slice_count; j++) { |
152 | texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j)); |
153 | } |
154 | |
155 | String texture_path = texture_count > 1 ? base_name + "_" + itos(i) + ".exr" : base_name + ".exr" ; |
156 | |
157 | Ref<ConfigFile> config; |
158 | config.instantiate(); |
159 | |
160 | if (FileAccess::exists(texture_path + ".import" )) { |
161 | config->load(texture_path + ".import" ); |
162 | } |
163 | |
164 | config->set_value("remap" , "importer" , "2d_array_texture" ); |
165 | config->set_value("remap" , "type" , "CompressedTexture2DArray" ); |
166 | if (!config->has_section_key("params" , "compress/mode" )) { |
167 | // User may want another compression, so leave it be, but default to VRAM uncompressed. |
168 | config->set_value("params" , "compress/mode" , 3); |
169 | } |
170 | config->set_value("params" , "compress/channel_pack" , 1); |
171 | config->set_value("params" , "mipmaps/generate" , false); |
172 | config->set_value("params" , "slices/horizontal" , 1); |
173 | config->set_value("params" , "slices/vertical" , texture_slice_count); |
174 | |
175 | config->save(texture_path + ".import" ); |
176 | |
177 | Error err = texture_image->save_exr(texture_path, false); |
178 | ERR_FAIL_COND_V(err, ret); |
179 | ResourceLoader::import(texture_path); |
180 | Ref<TextureLayered> t = ResourceLoader::load(texture_path); //if already loaded, it will be updated on refocus? |
181 | ERR_FAIL_COND_V(t.is_null(), ret); |
182 | ret[i] = t; |
183 | } |
184 | |
185 | return ret; |
186 | } |
187 | |
188 | RID LightmapGIData::get_rid() const { |
189 | return lightmap; |
190 | } |
191 | |
192 | void LightmapGIData::clear() { |
193 | users.clear(); |
194 | } |
195 | |
196 | void LightmapGIData::set_light_texture(const Ref<TextureLayered> &p_light_texture) { |
197 | light_texture = p_light_texture; |
198 | RS::get_singleton()->lightmap_set_textures(lightmap, light_texture.is_valid() ? light_texture->get_rid() : RID(), uses_spherical_harmonics); |
199 | } |
200 | |
201 | Ref<TextureLayered> LightmapGIData::get_light_texture() const { |
202 | return light_texture; |
203 | } |
204 | |
205 | void LightmapGIData::set_uses_spherical_harmonics(bool p_enable) { |
206 | uses_spherical_harmonics = p_enable; |
207 | RS::get_singleton()->lightmap_set_textures(lightmap, light_texture.is_valid() ? light_texture->get_rid() : RID(), uses_spherical_harmonics); |
208 | } |
209 | |
210 | bool LightmapGIData::is_using_spherical_harmonics() const { |
211 | return uses_spherical_harmonics; |
212 | } |
213 | |
214 | void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree, float p_baked_exposure) { |
215 | if (p_points.size()) { |
216 | int pc = p_points.size(); |
217 | ERR_FAIL_COND(pc * 9 != p_point_sh.size()); |
218 | ERR_FAIL_COND((p_tetrahedra.size() % 4) != 0); |
219 | ERR_FAIL_COND((p_bsp_tree.size() % 6) != 0); |
220 | RS::get_singleton()->lightmap_set_probe_capture_data(lightmap, p_points, p_point_sh, p_tetrahedra, p_bsp_tree); |
221 | RS::get_singleton()->lightmap_set_probe_bounds(lightmap, p_bounds); |
222 | RS::get_singleton()->lightmap_set_probe_interior(lightmap, p_interior); |
223 | } else { |
224 | RS::get_singleton()->lightmap_set_probe_capture_data(lightmap, PackedVector3Array(), PackedColorArray(), PackedInt32Array(), PackedInt32Array()); |
225 | RS::get_singleton()->lightmap_set_probe_bounds(lightmap, AABB()); |
226 | RS::get_singleton()->lightmap_set_probe_interior(lightmap, false); |
227 | } |
228 | RS::get_singleton()->lightmap_set_baked_exposure_normalization(lightmap, p_baked_exposure); |
229 | baked_exposure = p_baked_exposure; |
230 | interior = p_interior; |
231 | bounds = p_bounds; |
232 | } |
233 | |
234 | PackedVector3Array LightmapGIData::get_capture_points() const { |
235 | return RS::get_singleton()->lightmap_get_probe_capture_points(lightmap); |
236 | } |
237 | |
238 | PackedColorArray LightmapGIData::get_capture_sh() const { |
239 | return RS::get_singleton()->lightmap_get_probe_capture_sh(lightmap); |
240 | } |
241 | |
242 | PackedInt32Array LightmapGIData::get_capture_tetrahedra() const { |
243 | return RS::get_singleton()->lightmap_get_probe_capture_tetrahedra(lightmap); |
244 | } |
245 | |
246 | PackedInt32Array LightmapGIData::get_capture_bsp_tree() const { |
247 | return RS::get_singleton()->lightmap_get_probe_capture_bsp_tree(lightmap); |
248 | } |
249 | |
250 | AABB LightmapGIData::get_capture_bounds() const { |
251 | return bounds; |
252 | } |
253 | |
254 | bool LightmapGIData::is_interior() const { |
255 | return interior; |
256 | } |
257 | |
258 | float LightmapGIData::get_baked_exposure() const { |
259 | return baked_exposure; |
260 | } |
261 | |
262 | void LightmapGIData::_set_probe_data(const Dictionary &p_data) { |
263 | ERR_FAIL_COND(!p_data.has("bounds" )); |
264 | ERR_FAIL_COND(!p_data.has("points" )); |
265 | ERR_FAIL_COND(!p_data.has("tetrahedra" )); |
266 | ERR_FAIL_COND(!p_data.has("bsp" )); |
267 | ERR_FAIL_COND(!p_data.has("sh" )); |
268 | ERR_FAIL_COND(!p_data.has("interior" )); |
269 | ERR_FAIL_COND(!p_data.has("baked_exposure" )); |
270 | set_capture_data(p_data["bounds" ], p_data["interior" ], p_data["points" ], p_data["sh" ], p_data["tetrahedra" ], p_data["bsp" ], p_data["baked_exposure" ]); |
271 | } |
272 | |
273 | Dictionary LightmapGIData::_get_probe_data() const { |
274 | Dictionary d; |
275 | d["bounds" ] = get_capture_bounds(); |
276 | d["points" ] = get_capture_points(); |
277 | d["tetrahedra" ] = get_capture_tetrahedra(); |
278 | d["bsp" ] = get_capture_bsp_tree(); |
279 | d["sh" ] = get_capture_sh(); |
280 | d["interior" ] = is_interior(); |
281 | d["baked_exposure" ] = get_baked_exposure(); |
282 | return d; |
283 | } |
284 | |
285 | void LightmapGIData::_bind_methods() { |
286 | ClassDB::bind_method(D_METHOD("_set_user_data" , "data" ), &LightmapGIData::_set_user_data); |
287 | ClassDB::bind_method(D_METHOD("_get_user_data" ), &LightmapGIData::_get_user_data); |
288 | |
289 | ClassDB::bind_method(D_METHOD("set_light_texture" , "light_texture" ), &LightmapGIData::set_light_texture); |
290 | ClassDB::bind_method(D_METHOD("get_light_texture" ), &LightmapGIData::get_light_texture); |
291 | |
292 | ClassDB::bind_method(D_METHOD("_set_light_textures_data" , "data" ), &LightmapGIData::_set_light_textures_data); |
293 | ClassDB::bind_method(D_METHOD("_get_light_textures_data" ), &LightmapGIData::_get_light_textures_data); |
294 | |
295 | ClassDB::bind_method(D_METHOD("set_uses_spherical_harmonics" , "uses_spherical_harmonics" ), &LightmapGIData::set_uses_spherical_harmonics); |
296 | ClassDB::bind_method(D_METHOD("is_using_spherical_harmonics" ), &LightmapGIData::is_using_spherical_harmonics); |
297 | |
298 | ClassDB::bind_method(D_METHOD("add_user" , "path" , "uv_scale" , "slice_index" , "sub_instance" ), &LightmapGIData::add_user); |
299 | ClassDB::bind_method(D_METHOD("get_user_count" ), &LightmapGIData::get_user_count); |
300 | ClassDB::bind_method(D_METHOD("get_user_path" , "user_idx" ), &LightmapGIData::get_user_path); |
301 | ClassDB::bind_method(D_METHOD("clear_users" ), &LightmapGIData::clear_users); |
302 | |
303 | ClassDB::bind_method(D_METHOD("_set_probe_data" , "data" ), &LightmapGIData::_set_probe_data); |
304 | ClassDB::bind_method(D_METHOD("_get_probe_data" ), &LightmapGIData::_get_probe_data); |
305 | |
306 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture" , PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered" , PROPERTY_USAGE_EDITOR), "set_light_texture" , "get_light_texture" ); // property usage default but no save |
307 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_light_textures_data" , "_get_light_textures_data" ); |
308 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics" , "is_using_spherical_harmonics" ); |
309 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data" , "_get_user_data" ); |
310 | ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data" , "_get_probe_data" ); |
311 | } |
312 | |
313 | LightmapGIData::LightmapGIData() { |
314 | lightmap = RS::get_singleton()->lightmap_create(); |
315 | } |
316 | |
317 | LightmapGIData::~LightmapGIData() { |
318 | ERR_FAIL_NULL(RenderingServer::get_singleton()); |
319 | RS::get_singleton()->free(lightmap); |
320 | } |
321 | |
322 | /////////////////////////// |
323 | |
324 | void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &meshes, Vector<LightsFound> &lights, Vector<Vector3> &probes) { |
325 | MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); |
326 | if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) { |
327 | Ref<Mesh> mesh = mi->get_mesh(); |
328 | if (mesh.is_valid()) { |
329 | bool all_have_uv2_and_normal = true; |
330 | bool surfaces_found = false; |
331 | for (int i = 0; i < mesh->get_surface_count(); i++) { |
332 | if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { |
333 | continue; |
334 | } |
335 | if (!(mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_TEX_UV2)) { |
336 | all_have_uv2_and_normal = false; |
337 | break; |
338 | } |
339 | if (!(mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_NORMAL)) { |
340 | all_have_uv2_and_normal = false; |
341 | break; |
342 | } |
343 | surfaces_found = true; |
344 | } |
345 | |
346 | if (surfaces_found && all_have_uv2_and_normal) { |
347 | //READY TO BAKE! size hint could be computed if not found, actually.. |
348 | |
349 | MeshesFound mf; |
350 | mf.xform = get_global_transform().affine_inverse() * mi->get_global_transform(); |
351 | mf.node_path = get_path_to(mi); |
352 | mf.subindex = -1; |
353 | mf.mesh = mesh; |
354 | |
355 | static const int lightmap_scale[GeometryInstance3D::LIGHTMAP_SCALE_MAX] = { 1, 2, 4, 8 }; |
356 | mf.lightmap_scale = lightmap_scale[mi->get_lightmap_scale()]; |
357 | |
358 | Ref<Material> all_override = mi->get_material_override(); |
359 | for (int i = 0; i < mesh->get_surface_count(); i++) { |
360 | if (all_override.is_valid()) { |
361 | mf.overrides.push_back(all_override); |
362 | } else { |
363 | mf.overrides.push_back(mi->get_surface_override_material(i)); |
364 | } |
365 | } |
366 | |
367 | meshes.push_back(mf); |
368 | } |
369 | } |
370 | } |
371 | |
372 | Node3D *s = Object::cast_to<Node3D>(p_at_node); |
373 | |
374 | if (!mi && s) { |
375 | Array bmeshes = p_at_node->call("get_bake_bmeshes" ); |
376 | if (bmeshes.size() && (bmeshes.size() & 1) == 0) { |
377 | Transform3D xf = get_global_transform().affine_inverse() * s->get_global_transform(); |
378 | for (int i = 0; i < bmeshes.size(); i += 2) { |
379 | Ref<Mesh> mesh = bmeshes[i]; |
380 | if (!mesh.is_valid()) { |
381 | continue; |
382 | } |
383 | |
384 | MeshesFound mf; |
385 | |
386 | Transform3D mesh_xf = bmeshes[i + 1]; |
387 | mf.xform = xf * mesh_xf; |
388 | mf.node_path = get_path_to(s); |
389 | mf.subindex = i / 2; |
390 | mf.lightmap_scale = 1; |
391 | mf.mesh = mesh; |
392 | |
393 | meshes.push_back(mf); |
394 | } |
395 | } |
396 | } |
397 | |
398 | Light3D *light = Object::cast_to<Light3D>(p_at_node); |
399 | |
400 | if (light && light->get_bake_mode() != Light3D::BAKE_DISABLED) { |
401 | LightsFound lf; |
402 | lf.xform = get_global_transform().affine_inverse() * light->get_global_transform(); |
403 | lf.light = light; |
404 | lights.push_back(lf); |
405 | } |
406 | |
407 | LightmapProbe *probe = Object::cast_to<LightmapProbe>(p_at_node); |
408 | |
409 | if (probe) { |
410 | Transform3D xf = get_global_transform().affine_inverse() * probe->get_global_transform(); |
411 | probes.push_back(xf.origin); |
412 | } |
413 | |
414 | for (int i = 0; i < p_at_node->get_child_count(); i++) { |
415 | Node *child = p_at_node->get_child(i); |
416 | if (!child->get_owner()) { |
417 | continue; //maybe a helper |
418 | } |
419 | |
420 | _find_meshes_and_lights(child, meshes, lights, probes); |
421 | } |
422 | } |
423 | |
424 | int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const { |
425 | int over = 0; |
426 | int under = 0; |
427 | const BSPSimplex &s = p_simplices[p_simplex]; |
428 | for (int i = 0; i < 4; i++) { |
429 | const Vector3 v = p_points[s.vertices[i]]; |
430 | if (p_plane.has_point(v)) { |
431 | // Coplanar. |
432 | } else if (p_plane.is_point_over(v)) { |
433 | over++; |
434 | } else { |
435 | under++; |
436 | } |
437 | } |
438 | |
439 | ERR_FAIL_COND_V(under == 0 && over == 0, -2); //should never happen, we discarded flat simplices before, but in any case drop it from the bsp tree and throw an error |
440 | if (under == 0) { |
441 | return 1; // all over |
442 | } else if (over == 0) { |
443 | return -1; // all under |
444 | } else { |
445 | return 0; // crossing |
446 | } |
447 | } |
448 | |
449 | //#define DEBUG_BSP |
450 | |
451 | int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const LocalVector<Plane> &p_planes, LocalVector<int32_t> &planes_tested, const LocalVector<BSPSimplex> &p_simplices, const LocalVector<int32_t> &p_simplex_indices, LocalVector<BSPNode> &bsp_nodes) { |
452 | //if we reach here, it means there is more than one simplex |
453 | int32_t node_index = (int32_t)bsp_nodes.size(); |
454 | bsp_nodes.push_back(BSPNode()); |
455 | |
456 | //test with all the simplex planes |
457 | Plane best_plane; |
458 | float best_plane_score = -1.0; |
459 | |
460 | for (const int idx : p_simplex_indices) { |
461 | const BSPSimplex &s = p_simplices[idx]; |
462 | for (int j = 0; j < 4; j++) { |
463 | uint32_t plane_index = s.planes[j]; |
464 | if (planes_tested[plane_index] == node_index) { |
465 | continue; //tested this plane already |
466 | } |
467 | |
468 | planes_tested[plane_index] = node_index; |
469 | |
470 | static const int face_order[4][3] = { |
471 | { 0, 1, 2 }, |
472 | { 0, 2, 3 }, |
473 | { 0, 1, 3 }, |
474 | { 1, 2, 3 } |
475 | }; |
476 | |
477 | // despite getting rid of plane duplicates, we should still use here the actual plane to avoid numerical error |
478 | // from thinking this same simplex is intersecting rather than on a side |
479 | Vector3 v0 = p_points[s.vertices[face_order[j][0]]]; |
480 | Vector3 v1 = p_points[s.vertices[face_order[j][1]]]; |
481 | Vector3 v2 = p_points[s.vertices[face_order[j][2]]]; |
482 | |
483 | Plane plane(v0, v1, v2); |
484 | |
485 | //test with all the simplices |
486 | int over_count = 0; |
487 | int under_count = 0; |
488 | |
489 | for (const int &index : p_simplex_indices) { |
490 | int side = _bsp_get_simplex_side(p_points, p_simplices, plane, index); |
491 | if (side == -2) { |
492 | continue; //this simplex is invalid, skip for now |
493 | } else if (side < 0) { |
494 | under_count++; |
495 | } else if (side > 0) { |
496 | over_count++; |
497 | } |
498 | } |
499 | |
500 | if (under_count == 0 && over_count == 0) { |
501 | continue; //most likely precision issue with a flat simplex, do not try this plane |
502 | } |
503 | |
504 | if (under_count > over_count) { //make sure under is always less than over, so we can compute the same ratio |
505 | SWAP(under_count, over_count); |
506 | } |
507 | |
508 | float score = 0; //by default, score is 0 (worst) |
509 | if (over_count > 0) { |
510 | //give score mainly based on ratio (under / over), this means that this plane is splitting simplices a lot, but its balanced |
511 | score = float(under_count) / over_count; |
512 | } |
513 | |
514 | //adjusting priority over least splits, probably not a great idea |
515 | //score *= Math::sqrt(float(over_count + under_count) / p_simplex_indices.size()); //also multiply score |
516 | |
517 | if (score > best_plane_score) { |
518 | best_plane = plane; |
519 | best_plane_score = score; |
520 | } |
521 | } |
522 | } |
523 | |
524 | LocalVector<int32_t> indices_over; |
525 | LocalVector<int32_t> indices_under; |
526 | |
527 | //split again, but add to list |
528 | for (const uint32_t index : p_simplex_indices) { |
529 | int side = _bsp_get_simplex_side(p_points, p_simplices, best_plane, index); |
530 | |
531 | if (side == -2) { |
532 | continue; //simplex sits on the plane, does not make sense to use it |
533 | } |
534 | if (side <= 0) { |
535 | indices_under.push_back(index); |
536 | } |
537 | |
538 | if (side >= 0) { |
539 | indices_over.push_back(index); |
540 | } |
541 | } |
542 | |
543 | #ifdef DEBUG_BSP |
544 | print_line("node " + itos(node_index) + " found plane: " + best_plane + " score:" + rtos(best_plane_score) + " - over " + itos(indices_over.size()) + " under " + itos(indices_under.size()) + " intersecting " + itos(intersecting)); |
545 | #endif |
546 | |
547 | if (best_plane_score < 0.0 || indices_over.size() == p_simplex_indices.size() || indices_under.size() == p_simplex_indices.size()) { |
548 | ERR_FAIL_COND_V(p_simplex_indices.size() <= 1, 0); //should not happen, this is a bug |
549 | |
550 | // Failed to separate the tetrahedrons using planes |
551 | // this means Delaunay broke at some point. |
552 | // Luckily, because we are using tetrahedrons, we can resort to |
553 | // less precise but still working ways to generate the separating plane |
554 | // this will most likely look bad when interpolating, but at least it will not crash. |
555 | // and the artifact will most likely also be very small, so too difficult to notice. |
556 | |
557 | //find the longest axis |
558 | |
559 | WARN_PRINT("Inconsistency found in triangulation while building BSP, probe interpolation quality may degrade a bit." ); |
560 | |
561 | LocalVector<Vector3> centers; |
562 | AABB bounds_all; |
563 | for (uint32_t i = 0; i < p_simplex_indices.size(); i++) { |
564 | AABB bounds; |
565 | for (uint32_t j = 0; j < 4; j++) { |
566 | Vector3 p = p_points[p_simplices[p_simplex_indices[i]].vertices[j]]; |
567 | if (j == 0) { |
568 | bounds.position = p; |
569 | } else { |
570 | bounds.expand_to(p); |
571 | } |
572 | } |
573 | if (i == 0) { |
574 | centers.push_back(bounds.get_center()); |
575 | } else { |
576 | bounds_all.merge_with(bounds); |
577 | } |
578 | } |
579 | Vector3::Axis longest_axis = Vector3::Axis(bounds_all.get_longest_axis_index()); |
580 | |
581 | //find the simplex that will go under |
582 | uint32_t min_d_idx = 0xFFFFFFFF; |
583 | float min_d_dist = 1e20; |
584 | |
585 | for (uint32_t i = 0; i < centers.size(); i++) { |
586 | if (centers[i][longest_axis] < min_d_dist) { |
587 | min_d_idx = i; |
588 | min_d_dist = centers[i][longest_axis]; |
589 | } |
590 | } |
591 | //rebuild best_plane and over/under arrays |
592 | best_plane = Plane(); |
593 | best_plane.normal[longest_axis] = 1.0; |
594 | best_plane.d = min_d_dist; |
595 | |
596 | indices_under.clear(); |
597 | indices_under.push_back(min_d_idx); |
598 | |
599 | indices_over.clear(); |
600 | |
601 | for (uint32_t i = 0; i < p_simplex_indices.size(); i++) { |
602 | if (i == min_d_idx) { |
603 | continue; |
604 | } |
605 | indices_over.push_back(p_simplex_indices[i]); |
606 | } |
607 | } |
608 | |
609 | BSPNode node; |
610 | node.plane = best_plane; |
611 | |
612 | if (indices_under.size() == 0) { |
613 | //nothing to do here |
614 | node.under = BSPNode::EMPTY_LEAF; |
615 | } else if (indices_under.size() == 1) { |
616 | node.under = -(indices_under[0] + 1); |
617 | } else { |
618 | node.under = _compute_bsp_tree(p_points, p_planes, planes_tested, p_simplices, indices_under, bsp_nodes); |
619 | } |
620 | |
621 | if (indices_over.size() == 0) { |
622 | //nothing to do here |
623 | node.over = BSPNode::EMPTY_LEAF; |
624 | } else if (indices_over.size() == 1) { |
625 | node.over = -(indices_over[0] + 1); |
626 | } else { |
627 | node.over = _compute_bsp_tree(p_points, p_planes, planes_tested, p_simplices, indices_over, bsp_nodes); |
628 | } |
629 | |
630 | bsp_nodes[node_index] = node; |
631 | |
632 | return node_index; |
633 | } |
634 | |
635 | bool LightmapGI::_lightmap_bake_step_function(float p_completion, const String &p_text, void *ud, bool p_refresh) { |
636 | BakeStepUD *bsud = (BakeStepUD *)ud; |
637 | bool ret = false; |
638 | if (bsud->func) { |
639 | ret = bsud->func(bsud->from_percent + p_completion * (bsud->to_percent - bsud->from_percent), p_text, bsud->ud, p_refresh); |
640 | } |
641 | return ret; |
642 | } |
643 | |
644 | void LightmapGI::_plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cell_size, const Vector3 *p_triangle) { |
645 | for (int i = 0; i < 8; i++) { |
646 | Vector3i pos = p_cell->offset; |
647 | uint32_t half_size = p_cell->size / 2; |
648 | if (i & 1) { |
649 | pos.x += half_size; |
650 | } |
651 | if (i & 2) { |
652 | pos.y += half_size; |
653 | } |
654 | if (i & 4) { |
655 | pos.z += half_size; |
656 | } |
657 | |
658 | AABB subcell; |
659 | subcell.position = Vector3(pos) * p_cell_size; |
660 | subcell.size = Vector3(half_size, half_size, half_size) * p_cell_size; |
661 | |
662 | if (!Geometry3D::triangle_box_overlap(subcell.get_center(), subcell.size * 0.5, p_triangle)) { |
663 | continue; |
664 | } |
665 | |
666 | if (p_cell->children[i] == nullptr) { |
667 | GenProbesOctree *child = memnew(GenProbesOctree); |
668 | child->offset = pos; |
669 | child->size = half_size; |
670 | p_cell->children[i] = child; |
671 | } |
672 | |
673 | if (half_size > 1) { |
674 | //still levels missing |
675 | _plot_triangle_into_octree(p_cell->children[i], p_cell_size, p_triangle); |
676 | } |
677 | } |
678 | } |
679 | |
680 | void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool> &positions_used, const AABB &p_bounds) { |
681 | for (int i = 0; i < 8; i++) { |
682 | Vector3i pos = p_cell->offset; |
683 | if (i & 1) { |
684 | pos.x += p_cell->size; |
685 | } |
686 | if (i & 2) { |
687 | pos.y += p_cell->size; |
688 | } |
689 | if (i & 4) { |
690 | pos.z += p_cell->size; |
691 | } |
692 | |
693 | if (p_cell->size == 1 && !positions_used.has(pos)) { |
694 | //new position to insert! |
695 | Vector3 real_pos = p_bounds.position + Vector3(pos) * p_cell_size; |
696 | //see if a user submitted probe is too close |
697 | int ppcount = probe_positions.size(); |
698 | const Vector3 *pp = probe_positions.ptr(); |
699 | bool exists = false; |
700 | for (int j = 0; j < ppcount; j++) { |
701 | if (pp[j].is_equal_approx(real_pos)) { |
702 | exists = true; |
703 | break; |
704 | } |
705 | } |
706 | |
707 | if (!exists) { |
708 | new_probe_positions.push_back(real_pos); |
709 | } |
710 | |
711 | positions_used[pos] = true; |
712 | } |
713 | |
714 | if (p_cell->children[i] != nullptr) { |
715 | _gen_new_positions_from_octree(p_cell->children[i], p_cell_size, probe_positions, new_probe_positions, positions_used, p_bounds); |
716 | } |
717 | } |
718 | } |
719 | |
720 | LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_path, Lightmapper::BakeStepFunc p_bake_step, void *p_bake_userdata) { |
721 | if (p_image_data_path.is_empty()) { |
722 | if (get_light_data().is_null()) { |
723 | return BAKE_ERROR_NO_SAVE_PATH; |
724 | } |
725 | |
726 | p_image_data_path = get_light_data()->get_path(); |
727 | if (!p_image_data_path.is_resource_file()) { |
728 | return BAKE_ERROR_NO_SAVE_PATH; |
729 | } |
730 | } |
731 | |
732 | Ref<Lightmapper> lightmapper = Lightmapper::create(); |
733 | ERR_FAIL_COND_V(lightmapper.is_null(), BAKE_ERROR_NO_LIGHTMAPPER); |
734 | |
735 | BakeStepUD bsud; |
736 | bsud.func = p_bake_step; |
737 | bsud.ud = p_bake_userdata; |
738 | bsud.from_percent = 0.2; |
739 | bsud.to_percent = 0.8; |
740 | |
741 | if (p_bake_step) { |
742 | p_bake_step(0.0, RTR("Finding meshes, lights and probes" ), p_bake_userdata, true); |
743 | } |
744 | /* STEP 1, FIND MESHES, LIGHTS AND PROBES */ |
745 | Vector<Lightmapper::MeshData> mesh_data; |
746 | Vector<LightsFound> lights_found; |
747 | Vector<Vector3> probes_found; |
748 | AABB bounds; |
749 | { |
750 | Vector<MeshesFound> meshes_found; |
751 | _find_meshes_and_lights(p_from_node ? p_from_node : get_parent(), meshes_found, lights_found, probes_found); |
752 | |
753 | if (meshes_found.size() == 0) { |
754 | return BAKE_ERROR_NO_MESHES; |
755 | } |
756 | // create mesh data for insert |
757 | |
758 | //get the base material textures, help compute atlas size and bounds |
759 | for (int m_i = 0; m_i < meshes_found.size(); m_i++) { |
760 | if (p_bake_step) { |
761 | float p = (float)(m_i) / meshes_found.size(); |
762 | p_bake_step(p * 0.1, vformat(RTR("Preparing geometry %d/%d" ), m_i, meshes_found.size()), p_bake_userdata, false); |
763 | } |
764 | |
765 | MeshesFound &mf = meshes_found.write[m_i]; |
766 | |
767 | Size2i lightmap_size = mf.mesh->get_lightmap_size_hint(); |
768 | |
769 | if (lightmap_size == Size2i(0, 0)) { |
770 | // TODO we should compute a size if no lightmap hint is set, as we did in 3.x. |
771 | // For now set to basic size to avoid crash. |
772 | lightmap_size = Size2i(64, 64); |
773 | } |
774 | |
775 | lightmap_size *= mf.lightmap_scale; |
776 | TypedArray<RID> overrides; |
777 | overrides.resize(mf.overrides.size()); |
778 | for (int i = 0; i < mf.overrides.size(); i++) { |
779 | if (mf.overrides[i].is_valid()) { |
780 | overrides[i] = mf.overrides[i]->get_rid(); |
781 | } |
782 | } |
783 | TypedArray<Image> images = RS::get_singleton()->bake_render_uv2(mf.mesh->get_rid(), overrides, lightmap_size); |
784 | |
785 | ERR_FAIL_COND_V(images.is_empty(), BAKE_ERROR_CANT_CREATE_IMAGE); |
786 | |
787 | Ref<Image> albedo = images[RS::BAKE_CHANNEL_ALBEDO_ALPHA]; |
788 | Ref<Image> orm = images[RS::BAKE_CHANNEL_ORM]; |
789 | |
790 | //multiply albedo by metal |
791 | |
792 | Lightmapper::MeshData md; |
793 | |
794 | { |
795 | Dictionary d; |
796 | d["path" ] = mf.node_path; |
797 | if (mf.subindex >= 0) { |
798 | d["subindex" ] = mf.subindex; |
799 | } |
800 | md.userdata = d; |
801 | } |
802 | |
803 | { |
804 | if (albedo->get_format() != Image::FORMAT_RGBA8) { |
805 | albedo->convert(Image::FORMAT_RGBA8); |
806 | } |
807 | if (orm->get_format() != Image::FORMAT_RGBA8) { |
808 | orm->convert(Image::FORMAT_RGBA8); |
809 | } |
810 | Vector<uint8_t> albedo_alpha = albedo->get_data(); |
811 | Vector<uint8_t> orm_data = orm->get_data(); |
812 | |
813 | Vector<uint8_t> albedom; |
814 | uint32_t len = albedo_alpha.size(); |
815 | albedom.resize(len); |
816 | const uint8_t *r_aa = albedo_alpha.ptr(); |
817 | const uint8_t *r_orm = orm_data.ptr(); |
818 | uint8_t *w_albedo = albedom.ptrw(); |
819 | |
820 | for (uint32_t i = 0; i < len; i += 4) { |
821 | w_albedo[i + 0] = uint8_t(CLAMP(float(r_aa[i + 0]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255)); |
822 | w_albedo[i + 1] = uint8_t(CLAMP(float(r_aa[i + 1]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255)); |
823 | w_albedo[i + 2] = uint8_t(CLAMP(float(r_aa[i + 2]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255)); |
824 | w_albedo[i + 3] = 255; |
825 | } |
826 | |
827 | md.albedo_on_uv2.instantiate(); |
828 | md.albedo_on_uv2->set_data(lightmap_size.width, lightmap_size.height, false, Image::FORMAT_RGBA8, albedom); |
829 | } |
830 | |
831 | md.emission_on_uv2 = images[RS::BAKE_CHANNEL_EMISSION]; |
832 | if (md.emission_on_uv2->get_format() != Image::FORMAT_RGBAH) { |
833 | md.emission_on_uv2->convert(Image::FORMAT_RGBAH); |
834 | } |
835 | |
836 | //get geometry |
837 | |
838 | Basis normal_xform = mf.xform.basis.inverse().transposed(); |
839 | |
840 | for (int i = 0; i < mf.mesh->get_surface_count(); i++) { |
841 | if (mf.mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { |
842 | continue; |
843 | } |
844 | Array a = mf.mesh->surface_get_arrays(i); |
845 | |
846 | Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX]; |
847 | const Vector3 *vr = vertices.ptr(); |
848 | Vector<Vector2> uv = a[Mesh::ARRAY_TEX_UV2]; |
849 | const Vector2 *uvr = nullptr; |
850 | Vector<Vector3> normals = a[Mesh::ARRAY_NORMAL]; |
851 | const Vector3 *nr = nullptr; |
852 | Vector<int> index = a[Mesh::ARRAY_INDEX]; |
853 | |
854 | ERR_CONTINUE(uv.size() == 0); |
855 | ERR_CONTINUE(normals.size() == 0); |
856 | |
857 | uvr = uv.ptr(); |
858 | nr = normals.ptr(); |
859 | |
860 | int facecount; |
861 | const int *ir = nullptr; |
862 | |
863 | if (index.size()) { |
864 | facecount = index.size() / 3; |
865 | ir = index.ptr(); |
866 | } else { |
867 | facecount = vertices.size() / 3; |
868 | } |
869 | |
870 | for (int j = 0; j < facecount; j++) { |
871 | uint32_t vidx[3]; |
872 | |
873 | if (ir) { |
874 | for (int k = 0; k < 3; k++) { |
875 | vidx[k] = ir[j * 3 + k]; |
876 | } |
877 | } else { |
878 | for (int k = 0; k < 3; k++) { |
879 | vidx[k] = j * 3 + k; |
880 | } |
881 | } |
882 | |
883 | for (int k = 0; k < 3; k++) { |
884 | Vector3 v = mf.xform.xform(vr[vidx[k]]); |
885 | if (bounds == AABB()) { |
886 | bounds.position = v; |
887 | } else { |
888 | bounds.expand_to(v); |
889 | } |
890 | md.points.push_back(v); |
891 | |
892 | md.uv2.push_back(uvr[vidx[k]]); |
893 | md.normal.push_back(normal_xform.xform(nr[vidx[k]]).normalized()); |
894 | } |
895 | } |
896 | } |
897 | |
898 | mesh_data.push_back(md); |
899 | } |
900 | } |
901 | |
902 | /* STEP 2, CREATE PROBES */ |
903 | |
904 | if (p_bake_step) { |
905 | p_bake_step(0.3, RTR("Creating probes" ), p_bake_userdata, true); |
906 | } |
907 | |
908 | //bounds need to include the user probes |
909 | for (int i = 0; i < probes_found.size(); i++) { |
910 | bounds.expand_to(probes_found[i]); |
911 | } |
912 | |
913 | bounds.grow_by(bounds.size.length() * 0.001); |
914 | |
915 | if (gen_probes == GENERATE_PROBES_DISABLED) { |
916 | // generate 8 probes on bound endpoints |
917 | for (int i = 0; i < 8; i++) { |
918 | probes_found.push_back(bounds.get_endpoint(i)); |
919 | } |
920 | } else { |
921 | // detect probes from geometry |
922 | static const int subdiv_values[6] = { 0, 4, 8, 16, 32 }; |
923 | int subdiv = subdiv_values[gen_probes]; |
924 | |
925 | float subdiv_cell_size; |
926 | Vector3i bound_limit; |
927 | { |
928 | int longest_axis = bounds.get_longest_axis_index(); |
929 | subdiv_cell_size = bounds.size[longest_axis] / subdiv; |
930 | int axis_n1 = (longest_axis + 1) % 3; |
931 | int axis_n2 = (longest_axis + 2) % 3; |
932 | |
933 | bound_limit[longest_axis] = subdiv; |
934 | bound_limit[axis_n1] = int(Math::ceil(bounds.size[axis_n1] / subdiv_cell_size)); |
935 | bound_limit[axis_n2] = int(Math::ceil(bounds.size[axis_n2] / subdiv_cell_size)); |
936 | //compensate bounds |
937 | bounds.size[axis_n1] = bound_limit[axis_n1] * subdiv_cell_size; |
938 | bounds.size[axis_n2] = bound_limit[axis_n2] * subdiv_cell_size; |
939 | } |
940 | |
941 | GenProbesOctree octree; |
942 | octree.size = subdiv; |
943 | |
944 | for (int i = 0; i < mesh_data.size(); i++) { |
945 | if (p_bake_step) { |
946 | float p = (float)(i) / mesh_data.size(); |
947 | p_bake_step(0.3 + p * 0.1, vformat(RTR("Creating probes from mesh %d/%d" ), i, mesh_data.size()), p_bake_userdata, false); |
948 | } |
949 | |
950 | for (int j = 0; j < mesh_data[i].points.size(); j += 3) { |
951 | Vector3 points[3] = { mesh_data[i].points[j + 0] - bounds.position, mesh_data[i].points[j + 1] - bounds.position, mesh_data[i].points[j + 2] - bounds.position }; |
952 | _plot_triangle_into_octree(&octree, subdiv_cell_size, points); |
953 | } |
954 | } |
955 | |
956 | LocalVector<Vector3> new_probe_positions; |
957 | HashMap<Vector3i, bool> positions_used; |
958 | for (uint32_t i = 0; i < 8; i++) { //insert bounding endpoints |
959 | Vector3i pos; |
960 | if (i & 1) { |
961 | pos.x += bound_limit.x; |
962 | } |
963 | if (i & 2) { |
964 | pos.y += bound_limit.y; |
965 | } |
966 | if (i & 4) { |
967 | pos.z += bound_limit.z; |
968 | } |
969 | |
970 | positions_used[pos] = true; |
971 | Vector3 real_pos = bounds.position + Vector3(pos) * subdiv_cell_size; //use same formula for numerical stability |
972 | new_probe_positions.push_back(real_pos); |
973 | } |
974 | //skip first level, since probes are always added at bounds endpoints anyway (code above this) |
975 | for (int i = 0; i < 8; i++) { |
976 | if (octree.children[i]) { |
977 | _gen_new_positions_from_octree(octree.children[i], subdiv_cell_size, probes_found, new_probe_positions, positions_used, bounds); |
978 | } |
979 | } |
980 | |
981 | for (const Vector3 &position : new_probe_positions) { |
982 | probes_found.push_back(position); |
983 | } |
984 | } |
985 | |
986 | // Add everything to lightmapper |
987 | if (p_bake_step) { |
988 | p_bake_step(0.4, RTR("Preparing Lightmapper" ), p_bake_userdata, true); |
989 | } |
990 | |
991 | { |
992 | for (int i = 0; i < mesh_data.size(); i++) { |
993 | lightmapper->add_mesh(mesh_data[i]); |
994 | } |
995 | for (int i = 0; i < lights_found.size(); i++) { |
996 | Light3D *light = lights_found[i].light; |
997 | Transform3D xf = lights_found[i].xform; |
998 | |
999 | Color linear_color = light->get_color().srgb_to_linear(); |
1000 | float energy = light->get_param(Light3D::PARAM_ENERGY); |
1001 | if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units" )) { |
1002 | energy *= light->get_param(Light3D::PARAM_INTENSITY); |
1003 | linear_color *= light->get_correlated_color().srgb_to_linear(); |
1004 | } |
1005 | |
1006 | if (Object::cast_to<DirectionalLight3D>(light)) { |
1007 | DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); |
1008 | lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); |
1009 | } else if (Object::cast_to<OmniLight3D>(light)) { |
1010 | OmniLight3D *l = Object::cast_to<OmniLight3D>(light); |
1011 | if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units" )) { |
1012 | energy *= (1.0 / (Math_PI * 4.0)); |
1013 | } |
1014 | lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); |
1015 | } else if (Object::cast_to<SpotLight3D>(light)) { |
1016 | SpotLight3D *l = Object::cast_to<SpotLight3D>(light); |
1017 | if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units" )) { |
1018 | energy *= (1.0 / Math_PI); |
1019 | } |
1020 | lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); |
1021 | } |
1022 | } |
1023 | for (int i = 0; i < probes_found.size(); i++) { |
1024 | lightmapper->add_probe(probes_found[i]); |
1025 | } |
1026 | } |
1027 | |
1028 | Ref<Image> environment_image; |
1029 | Basis environment_transform; |
1030 | |
1031 | // Add everything to lightmapper |
1032 | if (environment_mode != ENVIRONMENT_MODE_DISABLED) { |
1033 | if (p_bake_step) { |
1034 | p_bake_step(4.1, RTR("Preparing Environment" ), p_bake_userdata, true); |
1035 | } |
1036 | |
1037 | environment_transform = get_global_transform().basis; |
1038 | |
1039 | switch (environment_mode) { |
1040 | case ENVIRONMENT_MODE_DISABLED: { |
1041 | //nothing |
1042 | } break; |
1043 | case ENVIRONMENT_MODE_SCENE: { |
1044 | Ref<World3D> world = get_world_3d(); |
1045 | if (world.is_valid()) { |
1046 | Ref<Environment> env = world->get_environment(); |
1047 | if (env.is_null()) { |
1048 | env = world->get_fallback_environment(); |
1049 | } |
1050 | |
1051 | if (env.is_valid()) { |
1052 | environment_image = RS::get_singleton()->environment_bake_panorama(env->get_rid(), true, Size2i(128, 64)); |
1053 | } |
1054 | } |
1055 | } break; |
1056 | case ENVIRONMENT_MODE_CUSTOM_SKY: { |
1057 | if (environment_custom_sky.is_valid()) { |
1058 | environment_image = RS::get_singleton()->sky_bake_panorama(environment_custom_sky->get_rid(), environment_custom_energy, true, Size2i(128, 64)); |
1059 | } |
1060 | |
1061 | } break; |
1062 | case ENVIRONMENT_MODE_CUSTOM_COLOR: { |
1063 | environment_image.instantiate(); |
1064 | environment_image->initialize_data(128, 64, false, Image::FORMAT_RGBAF); |
1065 | Color c = environment_custom_color; |
1066 | c.r *= environment_custom_energy; |
1067 | c.g *= environment_custom_energy; |
1068 | c.b *= environment_custom_energy; |
1069 | environment_image->fill(c); |
1070 | |
1071 | } break; |
1072 | } |
1073 | } |
1074 | |
1075 | float exposure_normalization = 1.0; |
1076 | if (camera_attributes.is_valid()) { |
1077 | exposure_normalization = camera_attributes->get_exposure_multiplier(); |
1078 | if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units" )) { |
1079 | exposure_normalization = camera_attributes->calculate_exposure_normalization(); |
1080 | } |
1081 | } |
1082 | |
1083 | Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, bounces, bias, max_texture_size, directional, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization); |
1084 | |
1085 | if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_TOO_SMALL) { |
1086 | return BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL; |
1087 | } else if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES) { |
1088 | return BAKE_ERROR_MESHES_INVALID; |
1089 | } |
1090 | |
1091 | /* POSTBAKE: Save Light Data */ |
1092 | |
1093 | Ref<LightmapGIData> gi_data; |
1094 | if (get_light_data().is_valid()) { |
1095 | gi_data = get_light_data(); |
1096 | set_light_data(Ref<LightmapGIData>()); //clear |
1097 | gi_data->clear(); |
1098 | } else { |
1099 | gi_data.instantiate(); |
1100 | } |
1101 | |
1102 | Ref<Texture2DArray> texture; |
1103 | { |
1104 | Vector<Ref<Image>> images; |
1105 | for (int i = 0; i < lightmapper->get_bake_texture_count(); i++) { |
1106 | images.push_back(lightmapper->get_bake_texture(i)); |
1107 | } |
1108 | |
1109 | texture.instantiate(); |
1110 | texture->create_from_images(images); |
1111 | } |
1112 | |
1113 | gi_data->set_light_texture(texture); |
1114 | gi_data->set_uses_spherical_harmonics(directional); |
1115 | |
1116 | for (int i = 0; i < lightmapper->get_bake_mesh_count(); i++) { |
1117 | Dictionary d = lightmapper->get_bake_mesh_userdata(i); |
1118 | NodePath np = d["path" ]; |
1119 | int32_t subindex = -1; |
1120 | if (d.has("subindex" )) { |
1121 | subindex = d["subindex" ]; |
1122 | } |
1123 | |
1124 | Rect2 uv_scale = lightmapper->get_bake_mesh_uv_scale(i); |
1125 | int slice_index = lightmapper->get_bake_mesh_texture_slice(i); |
1126 | gi_data->add_user(np, uv_scale, slice_index, subindex); |
1127 | } |
1128 | |
1129 | { |
1130 | // create tetrahedrons |
1131 | Vector<Vector3> points; |
1132 | Vector<Color> sh; |
1133 | points.resize(lightmapper->get_bake_probe_count()); |
1134 | sh.resize(lightmapper->get_bake_probe_count() * 9); |
1135 | for (int i = 0; i < lightmapper->get_bake_probe_count(); i++) { |
1136 | points.write[i] = lightmapper->get_bake_probe_point(i); |
1137 | Vector<Color> colors = lightmapper->get_bake_probe_sh(i); |
1138 | ERR_CONTINUE(colors.size() != 9); |
1139 | for (int j = 0; j < 9; j++) { |
1140 | sh.write[i * 9 + j] = colors[j]; |
1141 | } |
1142 | } |
1143 | |
1144 | //Obtain solved simplices |
1145 | |
1146 | if (p_bake_step) { |
1147 | p_bake_step(0.8, RTR("Generating Probe Volumes" ), p_bake_userdata, true); |
1148 | } |
1149 | Vector<Delaunay3D::OutputSimplex> solved_simplices = Delaunay3D::tetrahedralize(points); |
1150 | |
1151 | LocalVector<BSPSimplex> bsp_simplices; |
1152 | LocalVector<Plane> bsp_planes; |
1153 | LocalVector<int32_t> bsp_simplex_indices; |
1154 | PackedInt32Array tetrahedrons; |
1155 | |
1156 | for (int i = 0; i < solved_simplices.size(); i++) { |
1157 | //Prepare a special representation of the simplex, which uses a BSP Tree |
1158 | BSPSimplex bsp_simplex; |
1159 | for (int j = 0; j < 4; j++) { |
1160 | bsp_simplex.vertices[j] = solved_simplices[i].points[j]; |
1161 | } |
1162 | for (int j = 0; j < 4; j++) { |
1163 | static const int face_order[4][3] = { |
1164 | { 0, 1, 2 }, |
1165 | { 0, 2, 3 }, |
1166 | { 0, 1, 3 }, |
1167 | { 1, 2, 3 } |
1168 | }; |
1169 | Vector3 a = points[solved_simplices[i].points[face_order[j][0]]]; |
1170 | Vector3 b = points[solved_simplices[i].points[face_order[j][1]]]; |
1171 | Vector3 c = points[solved_simplices[i].points[face_order[j][2]]]; |
1172 | |
1173 | //store planes in an array, but ensure they are reused, to speed up processing |
1174 | |
1175 | Plane p(a, b, c); |
1176 | int plane_index = -1; |
1177 | for (uint32_t k = 0; k < bsp_planes.size(); k++) { |
1178 | if (bsp_planes[k].is_equal_approx_any_side(p)) { |
1179 | plane_index = k; |
1180 | break; |
1181 | } |
1182 | } |
1183 | |
1184 | if (plane_index == -1) { |
1185 | plane_index = bsp_planes.size(); |
1186 | bsp_planes.push_back(p); |
1187 | } |
1188 | |
1189 | bsp_simplex.planes[j] = plane_index; |
1190 | |
1191 | //also fill simplex array |
1192 | tetrahedrons.push_back(solved_simplices[i].points[j]); |
1193 | } |
1194 | |
1195 | bsp_simplex_indices.push_back(bsp_simplices.size()); |
1196 | bsp_simplices.push_back(bsp_simplex); |
1197 | } |
1198 | |
1199 | //#define DEBUG_SIMPLICES_AS_OBJ_FILE |
1200 | #ifdef DEBUG_SIMPLICES_AS_OBJ_FILE |
1201 | { |
1202 | Ref<FileAccess> f = FileAccess::open("res://bsp.obj" , FileAccess::WRITE); |
1203 | for (uint32_t i = 0; i < bsp_simplices.size(); i++) { |
1204 | f->store_line("o Simplex" + itos(i)); |
1205 | for (int j = 0; j < 4; j++) { |
1206 | f->store_line(vformat("v %f %f %f" , points[bsp_simplices[i].vertices[j]].x, points[bsp_simplices[i].vertices[j]].y, points[bsp_simplices[i].vertices[j]].z)); |
1207 | } |
1208 | static const int face_order[4][3] = { |
1209 | { 1, 2, 3 }, |
1210 | { 1, 3, 4 }, |
1211 | { 1, 2, 4 }, |
1212 | { 2, 3, 4 } |
1213 | }; |
1214 | |
1215 | for (int j = 0; j < 4; j++) { |
1216 | f->store_line(vformat("f %d %d %d" , 4 * i + face_order[j][0], 4 * i + face_order[j][1], 4 * i + face_order[j][2])); |
1217 | } |
1218 | } |
1219 | } |
1220 | #endif |
1221 | |
1222 | LocalVector<BSPNode> bsp_nodes; |
1223 | LocalVector<int32_t> planes_tested; |
1224 | planes_tested.resize(bsp_planes.size()); |
1225 | for (int &index : planes_tested) { |
1226 | index = 0x7FFFFFFF; |
1227 | } |
1228 | |
1229 | if (p_bake_step) { |
1230 | p_bake_step(0.9, RTR("Generating Probe Acceleration Structures" ), p_bake_userdata, true); |
1231 | } |
1232 | |
1233 | _compute_bsp_tree(points, bsp_planes, planes_tested, bsp_simplices, bsp_simplex_indices, bsp_nodes); |
1234 | |
1235 | PackedInt32Array bsp_array; |
1236 | bsp_array.resize(bsp_nodes.size() * 6); // six 32 bits values used for each BSP node |
1237 | { |
1238 | float *fptr = (float *)bsp_array.ptrw(); |
1239 | int32_t *iptr = (int32_t *)bsp_array.ptrw(); |
1240 | for (uint32_t i = 0; i < bsp_nodes.size(); i++) { |
1241 | fptr[i * 6 + 0] = bsp_nodes[i].plane.normal.x; |
1242 | fptr[i * 6 + 1] = bsp_nodes[i].plane.normal.y; |
1243 | fptr[i * 6 + 2] = bsp_nodes[i].plane.normal.z; |
1244 | fptr[i * 6 + 3] = bsp_nodes[i].plane.d; |
1245 | iptr[i * 6 + 4] = bsp_nodes[i].over; |
1246 | iptr[i * 6 + 5] = bsp_nodes[i].under; |
1247 | } |
1248 | //#define DEBUG_BSP_TREE |
1249 | #ifdef DEBUG_BSP_TREE |
1250 | Ref<FileAccess> f = FileAccess::open("res://bsp.txt" , FileAccess::WRITE); |
1251 | for (uint32_t i = 0; i < bsp_nodes.size(); i++) { |
1252 | f->store_line(itos(i) + " - plane: " + bsp_nodes[i].plane + " over: " + itos(bsp_nodes[i].over) + " under: " + itos(bsp_nodes[i].under)); |
1253 | } |
1254 | #endif |
1255 | } |
1256 | |
1257 | /* Obtain the colors from the images, they will be re-created as cubemaps on the server, depending on the driver */ |
1258 | |
1259 | gi_data->set_capture_data(bounds, interior, points, sh, tetrahedrons, bsp_array, exposure_normalization); |
1260 | /* Compute a BSP tree of the simplices, so it's easy to find the exact one */ |
1261 | } |
1262 | |
1263 | gi_data->set_path(p_image_data_path); |
1264 | Error err = ResourceSaver::save(gi_data); |
1265 | |
1266 | if (err != OK) { |
1267 | return BAKE_ERROR_CANT_CREATE_IMAGE; |
1268 | } |
1269 | |
1270 | set_light_data(gi_data); |
1271 | |
1272 | return BAKE_ERROR_OK; |
1273 | } |
1274 | |
1275 | void LightmapGI::_notification(int p_what) { |
1276 | switch (p_what) { |
1277 | case NOTIFICATION_POST_ENTER_TREE: { |
1278 | if (light_data.is_valid()) { |
1279 | _assign_lightmaps(); |
1280 | } |
1281 | } break; |
1282 | |
1283 | case NOTIFICATION_EXIT_TREE: { |
1284 | if (light_data.is_valid()) { |
1285 | _clear_lightmaps(); |
1286 | } |
1287 | } break; |
1288 | } |
1289 | } |
1290 | |
1291 | void LightmapGI::_assign_lightmaps() { |
1292 | ERR_FAIL_COND(!light_data.is_valid()); |
1293 | |
1294 | for (int i = 0; i < light_data->get_user_count(); i++) { |
1295 | Node *node = get_node(light_data->get_user_path(i)); |
1296 | int instance_idx = light_data->get_user_sub_instance(i); |
1297 | if (instance_idx >= 0) { |
1298 | RID instance_id = node->call("get_bake_mesh_instance" , instance_idx); |
1299 | if (instance_id.is_valid()) { |
1300 | RS::get_singleton()->instance_geometry_set_lightmap(instance_id, get_instance(), light_data->get_user_lightmap_uv_scale(i), light_data->get_user_lightmap_slice_index(i)); |
1301 | } |
1302 | } else { |
1303 | VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(node); |
1304 | ERR_CONTINUE(!vi); |
1305 | RS::get_singleton()->instance_geometry_set_lightmap(vi->get_instance(), get_instance(), light_data->get_user_lightmap_uv_scale(i), light_data->get_user_lightmap_slice_index(i)); |
1306 | } |
1307 | } |
1308 | } |
1309 | |
1310 | void LightmapGI::_clear_lightmaps() { |
1311 | ERR_FAIL_COND(!light_data.is_valid()); |
1312 | for (int i = 0; i < light_data->get_user_count(); i++) { |
1313 | Node *node = get_node(light_data->get_user_path(i)); |
1314 | int instance_idx = light_data->get_user_sub_instance(i); |
1315 | if (instance_idx >= 0) { |
1316 | RID instance_id = node->call("get_bake_mesh_instance" , instance_idx); |
1317 | if (instance_id.is_valid()) { |
1318 | RS::get_singleton()->instance_geometry_set_lightmap(instance_id, RID(), Rect2(), 0); |
1319 | } |
1320 | } else { |
1321 | VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(node); |
1322 | ERR_CONTINUE(!vi); |
1323 | RS::get_singleton()->instance_geometry_set_lightmap(vi->get_instance(), RID(), Rect2(), 0); |
1324 | } |
1325 | } |
1326 | } |
1327 | |
1328 | void LightmapGI::set_light_data(const Ref<LightmapGIData> &p_data) { |
1329 | if (light_data.is_valid()) { |
1330 | if (is_inside_tree()) { |
1331 | _clear_lightmaps(); |
1332 | } |
1333 | set_base(RID()); |
1334 | } |
1335 | light_data = p_data; |
1336 | |
1337 | if (light_data.is_valid()) { |
1338 | set_base(light_data->get_rid()); |
1339 | if (is_inside_tree()) { |
1340 | _assign_lightmaps(); |
1341 | } |
1342 | } |
1343 | |
1344 | update_gizmos(); |
1345 | } |
1346 | |
1347 | Ref<LightmapGIData> LightmapGI::get_light_data() const { |
1348 | return light_data; |
1349 | } |
1350 | |
1351 | void LightmapGI::set_bake_quality(BakeQuality p_quality) { |
1352 | bake_quality = p_quality; |
1353 | } |
1354 | |
1355 | LightmapGI::BakeQuality LightmapGI::get_bake_quality() const { |
1356 | return bake_quality; |
1357 | } |
1358 | |
1359 | AABB LightmapGI::get_aabb() const { |
1360 | return AABB(); |
1361 | } |
1362 | |
1363 | void LightmapGI::set_use_denoiser(bool p_enable) { |
1364 | use_denoiser = p_enable; |
1365 | } |
1366 | |
1367 | bool LightmapGI::is_using_denoiser() const { |
1368 | return use_denoiser; |
1369 | } |
1370 | |
1371 | void LightmapGI::set_directional(bool p_enable) { |
1372 | directional = p_enable; |
1373 | } |
1374 | |
1375 | bool LightmapGI::is_directional() const { |
1376 | return directional; |
1377 | } |
1378 | |
1379 | void LightmapGI::set_interior(bool p_enable) { |
1380 | interior = p_enable; |
1381 | } |
1382 | |
1383 | bool LightmapGI::is_interior() const { |
1384 | return interior; |
1385 | } |
1386 | |
1387 | void LightmapGI::set_environment_mode(EnvironmentMode p_mode) { |
1388 | environment_mode = p_mode; |
1389 | notify_property_list_changed(); |
1390 | } |
1391 | |
1392 | LightmapGI::EnvironmentMode LightmapGI::get_environment_mode() const { |
1393 | return environment_mode; |
1394 | } |
1395 | |
1396 | void LightmapGI::set_environment_custom_sky(const Ref<Sky> &p_sky) { |
1397 | environment_custom_sky = p_sky; |
1398 | } |
1399 | |
1400 | Ref<Sky> LightmapGI::get_environment_custom_sky() const { |
1401 | return environment_custom_sky; |
1402 | } |
1403 | |
1404 | void LightmapGI::set_environment_custom_color(const Color &p_color) { |
1405 | environment_custom_color = p_color; |
1406 | } |
1407 | |
1408 | Color LightmapGI::get_environment_custom_color() const { |
1409 | return environment_custom_color; |
1410 | } |
1411 | |
1412 | void LightmapGI::set_environment_custom_energy(float p_energy) { |
1413 | environment_custom_energy = p_energy; |
1414 | } |
1415 | |
1416 | float LightmapGI::get_environment_custom_energy() const { |
1417 | return environment_custom_energy; |
1418 | } |
1419 | |
1420 | void LightmapGI::set_bounces(int p_bounces) { |
1421 | ERR_FAIL_COND(p_bounces < 0 || p_bounces > 16); |
1422 | bounces = p_bounces; |
1423 | } |
1424 | |
1425 | int LightmapGI::get_bounces() const { |
1426 | return bounces; |
1427 | } |
1428 | |
1429 | void LightmapGI::set_bias(float p_bias) { |
1430 | ERR_FAIL_COND(p_bias < 0.00001); |
1431 | bias = p_bias; |
1432 | } |
1433 | |
1434 | float LightmapGI::get_bias() const { |
1435 | return bias; |
1436 | } |
1437 | |
1438 | void LightmapGI::set_max_texture_size(int p_size) { |
1439 | ERR_FAIL_COND_MSG(p_size < 2048, vformat("The LightmapGI maximum texture size supplied (%d) is too small. The minimum allowed value is 2048." , p_size)); |
1440 | ERR_FAIL_COND_MSG(p_size > 16384, vformat("The LightmapGI maximum texture size supplied (%d) is too large. The maximum allowed value is 16384." , p_size)); |
1441 | max_texture_size = p_size; |
1442 | } |
1443 | |
1444 | int LightmapGI::get_max_texture_size() const { |
1445 | return max_texture_size; |
1446 | } |
1447 | |
1448 | void LightmapGI::set_generate_probes(GenerateProbes p_generate_probes) { |
1449 | gen_probes = p_generate_probes; |
1450 | } |
1451 | |
1452 | LightmapGI::GenerateProbes LightmapGI::get_generate_probes() const { |
1453 | return gen_probes; |
1454 | } |
1455 | |
1456 | void LightmapGI::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { |
1457 | camera_attributes = p_camera_attributes; |
1458 | } |
1459 | |
1460 | Ref<CameraAttributes> LightmapGI::get_camera_attributes() const { |
1461 | return camera_attributes; |
1462 | } |
1463 | |
1464 | PackedStringArray LightmapGI::get_configuration_warnings() const { |
1465 | PackedStringArray warnings = Node::get_configuration_warnings(); |
1466 | |
1467 | if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" ) { |
1468 | warnings.push_back(RTR("LightmapGI nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release." )); |
1469 | return warnings; |
1470 | } |
1471 | |
1472 | return warnings; |
1473 | } |
1474 | |
1475 | void LightmapGI::_validate_property(PropertyInfo &p_property) const { |
1476 | if (p_property.name == "environment_custom_sky" && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { |
1477 | p_property.usage = PROPERTY_USAGE_NONE; |
1478 | } |
1479 | if (p_property.name == "environment_custom_color" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR) { |
1480 | p_property.usage = PROPERTY_USAGE_NONE; |
1481 | } |
1482 | if (p_property.name == "environment_custom_energy" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { |
1483 | p_property.usage = PROPERTY_USAGE_NONE; |
1484 | } |
1485 | } |
1486 | |
1487 | void LightmapGI::_bind_methods() { |
1488 | ClassDB::bind_method(D_METHOD("set_light_data" , "data" ), &LightmapGI::set_light_data); |
1489 | ClassDB::bind_method(D_METHOD("get_light_data" ), &LightmapGI::get_light_data); |
1490 | |
1491 | ClassDB::bind_method(D_METHOD("set_bake_quality" , "bake_quality" ), &LightmapGI::set_bake_quality); |
1492 | ClassDB::bind_method(D_METHOD("get_bake_quality" ), &LightmapGI::get_bake_quality); |
1493 | |
1494 | ClassDB::bind_method(D_METHOD("set_bounces" , "bounces" ), &LightmapGI::set_bounces); |
1495 | ClassDB::bind_method(D_METHOD("get_bounces" ), &LightmapGI::get_bounces); |
1496 | |
1497 | ClassDB::bind_method(D_METHOD("set_generate_probes" , "subdivision" ), &LightmapGI::set_generate_probes); |
1498 | ClassDB::bind_method(D_METHOD("get_generate_probes" ), &LightmapGI::get_generate_probes); |
1499 | |
1500 | ClassDB::bind_method(D_METHOD("set_bias" , "bias" ), &LightmapGI::set_bias); |
1501 | ClassDB::bind_method(D_METHOD("get_bias" ), &LightmapGI::get_bias); |
1502 | |
1503 | ClassDB::bind_method(D_METHOD("set_environment_mode" , "mode" ), &LightmapGI::set_environment_mode); |
1504 | ClassDB::bind_method(D_METHOD("get_environment_mode" ), &LightmapGI::get_environment_mode); |
1505 | |
1506 | ClassDB::bind_method(D_METHOD("set_environment_custom_sky" , "sky" ), &LightmapGI::set_environment_custom_sky); |
1507 | ClassDB::bind_method(D_METHOD("get_environment_custom_sky" ), &LightmapGI::get_environment_custom_sky); |
1508 | |
1509 | ClassDB::bind_method(D_METHOD("set_environment_custom_color" , "color" ), &LightmapGI::set_environment_custom_color); |
1510 | ClassDB::bind_method(D_METHOD("get_environment_custom_color" ), &LightmapGI::get_environment_custom_color); |
1511 | |
1512 | ClassDB::bind_method(D_METHOD("set_environment_custom_energy" , "energy" ), &LightmapGI::set_environment_custom_energy); |
1513 | ClassDB::bind_method(D_METHOD("get_environment_custom_energy" ), &LightmapGI::get_environment_custom_energy); |
1514 | |
1515 | ClassDB::bind_method(D_METHOD("set_max_texture_size" , "max_texture_size" ), &LightmapGI::set_max_texture_size); |
1516 | ClassDB::bind_method(D_METHOD("get_max_texture_size" ), &LightmapGI::get_max_texture_size); |
1517 | |
1518 | ClassDB::bind_method(D_METHOD("set_use_denoiser" , "use_denoiser" ), &LightmapGI::set_use_denoiser); |
1519 | ClassDB::bind_method(D_METHOD("is_using_denoiser" ), &LightmapGI::is_using_denoiser); |
1520 | |
1521 | ClassDB::bind_method(D_METHOD("set_interior" , "enable" ), &LightmapGI::set_interior); |
1522 | ClassDB::bind_method(D_METHOD("is_interior" ), &LightmapGI::is_interior); |
1523 | |
1524 | ClassDB::bind_method(D_METHOD("set_directional" , "directional" ), &LightmapGI::set_directional); |
1525 | ClassDB::bind_method(D_METHOD("is_directional" ), &LightmapGI::is_directional); |
1526 | |
1527 | ClassDB::bind_method(D_METHOD("set_camera_attributes" , "camera_attributes" ), &LightmapGI::set_camera_attributes); |
1528 | ClassDB::bind_method(D_METHOD("get_camera_attributes" ), &LightmapGI::get_camera_attributes); |
1529 | |
1530 | // ClassDB::bind_method(D_METHOD("bake", "from_node"), &LightmapGI::bake, DEFVAL(Variant())); |
1531 | |
1532 | ADD_GROUP("Tweaks" , "" ); |
1533 | ADD_PROPERTY(PropertyInfo(Variant::INT, "quality" , PROPERTY_HINT_ENUM, "Low,Medium,High,Ultra" ), "set_bake_quality" , "get_bake_quality" ); |
1534 | ADD_PROPERTY(PropertyInfo(Variant::INT, "bounces" , PROPERTY_HINT_RANGE, "0,16,1" ), "set_bounces" , "get_bounces" ); |
1535 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional" ), "set_directional" , "is_directional" ); |
1536 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior" ), "set_interior" , "is_interior" ); |
1537 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_denoiser" ), "set_use_denoiser" , "is_using_denoiser" ); |
1538 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bias" , PROPERTY_HINT_RANGE, "0.00001,0.1,0.00001,or_greater" ), "set_bias" , "get_bias" ); |
1539 | ADD_PROPERTY(PropertyInfo(Variant::INT, "max_texture_size" , PROPERTY_HINT_RANGE, "2048,16384,1" ), "set_max_texture_size" , "get_max_texture_size" ); |
1540 | ADD_GROUP("Environment" , "environment_" ); |
1541 | ADD_PROPERTY(PropertyInfo(Variant::INT, "environment_mode" , PROPERTY_HINT_ENUM, "Disabled,Scene,Custom Sky,Custom Color" ), "set_environment_mode" , "get_environment_mode" ); |
1542 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment_custom_sky" , PROPERTY_HINT_RESOURCE_TYPE, "Sky" ), "set_environment_custom_sky" , "get_environment_custom_sky" ); |
1543 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "environment_custom_color" , PROPERTY_HINT_COLOR_NO_ALPHA), "set_environment_custom_color" , "get_environment_custom_color" ); |
1544 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "environment_custom_energy" , PROPERTY_HINT_RANGE, "0,64,0.01" ), "set_environment_custom_energy" , "get_environment_custom_energy" ); |
1545 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes" , PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical" ), "set_camera_attributes" , "get_camera_attributes" ); |
1546 | ADD_GROUP("Gen Probes" , "generate_probes_" ); |
1547 | ADD_PROPERTY(PropertyInfo(Variant::INT, "generate_probes_subdiv" , PROPERTY_HINT_ENUM, "Disabled,4,8,16,32" ), "set_generate_probes" , "get_generate_probes" ); |
1548 | ADD_GROUP("Data" , "" ); |
1549 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_data" , PROPERTY_HINT_RESOURCE_TYPE, "LightmapGIData" ), "set_light_data" , "get_light_data" ); |
1550 | |
1551 | BIND_ENUM_CONSTANT(BAKE_QUALITY_LOW); |
1552 | BIND_ENUM_CONSTANT(BAKE_QUALITY_MEDIUM); |
1553 | BIND_ENUM_CONSTANT(BAKE_QUALITY_HIGH); |
1554 | BIND_ENUM_CONSTANT(BAKE_QUALITY_ULTRA); |
1555 | |
1556 | BIND_ENUM_CONSTANT(GENERATE_PROBES_DISABLED); |
1557 | BIND_ENUM_CONSTANT(GENERATE_PROBES_SUBDIV_4); |
1558 | BIND_ENUM_CONSTANT(GENERATE_PROBES_SUBDIV_8); |
1559 | BIND_ENUM_CONSTANT(GENERATE_PROBES_SUBDIV_16); |
1560 | BIND_ENUM_CONSTANT(GENERATE_PROBES_SUBDIV_32); |
1561 | |
1562 | BIND_ENUM_CONSTANT(BAKE_ERROR_OK); |
1563 | BIND_ENUM_CONSTANT(BAKE_ERROR_NO_SCENE_ROOT); |
1564 | BIND_ENUM_CONSTANT(BAKE_ERROR_FOREIGN_DATA); |
1565 | BIND_ENUM_CONSTANT(BAKE_ERROR_NO_LIGHTMAPPER); |
1566 | BIND_ENUM_CONSTANT(BAKE_ERROR_NO_SAVE_PATH); |
1567 | BIND_ENUM_CONSTANT(BAKE_ERROR_NO_MESHES); |
1568 | BIND_ENUM_CONSTANT(BAKE_ERROR_MESHES_INVALID); |
1569 | BIND_ENUM_CONSTANT(BAKE_ERROR_CANT_CREATE_IMAGE); |
1570 | BIND_ENUM_CONSTANT(BAKE_ERROR_USER_ABORTED); |
1571 | BIND_ENUM_CONSTANT(BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL); |
1572 | |
1573 | BIND_ENUM_CONSTANT(ENVIRONMENT_MODE_DISABLED); |
1574 | BIND_ENUM_CONSTANT(ENVIRONMENT_MODE_SCENE); |
1575 | BIND_ENUM_CONSTANT(ENVIRONMENT_MODE_CUSTOM_SKY); |
1576 | BIND_ENUM_CONSTANT(ENVIRONMENT_MODE_CUSTOM_COLOR); |
1577 | } |
1578 | |
1579 | LightmapGI::LightmapGI() { |
1580 | } |
1581 | |