2018-01-04 23:50:27 +00:00
/*************************************************************************/
2021-06-04 22:47:26 +00:00
/* lightmap_gi.cpp */
2018-01-04 23:50:27 +00:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 20:27:34 +00:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2018-01-04 23:50:27 +00:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2021-06-04 22:47:26 +00:00
# include "lightmap_gi.h"
2020-05-12 08:10:34 +00:00
2022-07-31 23:20:24 +00:00
# include "core/config/project_settings.h"
2018-10-05 02:00:02 +00:00
# include "core/io/config_file.h"
2020-05-01 12:34:23 +00:00
# include "core/math/delaunay_3d.h"
# include "lightmap_probe.h"
2021-08-12 23:05:59 +00:00
# include "scene/3d/mesh_instance_3d.h"
2022-07-31 23:20:24 +00:00
# include "scene/resources/camera_attributes.h"
# include "scene/resources/environment.h"
# include "scene/resources/sky.h"
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
void LightmapGIData : : add_user ( const NodePath & p_path , const Rect2 & p_uv_scale , int p_slice_index , int32_t p_sub_instance ) {
2017-12-14 11:59:46 +00:00
User user ;
user . path = p_path ;
2020-05-01 12:34:23 +00:00
user . uv_scale = p_uv_scale ;
user . slice_index = p_slice_index ;
user . sub_instance = p_sub_instance ;
2017-12-14 11:59:46 +00:00
users . push_back ( user ) ;
}
2021-06-04 22:47:26 +00:00
int LightmapGIData : : get_user_count ( ) const {
2017-12-14 11:59:46 +00:00
return users . size ( ) ;
}
2020-05-14 12:29:06 +00:00
2021-06-04 22:47:26 +00:00
NodePath LightmapGIData : : get_user_path ( int p_user ) const {
2017-12-14 11:59:46 +00:00
ERR_FAIL_INDEX_V ( p_user , users . size ( ) , NodePath ( ) ) ;
return users [ p_user ] . path ;
}
2021-06-04 22:47:26 +00:00
int32_t LightmapGIData : : get_user_sub_instance ( int p_user ) const {
2020-05-01 12:34:23 +00:00
ERR_FAIL_INDEX_V ( p_user , users . size ( ) , - 1 ) ;
return users [ p_user ] . sub_instance ;
}
2021-06-04 22:47:26 +00:00
Rect2 LightmapGIData : : get_user_lightmap_uv_scale ( int p_user ) const {
2020-05-01 12:34:23 +00:00
ERR_FAIL_INDEX_V ( p_user , users . size ( ) , Rect2 ( ) ) ;
return users [ p_user ] . uv_scale ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
int LightmapGIData : : get_user_lightmap_slice_index ( int p_user ) const {
2017-12-18 03:34:48 +00:00
ERR_FAIL_INDEX_V ( p_user , users . size ( ) , - 1 ) ;
2020-05-01 12:34:23 +00:00
return users [ p_user ] . slice_index ;
2017-12-18 03:34:48 +00:00
}
2021-06-04 22:47:26 +00:00
void LightmapGIData : : clear_users ( ) {
2017-12-14 11:59:46 +00:00
users . clear ( ) ;
}
2021-06-04 22:47:26 +00:00
void LightmapGIData : : _set_user_data ( const Array & p_data ) {
2022-06-08 19:51:31 +00:00
ERR_FAIL_COND ( p_data . is_empty ( ) ) ;
2020-05-01 12:34:23 +00:00
ERR_FAIL_COND ( ( p_data . size ( ) % 4 ) ! = 0 ) ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
for ( int i = 0 ; i < p_data . size ( ) ; i + = 4 ) {
add_user ( p_data [ i + 0 ] , p_data [ i + 1 ] , p_data [ i + 2 ] , p_data [ i + 3 ] ) ;
2017-12-14 11:59:46 +00:00
}
}
2021-06-04 22:47:26 +00:00
Array LightmapGIData : : _get_user_data ( ) const {
2017-12-14 11:59:46 +00:00
Array ret ;
for ( int i = 0 ; i < users . size ( ) ; i + + ) {
ret . push_back ( users [ i ] . path ) ;
2020-05-01 12:34:23 +00:00
ret . push_back ( users [ i ] . uv_scale ) ;
ret . push_back ( users [ i ] . slice_index ) ;
ret . push_back ( users [ i ] . sub_instance ) ;
2017-12-14 11:59:46 +00:00
}
return ret ;
}
2022-06-08 19:51:31 +00:00
void LightmapGIData : : _set_light_textures_data ( const Array & p_data ) {
ERR_FAIL_COND ( p_data . is_empty ( ) ) ;
if ( p_data . size ( ) = = 1 ) {
set_light_texture ( p_data [ 0 ] ) ;
} else {
Vector < Ref < Image > > images ;
for ( int i = 0 ; i < p_data . size ( ) ; i + + ) {
Ref < TextureLayered > texture = p_data [ i ] ;
2022-09-04 12:43:25 +00:00
ERR_FAIL_COND_MSG ( texture . is_null ( ) , vformat ( " Invalid TextureLayered at index %d. " , i ) ) ;
2022-06-08 19:51:31 +00:00
for ( int j = 0 ; j < texture - > get_layers ( ) ; j + + ) {
images . push_back ( texture - > get_layer_data ( j ) ) ;
}
}
Ref < Texture2DArray > combined_texture ;
combined_texture . instantiate ( ) ;
combined_texture - > create_from_images ( images ) ;
set_light_texture ( combined_texture ) ;
}
}
Array LightmapGIData : : _get_light_textures_data ( ) const {
Array ret ;
2022-06-17 00:27:08 +00:00
if ( light_texture . is_null ( ) | | light_texture - > get_layers ( ) = = 0 ) {
2022-06-08 19:51:31 +00:00
return ret ;
}
Vector < Ref < Image > > images ;
for ( int i = 0 ; i < light_texture - > get_layers ( ) ; i + + ) {
images . push_back ( light_texture - > get_layer_data ( i ) ) ;
}
int slice_count = images . size ( ) ;
int slice_width = images [ 0 ] - > get_width ( ) ;
int slice_height = images [ 0 ] - > get_height ( ) ;
int slices_per_texture = Image : : MAX_HEIGHT / slice_height ;
int texture_count = Math : : ceil ( slice_count / ( float ) slices_per_texture ) ;
ret . resize ( texture_count ) ;
String base_name = get_path ( ) . get_basename ( ) ;
int last_count = slice_count % slices_per_texture ;
for ( int i = 0 ; i < texture_count ; i + + ) {
int texture_slice_count = ( i = = texture_count - 1 & & last_count ! = 0 ) ? last_count : slices_per_texture ;
Ref < Image > texture_image ;
texture_image . instantiate ( ) ;
texture_image - > create ( slice_width , slice_height * texture_slice_count , false , images [ 0 ] - > get_format ( ) ) ;
for ( int j = 0 ; j < texture_slice_count ; j + + ) {
2022-07-09 20:43:34 +00:00
texture_image - > blit_rect ( images [ i * slices_per_texture + j ] , Rect2i ( 0 , 0 , slice_width , slice_height ) , Point2i ( 0 , slice_height * j ) ) ;
2022-06-08 19:51:31 +00:00
}
String texture_path = texture_count > 1 ? base_name + " _ " + itos ( i ) + " .exr " : base_name + " .exr " ;
Ref < ConfigFile > config ;
config . instantiate ( ) ;
if ( FileAccess : : exists ( texture_path + " .import " ) ) {
config - > load ( texture_path + " .import " ) ;
}
config - > set_value ( " remap " , " importer " , " 2d_array_texture " ) ;
config - > set_value ( " remap " , " type " , " CompressedTexture2DArray " ) ;
if ( ! config - > has_section_key ( " params " , " compress/mode " ) ) {
config - > set_value ( " params " , " compress/mode " , 2 ) ; //user may want another compression, so leave it be
}
config - > set_value ( " params " , " compress/channel_pack " , 1 ) ;
config - > set_value ( " params " , " mipmaps/generate " , false ) ;
config - > set_value ( " params " , " slices/horizontal " , 1 ) ;
config - > set_value ( " params " , " slices/vertical " , texture_slice_count ) ;
config - > save ( texture_path + " .import " ) ;
Error err = texture_image - > save_exr ( texture_path , false ) ;
ERR_FAIL_COND_V ( err , ret ) ;
ResourceLoader : : import ( texture_path ) ;
Ref < TextureLayered > t = ResourceLoader : : load ( texture_path ) ; //if already loaded, it will be updated on refocus?
ERR_FAIL_COND_V ( t . is_null ( ) , ret ) ;
ret [ i ] = t ;
}
return ret ;
}
2021-06-04 22:47:26 +00:00
RID LightmapGIData : : get_rid ( ) const {
2020-05-01 12:34:23 +00:00
return lightmap ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
void LightmapGIData : : clear ( ) {
2020-05-01 12:34:23 +00:00
users . clear ( ) ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
void LightmapGIData : : set_light_texture ( const Ref < TextureLayered > & p_light_texture ) {
2020-05-01 12:34:23 +00:00
light_texture = p_light_texture ;
RS : : get_singleton ( ) - > lightmap_set_textures ( lightmap , light_texture . is_valid ( ) ? light_texture - > get_rid ( ) : RID ( ) , uses_spherical_harmonics ) ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
Ref < TextureLayered > LightmapGIData : : get_light_texture ( ) const {
2020-05-01 12:34:23 +00:00
return light_texture ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
void LightmapGIData : : set_uses_spherical_harmonics ( bool p_enable ) {
2020-05-01 12:34:23 +00:00
uses_spherical_harmonics = p_enable ;
RS : : get_singleton ( ) - > lightmap_set_textures ( lightmap , light_texture . is_valid ( ) ? light_texture - > get_rid ( ) : RID ( ) , uses_spherical_harmonics ) ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
bool LightmapGIData : : is_using_spherical_harmonics ( ) const {
2020-05-01 12:34:23 +00:00
return uses_spherical_harmonics ;
}
2017-12-14 11:59:46 +00:00
2022-07-31 23:20:24 +00:00
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 ) {
2020-05-01 12:34:23 +00:00
if ( p_points . size ( ) ) {
int pc = p_points . size ( ) ;
ERR_FAIL_COND ( pc * 9 ! = p_point_sh . size ( ) ) ;
ERR_FAIL_COND ( ( p_tetrahedra . size ( ) % 4 ) ! = 0 ) ;
ERR_FAIL_COND ( ( p_bsp_tree . size ( ) % 6 ) ! = 0 ) ;
RS : : get_singleton ( ) - > lightmap_set_probe_capture_data ( lightmap , p_points , p_point_sh , p_tetrahedra , p_bsp_tree ) ;
RS : : get_singleton ( ) - > lightmap_set_probe_bounds ( lightmap , p_bounds ) ;
RS : : get_singleton ( ) - > lightmap_set_probe_interior ( lightmap , p_interior ) ;
} else {
RS : : get_singleton ( ) - > lightmap_set_probe_capture_data ( lightmap , PackedVector3Array ( ) , PackedColorArray ( ) , PackedInt32Array ( ) , PackedInt32Array ( ) ) ;
RS : : get_singleton ( ) - > lightmap_set_probe_bounds ( lightmap , AABB ( ) ) ;
RS : : get_singleton ( ) - > lightmap_set_probe_interior ( lightmap , false ) ;
}
2022-07-31 23:20:24 +00:00
RS : : get_singleton ( ) - > lightmap_set_baked_exposure_normalization ( lightmap , p_baked_exposure ) ;
baked_exposure = p_baked_exposure ;
2020-05-01 12:34:23 +00:00
interior = p_interior ;
bounds = p_bounds ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
PackedVector3Array LightmapGIData : : get_capture_points ( ) const {
2020-05-01 12:34:23 +00:00
return RS : : get_singleton ( ) - > lightmap_get_probe_capture_points ( lightmap ) ;
}
2020-05-14 12:29:06 +00:00
2021-06-04 22:47:26 +00:00
PackedColorArray LightmapGIData : : get_capture_sh ( ) const {
2020-05-01 12:34:23 +00:00
return RS : : get_singleton ( ) - > lightmap_get_probe_capture_sh ( lightmap ) ;
}
2020-05-14 12:29:06 +00:00
2021-06-04 22:47:26 +00:00
PackedInt32Array LightmapGIData : : get_capture_tetrahedra ( ) const {
2020-05-01 12:34:23 +00:00
return RS : : get_singleton ( ) - > lightmap_get_probe_capture_tetrahedra ( lightmap ) ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
PackedInt32Array LightmapGIData : : get_capture_bsp_tree ( ) const {
2020-05-01 12:34:23 +00:00
return RS : : get_singleton ( ) - > lightmap_get_probe_capture_bsp_tree ( lightmap ) ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
AABB LightmapGIData : : get_capture_bounds ( ) const {
2020-05-01 12:34:23 +00:00
return bounds ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
bool LightmapGIData : : is_interior ( ) const {
2020-05-01 12:34:23 +00:00
return interior ;
2017-12-14 11:59:46 +00:00
}
2022-07-31 23:20:24 +00:00
float LightmapGIData : : get_baked_exposure ( ) const {
return baked_exposure ;
}
2021-06-04 22:47:26 +00:00
void LightmapGIData : : _set_probe_data ( const Dictionary & p_data ) {
2020-05-01 12:34:23 +00:00
ERR_FAIL_COND ( ! p_data . has ( " bounds " ) ) ;
ERR_FAIL_COND ( ! p_data . has ( " points " ) ) ;
ERR_FAIL_COND ( ! p_data . has ( " tetrahedra " ) ) ;
ERR_FAIL_COND ( ! p_data . has ( " bsp " ) ) ;
ERR_FAIL_COND ( ! p_data . has ( " sh " ) ) ;
ERR_FAIL_COND ( ! p_data . has ( " interior " ) ) ;
2022-07-31 23:20:24 +00:00
ERR_FAIL_COND ( ! p_data . has ( " baked_exposure " ) ) ;
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 " ] ) ;
2020-05-01 12:34:23 +00:00
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
Dictionary LightmapGIData : : _get_probe_data ( ) const {
2020-05-01 12:34:23 +00:00
Dictionary d ;
d [ " bounds " ] = get_capture_bounds ( ) ;
d [ " points " ] = get_capture_points ( ) ;
d [ " tetrahedra " ] = get_capture_tetrahedra ( ) ;
d [ " bsp " ] = get_capture_bsp_tree ( ) ;
d [ " sh " ] = get_capture_sh ( ) ;
d [ " interior " ] = is_interior ( ) ;
2022-07-31 23:20:24 +00:00
d [ " baked_exposure " ] = get_baked_exposure ( ) ;
2020-05-01 12:34:23 +00:00
return d ;
2017-12-14 11:59:46 +00:00
}
2020-05-14 12:29:06 +00:00
2021-06-04 22:47:26 +00:00
void LightmapGIData : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " _set_user_data " , " data " ) , & LightmapGIData : : _set_user_data ) ;
ClassDB : : bind_method ( D_METHOD ( " _get_user_data " ) , & LightmapGIData : : _get_user_data ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_light_texture " , " light_texture " ) , & LightmapGIData : : set_light_texture ) ;
ClassDB : : bind_method ( D_METHOD ( " get_light_texture " ) , & LightmapGIData : : get_light_texture ) ;
2017-12-14 11:59:46 +00:00
2022-06-08 19:51:31 +00:00
ClassDB : : bind_method ( D_METHOD ( " _set_light_textures_data " , " data " ) , & LightmapGIData : : _set_light_textures_data ) ;
ClassDB : : bind_method ( D_METHOD ( " _get_light_textures_data " ) , & LightmapGIData : : _get_light_textures_data ) ;
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_uses_spherical_harmonics " , " uses_spherical_harmonics " ) , & LightmapGIData : : set_uses_spherical_harmonics ) ;
ClassDB : : bind_method ( D_METHOD ( " is_using_spherical_harmonics " ) , & LightmapGIData : : is_using_spherical_harmonics ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_user " , " path " , " uv_scale " , " slice_index " , " sub_instance " ) , & LightmapGIData : : add_user ) ;
ClassDB : : bind_method ( D_METHOD ( " get_user_count " ) , & LightmapGIData : : get_user_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_user_path " , " user_idx " ) , & LightmapGIData : : get_user_path ) ;
ClassDB : : bind_method ( D_METHOD ( " clear_users " ) , & LightmapGIData : : clear_users ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " _set_probe_data " , " data " ) , & LightmapGIData : : _set_probe_data ) ;
ClassDB : : bind_method ( D_METHOD ( " _get_probe_data " ) , & LightmapGIData : : _get_probe_data ) ;
2017-12-14 11:59:46 +00:00
2022-04-23 00:54:57 +00:00
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
2022-06-08 19:51:31 +00:00
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 " ) ;
2021-11-03 22:06:17 +00:00
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 " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : ARRAY , " user_data " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) , " _set_user_data " , " _get_user_data " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : DICTIONARY , " probe_data " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) , " _set_probe_data " , " _get_probe_data " ) ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
LightmapGIData : : LightmapGIData ( ) {
2020-05-01 12:34:23 +00:00
lightmap = RS : : get_singleton ( ) - > lightmap_create ( ) ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
LightmapGIData : : ~ LightmapGIData ( ) {
2020-05-01 12:34:23 +00:00
RS : : get_singleton ( ) - > free ( lightmap ) ;
2019-06-22 15:27:09 +00:00
}
2020-05-01 12:34:23 +00:00
///////////////////////////
2021-06-04 22:47:26 +00:00
void LightmapGI : : _find_meshes_and_lights ( Node * p_at_node , Vector < MeshesFound > & meshes , Vector < LightsFound > & lights , Vector < Vector3 > & probes ) {
2020-05-01 12:34:23 +00:00
MeshInstance3D * mi = Object : : cast_to < MeshInstance3D > ( p_at_node ) ;
2021-11-26 16:47:37 +00:00
if ( mi & & mi - > get_gi_mode ( ) = = GeometryInstance3D : : GI_MODE_STATIC & & mi - > is_visible_in_tree ( ) ) {
2017-12-14 11:59:46 +00:00
Ref < Mesh > mesh = mi - > get_mesh ( ) ;
if ( mesh . is_valid ( ) ) {
2020-05-01 12:34:23 +00:00
bool all_have_uv2_and_normal = true ;
bool surfaces_found = false ;
2017-12-14 11:59:46 +00:00
for ( int i = 0 ; i < mesh - > get_surface_count ( ) ; i + + ) {
2020-05-01 12:34:23 +00:00
if ( mesh - > surface_get_primitive_type ( i ) ! = Mesh : : PRIMITIVE_TRIANGLES ) {
continue ;
}
2017-12-14 11:59:46 +00:00
if ( ! ( mesh - > surface_get_format ( i ) & Mesh : : ARRAY_FORMAT_TEX_UV2 ) ) {
2020-05-01 12:34:23 +00:00
all_have_uv2_and_normal = false ;
break ;
}
if ( ! ( mesh - > surface_get_format ( i ) & Mesh : : ARRAY_FORMAT_NORMAL ) ) {
all_have_uv2_and_normal = false ;
2017-12-14 11:59:46 +00:00
break ;
}
2020-05-01 12:34:23 +00:00
surfaces_found = true ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
if ( surfaces_found & & all_have_uv2_and_normal ) {
2017-12-14 11:59:46 +00:00
//READY TO BAKE! size hint could be computed if not found, actually..
2020-05-01 12:34:23 +00:00
MeshesFound mf ;
mf . xform = get_global_transform ( ) . affine_inverse ( ) * mi - > get_global_transform ( ) ;
mf . node_path = get_path_to ( mi ) ;
mf . subindex = - 1 ;
mf . mesh = mesh ;
static const int lightmap_scale [ GeometryInstance3D : : LIGHTMAP_SCALE_MAX ] = { 1 , 2 , 4 , 8 } ;
mf . lightmap_scale = lightmap_scale [ mi - > get_lightmap_scale ( ) ] ;
Ref < Material > all_override = mi - > get_material_override ( ) ;
for ( int i = 0 ; i < mesh - > get_surface_count ( ) ; i + + ) {
if ( all_override . is_valid ( ) ) {
mf . overrides . push_back ( all_override ) ;
} else {
2021-04-14 03:45:16 +00:00
mf . overrides . push_back ( mi - > get_surface_override_material ( i ) ) ;
2017-12-14 11:59:46 +00:00
}
}
2020-05-01 12:34:23 +00:00
meshes . push_back ( mf ) ;
2017-12-14 11:59:46 +00:00
}
}
}
2020-05-01 12:34:23 +00:00
Node3D * s = Object : : cast_to < Node3D > ( p_at_node ) ;
2017-12-18 03:34:48 +00:00
if ( ! mi & & s ) {
2020-05-01 12:34:23 +00:00
Array bmeshes = p_at_node - > call ( " get_bake_bmeshes " ) ;
if ( bmeshes . size ( ) & & ( bmeshes . size ( ) & 1 ) = = 0 ) {
2020-10-17 05:08:21 +00:00
Transform3D xf = get_global_transform ( ) . affine_inverse ( ) * s - > get_global_transform ( ) ;
2020-05-01 12:34:23 +00:00
for ( int i = 0 ; i < bmeshes . size ( ) ; i + = 2 ) {
Ref < Mesh > mesh = bmeshes [ i ] ;
2020-05-14 14:41:43 +00:00
if ( ! mesh . is_valid ( ) ) {
2017-12-18 03:34:48 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2020-05-01 12:34:23 +00:00
MeshesFound mf ;
2020-10-17 05:08:21 +00:00
Transform3D mesh_xf = bmeshes [ i + 1 ] ;
2020-05-01 12:34:23 +00:00
mf . xform = xf * mesh_xf ;
mf . node_path = get_path_to ( s ) ;
mf . subindex = i / 2 ;
mf . lightmap_scale = 1 ;
mf . mesh = mesh ;
meshes . push_back ( mf ) ;
2017-12-18 03:34:48 +00:00
}
}
}
2020-05-01 12:34:23 +00:00
Light3D * light = Object : : cast_to < Light3D > ( p_at_node ) ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
if ( light & & light - > get_bake_mode ( ) ! = Light3D : : BAKE_DISABLED ) {
LightsFound lf ;
lf . xform = get_global_transform ( ) . affine_inverse ( ) * light - > get_global_transform ( ) ;
lf . light = light ;
lights . push_back ( lf ) ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
LightmapProbe * probe = Object : : cast_to < LightmapProbe > ( p_at_node ) ;
if ( probe ) {
2020-10-17 05:08:21 +00:00
Transform3D xf = get_global_transform ( ) . affine_inverse ( ) * probe - > get_global_transform ( ) ;
2020-05-01 12:34:23 +00:00
probes . push_back ( xf . origin ) ;
}
2017-12-14 11:59:46 +00:00
for ( int i = 0 ; i < p_at_node - > get_child_count ( ) ; i + + ) {
Node * child = p_at_node - > get_child ( i ) ;
2020-05-14 14:41:43 +00:00
if ( ! child - > get_owner ( ) ) {
2017-12-14 11:59:46 +00:00
continue ; //maybe a helper
2020-05-14 14:41:43 +00:00
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
_find_meshes_and_lights ( child , meshes , lights , probes ) ;
2017-12-14 11:59:46 +00:00
}
}
2021-06-04 22:47:26 +00:00
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 {
2020-05-01 12:34:23 +00:00
int over = 0 ;
int under = 0 ;
const BSPSimplex & s = p_simplices [ p_simplex ] ;
for ( int i = 0 ; i < 4 ; i + + ) {
const Vector3 v = p_points [ s . vertices [ i ] ] ;
2022-05-10 10:05:52 +00:00
if ( p_plane . has_point ( v ) ) {
// Coplanar.
2020-05-01 12:34:23 +00:00
} else if ( p_plane . is_point_over ( v ) ) {
over + + ;
} else {
under + + ;
}
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
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
if ( under = = 0 ) {
return 1 ; // all over
} else if ( over = = 0 ) {
return - 1 ; // all under
} else {
return 0 ; // crossing
}
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
//#define DEBUG_BSP
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
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 ) {
2020-05-01 12:34:23 +00:00
//if we reach here, it means there is more than one simplex
int32_t node_index = ( int32_t ) bsp_nodes . size ( ) ;
bsp_nodes . push_back ( BSPNode ( ) ) ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
//test with all the simplex planes
Plane best_plane ;
float best_plane_score = - 1.0 ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
for ( uint32_t i = 0 ; i < p_simplex_indices . size ( ) ; i + + ) {
const BSPSimplex & s = p_simplices [ p_simplex_indices [ i ] ] ;
for ( int j = 0 ; j < 4 ; j + + ) {
uint32_t plane_index = s . planes [ j ] ;
if ( planes_tested [ plane_index ] = = node_index ) {
continue ; //tested this plane already
}
planes_tested [ plane_index ] = node_index ;
static const int face_order [ 4 ] [ 3 ] = {
{ 0 , 1 , 2 } ,
{ 0 , 2 , 3 } ,
{ 0 , 1 , 3 } ,
{ 1 , 2 , 3 }
} ;
// despite getting rid of plane duplicates, we should still use here the actual plane to avoid numerical error
// from thinking this same simplex is intersecting rather than on a side
Vector3 v0 = p_points [ s . vertices [ face_order [ j ] [ 0 ] ] ] ;
Vector3 v1 = p_points [ s . vertices [ face_order [ j ] [ 1 ] ] ] ;
Vector3 v2 = p_points [ s . vertices [ face_order [ j ] [ 2 ] ] ] ;
Plane plane ( v0 , v1 , v2 ) ;
//test with all the simplices
int over_count = 0 ;
int under_count = 0 ;
for ( uint32_t k = 0 ; k < p_simplex_indices . size ( ) ; k + + ) {
int side = _bsp_get_simplex_side ( p_points , p_simplices , plane , p_simplex_indices [ k ] ) ;
if ( side = = - 2 ) {
continue ; //this simplex is invalid, skip for now
} else if ( side < 0 ) {
under_count + + ;
} else if ( side > 0 ) {
over_count + + ;
}
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
if ( under_count = = 0 & & over_count = = 0 ) {
continue ; //most likely precision issue with a flat simplex, do not try this plane
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
if ( under_count > over_count ) { //make sure under is always less than over, so we can compute the same ratio
SWAP ( under_count , over_count ) ;
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
float score = 0 ; //by default, score is 0 (worst)
if ( over_count > 0 ) {
//give score mainly based on ratio (under / over), this means that this plane is splitting simplices a lot, but its balanced
score = float ( under_count ) / over_count ;
}
//adjusting priority over least splits, probably not a great idea
//score *= Math::sqrt(float(over_count + under_count) / p_simplex_indices.size()); //also multiply score
if ( score > best_plane_score ) {
best_plane = plane ;
best_plane_score = score ;
}
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
LocalVector < int32_t > indices_over ;
LocalVector < int32_t > indices_under ;
//split again, but add to list
for ( uint32_t i = 0 ; i < p_simplex_indices . size ( ) ; i + + ) {
uint32_t index = p_simplex_indices [ i ] ;
int side = _bsp_get_simplex_side ( p_points , p_simplices , best_plane , index ) ;
if ( side = = - 2 ) {
continue ; //simplex sits on the plane, does not make sense to use it
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
if ( side < = 0 ) {
indices_under . push_back ( index ) ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
if ( side > = 0 ) {
indices_over . push_back ( index ) ;
2017-12-14 11:59:46 +00:00
}
}
2020-05-01 12:34:23 +00:00
# ifdef DEBUG_BSP
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 ) ) ;
# endif
if ( best_plane_score < 0.0 | | indices_over . size ( ) = = p_simplex_indices . size ( ) | | indices_under . size ( ) = = p_simplex_indices . size ( ) ) {
ERR_FAIL_COND_V ( p_simplex_indices . size ( ) < = 1 , 0 ) ; //should not happen, this is a bug
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
// Failed to separate the tetrahedrons using planes
2021-03-12 13:35:16 +00:00
// this means Delaunay broke at some point.
2020-05-01 12:34:23 +00:00
// Luckily, because we are using tetrahedrons, we can resort to
// less precise but still working ways to generate the separating plane
// this will most likely look bad when interpolating, but at least it will not crash.
// and the arctifact will most likely also be very small, so too difficult to notice.
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
//find the longest axis
WARN_PRINT ( " Inconsistency found in triangulation while building BSP, probe interpolation quality may degrade a bit. " ) ;
2017-12-21 14:03:17 +00:00
2020-05-01 12:34:23 +00:00
LocalVector < Vector3 > centers ;
AABB bounds_all ;
for ( uint32_t i = 0 ; i < p_simplex_indices . size ( ) ; i + + ) {
AABB bounds ;
for ( uint32_t j = 0 ; j < 4 ; j + + ) {
Vector3 p = p_points [ p_simplices [ p_simplex_indices [ i ] ] . vertices [ j ] ] ;
if ( j = = 0 ) {
bounds . position = p ;
} else {
bounds . expand_to ( p ) ;
}
}
if ( i = = 0 ) {
2021-09-20 18:48:52 +00:00
centers . push_back ( bounds . get_center ( ) ) ;
2020-05-01 12:34:23 +00:00
} else {
bounds_all . merge_with ( bounds ) ;
}
2017-12-21 14:03:17 +00:00
}
2020-05-01 12:34:23 +00:00
Vector3 : : Axis longest_axis = Vector3 : : Axis ( bounds_all . get_longest_axis_index ( ) ) ;
//find the simplex that will go under
uint32_t min_d_idx = 0xFFFFFFFF ;
float min_d_dist = 1e20 ;
for ( uint32_t i = 0 ; i < centers . size ( ) ; i + + ) {
if ( centers [ i ] [ longest_axis ] < min_d_dist ) {
min_d_idx = i ;
min_d_dist = centers [ i ] [ longest_axis ] ;
}
}
//rebuild best_plane and over/under arrays
best_plane = Plane ( ) ;
best_plane . normal [ longest_axis ] = 1.0 ;
best_plane . d = min_d_dist ;
indices_under . clear ( ) ;
indices_under . push_back ( min_d_idx ) ;
indices_over . clear ( ) ;
for ( uint32_t i = 0 ; i < p_simplex_indices . size ( ) ; i + + ) {
if ( i = = min_d_idx ) {
continue ;
}
indices_over . push_back ( p_simplex_indices [ i ] ) ;
}
}
BSPNode node ;
node . plane = best_plane ;
if ( indices_under . size ( ) = = 0 ) {
2021-03-12 13:35:16 +00:00
//nothing to do here
2020-05-01 12:34:23 +00:00
node . under = BSPNode : : EMPTY_LEAF ;
} else if ( indices_under . size ( ) = = 1 ) {
node . under = - ( indices_under [ 0 ] + 1 ) ;
} else {
node . under = _compute_bsp_tree ( p_points , p_planes , planes_tested , p_simplices , indices_under , bsp_nodes ) ;
}
if ( indices_over . size ( ) = = 0 ) {
2021-03-12 13:35:16 +00:00
//nothing to do here
2020-05-01 12:34:23 +00:00
node . over = BSPNode : : EMPTY_LEAF ;
} else if ( indices_over . size ( ) = = 1 ) {
node . over = - ( indices_over [ 0 ] + 1 ) ;
} else {
node . over = _compute_bsp_tree ( p_points , p_planes , planes_tested , p_simplices , indices_over , bsp_nodes ) ;
2017-12-21 14:03:17 +00:00
}
2020-05-01 12:34:23 +00:00
bsp_nodes [ node_index ] = node ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
return node_index ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
bool LightmapGI : : _lightmap_bake_step_function ( float p_completion , const String & p_text , void * ud , bool p_refresh ) {
2020-05-01 12:34:23 +00:00
BakeStepUD * bsud = ( BakeStepUD * ) ud ;
bool ret = false ;
if ( bsud - > func ) {
ret = bsud - > func ( bsud - > from_percent + p_completion * ( bsud - > to_percent - bsud - > from_percent ) , p_text , bsud - > ud , p_refresh ) ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
return ret ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
void LightmapGI : : _plot_triangle_into_octree ( GenProbesOctree * p_cell , float p_cell_size , const Vector3 * p_triangle ) {
2020-05-01 12:34:23 +00:00
for ( int i = 0 ; i < 8 ; i + + ) {
Vector3i pos = p_cell - > offset ;
uint32_t half_size = p_cell - > size / 2 ;
if ( i & 1 ) {
pos . x + = half_size ;
}
if ( i & 2 ) {
pos . y + = half_size ;
}
if ( i & 4 ) {
pos . z + = half_size ;
}
AABB subcell ;
subcell . position = Vector3 ( pos ) * p_cell_size ;
subcell . size = Vector3 ( half_size , half_size , half_size ) * p_cell_size ;
2017-12-14 11:59:46 +00:00
2021-09-20 18:48:52 +00:00
if ( ! Geometry3D : : triangle_box_overlap ( subcell . get_center ( ) , subcell . size * 0.5 , p_triangle ) ) {
2020-05-01 12:34:23 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
if ( p_cell - > children [ i ] = = nullptr ) {
GenProbesOctree * child = memnew ( GenProbesOctree ) ;
child - > offset = pos ;
child - > size = half_size ;
p_cell - > children [ i ] = child ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
if ( half_size > 1 ) {
//still levels missing
_plot_triangle_into_octree ( p_cell - > children [ i ] , p_cell_size , p_triangle ) ;
}
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
}
2020-05-14 12:29:06 +00:00
2022-06-18 14:20:55 +00:00
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 ) {
2020-05-01 12:34:23 +00:00
for ( int i = 0 ; i < 8 ; i + + ) {
Vector3i pos = p_cell - > offset ;
if ( i & 1 ) {
pos . x + = p_cell - > size ;
}
if ( i & 2 ) {
pos . y + = p_cell - > size ;
}
if ( i & 4 ) {
pos . z + = p_cell - > size ;
}
if ( p_cell - > size = = 1 & & ! positions_used . has ( pos ) ) {
//new position to insert!
Vector3 real_pos = p_bounds . position + Vector3 ( pos ) * p_cell_size ;
//see if a user submitted probe is too close
int ppcount = probe_positions . size ( ) ;
const Vector3 * pp = probe_positions . ptr ( ) ;
bool exists = false ;
for ( int j = 0 ; j < ppcount ; j + + ) {
2021-07-16 17:19:55 +00:00
if ( pp [ j ] . is_equal_approx ( real_pos ) ) {
2020-05-01 12:34:23 +00:00
exists = true ;
break ;
}
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
if ( ! exists ) {
new_probe_positions . push_back ( real_pos ) ;
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
positions_used [ pos ] = true ;
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
if ( p_cell - > children [ i ] ! = nullptr ) {
_gen_new_positions_from_octree ( p_cell - > children [ i ] , p_cell_size , probe_positions , new_probe_positions , positions_used , p_bounds ) ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
}
}
2020-05-14 12:29:06 +00:00
2021-06-04 22:47:26 +00:00
LightmapGI : : BakeError LightmapGI : : bake ( Node * p_from_node , String p_image_data_path , Lightmapper : : BakeStepFunc p_bake_step , void * p_bake_userdata ) {
2021-12-09 09:42:46 +00:00
if ( p_image_data_path . is_empty ( ) ) {
2020-05-01 12:34:23 +00:00
if ( get_light_data ( ) . is_null ( ) ) {
return BAKE_ERROR_NO_SAVE_PATH ;
}
p_image_data_path = get_light_data ( ) - > get_path ( ) ;
if ( ! p_image_data_path . is_resource_file ( ) ) {
return BAKE_ERROR_NO_SAVE_PATH ;
2017-12-14 11:59:46 +00:00
}
}
2020-05-01 12:34:23 +00:00
Ref < Lightmapper > lightmapper = Lightmapper : : create ( ) ;
ERR_FAIL_COND_V ( lightmapper . is_null ( ) , BAKE_ERROR_NO_LIGHTMAPPER ) ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
BakeStepUD bsud ;
bsud . func = p_bake_step ;
bsud . ud = p_bake_userdata ;
bsud . from_percent = 0.2 ;
bsud . to_percent = 0.8 ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
if ( p_bake_step ) {
2022-03-28 13:24:14 +00:00
p_bake_step ( 0.0 , RTR ( " Finding meshes, lights and probes " ) , p_bake_userdata , true ) ;
2020-05-01 12:34:23 +00:00
}
/* STEP 1, FIND MESHES, LIGHTS AND PROBES */
Vector < Lightmapper : : MeshData > mesh_data ;
Vector < LightsFound > lights_found ;
Vector < Vector3 > probes_found ;
AABB bounds ;
{
Vector < MeshesFound > meshes_found ;
_find_meshes_and_lights ( p_from_node ? p_from_node : get_parent ( ) , meshes_found , lights_found , probes_found ) ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
if ( meshes_found . size ( ) = = 0 ) {
return BAKE_ERROR_NO_MESHES ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
// create mesh data for insert
2017-12-14 11:59:46 +00:00
2021-03-12 13:35:16 +00:00
//get the base material textures, help compute atlas size and bounds
2020-05-01 12:34:23 +00:00
for ( int m_i = 0 ; m_i < meshes_found . size ( ) ; m_i + + ) {
if ( p_bake_step ) {
float p = ( float ) ( m_i ) / meshes_found . size ( ) ;
2022-03-28 13:24:14 +00:00
p_bake_step ( p * 0.1 , vformat ( RTR ( " Preparing geometry %d/%d " ) , m_i , meshes_found . size ( ) ) , p_bake_userdata , false ) ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
MeshesFound & mf = meshes_found . write [ m_i ] ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
Size2i lightmap_size = mf . mesh - > get_lightmap_size_hint ( ) * mf . lightmap_scale ;
2022-08-31 17:24:04 +00:00
TypedArray < RID > overrides ;
2020-05-01 12:34:23 +00:00
overrides . resize ( mf . overrides . size ( ) ) ;
for ( int i = 0 ; i < mf . overrides . size ( ) ; i + + ) {
if ( mf . overrides [ i ] . is_valid ( ) ) {
2022-08-31 17:24:04 +00:00
overrides [ i ] = mf . overrides [ i ] - > get_rid ( ) ;
2020-05-01 12:34:23 +00:00
}
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
TypedArray < Image > images = RS : : get_singleton ( ) - > bake_render_uv2 ( mf . mesh - > get_rid ( ) , overrides , lightmap_size ) ;
2017-12-14 11:59:46 +00:00
2020-12-15 12:04:21 +00:00
ERR_FAIL_COND_V ( images . is_empty ( ) , BAKE_ERROR_CANT_CREATE_IMAGE ) ;
2020-05-01 12:34:23 +00:00
Ref < Image > albedo = images [ RS : : BAKE_CHANNEL_ALBEDO_ALPHA ] ;
Ref < Image > orm = images [ RS : : BAKE_CHANNEL_ORM ] ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
//multiply albedo by metal
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
Lightmapper : : MeshData md ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
{
Dictionary d ;
d [ " path " ] = mf . node_path ;
if ( mf . subindex > = 0 ) {
d [ " subindex " ] = mf . subindex ;
}
md . userdata = d ;
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
{
if ( albedo - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
albedo - > convert ( Image : : FORMAT_RGBA8 ) ;
}
if ( orm - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
orm - > convert ( Image : : FORMAT_RGBA8 ) ;
}
Vector < uint8_t > albedo_alpha = albedo - > get_data ( ) ;
Vector < uint8_t > orm_data = orm - > get_data ( ) ;
Vector < uint8_t > albedom ;
uint32_t len = albedo_alpha . size ( ) ;
albedom . resize ( len ) ;
const uint8_t * r_aa = albedo_alpha . ptr ( ) ;
const uint8_t * r_orm = orm_data . ptr ( ) ;
uint8_t * w_albedo = albedom . ptrw ( ) ;
for ( uint32_t i = 0 ; i < len ; i + = 4 ) {
w_albedo [ i + 0 ] = uint8_t ( CLAMP ( float ( r_aa [ i + 0 ] ) * ( 1.0 - float ( r_orm [ i + 2 ] / 255.0 ) ) , 0 , 255 ) ) ;
w_albedo [ i + 1 ] = uint8_t ( CLAMP ( float ( r_aa [ i + 1 ] ) * ( 1.0 - float ( r_orm [ i + 2 ] / 255.0 ) ) , 0 , 255 ) ) ;
w_albedo [ i + 2 ] = uint8_t ( CLAMP ( float ( r_aa [ i + 2 ] ) * ( 1.0 - float ( r_orm [ i + 2 ] / 255.0 ) ) , 0 , 255 ) ) ;
w_albedo [ i + 3 ] = 255 ;
2017-12-14 11:59:46 +00:00
}
2021-06-17 22:03:09 +00:00
md . albedo_on_uv2 . instantiate ( ) ;
2020-05-01 12:34:23 +00:00
md . albedo_on_uv2 - > create ( lightmap_size . width , lightmap_size . height , false , Image : : FORMAT_RGBA8 , albedom ) ;
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
md . emission_on_uv2 = images [ RS : : BAKE_CHANNEL_EMISSION ] ;
if ( md . emission_on_uv2 - > get_format ( ) ! = Image : : FORMAT_RGBAH ) {
md . emission_on_uv2 - > convert ( Image : : FORMAT_RGBAH ) ;
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
//get geometry
Basis normal_xform = mf . xform . basis . inverse ( ) . transposed ( ) ;
for ( int i = 0 ; i < mf . mesh - > get_surface_count ( ) ; i + + ) {
if ( mf . mesh - > surface_get_primitive_type ( i ) ! = Mesh : : PRIMITIVE_TRIANGLES ) {
continue ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
Array a = mf . mesh - > surface_get_arrays ( i ) ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
Vector < Vector3 > vertices = a [ Mesh : : ARRAY_VERTEX ] ;
const Vector3 * vr = vertices . ptr ( ) ;
Vector < Vector2 > uv = a [ Mesh : : ARRAY_TEX_UV2 ] ;
const Vector2 * uvr = nullptr ;
Vector < Vector3 > normals = a [ Mesh : : ARRAY_NORMAL ] ;
const Vector3 * nr = nullptr ;
Vector < int > index = a [ Mesh : : ARRAY_INDEX ] ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
ERR_CONTINUE ( uv . size ( ) = = 0 ) ;
ERR_CONTINUE ( normals . size ( ) = = 0 ) ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
uvr = uv . ptr ( ) ;
nr = normals . ptr ( ) ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
int facecount ;
const int * ir = nullptr ;
2018-10-05 02:00:02 +00:00
2020-05-01 12:34:23 +00:00
if ( index . size ( ) ) {
facecount = index . size ( ) / 3 ;
ir = index . ptr ( ) ;
2018-10-05 02:00:02 +00:00
} else {
2020-05-01 12:34:23 +00:00
facecount = vertices . size ( ) / 3 ;
2018-10-05 02:00:02 +00:00
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
for ( int j = 0 ; j < facecount ; j + + ) {
uint32_t vidx [ 3 ] ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
if ( ir ) {
for ( int k = 0 ; k < 3 ; k + + ) {
vidx [ k ] = ir [ j * 3 + k ] ;
}
} else {
for ( int k = 0 ; k < 3 ; k + + ) {
vidx [ k ] = j * 3 + k ;
}
}
2018-10-05 02:00:02 +00:00
2020-05-01 12:34:23 +00:00
for ( int k = 0 ; k < 3 ; k + + ) {
Vector3 v = mf . xform . xform ( vr [ vidx [ k ] ] ) ;
if ( bounds = = AABB ( ) ) {
bounds . position = v ;
} else {
bounds . expand_to ( v ) ;
}
md . points . push_back ( v ) ;
md . uv2 . push_back ( uvr [ vidx [ k ] ] ) ;
md . normal . push_back ( normal_xform . xform ( nr [ vidx [ k ] ] ) . normalized ( ) ) ;
}
2018-10-05 02:00:02 +00:00
}
2020-05-01 12:34:23 +00:00
}
2018-10-05 02:00:02 +00:00
2020-05-01 12:34:23 +00:00
mesh_data . push_back ( md ) ;
}
}
2018-10-05 02:00:02 +00:00
2020-05-01 12:34:23 +00:00
/* STEP 2, CREATE PROBES */
if ( p_bake_step ) {
2022-03-28 13:24:14 +00:00
p_bake_step ( 0.3 , RTR ( " Creating probes " ) , p_bake_userdata , true ) ;
2020-05-01 12:34:23 +00:00
}
//bounds need to include the user probes
for ( int i = 0 ; i < probes_found . size ( ) ; i + + ) {
bounds . expand_to ( probes_found [ i ] ) ;
}
bounds . grow_by ( bounds . size . length ( ) * 0.001 ) ;
if ( gen_probes = = GENERATE_PROBES_DISABLED ) {
// generate 8 probes on bound endpoints
for ( int i = 0 ; i < 8 ; i + + ) {
probes_found . push_back ( bounds . get_endpoint ( i ) ) ;
}
} else {
// detect probes from geometry
static const int subdiv_values [ 6 ] = { 0 , 4 , 8 , 16 , 32 } ;
int subdiv = subdiv_values [ gen_probes ] ;
float subdiv_cell_size ;
Vector3i bound_limit ;
{
int longest_axis = bounds . get_longest_axis_index ( ) ;
subdiv_cell_size = bounds . size [ longest_axis ] / subdiv ;
int axis_n1 = ( longest_axis + 1 ) % 3 ;
int axis_n2 = ( longest_axis + 2 ) % 3 ;
bound_limit [ longest_axis ] = subdiv ;
bound_limit [ axis_n1 ] = int ( Math : : ceil ( bounds . size [ axis_n1 ] / subdiv_cell_size ) ) ;
bound_limit [ axis_n2 ] = int ( Math : : ceil ( bounds . size [ axis_n2 ] / subdiv_cell_size ) ) ;
//compensate bounds
bounds . size [ axis_n1 ] = bound_limit [ axis_n1 ] * subdiv_cell_size ;
bounds . size [ axis_n2 ] = bound_limit [ axis_n2 ] * subdiv_cell_size ;
}
GenProbesOctree octree ;
octree . size = subdiv ;
for ( int i = 0 ; i < mesh_data . size ( ) ; i + + ) {
if ( p_bake_step ) {
float p = ( float ) ( i ) / mesh_data . size ( ) ;
2022-03-28 13:24:14 +00:00
p_bake_step ( 0.3 + p * 0.1 , vformat ( RTR ( " Creating probes from mesh %d/%d " ) , i , mesh_data . size ( ) ) , p_bake_userdata , false ) ;
2018-10-05 02:00:02 +00:00
}
2020-05-01 12:34:23 +00:00
for ( int j = 0 ; j < mesh_data [ i ] . points . size ( ) ; j + = 3 ) {
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 } ;
_plot_triangle_into_octree ( & octree , subdiv_cell_size , points ) ;
}
}
LocalVector < Vector3 > new_probe_positions ;
2022-06-18 14:20:55 +00:00
HashMap < Vector3i , bool > positions_used ;
2020-05-01 12:34:23 +00:00
for ( uint32_t i = 0 ; i < 8 ; i + + ) { //insert bounding endpoints
Vector3i pos ;
if ( i & 1 ) {
pos . x + = bound_limit . x ;
}
if ( i & 2 ) {
pos . y + = bound_limit . y ;
}
if ( i & 4 ) {
pos . z + = bound_limit . z ;
}
positions_used [ pos ] = true ;
Vector3 real_pos = bounds . position + Vector3 ( pos ) * subdiv_cell_size ; //use same formula for numerical stability
new_probe_positions . push_back ( real_pos ) ;
}
//skip first level, since probes are always added at bounds endpoints anyway (code above this)
for ( int i = 0 ; i < 8 ; i + + ) {
if ( octree . children [ i ] ) {
_gen_new_positions_from_octree ( octree . children [ i ] , subdiv_cell_size , probes_found , new_probe_positions , positions_used , bounds ) ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
}
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
for ( uint32_t i = 0 ; i < new_probe_positions . size ( ) ; i + + ) {
probes_found . push_back ( new_probe_positions [ i ] ) ;
2017-12-14 11:59:46 +00:00
}
}
2020-05-01 12:34:23 +00:00
// Add everything to lightmapper
if ( p_bake_step ) {
2022-03-28 13:24:14 +00:00
p_bake_step ( 0.4 , RTR ( " Preparing Lightmapper " ) , p_bake_userdata , true ) ;
2020-05-01 12:34:23 +00:00
}
2017-12-14 11:59:46 +00:00
{
2020-05-01 12:34:23 +00:00
for ( int i = 0 ; i < mesh_data . size ( ) ; i + + ) {
lightmapper - > add_mesh ( mesh_data [ i ] ) ;
}
for ( int i = 0 ; i < lights_found . size ( ) ; i + + ) {
Light3D * light = lights_found [ i ] . light ;
2020-10-17 05:08:21 +00:00
Transform3D xf = lights_found [ i ] . xform ;
2020-05-01 12:34:23 +00:00
2022-04-13 08:37:22 +00:00
Color linear_color = light - > get_color ( ) . srgb_to_linear ( ) ;
2022-07-31 23:20:24 +00:00
float energy = light - > get_param ( Light3D : : PARAM_ENERGY ) ;
if ( GLOBAL_GET ( " rendering/lights_and_shadows/use_physical_light_units " ) ) {
energy * = light - > get_param ( Light3D : : PARAM_INTENSITY ) ;
linear_color * = light - > get_correlated_color ( ) . srgb_to_linear ( ) ;
}
2020-05-01 12:34:23 +00:00
if ( Object : : cast_to < DirectionalLight3D > ( light ) ) {
DirectionalLight3D * l = Object : : cast_to < DirectionalLight3D > ( light ) ;
2022-07-31 23:20:24 +00:00
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 ) ) ;
2020-05-01 12:34:23 +00:00
} else if ( Object : : cast_to < OmniLight3D > ( light ) ) {
OmniLight3D * l = Object : : cast_to < OmniLight3D > ( light ) ;
2022-09-15 00:39:25 +00:00
if ( GLOBAL_GET ( " rendering/lights_and_shadows/use_physical_light_units " ) ) {
energy * = ( 1.0 / ( Math_PI * 4.0 ) ) ;
}
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 ) ) ;
2020-05-01 12:34:23 +00:00
} else if ( Object : : cast_to < SpotLight3D > ( light ) ) {
SpotLight3D * l = Object : : cast_to < SpotLight3D > ( light ) ;
2022-09-15 00:39:25 +00:00
if ( GLOBAL_GET ( " rendering/lights_and_shadows/use_physical_light_units " ) ) {
energy * = ( 1.0 / Math_PI ) ;
}
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 ) ) ;
2020-05-01 12:34:23 +00:00
}
}
for ( int i = 0 ; i < probes_found . size ( ) ; i + + ) {
lightmapper - > add_probe ( probes_found [ i ] ) ;
}
}
Ref < Image > environment_image ;
Basis environment_transform ;
// Add everything to lightmapper
if ( environment_mode ! = ENVIRONMENT_MODE_DISABLED ) {
if ( p_bake_step ) {
2022-03-28 13:24:14 +00:00
p_bake_step ( 4.1 , RTR ( " Preparing Environment " ) , p_bake_userdata , true ) ;
2020-05-01 12:34:23 +00:00
}
environment_transform = get_global_transform ( ) . basis ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
switch ( environment_mode ) {
case ENVIRONMENT_MODE_DISABLED : {
//nothing
} break ;
case ENVIRONMENT_MODE_SCENE : {
Ref < World3D > world = get_world_3d ( ) ;
if ( world . is_valid ( ) ) {
Ref < Environment > env = world - > get_environment ( ) ;
if ( env . is_null ( ) ) {
env = world - > get_fallback_environment ( ) ;
}
if ( env . is_valid ( ) ) {
environment_image = RS : : get_singleton ( ) - > environment_bake_panorama ( env - > get_rid ( ) , true , Size2i ( 128 , 64 ) ) ;
}
}
} break ;
case ENVIRONMENT_MODE_CUSTOM_SKY : {
if ( environment_custom_sky . is_valid ( ) ) {
environment_image = RS : : get_singleton ( ) - > sky_bake_panorama ( environment_custom_sky - > get_rid ( ) , environment_custom_energy , true , Size2i ( 128 , 64 ) ) ;
}
} break ;
case ENVIRONMENT_MODE_CUSTOM_COLOR : {
2021-06-17 22:03:09 +00:00
environment_image . instantiate ( ) ;
2020-05-01 12:34:23 +00:00
environment_image - > create ( 128 , 64 , false , Image : : FORMAT_RGBAF ) ;
Color c = environment_custom_color ;
c . r * = environment_custom_energy ;
c . g * = environment_custom_energy ;
c . b * = environment_custom_energy ;
2022-01-08 11:58:15 +00:00
environment_image - > fill ( c ) ;
2017-12-14 11:59:46 +00:00
2020-05-01 12:34:23 +00:00
} break ;
}
2017-12-14 11:59:46 +00:00
}
2022-07-31 23:20:24 +00:00
float exposure_normalization = 1.0 ;
if ( camera_attributes . is_valid ( ) ) {
2022-09-15 00:39:25 +00:00
exposure_normalization = camera_attributes - > get_exposure_multiplier ( ) ;
if ( GLOBAL_GET ( " rendering/lights_and_shadows/use_physical_light_units " ) ) {
exposure_normalization = camera_attributes - > calculate_exposure_normalization ( ) ;
}
2022-07-31 23:20:24 +00:00
}
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 ) ;
2020-05-01 12:34:23 +00:00
if ( bake_err = = Lightmapper : : BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES ) {
return BAKE_ERROR_MESHES_INVALID ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
/* POSTBAKE: Save Light Data */
2021-06-04 22:47:26 +00:00
Ref < LightmapGIData > data ;
2020-05-01 12:34:23 +00:00
if ( get_light_data ( ) . is_valid ( ) ) {
data = get_light_data ( ) ;
2021-06-04 22:47:26 +00:00
set_light_data ( Ref < LightmapGIData > ( ) ) ; //clear
2020-05-01 12:34:23 +00:00
data - > clear ( ) ;
} else {
2021-06-17 22:03:09 +00:00
data . instantiate ( ) ;
2020-05-01 12:34:23 +00:00
}
2022-06-08 19:51:31 +00:00
Ref < Texture2DArray > texture ;
{
Vector < Ref < Image > > images ;
for ( int i = 0 ; i < lightmapper - > get_bake_texture_count ( ) ; i + + ) {
images . push_back ( lightmapper - > get_bake_texture ( i ) ) ;
}
texture . instantiate ( ) ;
texture - > create_from_images ( images ) ;
}
2020-05-01 12:34:23 +00:00
data - > set_light_texture ( texture ) ;
data - > set_uses_spherical_harmonics ( directional ) ;
for ( int i = 0 ; i < lightmapper - > get_bake_mesh_count ( ) ; i + + ) {
Dictionary d = lightmapper - > get_bake_mesh_userdata ( i ) ;
NodePath np = d [ " path " ] ;
int32_t subindex = - 1 ;
if ( d . has ( " subindex " ) ) {
subindex = d [ " subindex " ] ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
Rect2 uv_scale = lightmapper - > get_bake_mesh_uv_scale ( i ) ;
int slice_index = lightmapper - > get_bake_mesh_texture_slice ( i ) ;
data - > add_user ( np , uv_scale , slice_index , subindex ) ;
}
{
// create tetrahedrons
Vector < Vector3 > points ;
Vector < Color > sh ;
points . resize ( lightmapper - > get_bake_probe_count ( ) ) ;
sh . resize ( lightmapper - > get_bake_probe_count ( ) * 9 ) ;
for ( int i = 0 ; i < lightmapper - > get_bake_probe_count ( ) ; i + + ) {
points . write [ i ] = lightmapper - > get_bake_probe_point ( i ) ;
Vector < Color > colors = lightmapper - > get_bake_probe_sh ( i ) ;
ERR_CONTINUE ( colors . size ( ) ! = 9 ) ;
for ( int j = 0 ; j < 9 ; j + + ) {
sh . write [ i * 9 + j ] = colors [ j ] ;
}
}
//Obtain solved simplices
if ( p_bake_step ) {
2022-03-28 13:24:14 +00:00
p_bake_step ( 0.8 , RTR ( " Generating Probe Volumes " ) , p_bake_userdata , true ) ;
2020-05-01 12:34:23 +00:00
}
Vector < Delaunay3D : : OutputSimplex > solved_simplices = Delaunay3D : : tetrahedralize ( points ) ;
LocalVector < BSPSimplex > bsp_simplices ;
LocalVector < Plane > bsp_planes ;
LocalVector < int32_t > bsp_simplex_indices ;
PackedInt32Array tetrahedrons ;
for ( int i = 0 ; i < solved_simplices . size ( ) ; i + + ) {
//Prepare a special representation of the simplex, which uses a BSP Tree
BSPSimplex bsp_simplex ;
for ( int j = 0 ; j < 4 ; j + + ) {
bsp_simplex . vertices [ j ] = solved_simplices [ i ] . points [ j ] ;
}
for ( int j = 0 ; j < 4 ; j + + ) {
static const int face_order [ 4 ] [ 3 ] = {
{ 0 , 1 , 2 } ,
{ 0 , 2 , 3 } ,
{ 0 , 1 , 3 } ,
{ 1 , 2 , 3 }
} ;
Vector3 a = points [ solved_simplices [ i ] . points [ face_order [ j ] [ 0 ] ] ] ;
Vector3 b = points [ solved_simplices [ i ] . points [ face_order [ j ] [ 1 ] ] ] ;
Vector3 c = points [ solved_simplices [ i ] . points [ face_order [ j ] [ 2 ] ] ] ;
//store planes in an array, but ensure they are reused, to speed up processing
Plane p ( a , b , c ) ;
int plane_index = - 1 ;
for ( uint32_t k = 0 ; k < bsp_planes . size ( ) ; k + + ) {
if ( bsp_planes [ k ] . is_equal_approx_any_side ( p ) ) {
plane_index = k ;
break ;
}
}
if ( plane_index = = - 1 ) {
plane_index = bsp_planes . size ( ) ;
bsp_planes . push_back ( p ) ;
}
bsp_simplex . planes [ j ] = plane_index ;
//also fill simplex array
tetrahedrons . push_back ( solved_simplices [ i ] . points [ j ] ) ;
}
bsp_simplex_indices . push_back ( bsp_simplices . size ( ) ) ;
bsp_simplices . push_back ( bsp_simplex ) ;
}
//#define DEBUG_SIMPLICES_AS_OBJ_FILE
# ifdef DEBUG_SIMPLICES_AS_OBJ_FILE
{
2022-03-23 09:08:58 +00:00
Ref < FileAccess > f = FileAccess : : open ( " res://bsp.obj " , FileAccess : : WRITE ) ;
2020-05-01 12:34:23 +00:00
for ( uint32_t i = 0 ; i < bsp_simplices . size ( ) ; i + + ) {
f - > store_line ( " o Simplex " + itos ( i ) ) ;
for ( int j = 0 ; j < 4 ; j + + ) {
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 ) ) ;
}
static const int face_order [ 4 ] [ 3 ] = {
{ 1 , 2 , 3 } ,
{ 1 , 3 , 4 } ,
{ 1 , 2 , 4 } ,
{ 2 , 3 , 4 }
} ;
for ( int j = 0 ; j < 4 ; j + + ) {
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 ] ) ) ;
}
}
}
# endif
LocalVector < BSPNode > bsp_nodes ;
LocalVector < int32_t > planes_tested ;
planes_tested . resize ( bsp_planes . size ( ) ) ;
for ( uint32_t i = 0 ; i < planes_tested . size ( ) ; i + + ) {
planes_tested [ i ] = 0x7FFFFFFF ;
}
if ( p_bake_step ) {
2022-03-28 13:24:14 +00:00
p_bake_step ( 0.9 , RTR ( " Generating Probe Acceleration Structures " ) , p_bake_userdata , true ) ;
2020-05-01 12:34:23 +00:00
}
_compute_bsp_tree ( points , bsp_planes , planes_tested , bsp_simplices , bsp_simplex_indices , bsp_nodes ) ;
PackedInt32Array bsp_array ;
bsp_array . resize ( bsp_nodes . size ( ) * 6 ) ; // six 32 bits values used for each BSP node
{
float * fptr = ( float * ) bsp_array . ptrw ( ) ;
int32_t * iptr = ( int32_t * ) bsp_array . ptrw ( ) ;
for ( uint32_t i = 0 ; i < bsp_nodes . size ( ) ; i + + ) {
fptr [ i * 6 + 0 ] = bsp_nodes [ i ] . plane . normal . x ;
fptr [ i * 6 + 1 ] = bsp_nodes [ i ] . plane . normal . y ;
fptr [ i * 6 + 2 ] = bsp_nodes [ i ] . plane . normal . z ;
fptr [ i * 6 + 3 ] = bsp_nodes [ i ] . plane . d ;
iptr [ i * 6 + 4 ] = bsp_nodes [ i ] . over ;
iptr [ i * 6 + 5 ] = bsp_nodes [ i ] . under ;
}
//#define DEBUG_BSP_TREE
# ifdef DEBUG_BSP_TREE
2022-03-23 09:08:58 +00:00
Ref < FileAccess > f = FileAccess : : open ( " res://bsp.txt " , FileAccess : : WRITE ) ;
2020-05-01 12:34:23 +00:00
for ( uint32_t i = 0 ; i < bsp_nodes . size ( ) ; i + + ) {
f - > store_line ( itos ( i ) + " - plane: " + bsp_nodes [ i ] . plane + " over: " + itos ( bsp_nodes [ i ] . over ) + " under: " + itos ( bsp_nodes [ i ] . under ) ) ;
}
2017-12-14 11:59:46 +00:00
# endif
2020-05-01 12:34:23 +00:00
}
/* Obtain the colors from the images, they will be re-created as cubemaps on the server, depending on the driver */
2022-07-31 23:20:24 +00:00
data - > set_capture_data ( bounds , interior , points , sh , tetrahedrons , bsp_array , exposure_normalization ) ;
2020-05-01 12:34:23 +00:00
/* Compute a BSP tree of the simplices, so it's easy to find the exact one */
}
data - > set_path ( p_image_data_path ) ;
2022-06-02 23:33:42 +00:00
Error err = ResourceSaver : : save ( data ) ;
2020-05-01 12:34:23 +00:00
if ( err ! = OK ) {
return BAKE_ERROR_CANT_CREATE_IMAGE ;
2017-12-14 11:59:46 +00:00
}
2020-05-01 12:34:23 +00:00
set_light_data ( data ) ;
2017-12-14 11:59:46 +00:00
return BAKE_ERROR_OK ;
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : _notification ( int p_what ) {
2022-02-15 17:06:48 +00:00
switch ( p_what ) {
case NOTIFICATION_POST_ENTER_TREE : {
if ( light_data . is_valid ( ) ) {
_assign_lightmaps ( ) ;
}
} break ;
2017-12-14 11:59:46 +00:00
2022-02-15 17:06:48 +00:00
case NOTIFICATION_EXIT_TREE : {
if ( light_data . is_valid ( ) ) {
_clear_lightmaps ( ) ;
}
} break ;
2017-12-14 11:59:46 +00:00
}
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : _assign_lightmaps ( ) {
2017-12-14 11:59:46 +00:00
ERR_FAIL_COND ( ! light_data . is_valid ( ) ) ;
for ( int i = 0 ; i < light_data - > get_user_count ( ) ; i + + ) {
2017-12-18 03:34:48 +00:00
Node * node = get_node ( light_data - > get_user_path ( i ) ) ;
2020-05-01 12:34:23 +00:00
int instance_idx = light_data - > get_user_sub_instance ( i ) ;
2017-12-18 03:34:48 +00:00
if ( instance_idx > = 0 ) {
RID instance = node - > call ( " get_bake_mesh_instance " , instance_idx ) ;
if ( instance . is_valid ( ) ) {
2020-05-01 12:34:23 +00:00
RS : : get_singleton ( ) - > instance_geometry_set_lightmap ( instance , get_instance ( ) , light_data - > get_user_lightmap_uv_scale ( i ) , light_data - > get_user_lightmap_slice_index ( i ) ) ;
2017-12-18 03:34:48 +00:00
}
} else {
2020-05-01 12:34:23 +00:00
VisualInstance3D * vi = Object : : cast_to < VisualInstance3D > ( node ) ;
2017-12-18 03:34:48 +00:00
ERR_CONTINUE ( ! vi ) ;
2020-05-01 12:34:23 +00:00
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 ) ) ;
2017-12-18 03:34:48 +00:00
}
2017-12-14 11:59:46 +00:00
}
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : _clear_lightmaps ( ) {
2017-12-14 11:59:46 +00:00
ERR_FAIL_COND ( ! light_data . is_valid ( ) ) ;
for ( int i = 0 ; i < light_data - > get_user_count ( ) ; i + + ) {
Node * node = get_node ( light_data - > get_user_path ( i ) ) ;
2020-05-01 12:34:23 +00:00
int instance_idx = light_data - > get_user_sub_instance ( i ) ;
2017-12-18 03:34:48 +00:00
if ( instance_idx > = 0 ) {
RID instance = node - > call ( " get_bake_mesh_instance " , instance_idx ) ;
if ( instance . is_valid ( ) ) {
2020-05-01 12:34:23 +00:00
RS : : get_singleton ( ) - > instance_geometry_set_lightmap ( instance , RID ( ) , Rect2 ( ) , 0 ) ;
2017-12-18 03:34:48 +00:00
}
} else {
2020-05-01 12:34:23 +00:00
VisualInstance3D * vi = Object : : cast_to < VisualInstance3D > ( node ) ;
2017-12-18 03:34:48 +00:00
ERR_CONTINUE ( ! vi ) ;
2020-05-01 12:34:23 +00:00
RS : : get_singleton ( ) - > instance_geometry_set_lightmap ( vi - > get_instance ( ) , RID ( ) , Rect2 ( ) , 0 ) ;
2017-12-18 03:34:48 +00:00
}
2017-12-14 11:59:46 +00:00
}
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_light_data ( const Ref < LightmapGIData > & p_data ) {
2017-12-14 11:59:46 +00:00
if ( light_data . is_valid ( ) ) {
if ( is_inside_tree ( ) ) {
_clear_lightmaps ( ) ;
}
set_base ( RID ( ) ) ;
}
light_data = p_data ;
if ( light_data . is_valid ( ) ) {
set_base ( light_data - > get_rid ( ) ) ;
if ( is_inside_tree ( ) ) {
_assign_lightmaps ( ) ;
}
}
2020-05-01 12:34:23 +00:00
2021-06-23 14:49:50 +00:00
update_gizmos ( ) ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
Ref < LightmapGIData > LightmapGI : : get_light_data ( ) const {
2017-12-14 11:59:46 +00:00
return light_data ;
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_bake_quality ( BakeQuality p_quality ) {
2020-05-01 12:34:23 +00:00
bake_quality = p_quality ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
LightmapGI : : BakeQuality LightmapGI : : get_bake_quality ( ) const {
2020-05-01 12:34:23 +00:00
return bake_quality ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
AABB LightmapGI : : get_aabb ( ) const {
2020-05-01 12:34:23 +00:00
return AABB ( ) ;
}
2020-05-14 12:29:06 +00:00
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_use_denoiser ( bool p_enable ) {
2020-05-01 12:34:23 +00:00
use_denoiser = p_enable ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
bool LightmapGI : : is_using_denoiser ( ) const {
2020-05-01 12:34:23 +00:00
return use_denoiser ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_directional ( bool p_enable ) {
2020-05-01 12:34:23 +00:00
directional = p_enable ;
}
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
bool LightmapGI : : is_directional ( ) const {
2020-05-01 12:34:23 +00:00
return directional ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_interior ( bool p_enable ) {
2020-05-01 12:34:23 +00:00
interior = p_enable ;
}
2020-05-14 12:29:06 +00:00
2021-06-04 22:47:26 +00:00
bool LightmapGI : : is_interior ( ) const {
2020-05-01 12:34:23 +00:00
return interior ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_environment_mode ( EnvironmentMode p_mode ) {
2020-05-01 12:34:23 +00:00
environment_mode = p_mode ;
2021-02-10 20:18:45 +00:00
notify_property_list_changed ( ) ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
LightmapGI : : EnvironmentMode LightmapGI : : get_environment_mode ( ) const {
2020-05-01 12:34:23 +00:00
return environment_mode ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_environment_custom_sky ( const Ref < Sky > & p_sky ) {
2020-05-01 12:34:23 +00:00
environment_custom_sky = p_sky ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
Ref < Sky > LightmapGI : : get_environment_custom_sky ( ) const {
2020-05-01 12:34:23 +00:00
return environment_custom_sky ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_environment_custom_color ( const Color & p_color ) {
2020-05-01 12:34:23 +00:00
environment_custom_color = p_color ;
}
2020-05-14 12:29:06 +00:00
2021-06-04 22:47:26 +00:00
Color LightmapGI : : get_environment_custom_color ( ) const {
2020-05-01 12:34:23 +00:00
return environment_custom_color ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_environment_custom_energy ( float p_energy ) {
2020-05-01 12:34:23 +00:00
environment_custom_energy = p_energy ;
2017-12-14 11:59:46 +00:00
}
2020-05-14 12:29:06 +00:00
2021-06-04 22:47:26 +00:00
float LightmapGI : : get_environment_custom_energy ( ) const {
2020-05-01 12:34:23 +00:00
return environment_custom_energy ;
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_bounces ( int p_bounces ) {
2020-05-01 12:34:23 +00:00
ERR_FAIL_COND ( p_bounces < 0 | | p_bounces > 16 ) ;
bounces = p_bounces ;
}
2021-06-04 22:47:26 +00:00
int LightmapGI : : get_bounces ( ) const {
2020-05-01 12:34:23 +00:00
return bounces ;
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_bias ( float p_bias ) {
2020-05-01 12:34:23 +00:00
ERR_FAIL_COND ( p_bias < 0.00001 ) ;
bias = p_bias ;
}
2021-06-04 22:47:26 +00:00
float LightmapGI : : get_bias ( ) const {
2020-05-01 12:34:23 +00:00
return bias ;
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_max_texture_size ( int p_size ) {
2020-05-01 12:34:23 +00:00
ERR_FAIL_COND ( p_size < 2048 ) ;
max_texture_size = p_size ;
}
2021-06-04 22:47:26 +00:00
int LightmapGI : : get_max_texture_size ( ) const {
2020-05-01 12:34:23 +00:00
return max_texture_size ;
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : set_generate_probes ( GenerateProbes p_generate_probes ) {
2020-05-01 12:34:23 +00:00
gen_probes = p_generate_probes ;
}
2021-06-04 22:47:26 +00:00
LightmapGI : : GenerateProbes LightmapGI : : get_generate_probes ( ) const {
2020-05-01 12:34:23 +00:00
return gen_probes ;
}
2022-07-31 23:20:24 +00:00
void LightmapGI : : set_camera_attributes ( const Ref < CameraAttributes > & p_camera_attributes ) {
camera_attributes = p_camera_attributes ;
}
Ref < CameraAttributes > LightmapGI : : get_camera_attributes ( ) const {
return camera_attributes ;
}
2022-08-12 20:57:11 +00:00
void LightmapGI : : _validate_property ( PropertyInfo & p_property ) const {
if ( p_property . name = = " environment_custom_sky " & & environment_mode ! = ENVIRONMENT_MODE_CUSTOM_SKY ) {
p_property . usage = PROPERTY_USAGE_NONE ;
2020-05-01 12:34:23 +00:00
}
2022-08-12 20:57:11 +00:00
if ( p_property . name = = " environment_custom_color " & & environment_mode ! = ENVIRONMENT_MODE_CUSTOM_COLOR ) {
p_property . usage = PROPERTY_USAGE_NONE ;
2020-05-01 12:34:23 +00:00
}
2022-08-12 20:57:11 +00:00
if ( p_property . name = = " environment_custom_energy " & & environment_mode ! = ENVIRONMENT_MODE_CUSTOM_COLOR & & environment_mode ! = ENVIRONMENT_MODE_CUSTOM_SKY ) {
p_property . usage = PROPERTY_USAGE_NONE ;
2020-05-01 12:34:23 +00:00
}
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
void LightmapGI : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " set_light_data " , " data " ) , & LightmapGI : : set_light_data ) ;
ClassDB : : bind_method ( D_METHOD ( " get_light_data " ) , & LightmapGI : : get_light_data ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_bake_quality " , " bake_quality " ) , & LightmapGI : : set_bake_quality ) ;
ClassDB : : bind_method ( D_METHOD ( " get_bake_quality " ) , & LightmapGI : : get_bake_quality ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_bounces " , " bounces " ) , & LightmapGI : : set_bounces ) ;
ClassDB : : bind_method ( D_METHOD ( " get_bounces " ) , & LightmapGI : : get_bounces ) ;
2020-05-01 12:34:23 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_generate_probes " , " subdivision " ) , & LightmapGI : : set_generate_probes ) ;
ClassDB : : bind_method ( D_METHOD ( " get_generate_probes " ) , & LightmapGI : : get_generate_probes ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_bias " , " bias " ) , & LightmapGI : : set_bias ) ;
ClassDB : : bind_method ( D_METHOD ( " get_bias " ) , & LightmapGI : : get_bias ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_environment_mode " , " mode " ) , & LightmapGI : : set_environment_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_environment_mode " ) , & LightmapGI : : get_environment_mode ) ;
2019-06-22 15:27:09 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_environment_custom_sky " , " sky " ) , & LightmapGI : : set_environment_custom_sky ) ;
ClassDB : : bind_method ( D_METHOD ( " get_environment_custom_sky " ) , & LightmapGI : : get_environment_custom_sky ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_environment_custom_color " , " color " ) , & LightmapGI : : set_environment_custom_color ) ;
ClassDB : : bind_method ( D_METHOD ( " get_environment_custom_color " ) , & LightmapGI : : get_environment_custom_color ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_environment_custom_energy " , " energy " ) , & LightmapGI : : set_environment_custom_energy ) ;
ClassDB : : bind_method ( D_METHOD ( " get_environment_custom_energy " ) , & LightmapGI : : get_environment_custom_energy ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_max_texture_size " , " max_texture_size " ) , & LightmapGI : : set_max_texture_size ) ;
ClassDB : : bind_method ( D_METHOD ( " get_max_texture_size " ) , & LightmapGI : : get_max_texture_size ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_use_denoiser " , " use_denoiser " ) , & LightmapGI : : set_use_denoiser ) ;
ClassDB : : bind_method ( D_METHOD ( " is_using_denoiser " ) , & LightmapGI : : is_using_denoiser ) ;
2017-12-14 11:59:46 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_interior " , " enable " ) , & LightmapGI : : set_interior ) ;
ClassDB : : bind_method ( D_METHOD ( " is_interior " ) , & LightmapGI : : is_interior ) ;
2020-05-01 12:34:23 +00:00
2021-06-04 22:47:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_directional " , " directional " ) , & LightmapGI : : set_directional ) ;
ClassDB : : bind_method ( D_METHOD ( " is_directional " ) , & LightmapGI : : is_directional ) ;
2020-05-01 12:34:23 +00:00
2022-07-31 23:20:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_camera_attributes " , " camera_attributes " ) , & LightmapGI : : set_camera_attributes ) ;
ClassDB : : bind_method ( D_METHOD ( " get_camera_attributes " ) , & LightmapGI : : get_camera_attributes ) ;
2021-06-04 22:47:26 +00:00
// ClassDB::bind_method(D_METHOD("bake", "from_node"), &LightmapGI::bake, DEFVAL(Variant()));
2020-05-01 12:34:23 +00:00
ADD_GROUP ( " Tweaks " , " " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " quality " , PROPERTY_HINT_ENUM , " Low,Medium,High,Ultra " ) , " set_bake_quality " , " get_bake_quality " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " bounces " , PROPERTY_HINT_RANGE , " 0,16,1 " ) , " set_bounces " , " get_bounces " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " directional " ) , " set_directional " , " is_directional " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " interior " ) , " set_interior " , " is_interior " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " use_denoiser " ) , " set_use_denoiser " , " is_using_denoiser " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " bias " , PROPERTY_HINT_RANGE , " 0.00001,0.1,0.00001,or_greater " ) , " set_bias " , " get_bias " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " max_texture_size " ) , " set_max_texture_size " , " get_max_texture_size " ) ;
ADD_GROUP ( " Environment " , " environment_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " environment_mode " , PROPERTY_HINT_ENUM , " Disabled,Scene,Custom Sky,Custom Color " ) , " set_environment_mode " , " get_environment_mode " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " environment_custom_sky " , PROPERTY_HINT_RESOURCE_TYPE , " Sky " ) , " set_environment_custom_sky " , " get_environment_custom_sky " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : COLOR , " environment_custom_color " , PROPERTY_HINT_COLOR_NO_ALPHA ) , " set_environment_custom_color " , " get_environment_custom_color " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " environment_custom_energy " , PROPERTY_HINT_RANGE , " 0,64,0.01 " ) , " set_environment_custom_energy " , " get_environment_custom_energy " ) ;
2022-07-31 23:20:24 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " camera_attributes " , PROPERTY_HINT_RESOURCE_TYPE , " CameraAttributesPractical,CameraAttributesPhysical " ) , " set_camera_attributes " , " get_camera_attributes " ) ;
2020-05-01 12:34:23 +00:00
ADD_GROUP ( " Gen Probes " , " generate_probes_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " generate_probes_subdiv " , PROPERTY_HINT_ENUM , " Disabled,4,8,16,32 " ) , " set_generate_probes " , " get_generate_probes " ) ;
2017-12-21 14:03:17 +00:00
ADD_GROUP ( " Data " , " " ) ;
2021-06-04 22:47:26 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " light_data " , PROPERTY_HINT_RESOURCE_TYPE , " LightmapGIData " ) , " set_light_data " , " get_light_data " ) ;
2017-12-14 11:59:46 +00:00
BIND_ENUM_CONSTANT ( BAKE_QUALITY_LOW ) ;
BIND_ENUM_CONSTANT ( BAKE_QUALITY_MEDIUM ) ;
BIND_ENUM_CONSTANT ( BAKE_QUALITY_HIGH ) ;
2020-05-12 08:10:34 +00:00
BIND_ENUM_CONSTANT ( BAKE_QUALITY_ULTRA ) ;
BIND_ENUM_CONSTANT ( GENERATE_PROBES_DISABLED ) ;
BIND_ENUM_CONSTANT ( GENERATE_PROBES_SUBDIV_4 ) ;
BIND_ENUM_CONSTANT ( GENERATE_PROBES_SUBDIV_8 ) ;
BIND_ENUM_CONSTANT ( GENERATE_PROBES_SUBDIV_16 ) ;
BIND_ENUM_CONSTANT ( GENERATE_PROBES_SUBDIV_32 ) ;
2017-12-17 00:47:21 +00:00
BIND_ENUM_CONSTANT ( BAKE_ERROR_OK ) ;
2020-05-12 08:10:34 +00:00
BIND_ENUM_CONSTANT ( BAKE_ERROR_NO_LIGHTMAPPER ) ;
2017-12-17 00:47:21 +00:00
BIND_ENUM_CONSTANT ( BAKE_ERROR_NO_SAVE_PATH ) ;
BIND_ENUM_CONSTANT ( BAKE_ERROR_NO_MESHES ) ;
2020-05-12 08:10:34 +00:00
BIND_ENUM_CONSTANT ( BAKE_ERROR_MESHES_INVALID ) ;
2017-12-17 00:47:21 +00:00
BIND_ENUM_CONSTANT ( BAKE_ERROR_CANT_CREATE_IMAGE ) ;
BIND_ENUM_CONSTANT ( BAKE_ERROR_USER_ABORTED ) ;
2020-05-01 12:34:23 +00:00
BIND_ENUM_CONSTANT ( ENVIRONMENT_MODE_DISABLED ) ;
BIND_ENUM_CONSTANT ( ENVIRONMENT_MODE_SCENE ) ;
BIND_ENUM_CONSTANT ( ENVIRONMENT_MODE_CUSTOM_SKY ) ;
BIND_ENUM_CONSTANT ( ENVIRONMENT_MODE_CUSTOM_COLOR ) ;
2017-12-14 11:59:46 +00:00
}
2021-06-04 22:47:26 +00:00
LightmapGI : : LightmapGI ( ) {
2017-12-14 11:59:46 +00:00
}