resource_importer_texture.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. /**************************************************************************/
  2. /* resource_importer_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. #include "resource_importer_texture.h"
  31. #include "core/io/config_file.h"
  32. #include "core/io/image_loader.h"
  33. #include "editor/editor_file_system.h"
  34. #include "editor/editor_node.h"
  35. #include "scene/resources/texture.h"
  36. void ResourceImporterTexture::_texture_reimport_srgb(const Ref<StreamTexture> &p_tex) {
  37. singleton->mutex.lock();
  38. StringName path = p_tex->get_path();
  39. if (!singleton->make_flags.has(path)) {
  40. singleton->make_flags[path] = 0;
  41. }
  42. singleton->make_flags[path] |= MAKE_SRGB_FLAG;
  43. singleton->mutex.unlock();
  44. }
  45. void ResourceImporterTexture::_texture_reimport_3d(const Ref<StreamTexture> &p_tex) {
  46. singleton->mutex.lock();
  47. StringName path = p_tex->get_path();
  48. if (!singleton->make_flags.has(path)) {
  49. singleton->make_flags[path] = 0;
  50. }
  51. singleton->make_flags[path] |= MAKE_3D_FLAG;
  52. singleton->mutex.unlock();
  53. }
  54. void ResourceImporterTexture::_texture_reimport_normal(const Ref<StreamTexture> &p_tex) {
  55. singleton->mutex.lock();
  56. StringName path = p_tex->get_path();
  57. if (!singleton->make_flags.has(path)) {
  58. singleton->make_flags[path] = 0;
  59. }
  60. singleton->make_flags[path] |= MAKE_NORMAL_FLAG;
  61. singleton->mutex.unlock();
  62. }
  63. void ResourceImporterTexture::update_imports() {
  64. if (EditorFileSystem::get_singleton()->is_scanning() || EditorFileSystem::get_singleton()->is_importing()) {
  65. return; // do nothing for now
  66. }
  67. mutex.lock();
  68. if (make_flags.empty()) {
  69. mutex.unlock();
  70. return;
  71. }
  72. Vector<String> to_reimport;
  73. for (Map<StringName, int>::Element *E = make_flags.front(); E; E = E->next()) {
  74. Ref<ConfigFile> cf;
  75. cf.instance();
  76. String src_path = String(E->key()) + ".import";
  77. Error err = cf->load(src_path);
  78. ERR_CONTINUE(err != OK);
  79. bool changed = false;
  80. if (E->get() & MAKE_SRGB_FLAG && int(cf->get_value("params", "flags/srgb")) == 2) {
  81. cf->set_value("params", "flags/srgb", 1);
  82. changed = true;
  83. }
  84. if (E->get() & MAKE_NORMAL_FLAG && int(cf->get_value("params", "compress/normal_map")) == 0) {
  85. 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())));
  86. cf->set_value("params", "compress/normal_map", 1);
  87. changed = true;
  88. }
  89. if (E->get() & MAKE_3D_FLAG && bool(cf->get_value("params", "detect_3d"))) {
  90. print_line(vformat(TTR("%s: Texture detected as used in 3D. Enabling filter, repeat, mipmap generation and VRAM texture compression."), String(E->key())));
  91. cf->set_value("params", "detect_3d", false);
  92. cf->set_value("params", "compress/mode", 2);
  93. cf->set_value("params", "flags/repeat", true);
  94. cf->set_value("params", "flags/filter", true);
  95. cf->set_value("params", "flags/mipmaps", true);
  96. changed = true;
  97. }
  98. if (changed) {
  99. cf->save(src_path);
  100. to_reimport.push_back(E->key());
  101. }
  102. }
  103. make_flags.clear();
  104. mutex.unlock();
  105. if (to_reimport.size()) {
  106. EditorFileSystem::get_singleton()->reimport_files(to_reimport);
  107. }
  108. }
  109. String ResourceImporterTexture::get_importer_name() const {
  110. return "texture";
  111. }
  112. String ResourceImporterTexture::get_visible_name() const {
  113. return "Texture";
  114. }
  115. void ResourceImporterTexture::get_recognized_extensions(List<String> *p_extensions) const {
  116. ImageLoader::get_recognized_extensions(p_extensions);
  117. }
  118. String ResourceImporterTexture::get_save_extension() const {
  119. return "stex";
  120. }
  121. String ResourceImporterTexture::get_resource_type() const {
  122. return "StreamTexture";
  123. }
  124. bool ResourceImporterTexture::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
  125. if (p_option == "compress/lossy_quality") {
  126. int compress_mode = int(p_options["compress/mode"]);
  127. if (compress_mode != COMPRESS_LOSSY && compress_mode != COMPRESS_VIDEO_RAM) {
  128. return false;
  129. }
  130. } else if (p_option == "compress/hdr_mode") {
  131. int compress_mode = int(p_options["compress/mode"]);
  132. if (compress_mode != COMPRESS_VIDEO_RAM) {
  133. return false;
  134. }
  135. } else if (p_option == "compress/normal_map") {
  136. int compress_mode = int(p_options["compress/mode"]);
  137. if (compress_mode == COMPRESS_LOSSLESS) {
  138. return false;
  139. }
  140. } else if (p_option == "compress/bptc_ldr") {
  141. int compress_mode = int(p_options["compress/mode"]);
  142. if (compress_mode != COMPRESS_VIDEO_RAM) {
  143. return false;
  144. }
  145. if (!ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) {
  146. return false;
  147. }
  148. }
  149. return true;
  150. }
  151. int ResourceImporterTexture::get_preset_count() const {
  152. return 4;
  153. }
  154. String ResourceImporterTexture::get_preset_name(int p_idx) const {
  155. static const char *preset_names[] = {
  156. TTRC("2D, Detect 3D"),
  157. TTRC("2D"),
  158. TTRC("2D Pixel"),
  159. TTRC("3D"),
  160. };
  161. return TTRGET(preset_names[p_idx]);
  162. }
  163. void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options, int p_preset) const {
  164. r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0));
  165. r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
  166. r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_mode", PROPERTY_HINT_ENUM, "Enabled,Force RGBE"), 0));
  167. r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Enabled,RGBA Only"), 0));
  168. r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0));
  169. r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), p_preset == PRESET_3D ? 1 : 0));
  170. r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), p_preset != PRESET_2D_PIXEL));
  171. r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/mipmaps"), p_preset == PRESET_3D));
  172. r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/anisotropic"), false));
  173. r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/srgb", PROPERTY_HINT_ENUM, "Disable,Enable,Detect"), 2));
  174. r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), p_preset != PRESET_3D));
  175. r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/premult_alpha"), false));
  176. r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/HDR_as_SRGB"), false));
  177. r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/invert_color"), false));
  178. r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/normal_map_invert_y"), false));
  179. r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "stream"), false));
  180. r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0));
  181. r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "detect_3d"), p_preset == PRESET_DETECT));
  182. r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0));
  183. }
  184. void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal, bool p_force_po2_for_compressed) {
  185. FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE);
  186. ERR_FAIL_NULL(f);
  187. f->store_8('G');
  188. f->store_8('D');
  189. f->store_8('S');
  190. f->store_8('T'); //godot streamable texture
  191. bool resize_to_po2 = false;
  192. if (p_compress_mode == COMPRESS_VIDEO_RAM && p_force_po2_for_compressed && (p_mipmaps || p_texture_flags & Texture::FLAG_REPEAT)) {
  193. resize_to_po2 = true;
  194. f->store_16(next_power_of_2(p_image->get_width()));
  195. f->store_16(p_image->get_width());
  196. f->store_16(next_power_of_2(p_image->get_height()));
  197. f->store_16(p_image->get_height());
  198. } else {
  199. f->store_16(p_image->get_width());
  200. f->store_16(0);
  201. f->store_16(p_image->get_height());
  202. f->store_16(0);
  203. }
  204. f->store_32(p_texture_flags);
  205. uint32_t format = 0;
  206. if (p_streamable) {
  207. format |= StreamTexture::FORMAT_BIT_STREAM;
  208. }
  209. if (p_mipmaps) {
  210. format |= StreamTexture::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit
  211. }
  212. if (p_detect_3d) {
  213. format |= StreamTexture::FORMAT_BIT_DETECT_3D;
  214. }
  215. if (p_detect_srgb) {
  216. format |= StreamTexture::FORMAT_BIT_DETECT_SRGB;
  217. }
  218. if (p_detect_normal) {
  219. format |= StreamTexture::FORMAT_BIT_DETECT_NORMAL;
  220. }
  221. if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_image->get_format() > Image::FORMAT_RGBA8) {
  222. p_compress_mode = COMPRESS_UNCOMPRESSED; //these can't go as lossy
  223. }
  224. switch (p_compress_mode) {
  225. case COMPRESS_LOSSLESS: {
  226. bool lossless_force_png = ProjectSettings::get_singleton()->get("rendering/misc/lossless_compression/force_png") ||
  227. !Image::_webp_mem_loader_func; // WebP module disabled.
  228. bool use_webp = !lossless_force_png && p_image->get_width() <= 16383 && p_image->get_height() <= 16383; // WebP has a size limit
  229. Ref<Image> image = p_image->duplicate();
  230. if (p_mipmaps) {
  231. image->generate_mipmaps();
  232. } else {
  233. image->clear_mipmaps();
  234. }
  235. int mmc = image->get_mipmap_count() + 1;
  236. if (use_webp) {
  237. format |= StreamTexture::FORMAT_BIT_WEBP;
  238. } else {
  239. format |= StreamTexture::FORMAT_BIT_PNG;
  240. }
  241. f->store_32(format);
  242. f->store_32(mmc);
  243. for (int i = 0; i < mmc; i++) {
  244. if (i > 0) {
  245. image->shrink_x2();
  246. }
  247. PoolVector<uint8_t> data;
  248. if (use_webp) {
  249. data = Image::webp_lossless_packer(image);
  250. } else {
  251. data = Image::png_packer(image);
  252. }
  253. int data_len = data.size();
  254. f->store_32(data_len);
  255. PoolVector<uint8_t>::Read r = data.read();
  256. f->store_buffer(r.ptr(), data_len);
  257. }
  258. } break;
  259. case COMPRESS_LOSSY: {
  260. Ref<Image> image = p_image->duplicate();
  261. if (p_mipmaps) {
  262. image->generate_mipmaps();
  263. } else {
  264. image->clear_mipmaps();
  265. }
  266. int mmc = image->get_mipmap_count() + 1;
  267. format |= StreamTexture::FORMAT_BIT_WEBP;
  268. f->store_32(format);
  269. f->store_32(mmc);
  270. for (int i = 0; i < mmc; i++) {
  271. if (i > 0) {
  272. image->shrink_x2();
  273. }
  274. PoolVector<uint8_t> data = Image::webp_lossy_packer(image, p_lossy_quality);
  275. int data_len = data.size();
  276. f->store_32(data_len);
  277. PoolVector<uint8_t>::Read r = data.read();
  278. f->store_buffer(r.ptr(), data_len);
  279. }
  280. } break;
  281. case COMPRESS_VIDEO_RAM: {
  282. Ref<Image> image = p_image->duplicate();
  283. if (resize_to_po2) {
  284. image->resize_to_po2();
  285. }
  286. if (p_mipmaps) {
  287. image->generate_mipmaps(p_force_normal);
  288. }
  289. if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) {
  290. image->convert(Image::FORMAT_RGBE9995);
  291. } else {
  292. Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC;
  293. if (p_force_normal) {
  294. csource = Image::COMPRESS_SOURCE_NORMAL;
  295. } else if (p_texture_flags & VS::TEXTURE_FLAG_CONVERT_TO_LINEAR) {
  296. csource = Image::COMPRESS_SOURCE_SRGB;
  297. }
  298. image->compress(p_vram_compression, csource, p_lossy_quality);
  299. }
  300. format |= image->get_format();
  301. f->store_32(format);
  302. PoolVector<uint8_t> data = image->get_data();
  303. int dl = data.size();
  304. PoolVector<uint8_t>::Read r = data.read();
  305. f->store_buffer(r.ptr(), dl);
  306. } break;
  307. case COMPRESS_UNCOMPRESSED: {
  308. Ref<Image> image = p_image->duplicate();
  309. if (p_mipmaps) {
  310. image->generate_mipmaps();
  311. } else {
  312. image->clear_mipmaps();
  313. }
  314. format |= image->get_format();
  315. f->store_32(format);
  316. PoolVector<uint8_t> data = image->get_data();
  317. int dl = data.size();
  318. PoolVector<uint8_t>::Read r = data.read();
  319. f->store_buffer(r.ptr(), dl);
  320. } break;
  321. }
  322. memdelete(f);
  323. }
  324. Error ResourceImporterTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
  325. int compress_mode = p_options["compress/mode"];
  326. float lossy = p_options["compress/lossy_quality"];
  327. int repeat = p_options["flags/repeat"];
  328. bool filter = p_options["flags/filter"];
  329. bool mipmaps = p_options["flags/mipmaps"];
  330. bool anisotropic = p_options["flags/anisotropic"];
  331. int srgb = p_options["flags/srgb"];
  332. bool fix_alpha_border = p_options["process/fix_alpha_border"];
  333. bool premult_alpha = p_options["process/premult_alpha"];
  334. bool invert_color = p_options["process/invert_color"];
  335. bool normal_map_invert_y = p_options["process/normal_map_invert_y"];
  336. bool stream = p_options["stream"];
  337. int size_limit = p_options["size_limit"];
  338. bool hdr_as_srgb = p_options["process/HDR_as_SRGB"];
  339. int normal = p_options["compress/normal_map"];
  340. float scale = p_options["svg/scale"];
  341. bool force_rgbe = p_options["compress/hdr_mode"];
  342. int bptc_ldr = p_options["compress/bptc_ldr"];
  343. Ref<Image> image;
  344. image.instance();
  345. Error err = ImageLoader::load_image(p_source_file, image, nullptr, hdr_as_srgb, scale);
  346. if (err != OK) {
  347. return err;
  348. }
  349. Array formats_imported;
  350. int tex_flags = 0;
  351. if (repeat > 0) {
  352. tex_flags |= Texture::FLAG_REPEAT;
  353. const bool min_gles3 = GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3" &&
  354. !GLOBAL_GET("rendering/quality/driver/fallback_to_gles2");
  355. if (!min_gles3 && !image->is_size_po2()) {
  356. // The project can be run using GLES2. GLES2 does not guarantee that
  357. // repeating textures with a non-power-of-two size will be displayed
  358. // without artifacts (due to upscaling to the nearest power of 2).
  359. if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2")) {
  360. WARN_PRINT(vformat("%s: Imported a repeating texture with a size of %dx%d, but the project is configured to allow falling back to GLES2.\nNon-power-of-2 repeating textures may not display correctly on some platforms such as HTML5. This is because GLES2 does not mandate support for non-power-of-2 repeating textures.",
  361. p_source_file, image->get_width(), image->get_height()));
  362. } else {
  363. WARN_PRINT(vformat("%s: Imported a repeating texture with a size of %dx%d, but the project is configured to use GLES2.\nNon-power-of-2 repeating textures may not display correctly on some platforms such as HTML5. This is because GLES2 does not mandate support for non-power-of-2 repeating textures.",
  364. p_source_file, image->get_width(), image->get_height()));
  365. }
  366. }
  367. }
  368. if (repeat == 2) {
  369. tex_flags |= Texture::FLAG_MIRRORED_REPEAT;
  370. }
  371. if (filter) {
  372. tex_flags |= Texture::FLAG_FILTER;
  373. }
  374. if (mipmaps || compress_mode == COMPRESS_VIDEO_RAM) {
  375. tex_flags |= Texture::FLAG_MIPMAPS;
  376. }
  377. if (anisotropic) {
  378. tex_flags |= Texture::FLAG_ANISOTROPIC_FILTER;
  379. }
  380. if (srgb == 1) {
  381. tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR;
  382. }
  383. if (size_limit > 0 && (image->get_width() > size_limit || image->get_height() > size_limit)) {
  384. //limit size
  385. if (image->get_width() >= image->get_height()) {
  386. int new_width = size_limit;
  387. int new_height = image->get_height() * new_width / image->get_width();
  388. image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
  389. } else {
  390. int new_height = size_limit;
  391. int new_width = image->get_width() * new_height / image->get_height();
  392. image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
  393. }
  394. if (normal == 1) {
  395. image->normalize();
  396. }
  397. }
  398. if (fix_alpha_border) {
  399. image->fix_alpha_edges();
  400. }
  401. if (premult_alpha) {
  402. image->premultiply_alpha();
  403. }
  404. if (invert_color) {
  405. int height = image->get_height();
  406. int width = image->get_width();
  407. image->lock();
  408. for (int i = 0; i < width; i++) {
  409. for (int j = 0; j < height; j++) {
  410. image->set_pixel(i, j, image->get_pixel(i, j).inverted());
  411. }
  412. }
  413. image->unlock();
  414. }
  415. if (normal_map_invert_y) {
  416. // Inverting the green channel can be used to flip a normal map's direction.
  417. // There's no standard when it comes to normal map Y direction, so this is
  418. // sometimes needed when using a normal map exported from another program.
  419. // See <http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates>.
  420. const int height = image->get_height();
  421. const int width = image->get_width();
  422. image->lock();
  423. for (int i = 0; i < width; i++) {
  424. for (int j = 0; j < height; j++) {
  425. const Color color = image->get_pixel(i, j);
  426. image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b));
  427. }
  428. }
  429. image->unlock();
  430. }
  431. bool detect_3d = p_options["detect_3d"];
  432. bool detect_srgb = srgb == 2;
  433. bool detect_normal = normal == 0;
  434. bool force_normal = normal == 1;
  435. if (compress_mode == COMPRESS_VIDEO_RAM) {
  436. //must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
  437. //Android, GLES 2.x
  438. bool ok_on_pc = false;
  439. bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
  440. bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGBA5551);
  441. bool can_bptc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc");
  442. bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc");
  443. if (can_bptc) {
  444. Image::DetectChannels channels = image->get_detected_channels();
  445. if (is_hdr) {
  446. if (channels == Image::DETECTED_LA || channels == Image::DETECTED_RGBA) {
  447. can_bptc = false;
  448. }
  449. } else if (is_ldr) {
  450. //handle "RGBA Only" setting
  451. if (bptc_ldr == 1 && channels != Image::DETECTED_LA && channels != Image::DETECTED_RGBA) {
  452. can_bptc = false;
  453. }
  454. }
  455. formats_imported.push_back("bptc");
  456. }
  457. if (!can_bptc && is_hdr && !force_rgbe) {
  458. //convert to ldr if this can't be stored hdr
  459. image->convert(Image::FORMAT_RGBA8);
  460. }
  461. if (can_bptc || can_s3tc) {
  462. _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, false);
  463. r_platform_variants->push_back("s3tc");
  464. formats_imported.push_back("s3tc");
  465. ok_on_pc = true;
  466. }
  467. if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) {
  468. _save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, true);
  469. r_platform_variants->push_back("etc2");
  470. formats_imported.push_back("etc2");
  471. }
  472. if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) {
  473. _save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, true);
  474. r_platform_variants->push_back("etc");
  475. formats_imported.push_back("etc");
  476. }
  477. if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) {
  478. _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, true);
  479. r_platform_variants->push_back("pvrtc");
  480. formats_imported.push_back("pvrtc");
  481. }
  482. if (!ok_on_pc) {
  483. EditorNode::add_io_error(TTR("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC."));
  484. }
  485. } else {
  486. //import normally
  487. _save_stex(image, p_save_path + ".stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, false);
  488. }
  489. if (r_metadata) {
  490. Dictionary metadata;
  491. metadata["vram_texture"] = compress_mode == COMPRESS_VIDEO_RAM;
  492. if (formats_imported.size()) {
  493. metadata["imported_formats"] = formats_imported;
  494. }
  495. *r_metadata = metadata;
  496. }
  497. return OK;
  498. }
  499. const char *ResourceImporterTexture::compression_formats[] = {
  500. "bptc",
  501. "s3tc",
  502. "etc",
  503. "etc2",
  504. "pvrtc",
  505. nullptr
  506. };
  507. String ResourceImporterTexture::get_import_settings_string() const {
  508. String s;
  509. int index = 0;
  510. while (compression_formats[index]) {
  511. String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]);
  512. bool test = ProjectSettings::get_singleton()->get(setting_path);
  513. if (test) {
  514. s += String(compression_formats[index]);
  515. }
  516. index++;
  517. }
  518. return s;
  519. }
  520. bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) const {
  521. //will become invalid if formats are missing to import
  522. Dictionary metadata = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
  523. if (!metadata.has("vram_texture")) {
  524. return false;
  525. }
  526. bool vram = metadata["vram_texture"];
  527. if (!vram) {
  528. return true; //do not care about non vram
  529. }
  530. Vector<String> formats_imported;
  531. if (metadata.has("imported_formats")) {
  532. formats_imported = metadata["imported_formats"];
  533. }
  534. int index = 0;
  535. bool valid = true;
  536. while (compression_formats[index]) {
  537. String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]);
  538. bool test = ProjectSettings::get_singleton()->get(setting_path);
  539. if (test) {
  540. if (formats_imported.find(compression_formats[index]) == -1) {
  541. valid = false;
  542. break;
  543. }
  544. }
  545. index++;
  546. }
  547. return valid;
  548. }
  549. ResourceImporterTexture *ResourceImporterTexture::singleton = nullptr;
  550. ResourceImporterTexture::ResourceImporterTexture() {
  551. singleton = this;
  552. StreamTexture::request_3d_callback = _texture_reimport_3d;
  553. StreamTexture::request_srgb_callback = _texture_reimport_srgb;
  554. StreamTexture::request_normal_callback = _texture_reimport_normal;
  555. }