| 1 | /**************************************************************************/ |
| 2 | /* resource_importer_layered_texture.cpp */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #include "resource_importer_layered_texture.h" |
| 32 | |
| 33 | #include "core/config/project_settings.h" |
| 34 | #include "core/error/error_macros.h" |
| 35 | #include "core/io/config_file.h" |
| 36 | #include "core/io/image_loader.h" |
| 37 | #include "core/object/ref_counted.h" |
| 38 | #include "editor/editor_file_system.h" |
| 39 | #include "editor/editor_node.h" |
| 40 | #include "editor/import/resource_importer_texture.h" |
| 41 | #include "editor/import/resource_importer_texture_settings.h" |
| 42 | #include "scene/resources/compressed_texture.h" |
| 43 | #include "scene/resources/texture.h" |
| 44 | |
| 45 | String ResourceImporterLayeredTexture::get_importer_name() const { |
| 46 | switch (mode) { |
| 47 | case MODE_CUBEMAP: { |
| 48 | return "cubemap_texture" ; |
| 49 | } break; |
| 50 | case MODE_2D_ARRAY: { |
| 51 | return "2d_array_texture" ; |
| 52 | } break; |
| 53 | case MODE_CUBEMAP_ARRAY: { |
| 54 | return "cubemap_array_texture" ; |
| 55 | } break; |
| 56 | case MODE_3D: { |
| 57 | return "3d_texture" ; |
| 58 | } break; |
| 59 | } |
| 60 | |
| 61 | ERR_FAIL_V("" ); |
| 62 | } |
| 63 | |
| 64 | String ResourceImporterLayeredTexture::get_visible_name() const { |
| 65 | switch (mode) { |
| 66 | case MODE_CUBEMAP: { |
| 67 | return "Cubemap" ; |
| 68 | } break; |
| 69 | case MODE_2D_ARRAY: { |
| 70 | return "Texture2DArray" ; |
| 71 | } break; |
| 72 | case MODE_CUBEMAP_ARRAY: { |
| 73 | return "CubemapArray" ; |
| 74 | } break; |
| 75 | case MODE_3D: { |
| 76 | return "Texture3D" ; |
| 77 | } break; |
| 78 | } |
| 79 | |
| 80 | ERR_FAIL_V("" ); |
| 81 | } |
| 82 | |
| 83 | void ResourceImporterLayeredTexture::get_recognized_extensions(List<String> *p_extensions) const { |
| 84 | ImageLoader::get_recognized_extensions(p_extensions); |
| 85 | } |
| 86 | |
| 87 | String ResourceImporterLayeredTexture::get_save_extension() const { |
| 88 | switch (mode) { |
| 89 | case MODE_CUBEMAP: { |
| 90 | return "ccube" ; |
| 91 | } break; |
| 92 | case MODE_2D_ARRAY: { |
| 93 | return "ctexarray" ; |
| 94 | } break; |
| 95 | case MODE_CUBEMAP_ARRAY: { |
| 96 | return "ccubearray" ; |
| 97 | } break; |
| 98 | case MODE_3D: { |
| 99 | return "ctex3d" ; |
| 100 | } break; |
| 101 | } |
| 102 | |
| 103 | ERR_FAIL_V(String()); |
| 104 | } |
| 105 | |
| 106 | String ResourceImporterLayeredTexture::get_resource_type() const { |
| 107 | switch (mode) { |
| 108 | case MODE_CUBEMAP: { |
| 109 | return "CompressedCubemap" ; |
| 110 | } break; |
| 111 | case MODE_2D_ARRAY: { |
| 112 | return "CompressedTexture2DArray" ; |
| 113 | } break; |
| 114 | case MODE_CUBEMAP_ARRAY: { |
| 115 | return "CompressedCubemapArray" ; |
| 116 | } break; |
| 117 | case MODE_3D: { |
| 118 | return "CompressedTexture3D" ; |
| 119 | } break; |
| 120 | } |
| 121 | ERR_FAIL_V(String()); |
| 122 | } |
| 123 | |
| 124 | bool ResourceImporterLayeredTexture::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const { |
| 125 | if (p_option == "compress/lossy_quality" && p_options.has("compress/mode" )) { |
| 126 | return int(p_options["compress/mode" ]) == COMPRESS_LOSSY; |
| 127 | } |
| 128 | if ((p_option == "compress/high_quality" || p_option == "compress/hdr_compression" ) && p_options.has("compress/mode" )) { |
| 129 | return int(p_options["compress/mode" ]) == COMPRESS_VRAM_COMPRESSED; |
| 130 | } |
| 131 | return true; |
| 132 | } |
| 133 | |
| 134 | int ResourceImporterLayeredTexture::get_preset_count() const { |
| 135 | return 0; |
| 136 | } |
| 137 | |
| 138 | String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const { |
| 139 | return "" ; |
| 140 | } |
| 141 | |
| 142 | void ResourceImporterLayeredTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { |
| 143 | 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), 1)); |
| 144 | r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/high_quality" ), false)); |
| 145 | r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality" , PROPERTY_HINT_RANGE, "0,1,0.01" ), 0.7)); |
| 146 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression" , PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always" ), 1)); |
| 147 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack" , PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized,Normal Map (RG Channels)" ), 0)); |
| 148 | r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate" ), true)); |
| 149 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit" , PROPERTY_HINT_RANGE, "-1,256" ), -1)); |
| 150 | |
| 151 | if (mode == MODE_2D_ARRAY || mode == MODE_3D) { |
| 152 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/horizontal" , PROPERTY_HINT_RANGE, "1,256,1" ), 8)); |
| 153 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/vertical" , PROPERTY_HINT_RANGE, "1,256,1" ), 8)); |
| 154 | } |
| 155 | if (mode == MODE_CUBEMAP || mode == MODE_CUBEMAP_ARRAY) { |
| 156 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/arrangement" , PROPERTY_HINT_ENUM, "1x6,2x3,3x2,6x1" ), 1)); |
| 157 | if (mode == MODE_CUBEMAP_ARRAY) { |
| 158 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/layout" , PROPERTY_HINT_ENUM, "Horizontal,Vertical" ), 1)); |
| 159 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount" , PROPERTY_HINT_RANGE, "1,1024,1,or_greater" ), 1)); |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2) { |
| 165 | Vector<Ref<Image>> mipmap_images; //for 3D |
| 166 | |
| 167 | if (mode == MODE_3D) { |
| 168 | //3D saves in its own way |
| 169 | |
| 170 | for (int i = 0; i < p_images.size(); i++) { |
| 171 | if (p_images.write[i]->has_mipmaps()) { |
| 172 | p_images.write[i]->clear_mipmaps(); |
| 173 | } |
| 174 | |
| 175 | if (p_force_po2) { |
| 176 | p_images.write[i]->resize_to_po2(); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | if (p_mipmaps) { |
| 181 | Vector<Ref<Image>> parent_images = p_images; |
| 182 | //create 3D mipmaps, this is horrible, though not used very often |
| 183 | int w = p_images[0]->get_width(); |
| 184 | int h = p_images[0]->get_height(); |
| 185 | int d = p_images.size(); |
| 186 | |
| 187 | while (w > 1 || h > 1 || d > 1) { |
| 188 | Vector<Ref<Image>> mipmaps; |
| 189 | int mm_w = MAX(1, w >> 1); |
| 190 | int mm_h = MAX(1, h >> 1); |
| 191 | int mm_d = MAX(1, d >> 1); |
| 192 | |
| 193 | for (int i = 0; i < mm_d; i++) { |
| 194 | Ref<Image> mm = Image::create_empty(mm_w, mm_h, false, p_images[0]->get_format()); |
| 195 | Vector3 pos; |
| 196 | pos.z = float(i) * float(d) / float(mm_d) + 0.5; |
| 197 | for (int x = 0; x < mm_w; x++) { |
| 198 | for (int y = 0; y < mm_h; y++) { |
| 199 | pos.x = float(x) * float(w) / float(mm_w) + 0.5; |
| 200 | pos.y = float(y) * float(h) / float(mm_h) + 0.5; |
| 201 | |
| 202 | Vector3i posi = Vector3i(pos); |
| 203 | Vector3 fract = pos - Vector3(posi); |
| 204 | Vector3i posi_n = posi; |
| 205 | if (posi_n.x < w - 1) { |
| 206 | posi_n.x++; |
| 207 | } |
| 208 | if (posi_n.y < h - 1) { |
| 209 | posi_n.y++; |
| 210 | } |
| 211 | if (posi_n.z < d - 1) { |
| 212 | posi_n.z++; |
| 213 | } |
| 214 | |
| 215 | Color c000 = parent_images[posi.z]->get_pixel(posi.x, posi.y); |
| 216 | Color c100 = parent_images[posi.z]->get_pixel(posi_n.x, posi.y); |
| 217 | Color c010 = parent_images[posi.z]->get_pixel(posi.x, posi_n.y); |
| 218 | Color c110 = parent_images[posi.z]->get_pixel(posi_n.x, posi_n.y); |
| 219 | Color c001 = parent_images[posi_n.z]->get_pixel(posi.x, posi.y); |
| 220 | Color c101 = parent_images[posi_n.z]->get_pixel(posi_n.x, posi.y); |
| 221 | Color c011 = parent_images[posi_n.z]->get_pixel(posi.x, posi_n.y); |
| 222 | Color c111 = parent_images[posi_n.z]->get_pixel(posi_n.x, posi_n.y); |
| 223 | |
| 224 | Color cx00 = c000.lerp(c100, fract.x); |
| 225 | Color cx01 = c001.lerp(c101, fract.x); |
| 226 | Color cx10 = c010.lerp(c110, fract.x); |
| 227 | Color cx11 = c011.lerp(c111, fract.x); |
| 228 | |
| 229 | Color cy0 = cx00.lerp(cx10, fract.y); |
| 230 | Color cy1 = cx01.lerp(cx11, fract.y); |
| 231 | |
| 232 | Color cz = cy0.lerp(cy1, fract.z); |
| 233 | |
| 234 | mm->set_pixel(x, y, cz); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | mipmaps.push_back(mm); |
| 239 | } |
| 240 | |
| 241 | w = mm_w; |
| 242 | h = mm_h; |
| 243 | d = mm_d; |
| 244 | |
| 245 | mipmap_images.append_array(mipmaps); |
| 246 | parent_images = mipmaps; |
| 247 | } |
| 248 | } |
| 249 | } else { |
| 250 | for (int i = 0; i < p_images.size(); i++) { |
| 251 | if (p_force_po2) { |
| 252 | p_images.write[i]->resize_to_po2(); |
| 253 | } |
| 254 | |
| 255 | if (p_mipmaps) { |
| 256 | p_images.write[i]->generate_mipmaps(p_csource == Image::COMPRESS_SOURCE_NORMAL); |
| 257 | } else { |
| 258 | p_images.write[i]->clear_mipmaps(); |
| 259 | } |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | Ref<FileAccess> f = FileAccess::open(p_to_path, FileAccess::WRITE); |
| 264 | f->store_8('G'); |
| 265 | f->store_8('S'); |
| 266 | f->store_8('T'); |
| 267 | f->store_8('L'); |
| 268 | |
| 269 | f->store_32(CompressedTextureLayered::FORMAT_VERSION); |
| 270 | f->store_32(p_images.size()); // For 2d layers or 3d depth. |
| 271 | f->store_32(mode); |
| 272 | f->store_32(0); |
| 273 | |
| 274 | f->store_32(0); |
| 275 | f->store_32(mipmap_images.size()); // Adjust the amount of mipmaps. |
| 276 | f->store_32(0); |
| 277 | f->store_32(0); |
| 278 | |
| 279 | for (int i = 0; i < p_images.size(); i++) { |
| 280 | ResourceImporterTexture::save_to_ctex_format(f, p_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy); |
| 281 | } |
| 282 | |
| 283 | for (int i = 0; i < mipmap_images.size(); i++) { |
| 284 | ResourceImporterTexture::save_to_ctex_format(f, mipmap_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | Error ResourceImporterLayeredTexture::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) { |
| 289 | int compress_mode = p_options["compress/mode" ]; |
| 290 | float lossy = p_options["compress/lossy_quality" ]; |
| 291 | bool high_quality = p_options["compress/high_quality" ]; |
| 292 | int hdr_compression = p_options["compress/hdr_compression" ]; |
| 293 | bool mipmaps = p_options["mipmaps/generate" ]; |
| 294 | |
| 295 | int channel_pack = p_options["compress/channel_pack" ]; |
| 296 | int hslices = (p_options.has("slices/horizontal" )) ? int(p_options["slices/horizontal" ]) : 0; |
| 297 | int vslices = (p_options.has("slices/vertical" )) ? int(p_options["slices/vertical" ]) : 0; |
| 298 | int arrangement = (p_options.has("slices/arrangement" )) ? int(p_options["slices/arrangement" ]) : 0; |
| 299 | int layout = (p_options.has("slices/layout" )) ? int(p_options["slices/layout" ]) : 0; |
| 300 | int amount = (p_options.has("slices/amount" )) ? int(p_options["slices/amount" ]) : 0; |
| 301 | |
| 302 | if (mode == MODE_CUBEMAP || mode == MODE_CUBEMAP_ARRAY) { |
| 303 | switch (arrangement) { |
| 304 | case CUBEMAP_FORMAT_1X6: { |
| 305 | hslices = 1; |
| 306 | vslices = 6; |
| 307 | } break; |
| 308 | case CUBEMAP_FORMAT_2X3: { |
| 309 | hslices = 2; |
| 310 | vslices = 3; |
| 311 | } break; |
| 312 | case CUBEMAP_FORMAT_3X2: { |
| 313 | hslices = 3; |
| 314 | vslices = 2; |
| 315 | } break; |
| 316 | case CUBEMAP_FORMAT_6X1: { |
| 317 | hslices = 6; |
| 318 | vslices = 1; |
| 319 | } break; |
| 320 | } |
| 321 | |
| 322 | if (mode == MODE_CUBEMAP_ARRAY) { |
| 323 | if (layout == 0) { |
| 324 | hslices *= amount; |
| 325 | } else { |
| 326 | vslices *= amount; |
| 327 | } |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | Ref<Image> image; |
| 332 | image.instantiate(); |
| 333 | Error err = ImageLoader::load_image(p_source_file, image); |
| 334 | if (err != OK) { |
| 335 | return err; |
| 336 | } |
| 337 | |
| 338 | if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) { |
| 339 | //basis universal does not support float formats, fall back |
| 340 | compress_mode = COMPRESS_VRAM_COMPRESSED; |
| 341 | } |
| 342 | |
| 343 | if (compress_mode == COMPRESS_VRAM_COMPRESSED) { |
| 344 | mipmaps = true; |
| 345 | |
| 346 | //if using video ram, optimize |
| 347 | if (channel_pack == 0) { |
| 348 | //remove alpha if not needed, so compression is more efficient |
| 349 | if (image->get_format() == Image::FORMAT_RGBA8 && !image->detect_alpha()) { |
| 350 | image->convert(Image::FORMAT_RGB8); |
| 351 | } |
| 352 | } else if (image->get_format() < Image::FORMAT_RGBA8) { |
| 353 | image->optimize_channels(); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC; |
| 358 | if (channel_pack == 0) { |
| 359 | csource = Image::COMPRESS_SOURCE_SRGB; |
| 360 | } else if (channel_pack == 2) { |
| 361 | // force normal |
| 362 | csource = Image::COMPRESS_SOURCE_NORMAL; |
| 363 | } |
| 364 | |
| 365 | Image::UsedChannels used_channels = image->detect_used_channels(csource); |
| 366 | |
| 367 | Vector<Ref<Image>> slices; |
| 368 | |
| 369 | int slice_w = image->get_width() / hslices; |
| 370 | int slice_h = image->get_height() / vslices; |
| 371 | |
| 372 | for (int i = 0; i < vslices; i++) { |
| 373 | for (int j = 0; j < hslices; j++) { |
| 374 | int x = slice_w * j; |
| 375 | int y = slice_h * i; |
| 376 | Ref<Image> slice = image->get_region(Rect2i(x, y, slice_w, slice_h)); |
| 377 | ERR_CONTINUE(slice.is_null() || slice->is_empty()); |
| 378 | if (slice->get_width() != slice_w || slice->get_height() != slice_h) { |
| 379 | slice->resize(slice_w, slice_h); |
| 380 | } |
| 381 | slices.push_back(slice); |
| 382 | } |
| 383 | } |
| 384 | Array formats_imported; |
| 385 | Ref<LayeredTextureImport> texture_import; |
| 386 | texture_import.instantiate(); |
| 387 | texture_import->csource = &csource; |
| 388 | texture_import->save_path = p_save_path; |
| 389 | texture_import->options = p_options; |
| 390 | texture_import->platform_variants = r_platform_variants; |
| 391 | texture_import->image = image; |
| 392 | texture_import->formats_imported = formats_imported; |
| 393 | texture_import->slices = &slices; |
| 394 | texture_import->compress_mode = compress_mode; |
| 395 | texture_import->lossy = lossy; |
| 396 | texture_import->hdr_compression = hdr_compression; |
| 397 | texture_import->mipmaps = mipmaps; |
| 398 | texture_import->used_channels = used_channels; |
| 399 | texture_import->high_quality = high_quality; |
| 400 | |
| 401 | _check_compress_ctex(p_source_file, texture_import); |
| 402 | if (r_metadata) { |
| 403 | Dictionary meta; |
| 404 | meta["vram_texture" ] = compress_mode == COMPRESS_VRAM_COMPRESSED; |
| 405 | if (formats_imported.size()) { |
| 406 | meta["imported_formats" ] = formats_imported; |
| 407 | } |
| 408 | *r_metadata = meta; |
| 409 | } |
| 410 | |
| 411 | return OK; |
| 412 | } |
| 413 | |
| 414 | const char *ResourceImporterLayeredTexture::compression_formats[] = { |
| 415 | "s3tc_bptc" , |
| 416 | "etc2_astc" , |
| 417 | nullptr |
| 418 | }; |
| 419 | |
| 420 | String ResourceImporterLayeredTexture::get_import_settings_string() const { |
| 421 | String s; |
| 422 | |
| 423 | int index = 0; |
| 424 | while (compression_formats[index]) { |
| 425 | String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); |
| 426 | bool test = GLOBAL_GET(setting_path); |
| 427 | if (test) { |
| 428 | s += String(compression_formats[index]); |
| 429 | } |
| 430 | index++; |
| 431 | } |
| 432 | |
| 433 | return s; |
| 434 | } |
| 435 | |
| 436 | bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path) const { |
| 437 | //will become invalid if formats are missing to import |
| 438 | Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path); |
| 439 | |
| 440 | if (!meta.has("vram_texture" )) { |
| 441 | return false; |
| 442 | } |
| 443 | |
| 444 | bool vram = meta["vram_texture" ]; |
| 445 | if (!vram) { |
| 446 | return true; //do not care about non vram |
| 447 | } |
| 448 | |
| 449 | Vector<String> formats_imported; |
| 450 | if (meta.has("imported_formats" )) { |
| 451 | formats_imported = meta["imported_formats" ]; |
| 452 | } |
| 453 | |
| 454 | int index = 0; |
| 455 | bool valid = true; |
| 456 | while (compression_formats[index]) { |
| 457 | String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); |
| 458 | if (ProjectSettings::get_singleton()->has_setting(setting_path)) { |
| 459 | bool test = GLOBAL_GET(setting_path); |
| 460 | if (test) { |
| 461 | if (!formats_imported.has(compression_formats[index])) { |
| 462 | valid = false; |
| 463 | break; |
| 464 | } |
| 465 | } |
| 466 | } else { |
| 467 | WARN_PRINT("Setting for imported format not found: " + setting_path); |
| 468 | } |
| 469 | index++; |
| 470 | } |
| 471 | |
| 472 | return valid; |
| 473 | } |
| 474 | |
| 475 | ResourceImporterLayeredTexture *ResourceImporterLayeredTexture::singleton = nullptr; |
| 476 | |
| 477 | ResourceImporterLayeredTexture::ResourceImporterLayeredTexture(bool p_singleton) { |
| 478 | // This should only be set through the EditorNode. |
| 479 | if (p_singleton) { |
| 480 | singleton = this; |
| 481 | } |
| 482 | |
| 483 | mode = MODE_CUBEMAP; |
| 484 | } |
| 485 | |
| 486 | ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() { |
| 487 | if (singleton == this) { |
| 488 | singleton = nullptr; |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | void ResourceImporterLayeredTexture::_check_compress_ctex(const String &p_source_file, Ref<LayeredTextureImport> r_texture_import) { |
| 493 | String extension = get_save_extension(); |
| 494 | ERR_FAIL_NULL(r_texture_import->csource); |
| 495 | if (r_texture_import->compress_mode != COMPRESS_VRAM_COMPRESSED) { |
| 496 | // Import normally. |
| 497 | _save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, Image::COMPRESS_S3TC /* IGNORED */, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false); |
| 498 | return; |
| 499 | } |
| 500 | // Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). |
| 501 | // Android, GLES 2.x |
| 502 | |
| 503 | const bool can_s3tc_bptc = ResourceImporterTextureSettings::should_import_s3tc_bptc(); |
| 504 | const bool can_etc2_astc = ResourceImporterTextureSettings::should_import_etc2_astc(); |
| 505 | |
| 506 | // Add list of formats imported |
| 507 | if (can_s3tc_bptc) { |
| 508 | r_texture_import->formats_imported.push_back("s3tc_bptc" ); |
| 509 | } |
| 510 | if (can_etc2_astc) { |
| 511 | r_texture_import->formats_imported.push_back("etc2_astc" ); |
| 512 | } |
| 513 | |
| 514 | bool can_compress_hdr = r_texture_import->hdr_compression > 0; |
| 515 | ERR_FAIL_NULL(r_texture_import->image); |
| 516 | bool is_hdr = (r_texture_import->image->get_format() >= Image::FORMAT_RF && r_texture_import->image->get_format() <= Image::FORMAT_RGBE9995); |
| 517 | ERR_FAIL_NULL(r_texture_import->slices); |
| 518 | // Can compress hdr, but hdr with alpha is not compressible. |
| 519 | bool use_uncompressed = false; |
| 520 | |
| 521 | if (is_hdr) { |
| 522 | if (r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA) { |
| 523 | if (r_texture_import->hdr_compression == 2) { |
| 524 | // The user selected to compress hdr anyway, so force an alpha-less format. |
| 525 | if (r_texture_import->image->get_format() == Image::FORMAT_RGBAF) { |
| 526 | for (int i = 0; i < r_texture_import->slices->size(); i++) { |
| 527 | r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBF); |
| 528 | } |
| 529 | |
| 530 | } else if (r_texture_import->image->get_format() == Image::FORMAT_RGBAH) { |
| 531 | for (int i = 0; i < r_texture_import->slices->size(); i++) { |
| 532 | r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBH); |
| 533 | } |
| 534 | } |
| 535 | } else { |
| 536 | can_compress_hdr = false; |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | if (!can_compress_hdr) { |
| 541 | //default to rgbe |
| 542 | if (r_texture_import->image->get_format() != Image::FORMAT_RGBE9995) { |
| 543 | for (int i = 0; i < r_texture_import->slices->size(); i++) { |
| 544 | r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBE9995); |
| 545 | } |
| 546 | } |
| 547 | use_uncompressed = true; |
| 548 | } |
| 549 | } |
| 550 | |
| 551 | if (use_uncompressed) { |
| 552 | _save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + extension, COMPRESS_VRAM_UNCOMPRESSED, r_texture_import->lossy, Image::COMPRESS_S3TC /* IGNORED */, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false); |
| 553 | } else { |
| 554 | if (can_s3tc_bptc) { |
| 555 | Image::CompressMode image_compress_mode; |
| 556 | String image_compress_format; |
| 557 | if (r_texture_import->high_quality || is_hdr) { |
| 558 | image_compress_mode = Image::COMPRESS_BPTC; |
| 559 | image_compress_format = "bptc" ; |
| 560 | } else { |
| 561 | image_compress_mode = Image::COMPRESS_S3TC; |
| 562 | image_compress_format = "s3tc" ; |
| 563 | } |
| 564 | _save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + image_compress_format + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, image_compress_mode, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true); |
| 565 | r_texture_import->platform_variants->push_back(image_compress_format); |
| 566 | } |
| 567 | |
| 568 | if (can_etc2_astc) { |
| 569 | Image::CompressMode image_compress_mode; |
| 570 | String image_compress_format; |
| 571 | if (r_texture_import->high_quality || is_hdr) { |
| 572 | image_compress_mode = Image::COMPRESS_ASTC; |
| 573 | image_compress_format = "astc" ; |
| 574 | } else { |
| 575 | image_compress_mode = Image::COMPRESS_ETC2; |
| 576 | image_compress_format = "etc2" ; |
| 577 | } |
| 578 | _save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + image_compress_format + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, image_compress_mode, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true); |
| 579 | r_texture_import->platform_variants->push_back(image_compress_format); |
| 580 | } |
| 581 | } |
| 582 | } |
| 583 | |