2017-03-05 15:47:28 +01:00
/*************************************************************************/
/* resource_importer_texture.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2017-03-05 15:47:28 +01:00
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2017-03-05 15:47:28 +01: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. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2017-02-03 00:08:50 -03:00
# include "resource_importer_texture.h"
2017-03-05 15:47:28 +01:00
2022-02-12 02:46:22 +01:00
# include "core/config/project_settings.h"
2018-09-11 18:13:45 +02:00
# include "core/io/config_file.h"
# include "core/io/image_loader.h"
2019-09-26 23:16:44 -03:00
# include "core/version.h"
2017-03-05 14:21:25 +01:00
# include "editor/editor_file_system.h"
2017-09-13 09:13:23 +02:00
# include "editor/editor_node.h"
2022-03-05 16:43:38 +01:00
void ResourceImporterTexture : : _texture_reimport_roughness ( const Ref < CompressedTexture2D > & p_tex , const String & p_normal_path , RS : : TextureDetectRoughnessChannel p_channel ) {
2021-12-17 15:56:41 +00:00
ERR_FAIL_COND ( p_tex . is_null ( ) ) ;
2020-02-26 11:28:13 +01:00
MutexLock lock ( singleton - > mutex ) ;
2017-02-06 00:38:39 -03:00
StringName path = p_tex - > get_path ( ) ;
if ( ! singleton - > make_flags . has ( path ) ) {
2019-06-11 15:43:37 -03:00
singleton - > make_flags [ path ] = MakeInfo ( ) ;
2017-02-06 00:38:39 -03:00
}
2019-06-11 15:43:37 -03:00
singleton - > make_flags [ path ] . flags | = MAKE_ROUGHNESS_FLAG ;
singleton - > make_flags [ path ] . channel_for_roughness = p_channel ;
singleton - > make_flags [ path ] . normal_path_for_roughness = p_normal_path ;
2017-02-06 00:38:39 -03:00
}
2022-03-05 16:43:38 +01:00
void ResourceImporterTexture : : _texture_reimport_3d ( const Ref < CompressedTexture2D > & p_tex ) {
2021-12-17 15:56:41 +00:00
ERR_FAIL_COND ( p_tex . is_null ( ) ) ;
2020-02-26 11:28:13 +01:00
MutexLock lock ( singleton - > mutex ) ;
2017-02-06 00:38:39 -03:00
StringName path = p_tex - > get_path ( ) ;
if ( ! singleton - > make_flags . has ( path ) ) {
2019-06-11 15:43:37 -03:00
singleton - > make_flags [ path ] = MakeInfo ( ) ;
2017-02-06 00:38:39 -03:00
}
2019-06-11 15:43:37 -03:00
singleton - > make_flags [ path ] . flags | = MAKE_3D_FLAG ;
2017-02-06 00:38:39 -03:00
}
2022-03-05 16:43:38 +01:00
void ResourceImporterTexture : : _texture_reimport_normal ( const Ref < CompressedTexture2D > & p_tex ) {
2021-12-17 15:56:41 +00:00
ERR_FAIL_COND ( p_tex . is_null ( ) ) ;
2020-02-26 11:28:13 +01:00
MutexLock lock ( singleton - > mutex ) ;
2017-06-16 21:47:28 -03:00
StringName path = p_tex - > get_path ( ) ;
if ( ! singleton - > make_flags . has ( path ) ) {
2019-06-11 15:43:37 -03:00
singleton - > make_flags [ path ] = MakeInfo ( ) ;
2017-06-16 21:47:28 -03:00
}
2019-06-11 15:43:37 -03:00
singleton - > make_flags [ path ] . flags | = MAKE_NORMAL_FLAG ;
2017-06-16 21:47:28 -03:00
}
2017-02-06 00:38:39 -03:00
void ResourceImporterTexture : : update_imports ( ) {
if ( EditorFileSystem : : get_singleton ( ) - > is_scanning ( ) | | EditorFileSystem : : get_singleton ( ) - > is_importing ( ) ) {
2019-05-19 12:34:40 +02:00
return ; // do nothing for now
2017-02-06 00:38:39 -03:00
}
2020-02-26 11:28:13 +01:00
MutexLock lock ( mutex ) ;
2017-02-06 00:38:39 -03:00
Vector < String > to_reimport ;
2020-02-26 11:28:13 +01:00
{
2020-12-15 12:04:21 +00:00
if ( make_flags . is_empty ( ) ) {
2020-02-26 11:28:13 +01:00
return ;
}
2017-02-06 00:38:39 -03:00
2021-08-09 14:13:42 -06:00
for ( const KeyValue < StringName , MakeInfo > & E : make_flags ) {
2020-02-26 11:28:13 +01:00
Ref < ConfigFile > cf ;
2021-06-17 16:03:09 -06:00
cf . instantiate ( ) ;
2021-08-09 14:13:42 -06:00
String src_path = String ( E . key ) + " .import " ;
2017-02-06 00:38:39 -03:00
2020-02-26 11:28:13 +01:00
Error err = cf - > load ( src_path ) ;
ERR_CONTINUE ( err ! = OK ) ;
2019-06-11 15:43:37 -03:00
2020-02-26 11:28:13 +01:00
bool changed = false ;
2017-02-06 00:38:39 -03:00
2022-06-20 15:30:19 +02:00
if ( E . value . flags & MAKE_NORMAL_FLAG & & int ( cf - > get_value ( " params " , " compress/normal_map " ) ) = = 0 ) {
2022-06-13 20:16:14 +02:00
print_line ( vformat ( TTR ( " %s: Texture detected as used as a normal map in 3D. Enabling red-green texture compression to reduce memory usage (blue channel is discarded). " ) , String ( E . key ) ) ) ;
2020-02-26 11:28:13 +01:00
cf - > set_value ( " params " , " compress/normal_map " , 1 ) ;
changed = true ;
}
2017-06-16 21:47:28 -03:00
2021-08-09 14:13:42 -06:00
if ( E . value . flags & MAKE_ROUGHNESS_FLAG & & int ( cf - > get_value ( " params " , " roughness/mode " ) ) = = 0 ) {
2022-06-13 20:16:14 +02:00
print_line ( vformat ( TTR ( " %s: Texture detected as used as a roughness map in 3D. Enabling roughness limiter based on the detected associated normal map at %s. " ) , String ( E . key ) , E . value . normal_path_for_roughness ) ) ;
2021-08-09 14:13:42 -06:00
cf - > set_value ( " params " , " roughness/mode " , E . value . channel_for_roughness + 2 ) ;
cf - > set_value ( " params " , " roughness/src_normal " , E . value . normal_path_for_roughness ) ;
2020-02-26 11:28:13 +01:00
changed = true ;
2019-09-26 23:16:44 -03:00
}
2017-02-06 00:38:39 -03:00
2022-06-20 15:30:19 +02:00
if ( E . value . flags & MAKE_3D_FLAG & & bool ( cf - > get_value ( " params " , " detect_3d/compress_to " ) ) ) {
const int compress_to = cf - > get_value ( " params " , " detect_3d/compress_to " ) ;
String compress_string ;
cf - > set_value ( " params " , " detect_3d/compress_to " , 0 ) ;
if ( compress_to = = 1 ) {
cf - > set_value ( " params " , " compress/mode " , COMPRESS_VRAM_COMPRESSED ) ;
compress_string = " VRAM Compressed (S3TC/ETC/BPTC) " ;
} else if ( compress_to = = 2 ) {
cf - > set_value ( " params " , " compress/mode " , COMPRESS_BASIS_UNIVERSAL ) ;
compress_string = " Basis Universal " ;
}
print_line ( vformat ( TTR ( " %s: Texture detected as used in 3D. Enabling mipmap generation and setting the texture compression mode to %s. " ) , String ( E . key ) , compress_string ) ) ;
cf - > set_value ( " params " , " mipmaps/generate " , true ) ;
changed = true ;
}
2020-02-26 11:28:13 +01:00
if ( changed ) {
cf - > save ( src_path ) ;
2021-08-09 14:13:42 -06:00
to_reimport . push_back ( E . key ) ;
2020-02-26 11:28:13 +01:00
}
}
2017-02-06 00:38:39 -03:00
2020-02-26 11:28:13 +01:00
make_flags . clear ( ) ;
}
2017-02-06 00:38:39 -03:00
if ( to_reimport . size ( ) ) {
EditorFileSystem : : get_singleton ( ) - > reimport_files ( to_reimport ) ;
}
}
2017-02-01 09:45:45 -03:00
String ResourceImporterTexture : : get_importer_name ( ) const {
return " texture " ;
}
2017-03-05 16:44:50 +01:00
String ResourceImporterTexture : : get_visible_name ( ) const {
2019-06-11 15:43:37 -03:00
return " Texture2D " ;
2017-02-01 09:45:45 -03:00
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
void ResourceImporterTexture : : get_recognized_extensions ( List < String > * p_extensions ) const {
2017-02-01 09:45:45 -03:00
ImageLoader : : get_recognized_extensions ( p_extensions ) ;
}
2020-05-14 14:29:06 +02:00
2017-02-01 09:45:45 -03:00
String ResourceImporterTexture : : get_save_extension ( ) const {
2022-03-05 16:43:38 +01:00
return " ctex " ;
2017-02-01 09:45:45 -03:00
}
2017-03-05 16:44:50 +01:00
String ResourceImporterTexture : : get_resource_type ( ) const {
2022-03-05 16:43:38 +01:00
return " CompressedTexture2D " ;
2017-02-01 09:45:45 -03:00
}
2022-05-13 15:04:37 +02:00
bool ResourceImporterTexture : : get_option_visibility ( const String & p_path , const String & p_option , const HashMap < StringName , Variant > & p_options ) const {
2017-04-26 10:49:08 -05:00
if ( p_option = = " compress/lossy_quality " ) {
int compress_mode = int ( p_options [ " compress/mode " ] ) ;
2019-09-26 23:16:44 -03:00
if ( compress_mode ! = COMPRESS_LOSSY & & compress_mode ! = COMPRESS_VRAM_COMPRESSED ) {
2017-04-26 10:49:08 -05:00
return false ;
}
2018-08-27 13:47:13 -03:00
} else if ( p_option = = " compress/hdr_mode " ) {
2018-08-22 18:31:12 -04:00
int compress_mode = int ( p_options [ " compress/mode " ] ) ;
2019-09-26 23:16:44 -03:00
if ( compress_mode < COMPRESS_VRAM_COMPRESSED ) {
2018-08-22 18:31:12 -04:00
return false ;
}
2022-03-26 19:21:07 -04:00
} else if ( p_option = = " compress/normal_map " ) {
int compress_mode = int ( p_options [ " compress/mode " ] ) ;
if ( compress_mode = = COMPRESS_LOSSLESS ) {
return false ;
}
2019-09-26 23:16:44 -03:00
} else if ( p_option = = " mipmaps/limit " ) {
return p_options [ " mipmaps/generate " ] ;
2018-08-27 23:05:04 -04:00
} else if ( p_option = = " compress/bptc_ldr " ) {
int compress_mode = int ( p_options [ " compress/mode " ] ) ;
2019-09-26 23:16:44 -03:00
if ( compress_mode < COMPRESS_VRAM_COMPRESSED ) {
2018-08-27 23:05:04 -04:00
return false ;
}
2021-02-17 13:44:49 -03:00
if ( ! ProjectSettings : : get_singleton ( ) - > get ( " rendering/textures/vram_compression/import_bptc " ) ) {
2018-08-27 23:05:04 -04:00
return false ;
}
2017-04-26 10:49:08 -05:00
}
2017-02-01 09:45:45 -03:00
return true ;
}
int ResourceImporterTexture : : get_preset_count ( ) const {
2020-06-14 15:08:09 +02:00
return 3 ;
2017-02-01 09:45:45 -03:00
}
2020-05-14 14:29:06 +02:00
2017-02-01 09:45:45 -03:00
String ResourceImporterTexture : : get_preset_name ( int p_idx ) const {
2017-03-05 16:44:50 +01:00
static const char * preset_names [ ] = {
2022-04-27 13:50:54 +08:00
TTRC ( " 2D/3D (Auto-Detect) " ) ,
TTRC ( " 2D " ) ,
TTRC ( " 3D " ) ,
2017-02-01 09:45:45 -03:00
} ;
2022-04-27 13:50:54 +08:00
return TTRGET ( preset_names [ p_idx ] ) ;
2017-02-01 09:45:45 -03:00
}
2021-11-14 14:02:38 -03:00
void ResourceImporterTexture : : get_import_options ( const String & p_path , List < ImportOption > * r_options , int p_preset ) const {
2019-09-26 23:16:44 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : INT , " compress/mode " , PROPERTY_HINT_ENUM , " Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal " , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED ) , p_preset = = PRESET_3D ? 2 : 0 ) ) ;
2020-02-24 15:20:53 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : FLOAT , " compress/lossy_quality " , PROPERTY_HINT_RANGE , " 0,1,0.01 " ) , 0.7 ) ) ;
2020-05-01 09:34:23 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : INT , " compress/hdr_compression " , PROPERTY_HINT_ENUM , " Disabled,Opaque Only,Always " ) , 1 ) ) ;
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : INT , " compress/bptc_ldr " , PROPERTY_HINT_ENUM , " Disabled,Enabled,RGBA Only " ) , 0 ) ) ;
2017-06-16 21:47:28 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : INT , " compress/normal_map " , PROPERTY_HINT_ENUM , " Detect,Enable,Disabled " ) , 0 ) ) ;
2019-06-11 15:43:37 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : INT , " compress/channel_pack " , PROPERTY_HINT_ENUM , " sRGB Friendly,Optimized " ) , 0 ) ) ;
2019-09-26 23:16:44 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : BOOL , " mipmaps/generate " ) , ( p_preset = = PRESET_3D ? true : false ) ) ) ;
2019-09-27 09:05:22 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : INT , " mipmaps/limit " , PROPERTY_HINT_RANGE , " -1,256 " ) , - 1 ) ) ;
2019-06-11 15:43:37 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : INT , " roughness/mode " , PROPERTY_HINT_ENUM , " Detect,Disabled,Red,Green,Blue,Alpha,Gray " ) , 0 ) ) ;
2022-01-17 12:39:57 +01:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : STRING , " roughness/src_normal " , PROPERTY_HINT_FILE , " *.bmp,*.dds,*.exr,*.jpeg,*.jpg,*.hdr,*.png,*.svg,*.tga,*.webp " ) , " " ) ) ;
2019-06-26 15:08:25 +02:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : BOOL , " process/fix_alpha_border " ) , p_preset ! = PRESET_3D ) ) ;
2017-06-25 08:01:15 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : BOOL , " process/premult_alpha " ) , false ) ) ;
2020-05-31 19:27:29 +02:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : BOOL , " process/normal_map_invert_y " ) , false ) ) ;
2022-02-12 02:39:10 +01:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : BOOL , " process/hdr_as_srgb " ) , false ) ) ;
2022-02-16 01:43:44 +01:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : BOOL , " process/hdr_clamp_exposure " ) , false ) ) ;
2019-09-14 00:37:42 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : INT , " process/size_limit " , PROPERTY_HINT_RANGE , " 0,4096,1 " ) , 0 ) ) ;
2019-09-27 09:05:22 -03:00
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : INT , " detect_3d/compress_to " , PROPERTY_HINT_ENUM , " Disabled,VRAM Compressed,Basis Universal " ) , ( p_preset = = PRESET_DETECT ) ? 1 : 0 ) ) ;
2022-01-16 21:18:00 +01:00
if ( p_path . get_extension ( ) = = " svg " ) {
r_options - > push_back ( ImportOption ( PropertyInfo ( Variant : : FLOAT , " svg/scale " , PROPERTY_HINT_RANGE , " 0.001,100,0.001 " ) , 1.0 ) ) ;
}
2017-02-01 09:45:45 -03:00
}
2022-03-23 11:08:58 +02:00
void ResourceImporterTexture : : save_to_ctex_format ( Ref < FileAccess > f , const Ref < Image > & p_image , CompressMode p_compress_mode , Image : : UsedChannels p_channels , Image : : CompressMode p_compress_format , float p_lossy_quality ) {
2017-02-01 09:45:45 -03:00
switch ( p_compress_mode ) {
case COMPRESS_LOSSLESS : {
2021-08-13 15:17:55 -05:00
bool lossless_force_png = ProjectSettings : : get_singleton ( ) - > get ( " rendering/textures/lossless_compression/force_png " ) | |
! Image : : _webp_mem_loader_func ; // WebP module disabled.
2021-04-12 18:56:50 +02:00
bool use_webp = ! lossless_force_png & & p_image - > get_width ( ) < = 16383 & & p_image - > get_height ( ) < = 16383 ; // WebP has a size limit
2022-03-05 16:43:38 +01:00
f - > store_32 ( use_webp ? CompressedTexture2D : : DATA_FORMAT_WEBP : CompressedTexture2D : : DATA_FORMAT_PNG ) ;
2019-09-26 23:16:44 -03:00
f - > store_16 ( p_image - > get_width ( ) ) ;
f - > store_16 ( p_image - > get_height ( ) ) ;
f - > store_32 ( p_image - > get_mipmap_count ( ) ) ;
f - > store_32 ( p_image - > get_format ( ) ) ;
2017-02-01 09:45:45 -03:00
2019-09-26 23:16:44 -03:00
for ( int i = 0 ; i < p_image - > get_mipmap_count ( ) + 1 ; i + + ) {
2021-04-12 18:56:50 +02:00
Vector < uint8_t > data ;
if ( use_webp ) {
data = Image : : webp_lossless_packer ( p_image - > get_image_from_mipmap ( i ) ) ;
} else {
data = Image : : png_packer ( p_image - > get_image_from_mipmap ( i ) ) ;
}
2017-02-01 09:45:45 -03:00
int data_len = data . size ( ) ;
f - > store_32 ( data_len ) ;
2020-02-17 18:06:54 -03:00
const uint8_t * r = data . ptr ( ) ;
f - > store_buffer ( r , data_len ) ;
2017-02-01 09:45:45 -03:00
}
} break ;
case COMPRESS_LOSSY : {
2022-03-05 16:43:38 +01:00
f - > store_32 ( CompressedTexture2D : : DATA_FORMAT_WEBP ) ;
2019-09-26 23:16:44 -03:00
f - > store_16 ( p_image - > get_width ( ) ) ;
f - > store_16 ( p_image - > get_height ( ) ) ;
f - > store_32 ( p_image - > get_mipmap_count ( ) ) ;
f - > store_32 ( p_image - > get_format ( ) ) ;
2017-02-01 09:45:45 -03:00
2019-09-26 23:16:44 -03:00
for ( int i = 0 ; i < p_image - > get_mipmap_count ( ) + 1 ; i + + ) {
2021-04-12 18:56:50 +02:00
Vector < uint8_t > data = Image : : webp_lossy_packer ( p_image - > get_image_from_mipmap ( i ) , p_lossy_quality ) ;
2017-02-01 09:45:45 -03:00
int data_len = data . size ( ) ;
f - > store_32 ( data_len ) ;
2020-02-17 18:06:54 -03:00
const uint8_t * r = data . ptr ( ) ;
f - > store_buffer ( r , data_len ) ;
2017-02-01 09:45:45 -03:00
}
} break ;
2019-09-26 23:16:44 -03:00
case COMPRESS_VRAM_COMPRESSED : {
2017-05-17 07:36:47 -03:00
Ref < Image > image = p_image - > duplicate ( ) ;
2017-05-26 21:49:49 -03:00
2020-05-01 09:34:23 -03:00
image - > compress_from_channels ( p_compress_format , p_channels , p_lossy_quality ) ;
2017-02-01 09:45:45 -03:00
2022-03-05 16:43:38 +01:00
f - > store_32 ( CompressedTexture2D : : DATA_FORMAT_IMAGE ) ;
2019-09-26 23:16:44 -03:00
f - > store_16 ( image - > get_width ( ) ) ;
f - > store_16 ( image - > get_height ( ) ) ;
f - > store_32 ( image - > get_mipmap_count ( ) ) ;
f - > store_32 ( image - > get_format ( ) ) ;
2017-02-01 09:45:45 -03:00
2020-02-17 18:06:54 -03:00
Vector < uint8_t > data = image - > get_data ( ) ;
2017-02-01 09:45:45 -03:00
int dl = data . size ( ) ;
2020-02-17 18:06:54 -03:00
const uint8_t * r = data . ptr ( ) ;
f - > store_buffer ( r , dl ) ;
2017-02-01 09:45:45 -03:00
} break ;
2019-09-26 23:16:44 -03:00
case COMPRESS_VRAM_UNCOMPRESSED : {
2022-03-05 16:43:38 +01:00
f - > store_32 ( CompressedTexture2D : : DATA_FORMAT_IMAGE ) ;
2019-09-26 23:16:44 -03:00
f - > store_16 ( p_image - > get_width ( ) ) ;
f - > store_16 ( p_image - > get_height ( ) ) ;
f - > store_32 ( p_image - > get_mipmap_count ( ) ) ;
f - > store_32 ( p_image - > get_format ( ) ) ;
2017-02-01 09:45:45 -03:00
2020-02-17 18:06:54 -03:00
Vector < uint8_t > data = p_image - > get_data ( ) ;
2017-02-01 09:45:45 -03:00
int dl = data . size ( ) ;
2020-02-17 18:06:54 -03:00
const uint8_t * r = data . ptr ( ) ;
2017-02-01 09:45:45 -03:00
2020-02-17 18:06:54 -03:00
f - > store_buffer ( r , dl ) ;
2017-02-01 09:45:45 -03:00
} break ;
2019-09-26 23:16:44 -03:00
case COMPRESS_BASIS_UNIVERSAL : {
2022-03-05 16:43:38 +01:00
f - > store_32 ( CompressedTexture2D : : DATA_FORMAT_BASIS_UNIVERSAL ) ;
2019-09-26 23:16:44 -03:00
f - > store_16 ( p_image - > get_width ( ) ) ;
f - > store_16 ( p_image - > get_height ( ) ) ;
f - > store_32 ( p_image - > get_mipmap_count ( ) ) ;
f - > store_32 ( p_image - > get_format ( ) ) ;
for ( int i = 0 ; i < p_image - > get_mipmap_count ( ) + 1 ; i + + ) {
2020-02-17 18:06:54 -03:00
Vector < uint8_t > data = Image : : basis_universal_packer ( p_image - > get_image_from_mipmap ( i ) , p_channels ) ;
2019-09-26 23:16:44 -03:00
int data_len = data . size ( ) ;
f - > store_32 ( data_len ) ;
2020-02-17 18:06:54 -03:00
const uint8_t * r = data . ptr ( ) ;
f - > store_buffer ( r , data_len ) ;
2019-09-26 23:16:44 -03:00
}
} break ;
2017-02-01 09:45:45 -03:00
}
2019-09-26 23:16:44 -03:00
}
2022-03-09 20:22:36 -07:00
void ResourceImporterTexture : : _save_ctex ( const Ref < Image > & p_image , const String & p_to_path , CompressMode p_compress_mode , float p_lossy_quality , Image : : CompressMode p_vram_compression , bool p_mipmaps , bool p_streamable , bool p_detect_3d , bool p_detect_roughness , bool p_detect_normal , bool p_force_normal , bool p_srgb_friendly , bool p_force_po2_for_compressed , uint32_t p_limit_mipmap , const Ref < Image > & p_normal , Image : : RoughnessChannel p_roughness_channel ) {
2022-03-23 11:08:58 +02:00
Ref < FileAccess > f = FileAccess : : open ( p_to_path , FileAccess : : WRITE ) ;
ERR_FAIL_COND ( f . is_null ( ) ) ;
2019-09-26 23:16:44 -03:00
f - > store_8 ( ' G ' ) ;
f - > store_8 ( ' S ' ) ;
f - > store_8 ( ' T ' ) ;
f - > store_8 ( ' 2 ' ) ; //godot streamable texture 2D
//format version
2022-03-05 16:43:38 +01:00
f - > store_32 ( CompressedTexture2D : : FORMAT_VERSION ) ;
2019-09-26 23:16:44 -03:00
//texture may be resized later, so original size must be saved first
f - > store_32 ( p_image - > get_width ( ) ) ;
f - > store_32 ( p_image - > get_height ( ) ) ;
uint32_t flags = 0 ;
2020-05-14 16:41:43 +02:00
if ( p_streamable ) {
2022-03-05 16:43:38 +01:00
flags | = CompressedTexture2D : : FORMAT_BIT_STREAM ;
2020-05-14 16:41:43 +02:00
}
if ( p_mipmaps ) {
2022-03-05 16:43:38 +01:00
flags | = CompressedTexture2D : : FORMAT_BIT_HAS_MIPMAPS ; //mipmaps bit
2020-05-14 16:41:43 +02:00
}
if ( p_detect_3d ) {
2022-03-05 16:43:38 +01:00
flags | = CompressedTexture2D : : FORMAT_BIT_DETECT_3D ;
2020-05-14 16:41:43 +02:00
}
if ( p_detect_roughness ) {
2022-03-05 16:43:38 +01:00
flags | = CompressedTexture2D : : FORMAT_BIT_DETECT_ROUGNESS ;
2020-05-14 16:41:43 +02:00
}
if ( p_detect_normal ) {
2022-03-05 16:43:38 +01:00
flags | = CompressedTexture2D : : FORMAT_BIT_DETECT_NORMAL ;
2020-05-14 16:41:43 +02:00
}
2019-09-26 23:16:44 -03:00
f - > store_32 ( flags ) ;
f - > store_32 ( p_limit_mipmap ) ;
2020-03-11 13:59:18 -04:00
//reserved for future use
2019-09-26 23:16:44 -03:00
f - > store_32 ( 0 ) ;
f - > store_32 ( 0 ) ;
f - > store_32 ( 0 ) ;
/*
print_line("streamable " + itos(p_streamable));
print_line("mipmaps " + itos(p_mipmaps));
print_line("detect_3d " + itos(p_detect_3d));
print_line("roughness " + itos(p_detect_roughness));
print_line("normal " + itos(p_detect_normal));
*/
if ( ( p_compress_mode = = COMPRESS_LOSSLESS | | p_compress_mode = = COMPRESS_LOSSY ) & & p_image - > get_format ( ) > Image : : FORMAT_RGBA8 ) {
p_compress_mode = COMPRESS_VRAM_UNCOMPRESSED ; //these can't go as lossy
}
Ref < Image > image = p_image - > duplicate ( ) ;
if ( ( ( p_compress_mode = = COMPRESS_BASIS_UNIVERSAL ) | | ( p_compress_mode = = COMPRESS_VRAM_COMPRESSED & & p_force_po2_for_compressed ) ) & & p_mipmaps ) {
image - > resize_to_po2 ( ) ;
}
if ( p_mipmaps & & ( ! image - > has_mipmaps ( ) | | p_force_normal ) ) {
image - > generate_mipmaps ( p_force_normal ) ;
}
if ( ! p_mipmaps ) {
image - > clear_mipmaps ( ) ;
}
2020-01-26 20:09:40 -03:00
if ( image - > has_mipmaps ( ) & & p_normal . is_valid ( ) ) {
image - > generate_mipmap_roughness ( p_roughness_channel , p_normal ) ;
}
2019-09-26 23:16:44 -03:00
Image : : CompressSource csource = Image : : COMPRESS_SOURCE_GENERIC ;
if ( p_force_normal ) {
csource = Image : : COMPRESS_SOURCE_NORMAL ;
} else if ( p_srgb_friendly ) {
csource = Image : : COMPRESS_SOURCE_SRGB ;
}
Image : : UsedChannels used_channels = image - > detect_used_channels ( csource ) ;
2022-03-09 20:22:36 -07:00
save_to_ctex_format ( f , image , p_compress_mode , used_channels , p_vram_compression , p_lossy_quality ) ;
2017-02-01 09:45:45 -03:00
}
2022-05-13 15:04:37 +02:00
Error ResourceImporterTexture : : import ( const String & p_source_file , const String & p_save_path , const HashMap < StringName , Variant > & p_options , List < String > * r_platform_variants , List < String > * r_gen_files , Variant * r_metadata ) {
2022-08-23 13:25:14 +03:00
// Parse import options.
int32_t loader_flags = ImageFormatLoader : : FLAG_NONE ;
// Compression.
2019-09-26 23:16:44 -03:00
CompressMode compress_mode = CompressMode ( int ( p_options [ " compress/mode " ] ) ) ;
2022-02-12 02:39:10 +01:00
const float lossy = p_options [ " compress/lossy_quality " ] ;
const int pack_channels = p_options [ " compress/channel_pack " ] ;
2022-08-23 13:25:14 +03:00
const int normal = p_options [ " compress/normal_map " ] ;
const int hdr_compression = p_options [ " compress/hdr_compression " ] ;
const int bptc_ldr = p_options [ " compress/bptc_ldr " ] ;
// Mipmaps.
2022-02-12 02:39:10 +01:00
const bool mipmaps = p_options [ " mipmaps/generate " ] ;
const uint32_t mipmap_limit = mipmaps ? uint32_t ( p_options [ " mipmaps/limit " ] ) : uint32_t ( - 1 ) ;
2022-08-23 13:25:14 +03:00
// Roughness.
const int roughness = p_options [ " roughness/mode " ] ;
const String normal_map = p_options [ " roughness/src_normal " ] ;
// Processing.
2022-02-12 02:39:10 +01:00
const bool fix_alpha_border = p_options [ " process/fix_alpha_border " ] ;
const bool premult_alpha = p_options [ " process/premult_alpha " ] ;
const bool normal_map_invert_y = p_options [ " process/normal_map_invert_y " ] ;
2022-02-12 02:37:14 +01:00
// Support for texture streaming is not implemented yet.
const bool stream = false ;
2022-02-12 02:39:10 +01:00
const int size_limit = p_options [ " process/size_limit " ] ;
const bool hdr_as_srgb = p_options [ " process/hdr_as_srgb " ] ;
2022-08-23 13:25:14 +03:00
if ( hdr_as_srgb ) {
loader_flags | = ImageFormatLoader : : FLAG_FORCE_LINEAR ;
}
2022-02-16 01:43:44 +01:00
const bool hdr_clamp_exposure = p_options [ " process/hdr_clamp_exposure " ] ;
2022-08-23 13:25:14 +03:00
2022-01-16 21:18:00 +01:00
float scale = 1.0 ;
2022-08-23 13:25:14 +03:00
// SVG-specific options.
2022-01-16 21:18:00 +01:00
if ( p_options . has ( " svg/scale " ) ) {
scale = p_options [ " svg/scale " ] ;
}
2020-01-26 20:09:40 -03:00
Ref < Image > normal_image ;
2020-03-31 13:50:25 +02:00
Image : : RoughnessChannel roughness_channel = Image : : ROUGHNESS_CHANNEL_R ;
2020-01-26 20:09:40 -03:00
if ( mipmaps & & roughness > 1 & & FileAccess : : exists ( normal_map ) ) {
2021-06-17 16:03:09 -06:00
normal_image . instantiate ( ) ;
2020-01-26 20:09:40 -03:00
if ( ImageLoader : : load_image ( normal_map , normal_image ) = = OK ) {
roughness_channel = Image : : RoughnessChannel ( roughness - 2 ) ;
}
}
2022-08-23 13:25:14 +03:00
2017-05-17 07:36:47 -03:00
Ref < Image > image ;
2021-06-17 16:03:09 -06:00
image . instantiate ( ) ;
2022-08-23 13:25:14 +03:00
Error err = ImageLoader : : load_image ( p_source_file , image , nullptr , loader_flags , scale ) ;
2020-05-14 16:41:43 +02:00
if ( err ! = OK ) {
2017-02-01 09:45:45 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2017-02-01 09:45:45 -03:00
2019-02-26 18:43:37 -03:00
Array formats_imported ;
2017-05-17 07:36:47 -03:00
if ( size_limit > 0 & & ( image - > get_width ( ) > size_limit | | image - > get_height ( ) > size_limit ) ) {
2017-02-01 09:45:45 -03:00
//limit size
2017-05-17 07:36:47 -03:00
if ( image - > get_width ( ) > = image - > get_height ( ) ) {
2017-02-01 09:45:45 -03:00
int new_width = size_limit ;
2017-05-17 07:36:47 -03:00
int new_height = image - > get_height ( ) * new_width / image - > get_width ( ) ;
2017-02-01 09:45:45 -03:00
2017-05-17 07:36:47 -03:00
image - > resize ( new_width , new_height , Image : : INTERPOLATE_CUBIC ) ;
2017-02-01 09:45:45 -03:00
} else {
int new_height = size_limit ;
2017-05-17 07:36:47 -03:00
int new_width = image - > get_width ( ) * new_height / image - > get_height ( ) ;
2017-02-01 09:45:45 -03:00
2017-05-17 07:36:47 -03:00
image - > resize ( new_width , new_height , Image : : INTERPOLATE_CUBIC ) ;
2017-02-01 09:45:45 -03:00
}
2018-07-03 10:55:50 -03:00
2018-11-25 17:05:35 -03:00
if ( normal = = 1 ) {
2018-07-03 10:55:50 -03:00
image - > normalize ( ) ;
}
2017-02-01 09:45:45 -03:00
}
if ( fix_alpha_border ) {
2017-05-17 07:36:47 -03:00
image - > fix_alpha_edges ( ) ;
2017-02-01 09:45:45 -03:00
}
if ( premult_alpha ) {
2017-05-17 07:36:47 -03:00
image - > premultiply_alpha ( ) ;
2017-02-01 09:45:45 -03:00
}
2020-05-31 19:27:29 +02:00
if ( normal_map_invert_y ) {
// Inverting the green channel can be used to flip a normal map's direction.
// There's no standard when it comes to normal map Y direction, so this is
// sometimes needed when using a normal map exported from another program.
// See <http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates>.
const int height = image - > get_height ( ) ;
const int width = image - > get_width ( ) ;
2018-08-17 13:28:07 -03:00
2018-09-02 10:13:19 -03:00
for ( int i = 0 ; i < width ; i + + ) {
for ( int j = 0 ; j < height ; j + + ) {
2020-05-31 19:27:29 +02:00
const Color color = image - > get_pixel ( i , j ) ;
image - > set_pixel ( i , j , Color ( color . r , 1 - color . g , color . b ) ) ;
2018-08-17 13:28:07 -03:00
}
}
}
2022-02-16 01:43:44 +01:00
if ( hdr_clamp_exposure ) {
// Clamp HDR exposure following Filament's tonemapping formula.
// This can be used to reduce fireflies in environment maps or reduce the influence
// of the sun from an HDRI panorama on environment lighting (when a DirectionalLight3D is used instead).
const int height = image - > get_height ( ) ;
const int width = image - > get_width ( ) ;
// These values are chosen arbitrarily and seem to produce good results with 4,096 samples.
const float linear = 4096.0 ;
const float compressed = 16384.0 ;
for ( int i = 0 ; i < width ; i + + ) {
for ( int j = 0 ; j < height ; j + + ) {
const Color color = image - > get_pixel ( i , j ) ;
const float luma = color . get_luminance ( ) ;
Color clamped_color ;
if ( luma < = linear ) {
clamped_color = color ;
} else {
clamped_color = ( color / luma ) * ( ( linear * linear - compressed * luma ) / ( 2 * linear - compressed - luma ) ) ;
}
image - > set_pixel ( i , j , clamped_color ) ;
}
}
}
2019-09-26 23:16:44 -03:00
if ( compress_mode = = COMPRESS_BASIS_UNIVERSAL & & image - > get_format ( ) > = Image : : FORMAT_RF ) {
//basis universal does not support float formats, fall back
compress_mode = COMPRESS_VRAM_COMPRESSED ;
}
bool detect_3d = int ( p_options [ " detect_3d/compress_to " ] ) > 0 ;
2019-06-11 15:43:37 -03:00
bool detect_roughness = roughness = = 0 ;
2017-06-16 21:47:28 -03:00
bool detect_normal = normal = = 0 ;
bool force_normal = normal = = 1 ;
2019-06-11 15:43:37 -03:00
bool srgb_friendly_pack = pack_channels = = 0 ;
2017-02-01 09:45:45 -03:00
2019-09-26 23:16:44 -03:00
if ( compress_mode = = COMPRESS_VRAM_COMPRESSED ) {
2017-07-19 17:00:46 -03:00
//must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
2017-02-01 09:45:45 -03:00
//Android, GLES 2.x
2017-09-05 20:17:26 -03:00
2022-01-18 04:39:55 -08:00
const bool is_hdr = ( image - > get_format ( ) > = Image : : FORMAT_RF & & image - > get_format ( ) < = Image : : FORMAT_RGBE9995 ) ;
2019-09-26 23:16:44 -03:00
bool is_ldr = ( image - > get_format ( ) > = Image : : FORMAT_L8 & & image - > get_format ( ) < = Image : : FORMAT_RGB565 ) ;
2022-01-18 04:39:55 -08:00
const bool can_bptc = ProjectSettings : : get_singleton ( ) - > get ( " rendering/textures/vram_compression/import_bptc " ) ;
const bool can_s3tc = ProjectSettings : : get_singleton ( ) - > get ( " rendering/textures/vram_compression/import_s3tc " ) ;
2017-09-05 20:17:26 -03:00
2018-08-27 13:47:13 -03:00
if ( can_bptc ) {
2020-05-01 09:34:23 -03:00
//add to the list anyway
formats_imported . push_back ( " bptc " ) ;
}
2018-08-27 23:05:04 -04:00
2020-05-01 09:34:23 -03:00
bool can_compress_hdr = hdr_compression > 0 ;
bool has_alpha = image - > detect_alpha ( ) ! = Image : : ALPHA_NONE ;
if ( is_hdr & & can_compress_hdr ) {
if ( has_alpha ) {
//can compress hdr, but hdr with alpha is not compressible
if ( hdr_compression = = 2 ) {
//but user selected to compress hdr anyway, so force an alpha-less format.
if ( image - > get_format ( ) = = Image : : FORMAT_RGBAF ) {
image - > convert ( Image : : FORMAT_RGBF ) ;
} else if ( image - > get_format ( ) = = Image : : FORMAT_RGBAH ) {
image - > convert ( Image : : FORMAT_RGBH ) ;
}
} else {
can_compress_hdr = false ;
2018-08-27 23:05:04 -04:00
}
2020-05-01 09:34:23 -03:00
}
2018-08-27 23:05:04 -04:00
2022-01-18 04:39:55 -08:00
if ( ! can_compress_hdr ) {
//fallback to RGBE99995
if ( image - > get_format ( ) ! = Image : : FORMAT_RGBE9995 ) {
image - > convert ( Image : : FORMAT_RGBE9995 ) ;
2018-08-27 23:05:04 -04:00
}
2020-05-01 09:34:23 -03:00
}
2018-08-27 23:05:04 -04:00
}
2018-08-21 22:56:04 -04:00
2022-01-18 04:39:55 -08:00
bool ok_on_pc = false ;
2018-08-27 23:05:04 -04:00
if ( can_bptc | | can_s3tc ) {
2022-01-18 04:39:55 -08:00
ok_on_pc = true ;
Image : : CompressMode image_compress_mode = Image : : COMPRESS_BPTC ;
if ( ! bptc_ldr & & can_s3tc & & is_ldr ) {
image_compress_mode = Image : : COMPRESS_S3TC ;
}
2022-03-09 20:22:36 -07:00
_save_ctex ( image , p_save_path + " .s3tc.ctex " , compress_mode , lossy , image_compress_mode , mipmaps , stream , detect_3d , detect_roughness , detect_normal , force_normal , srgb_friendly_pack , false , mipmap_limit , normal_image , roughness_channel ) ;
2017-06-16 21:47:28 -03:00
r_platform_variants - > push_back ( " s3tc " ) ;
2019-02-26 18:43:37 -03:00
formats_imported . push_back ( " s3tc " ) ;
2017-06-16 21:47:28 -03:00
}
2021-02-17 13:44:49 -03:00
if ( ProjectSettings : : get_singleton ( ) - > get ( " rendering/textures/vram_compression/import_etc2 " ) ) {
2022-03-09 20:22:36 -07:00
_save_ctex ( image , p_save_path + " .etc2.ctex " , compress_mode , lossy , Image : : COMPRESS_ETC2 , mipmaps , stream , detect_3d , detect_roughness , detect_normal , force_normal , srgb_friendly_pack , true , mipmap_limit , normal_image , roughness_channel ) ;
2017-06-16 21:47:28 -03:00
r_platform_variants - > push_back ( " etc2 " ) ;
2019-02-26 18:43:37 -03:00
formats_imported . push_back ( " etc2 " ) ;
2017-06-16 21:47:28 -03:00
}
2021-02-17 13:44:49 -03:00
if ( ProjectSettings : : get_singleton ( ) - > get ( " rendering/textures/vram_compression/import_etc " ) ) {
2022-03-09 20:22:36 -07:00
_save_ctex ( image , p_save_path + " .etc.ctex " , compress_mode , lossy , Image : : COMPRESS_ETC , mipmaps , stream , detect_3d , detect_roughness , detect_normal , force_normal , srgb_friendly_pack , true , mipmap_limit , normal_image , roughness_channel ) ;
2017-07-19 17:00:46 -03:00
r_platform_variants - > push_back ( " etc " ) ;
2019-02-26 18:43:37 -03:00
formats_imported . push_back ( " etc " ) ;
2017-07-19 17:00:46 -03:00
}
2017-09-05 20:17:26 -03:00
if ( ! ok_on_pc ) {
2022-04-16 13:09:49 +02:00
EditorNode : : add_io_error ( vformat ( TTR ( " %s: No suitable desktop VRAM compression algorithm enabled in Project Settings (S3TC or BPTC). This texture may not display correctly on desktop platforms. " ) , p_source_file ) ) ;
2017-09-05 20:17:26 -03:00
}
2017-02-01 09:45:45 -03:00
} else {
//import normally
2022-03-09 20:22:36 -07:00
_save_ctex ( image , p_save_path + " .ctex " , compress_mode , lossy , Image : : COMPRESS_S3TC /*this is ignored */ , mipmaps , stream , detect_3d , detect_roughness , detect_normal , force_normal , srgb_friendly_pack , false , mipmap_limit , normal_image , roughness_channel ) ;
2017-02-01 09:45:45 -03:00
}
2019-02-26 18:43:37 -03:00
if ( r_metadata ) {
Dictionary metadata ;
2019-09-26 23:16:44 -03:00
metadata [ " vram_texture " ] = compress_mode = = COMPRESS_VRAM_COMPRESSED ;
2019-02-26 18:43:37 -03:00
if ( formats_imported . size ( ) ) {
metadata [ " imported_formats " ] = formats_imported ;
}
* r_metadata = metadata ;
}
2017-02-01 09:45:45 -03:00
return OK ;
}
2019-02-26 18:43:37 -03:00
const char * ResourceImporterTexture : : compression_formats [ ] = {
" bptc " ,
" s3tc " ,
" etc " ,
" etc2 " ,
2020-04-02 01:20:12 +02:00
nullptr
2019-02-26 18:43:37 -03:00
} ;
String ResourceImporterTexture : : get_import_settings_string ( ) const {
String s ;
int index = 0 ;
while ( compression_formats [ index ] ) {
2021-02-17 13:44:49 -03:00
String setting_path = " rendering/textures/vram_compression/import_ " + String ( compression_formats [ index ] ) ;
2019-02-26 18:43:37 -03:00
bool test = ProjectSettings : : get_singleton ( ) - > get ( setting_path ) ;
if ( test ) {
s + = String ( compression_formats [ index ] ) ;
}
index + + ;
}
return s ;
}
bool ResourceImporterTexture : : are_import_settings_valid ( const String & p_path ) const {
//will become invalid if formats are missing to import
Dictionary metadata = ResourceFormatImporter : : get_singleton ( ) - > get_resource_metadata ( p_path ) ;
if ( ! metadata . has ( " vram_texture " ) ) {
return false ;
}
bool vram = metadata [ " vram_texture " ] ;
if ( ! vram ) {
return true ; //do not care about non vram
}
Vector < String > formats_imported ;
if ( metadata . has ( " imported_formats " ) ) {
formats_imported = metadata [ " imported_formats " ] ;
}
int index = 0 ;
bool valid = true ;
while ( compression_formats [ index ] ) {
2021-02-17 13:44:49 -03:00
String setting_path = " rendering/textures/vram_compression/import_ " + String ( compression_formats [ index ] ) ;
2019-02-26 18:43:37 -03:00
bool test = ProjectSettings : : get_singleton ( ) - > get ( setting_path ) ;
if ( test ) {
2022-02-02 00:04:13 +05:45
if ( ! formats_imported . has ( compression_formats [ index ] ) ) {
2019-02-26 18:43:37 -03:00
valid = false ;
break ;
}
}
index + + ;
}
return valid ;
}
2020-04-02 01:20:12 +02:00
ResourceImporterTexture * ResourceImporterTexture : : singleton = nullptr ;
2017-02-06 00:38:39 -03:00
2017-03-05 16:44:50 +01:00
ResourceImporterTexture : : ResourceImporterTexture ( ) {
singleton = this ;
2022-03-05 16:43:38 +01:00
CompressedTexture2D : : request_3d_callback = _texture_reimport_3d ;
CompressedTexture2D : : request_roughness_callback = _texture_reimport_roughness ;
CompressedTexture2D : : request_normal_callback = _texture_reimport_normal ;
2017-02-01 09:45:45 -03:00
}
2017-02-06 00:38:39 -03:00
2017-03-05 16:44:50 +01:00
ResourceImporterTexture : : ~ ResourceImporterTexture ( ) {
2017-02-06 00:38:39 -03:00
}