diff --git a/core/image.cpp b/core/image.cpp index 19440d171808..65905c83e858 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -987,8 +987,10 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & int pixsize = get_format_pixel_size(p_format); int pixshift = get_format_pixel_rshift(p_format); int block = get_format_block_size(p_format); - int minw, minh; - get_format_min_pixel_size(p_format, minw, minh); + //technically, you can still compress up to 1 px no matter the format, so commenting this + //int minw, minh; + //get_format_min_pixel_size(p_format, minw, minh); + int minw = 1, minh = 1; while (true) { @@ -1304,7 +1306,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); if (size != p_data.size()) { - ERR_EXPLAIN("Expected data size of " + itos(size) + " in Image::create()"); + ERR_EXPLAIN("Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); ERR_FAIL_COND(p_data.size() != size); } @@ -1592,10 +1594,10 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref((Image *)this)); } -int Image::get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps) { +int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { int mm; - return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps); + return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps ? -1 : 0); } int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format) { @@ -2376,6 +2378,17 @@ Image::DetectChannels Image::get_detected_channels() { return DETECTED_RGBA; } +void Image::optimize_channels() { + switch (get_detected_channels()) { + case DETECTED_L: convert(FORMAT_L8); break; + case DETECTED_LA: convert(FORMAT_LA8); break; + case DETECTED_R: convert(FORMAT_R8); break; + case DETECTED_RG: convert(FORMAT_RG8); break; + case DETECTED_RGB: convert(FORMAT_RGB8); break; + case DETECTED_RGBA: convert(FORMAT_RGBA8); break; + } +} + void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("get_width"), &Image::get_width); diff --git a/core/image.h b/core/image.h index 8c4854e0539f..c8dd647c3124 100644 --- a/core/image.h +++ b/core/image.h @@ -116,7 +116,8 @@ public: enum CompressSource { COMPRESS_SOURCE_GENERIC, COMPRESS_SOURCE_SRGB, - COMPRESS_SOURCE_NORMAL + COMPRESS_SOURCE_NORMAL, + COMPRESS_SOURCE_LAYERED, }; //some functions provided by something else @@ -272,7 +273,7 @@ public: static int get_format_block_size(Format p_format); static void get_format_min_pixel_size(Format p_format, int &r_w, int &r_h); - static int get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps = 0); + static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false); static int get_image_required_mipmaps(int p_width, int p_height, Format p_format); enum CompressMode { @@ -329,6 +330,7 @@ public: }; DetectChannels get_detected_channels(); + void optimize_channels(); Color get_pixelv(const Point2 &p_src) const; Color get_pixel(int p_x, int p_y) const; diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 26c89a03391c..697d357a7d79 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -370,13 +370,17 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ switch (p_type) { case VS::TEXTURE_TYPE_2D: { texture->target = GL_TEXTURE_2D; + texture->images.resize(1); } break; case VS::TEXTURE_TYPE_CUBEMAP: { texture->target = GL_TEXTURE_CUBE_MAP; + texture->images.resize(6); } break; case VS::TEXTURE_TYPE_2D_ARRAY: { + texture->images.resize(p_depth_3d); } break; case VS::TEXTURE_TYPE_3D: { + texture->images.resize(p_depth_3d); } break; } @@ -419,7 +423,7 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref &p bool compressed = false; if (config.keep_original_textures && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { - texture->images[p_layer] = p_image; + texture->images.write[p_layer] = p_image; } Ref img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, format, internal_format, type, compressed); diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index b79e87633ddb..8bc3369dbb5d 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -258,7 +258,7 @@ public: RenderTarget *render_target; - Ref images[6]; + Vector > images; bool redraw_if_visible; diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 83792ca19fed..a5c81d6c4d78 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -622,15 +622,19 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_ switch (p_type) { case VS::TEXTURE_TYPE_2D: { texture->target = GL_TEXTURE_2D; + texture->images.resize(1); } break; case VS::TEXTURE_TYPE_CUBEMAP: { texture->target = GL_TEXTURE_CUBE_MAP; + texture->images.resize(6); } break; case VS::TEXTURE_TYPE_2D_ARRAY: { texture->target = GL_TEXTURE_2D_ARRAY; + texture->images.resize(p_depth_3d); } break; case VS::TEXTURE_TYPE_3D: { texture->target = GL_TEXTURE_3D; + texture->images.resize(p_depth_3d); } break; } @@ -703,7 +707,7 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref &p bool srgb; if (config.keep_original_textures && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { - texture->images[p_layer] = p_image; + texture->images.write[p_layer] = p_image; } Ref img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, format, internal_format, type, compressed, srgb); diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 5b8b524021c8..f55c8026eaf6 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -277,7 +277,7 @@ public: RenderTarget *render_target; - Ref images[6]; + Vector > images; VisualServer::TextureDetectCallback detect_3d; void *detect_3d_ud; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 15e40bfba12e..a4b6cbffb249 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -63,6 +63,7 @@ #include "editor/import/resource_importer_bitmask.h" #include "editor/import/resource_importer_csv_translation.h" #include "editor/import/resource_importer_image.h" +#include "editor/import/resource_importer_layered_texture.h" #include "editor/import/resource_importer_obj.h" #include "editor/import/resource_importer_scene.h" #include "editor/import/resource_importer_texture.h" @@ -4666,6 +4667,16 @@ EditorNode::EditorNode() { import_texture.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_texture); + Ref import_3d; + import_3d.instance(); + import_3d->set_3d(true); + ResourceFormatImporter::get_singleton()->add_importer(import_3d); + + Ref import_array; + import_array.instance(); + import_array->set_3d(false); + ResourceFormatImporter::get_singleton()->add_importer(import_array); + Ref import_image; import_image.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_image); diff --git a/editor/icons/icon_texture_3_d.svg b/editor/icons/icon_texture_3_d.svg new file mode 100644 index 000000000000..dafdc8c68df5 --- /dev/null +++ b/editor/icons/icon_texture_3_d.svg @@ -0,0 +1,75 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/editor/icons/icon_texture_array.svg b/editor/icons/icon_texture_array.svg new file mode 100644 index 000000000000..8297fc0f5d86 --- /dev/null +++ b/editor/icons/icon_texture_array.svg @@ -0,0 +1,77 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp new file mode 100644 index 000000000000..2f958a6fdd21 --- /dev/null +++ b/editor/import/resource_importer_layered_texture.cpp @@ -0,0 +1,274 @@ +#include "resource_importer_layered_texture.h" + +#include "resource_importer_texture.h" + +#include "editor/editor_file_system.h" +#include "editor/editor_node.h" +#include "io/config_file.h" +#include "io/image_loader.h" +#include "scene/resources/texture.h" + +String ResourceImporterLayeredTexture::get_importer_name() const { + + return is_3d ? "texture_3d" : "texture_array"; +} + +String ResourceImporterLayeredTexture::get_visible_name() const { + + return is_3d ? "Texture3D" : "TextureArray"; +} +void ResourceImporterLayeredTexture::get_recognized_extensions(List *p_extensions) const { + + ImageLoader::get_recognized_extensions(p_extensions); +} +String ResourceImporterLayeredTexture::get_save_extension() const { + return is_3d ? "tex3d" : "texarr"; +} + +String ResourceImporterLayeredTexture::get_resource_type() const { + + return is_3d ? "Texture3D" : "TextureArray"; +} + +bool ResourceImporterLayeredTexture::get_option_visibility(const String &p_option, const Map &p_options) const { + + return true; +} + +int ResourceImporterLayeredTexture::get_preset_count() const { + return 3; +} +String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const { + + static const char *preset_names[] = { + "3D", + "2D", + "ColorCorrect" + }; + + return preset_names[p_idx]; +} + +void ResourceImporterLayeredTexture::get_import_options(List *r_options, int p_preset) const { + + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 1 : 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/mipmaps"), p_preset == PRESET_COLOR_CORRECT ? 0 : 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/srgb", PROPERTY_HINT_ENUM, "Disable,Enable"), p_preset == PRESET_3D ? 1 : 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/horizontal", PROPERTY_HINT_RANGE, "1,256,1"), p_preset == PRESET_COLOR_CORRECT ? 16 : 8)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/vertical", PROPERTY_HINT_RANGE, "1,256,1"), p_preset == PRESET_COLOR_CORRECT ? 1 : 8)); +} + +void ResourceImporterLayeredTexture::_save_tex(const Vector > &p_images, const String &p_to_path, int p_compress_mode, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags) { + + FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE); + f->store_8('G'); + f->store_8('D'); + if (is_3d) { + f->store_8('3'); + } else { + f->store_8('A'); + } + f->store_8('T'); //godot streamable texture + + f->store_32(p_images[0]->get_width()); + f->store_32(p_images[0]->get_height()); + f->store_32(p_images.size()); //depth + f->store_32(p_texture_flags); + if (p_compress_mode != COMPRESS_VIDEO_RAM) { + //vram needs to do a first compression to tell what the format is, for the rest its ok + f->store_32(p_images[0]->get_format()); + f->store_32(p_compress_mode); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed + } + + if ((p_compress_mode == COMPRESS_LOSSLESS) && p_images[0]->get_format() > Image::FORMAT_RGBA8) { + p_compress_mode = COMPRESS_UNCOMPRESSED; //these can't go as lossy + } + + for (int i = 0; i < p_images.size(); i++) { + + switch (p_compress_mode) { + case COMPRESS_LOSSLESS: { + + Ref image = p_images[i]->duplicate(); + if (p_mipmaps) { + image->generate_mipmaps(); + } else { + image->clear_mipmaps(); + } + + int mmc = image->get_mipmap_count() + 1; + f->store_32(mmc); + + for (int i = 0; i < mmc; i++) { + + if (i > 0) { + image->shrink_x2(); + } + + PoolVector data = Image::lossless_packer(image); + int data_len = data.size(); + f->store_32(data_len); + + PoolVector::Read r = data.read(); + f->store_buffer(r.ptr(), data_len); + } + + } break; + case COMPRESS_VIDEO_RAM: { + + Ref image = p_images[i]->duplicate(); + image->generate_mipmaps(false); + + Image::CompressSource csource = Image::COMPRESS_SOURCE_LAYERED; + image->compress(p_vram_compression, csource, 0.7); + + if (i == 0) { + //hack so we can properly tell the format + f->store_32(image->get_format()); + f->store_32(p_compress_mode); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed + } + + PoolVector data = image->get_data(); + int dl = data.size(); + + PoolVector::Read r = data.read(); + f->store_buffer(r.ptr(), dl); + } break; + case COMPRESS_UNCOMPRESSED: { + + Ref image = p_images[i]->duplicate(); + + if (p_mipmaps) { + image->generate_mipmaps(); + } else { + image->clear_mipmaps(); + } + + PoolVector data = image->get_data(); + int dl = data.size(); + + PoolVector::Read r = data.read(); + + f->store_buffer(r.ptr(), dl); + + } break; + } + } + + memdelete(f); +} + +Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const Map &p_options, List *r_platform_variants, List *r_gen_files) { + + int compress_mode = p_options["compress/mode"]; + int repeat = p_options["flags/repeat"]; + bool filter = p_options["flags/filter"]; + bool mipmaps = p_options["flags/mipmaps"]; + int srgb = p_options["flags/srgb"]; + int hslices = p_options["slices/horizontal"]; + int vslices = p_options["slices/vertical"]; + + Ref image; + image.instance(); + Error err = ImageLoader::load_image(p_source_file, image, NULL, false, 1.0); + if (err != OK) + return err; + + int tex_flags = 0; + if (repeat > 0) + tex_flags |= Texture::FLAG_REPEAT; + if (repeat == 2) + tex_flags |= Texture::FLAG_MIRRORED_REPEAT; + if (filter) + tex_flags |= Texture::FLAG_FILTER; + if (mipmaps || compress_mode == COMPRESS_VIDEO_RAM) + tex_flags |= Texture::FLAG_MIPMAPS; + if (srgb == 1) + tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR; + + Vector > slices; + + int slice_w = image->get_width() / hslices; + int slice_h = image->get_height() / vslices; + + //optimize + if (compress_mode == COMPRESS_VIDEO_RAM) { + //if using video ram, optimize + if (srgb) { + //remove alpha if not needed, so compression is more efficient + if (image->get_format() == Image::FORMAT_RGBA8 && !image->detect_alpha()) { + image->convert(Image::FORMAT_RGB8); + } + } else { + image->optimize_channels(); + } + } + + for (int i = 0; i < vslices; i++) { + for (int j = 0; j < hslices; j++) { + int x = slice_w * j; + int y = slice_h * i; + Ref slice = image->get_rect(Rect2(x, y, slice_w, slice_h)); + ERR_CONTINUE(slice.is_null() || slice->empty()); + if (slice->get_width() != slice_w || slice->get_height() != slice_h) { + slice->resize(slice_w, slice_h); + } + slices.push_back(slice); + } + } + + String extension = get_save_extension(); + + if (compress_mode == COMPRESS_VIDEO_RAM) { + //must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). + //Android, GLES 2.x + + bool ok_on_pc = false; + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc")) { + + _save_tex(slices, p_save_path + ".s3tc." + extension, compress_mode, Image::COMPRESS_S3TC, mipmaps, tex_flags); + r_platform_variants->push_back("s3tc"); + ok_on_pc = true; + } + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { + + _save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, Image::COMPRESS_ETC2, mipmaps, tex_flags); + r_platform_variants->push_back("etc2"); + } + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) { + _save_tex(slices, p_save_path + ".etc." + extension, compress_mode, Image::COMPRESS_ETC, mipmaps, tex_flags); + r_platform_variants->push_back("etc"); + } + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { + + _save_tex(slices, p_save_path + ".pvrtc." + extension, compress_mode, Image::COMPRESS_PVRTC4, mipmaps, tex_flags); + r_platform_variants->push_back("pvrtc"); + } + + if (!ok_on_pc) { + EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correcly on PC."); + } + } else { + //import normally + _save_tex(slices, p_save_path + "." + extension, compress_mode, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, tex_flags); + } + + return OK; +} + +ResourceImporterLayeredTexture *ResourceImporterLayeredTexture::singleton = NULL; + +ResourceImporterLayeredTexture::ResourceImporterLayeredTexture() { + + singleton = this; + is_3d = true; +} + +ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() { +} diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h new file mode 100644 index 000000000000..ec73b2624db5 --- /dev/null +++ b/editor/import/resource_importer_layered_texture.h @@ -0,0 +1,57 @@ +#ifndef RESOURCE_IMPORTER_LAYERED_TEXTURE_H +#define RESOURCE_IMPORTER_LAYERED_TEXTURE_H + +#include "image.h" +#include "io/resource_import.h" + +class StreamTexture; + +class ResourceImporterLayeredTexture : public ResourceImporter { + GDCLASS(ResourceImporterLayeredTexture, ResourceImporter) + + bool is_3d; + +protected: + static void _texture_reimport_srgb(const Ref &p_tex); + static void _texture_reimport_3d(const Ref &p_tex); + static void _texture_reimport_normal(const Ref &p_tex); + + static ResourceImporterLayeredTexture *singleton; + +public: + static ResourceImporterLayeredTexture *get_singleton() { return singleton; } + virtual String get_importer_name() const; + virtual String get_visible_name() const; + virtual void get_recognized_extensions(List *p_extensions) const; + virtual String get_save_extension() const; + virtual String get_resource_type() const; + + enum Preset { + PRESET_3D, + PRESET_2D, + PRESET_COLOR_CORRECT, + }; + + enum CompressMode { + COMPRESS_LOSSLESS, + COMPRESS_VIDEO_RAM, + COMPRESS_UNCOMPRESSED + }; + + virtual int get_preset_count() const; + virtual String get_preset_name(int p_idx) const; + + virtual void get_import_options(List *r_options, int p_preset = 0) const; + virtual bool get_option_visibility(const String &p_option, const Map &p_options) const; + + void _save_tex(const Vector > &p_images, const String &p_to_path, int p_compress_mode, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags); + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map &p_options, List *r_platform_variants, List *r_gen_files = NULL); + + void update_imports(); + + void set_3d(bool p_3d) { is_3d = p_3d; } + ResourceImporterLayeredTexture(); + ~ResourceImporterLayeredTexture(); +}; +#endif // RESOURCE_IMPORTER_LAYERED_TEXTURE_H diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp index 8a674bc8c1bb..ddfa7af7711c 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_etc.cpp @@ -98,6 +98,33 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f Image::Format img_format = p_img->get_format(); Image::DetectChannels detected_channels = p_img->get_detected_channels(); + if (p_source == Image::COMPRESS_SOURCE_LAYERED) { + //keep what comes in + switch (p_img->get_format()) { + case Image::FORMAT_L8: { + detected_channels = Image::DETECTED_L; + } break; + case Image::FORMAT_LA8: { + detected_channels = Image::DETECTED_LA; + } break; + case Image::FORMAT_R8: { + detected_channels = Image::DETECTED_R; + } break; + case Image::FORMAT_RG8: { + detected_channels = Image::DETECTED_RG; + } break; + case Image::FORMAT_RGB8: { + detected_channels = Image::DETECTED_RGB; + } break; + case Image::FORMAT_RGBA8: + case Image::FORMAT_RGBA4444: + case Image::FORMAT_RGBA5551: { + detected_channels = Image::DETECTED_RGBA; + } break; + default: {} + } + } + if (p_source == Image::COMPRESS_SOURCE_SRGB && (detected_channels == Image::DETECTED_R || detected_channels == Image::DETECTED_RG)) { //R and RG do not support SRGB detected_channels = Image::DETECTED_RGB; @@ -147,7 +174,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f PoolVector::Read r = img->get_data().read(); - int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps() ? -1 : 0); + int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps()); int mmc = 1 + (p_img->has_mipmaps() ? Image::get_image_required_mipmaps(imgw, imgh, etc_format) : 0); PoolVector dst_data; diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp index 0cf24dd8d86e..f6be53741345 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_compress_squish.cpp @@ -46,7 +46,7 @@ void image_decompress_squish(Image *p_image) { Image::Format target_format = Image::FORMAT_RGBA8; PoolVector data; - int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0); + int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps()); int mm_count = p_image->get_mipmap_count(); data.resize(target_size); @@ -96,6 +96,33 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) { Image::DetectChannels dc = p_image->get_detected_channels(); + if (p_source == Image::COMPRESS_SOURCE_LAYERED) { + //keep what comes in + switch (p_image->get_format()) { + case Image::FORMAT_L8: { + dc = Image::DETECTED_L; + } break; + case Image::FORMAT_LA8: { + dc = Image::DETECTED_LA; + } break; + case Image::FORMAT_R8: { + dc = Image::DETECTED_R; + } break; + case Image::FORMAT_RG8: { + dc = Image::DETECTED_RG; + } break; + case Image::FORMAT_RGB8: { + dc = Image::DETECTED_RGB; + } break; + case Image::FORMAT_RGBA8: + case Image::FORMAT_RGBA4444: + case Image::FORMAT_RGBA5551: { + dc = Image::DETECTED_RGBA; + } break; + default: {} + } + } + p_image->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert if (p_source == Image::COMPRESS_SOURCE_SRGB && (dc == Image::DETECTED_R || dc == Image::DETECTED_RG)) { @@ -148,7 +175,7 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) { } PoolVector data; - int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0); + int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps()); int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0; data.resize(target_size); int shift = Image::get_format_pixel_rshift(target_format); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index f681d30ccc0d..382bddfb4e23 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -217,6 +217,7 @@ static ResourceFormatLoaderText *resource_loader_text = NULL; static ResourceFormatLoaderDynamicFont *resource_loader_dynamic_font = NULL; static ResourceFormatLoaderStreamTexture *resource_loader_stream_texture = NULL; +static ResourceFormatLoaderTextureLayered *resource_loader_texture_layered = NULL; static ResourceFormatLoaderBMFont *resource_loader_bmfont = NULL; @@ -237,6 +238,9 @@ void register_scene_types() { resource_loader_stream_texture = memnew(ResourceFormatLoaderStreamTexture); ResourceLoader::add_resource_format_loader(resource_loader_stream_texture); + resource_loader_texture_layered = memnew(ResourceFormatLoaderTextureLayered); + ResourceLoader::add_resource_format_loader(resource_loader_texture_layered); + resource_loader_theme = memnew(ResourceFormatLoaderTheme); ResourceLoader::add_resource_format_loader(resource_loader_theme); @@ -615,7 +619,9 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_virtual_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_virtual_class(); ClassDB::register_class(); @@ -729,6 +735,7 @@ void unregister_scene_types() { memdelete(resource_loader_dynamic_font); memdelete(resource_loader_stream_texture); + memdelete(resource_loader_texture_layered); memdelete(resource_loader_theme); DynamicFont::finish_dynamic_fonts(); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index a5b5375130a3..536c653a0cfb 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -151,13 +151,6 @@ bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const { void ImageTexture::_get_property_list(List *p_list) const { - PropertyHint img_hint = PROPERTY_HINT_NONE; - if (storage == STORAGE_COMPRESS_LOSSY) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSY; - } else if (storage == STORAGE_COMPRESS_LOSSLESS) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS; - } - p_list->push_back(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic,sRGB,Mirrored Repeat")); p_list->push_back(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "")); @@ -592,7 +585,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla int sh = th; int mipmaps = Image::get_image_required_mipmaps(tw, th, format); - int total_size = Image::get_image_data_size(tw, th, format, mipmaps); + int total_size = Image::get_image_data_size(tw, th, format, true); int idx = 0; int ofs = 0; @@ -1155,7 +1148,6 @@ void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile Size2 scale = p_rect.size / size; - RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); for (int i = 0; i < pieces.size(); i++) { // TODO @@ -1170,7 +1162,6 @@ void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons Size2 scale = p_rect.size / p_src_rect.size; - RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); for (int i = 0; i < pieces.size(); i++) { // TODO @@ -1322,13 +1313,6 @@ bool CubeMap::_get(const StringName &p_name, Variant &r_ret) const { void CubeMap::_get_property_list(List *p_list) const { - PropertyHint img_hint = PROPERTY_HINT_NONE; - if (storage == STORAGE_COMPRESS_LOSSY) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSY; - } else if (storage == STORAGE_COMPRESS_LOSSLESS) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS; - } - p_list->push_back(PropertyInfo(Variant::OBJECT, "side/left", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/right", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/bottom", PROPERTY_HINT_RESOURCE_TYPE, "Image")); @@ -1876,112 +1860,9 @@ AnimatedTexture::AnimatedTexture() { AnimatedTexture::~AnimatedTexture() { VS::get_singleton()->free(proxy); } +/////////////////////////////// -bool Texture3D::get_split_single_image_enabled() const { - return split_single_image_enabled; -} - -void Texture3D::set_split_single_image_enabled(bool p_split_enabled) { - split_single_image_enabled = p_split_enabled; - - _change_notify(); -} - -uint32_t Texture3D::get_split_single_image_h_split() const { - return split_single_image_h_split; -} - -void Texture3D::set_split_single_image_h_split(uint32_t p_h_split) { - split_single_image_h_split = p_h_split; - - if (split_single_image_image.is_valid()) - create_from_image(split_single_image_image, split_single_image_h_split, split_single_image_v_split, split_single_image_num_layers, flags); -} - -uint32_t Texture3D::get_split_single_image_v_split() const { - return split_single_image_v_split; -} - -void Texture3D::set_split_single_image_v_split(uint32_t p_v_split) { - split_single_image_v_split = p_v_split; - - if (split_single_image_image.is_valid()) - create_from_image(split_single_image_image, split_single_image_h_split, split_single_image_v_split, split_single_image_num_layers, flags); -} - -uint32_t Texture3D::get_split_single_image_num_layers() const { - return split_single_image_num_layers; -} - -void Texture3D::set_split_single_image_num_layers(uint32_t p_num_layers) { - split_single_image_num_layers = p_num_layers; - - if (split_single_image_image.is_valid()) - create_from_image(split_single_image_image, split_single_image_h_split, split_single_image_v_split, split_single_image_num_layers, flags); -} - -Ref Texture3D::get_split_single_image_image() const { - return split_single_image_image; -} - -void Texture3D::set_split_single_image_image(const Ref &p_image) { - split_single_image_image = p_image; - - create_from_image(split_single_image_image, split_single_image_h_split, split_single_image_v_split, split_single_image_num_layers, flags); -} - -void Texture3D::_validate_property(PropertyInfo &property) const { - if (property.name.begins_with("split_single_image_") && property.name != "split_single_image_enabled" && !split_single_image_enabled) { - property.usage = 0; - } -} - -void Texture3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_flags", "flags"), &Texture3D::set_flags); - ClassDB::bind_method(D_METHOD("get_flags"), &Texture3D::get_flags); - - ClassDB::bind_method(D_METHOD("get_format"), &Texture3D::get_format); - - ClassDB::bind_method(D_METHOD("get_width"), &Texture3D::get_width); - ClassDB::bind_method(D_METHOD("get_height"), &Texture3D::get_height); - ClassDB::bind_method(D_METHOD("get_depth"), &Texture3D::get_depth); - - ClassDB::bind_method(D_METHOD("set_split_single_image_enabled", "split_enabled"), &Texture3D::set_split_single_image_enabled); - ClassDB::bind_method(D_METHOD("get_split_single_image_enabled"), &Texture3D::get_split_single_image_enabled); - - ClassDB::bind_method(D_METHOD("set_split_single_image_h_split", "h_split"), &Texture3D::set_split_single_image_h_split); - ClassDB::bind_method(D_METHOD("get_split_single_image_h_split"), &Texture3D::get_split_single_image_h_split); - - ClassDB::bind_method(D_METHOD("set_split_single_image_v_split", "v_split"), &Texture3D::set_split_single_image_v_split); - ClassDB::bind_method(D_METHOD("get_split_single_image_v_split"), &Texture3D::get_split_single_image_v_split); - - ClassDB::bind_method(D_METHOD("set_split_single_image_num_layers", "num_layers"), &Texture3D::set_split_single_image_num_layers); - ClassDB::bind_method(D_METHOD("get_split_single_image_num_layers"), &Texture3D::get_split_single_image_num_layers); - - ClassDB::bind_method(D_METHOD("set_split_single_image_image", "image"), &Texture3D::set_split_single_image_image); - ClassDB::bind_method(D_METHOD("get_split_single_image_image"), &Texture3D::get_split_single_image_image); - - ClassDB::bind_method(D_METHOD("create", "width", "height", "depth", "format", "flags"), &Texture3D::create, DEFVAL(FLAGS_DEFAULT)); - ClassDB::bind_method(D_METHOD("create_from_image", "image", "h_split", "v_split", "num_layer", "flags"), &Texture3D::create_from_image, DEFVAL(FLAGS_DEFAULT)); - - ClassDB::bind_method(D_METHOD("set_data_partial", "image", "x_offset", "y_offset", "layer", "mipmap"), &Texture3D::set_data_partial, DEFVAL(0)); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter"), "set_flags", "get_flags"); - - BIND_ENUM_CONSTANT(FLAG_MIPMAPS); - BIND_ENUM_CONSTANT(FLAG_REPEAT); - BIND_ENUM_CONSTANT(FLAG_FILTER); - BIND_ENUM_CONSTANT(FLAGS_DEFAULT); - - ADD_GROUP("Split single image", "split_single_image_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "split_single_image_enabled"), "set_split_single_image_enabled", "get_split_single_image_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "split_single_image_h_split", PROPERTY_HINT_RANGE, "0,1000"), "set_split_single_image_h_split", "get_split_single_image_h_split"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "split_single_image_v_split", PROPERTY_HINT_RANGE, "0,1000"), "set_split_single_image_v_split", "get_split_single_image_v_split"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "split_single_image_num_layers"), "set_split_single_image_num_layers", "get_split_single_image_num_layers"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "split_single_image_image", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_split_single_image_image", "get_split_single_image_image"); -} - -void Texture3D::set_flags(uint32_t p_flags) { +void TextureLayered::set_flags(uint32_t p_flags) { flags = p_flags; if (texture.is_valid()) { @@ -1989,28 +1870,71 @@ void Texture3D::set_flags(uint32_t p_flags) { } } -uint32_t Texture3D::get_flags() const { +uint32_t TextureLayered::get_flags() const { return flags; } -Image::Format Texture3D::get_format() const { +Image::Format TextureLayered::get_format() const { return format; } -uint32_t Texture3D::get_width() const { +uint32_t TextureLayered::get_width() const { return width; } -uint32_t Texture3D::get_height() const { +uint32_t TextureLayered::get_height() const { return height; } -uint32_t Texture3D::get_depth() const { +uint32_t TextureLayered::get_depth() const { return depth; } -void Texture3D::create(uint32_t p_width, uint32_t p_height, uint32_t p_depth, Image::Format p_format, uint32_t p_flags) { - VS::get_singleton()->texture_allocate(texture, p_width, p_height, p_depth, p_format, VS::TEXTURE_TYPE_3D, p_flags); +void TextureLayered::_set_data(const Dictionary &p_data) { + ERR_FAIL_COND(!p_data.has("width")); + ERR_FAIL_COND(!p_data.has("height")); + ERR_FAIL_COND(!p_data.has("depth")); + ERR_FAIL_COND(!p_data.has("format")); + ERR_FAIL_COND(!p_data.has("flags")); + ERR_FAIL_COND(!p_data.has("layers")); + int w = p_data["width"]; + int h = p_data["height"]; + int d = p_data["depth"]; + Image::Format format = Image::Format(int(p_data["format"])); + int flags = p_data["flags"]; + Array layers = p_data["layers"]; + ERR_FAIL_COND(layers.size() != d); + + create(w, h, d, format, flags); + + for (int i = 0; i < layers.size(); i++) { + Ref img = layers[i]; + ERR_CONTINUE(!img.is_valid()); + ERR_CONTINUE(img->get_format() != format); + ERR_CONTINUE(img->get_width() != w); + ERR_CONTINUE(img->get_height() != h); + set_layer_data(img, i); + } +} + +Dictionary TextureLayered::_get_data() const { + Dictionary d; + d["width"] = width; + d["height"] = height; + d["depth"] = depth; + d["flags"] = flags; + d["format"] = format; + + Array layers; + for (int i = 0; i < depth; i++) { + layers.push_back(get_layer_data(i)); + } + d["layers"] = layers; + return d; +} + +void TextureLayered::create(uint32_t p_width, uint32_t p_height, uint32_t p_depth, Image::Format p_format, uint32_t p_flags) { + VS::get_singleton()->texture_allocate(texture, p_width, p_height, p_depth, p_format, is_3d ? VS::TEXTURE_TYPE_3D : VS::TEXTURE_TYPE_2D_ARRAY, p_flags); width = p_width; height = p_height; @@ -2019,40 +1943,27 @@ void Texture3D::create(uint32_t p_width, uint32_t p_height, uint32_t p_depth, Im flags = p_flags; } -void Texture3D::create_from_image(const Ref &p_image, uint32_t p_h_split, uint32_t p_v_split, uint32_t p_num_layer, uint32_t flags) { - - ERR_FAIL_COND(p_image.is_null()); - ERR_FAIL_COND((p_h_split + 1) * (p_v_split + 1) < p_num_layer); - - uint32_t total_width = p_image->get_width(); - uint32_t total_height = p_image->get_height(); - - uint32_t width = total_width / (p_h_split + 1); - uint32_t height = total_height / (p_v_split + 1); - - create(width, height, p_num_layer, p_image->get_format(), flags); - - for (uint32_t i = 0; i < p_num_layer; i++) { - uint32_t row = i / (p_v_split + 1); - uint32_t col = i % (p_v_split + 1); - - uint32_t x_offset = col * width; - uint32_t y_offset = row * height; - - VS::get_singleton()->texture_set_data_partial(texture, p_image, x_offset, y_offset, width, height, 0, 0, 0, i); - } -} - -void Texture3D::set_data_partial(const Ref &p_image, int p_x_ofs, int p_y_ofs, int p_layer, int p_mipmap) { +void TextureLayered::set_layer_data(const Ref &p_image, int p_layer) { ERR_FAIL_COND(!texture.is_valid()); - VS::get_singleton()->texture_set_data_partial(texture, p_image, 0, 0, p_image->get_width(), p_image->get_height(), p_x_ofs, p_y_ofs, p_mipmap, p_layer); + VS::get_singleton()->texture_set_data(texture, p_image, p_layer); } -RID Texture3D::get_rid() const { +Ref TextureLayered::get_layer_data(int p_layer) const { + + ERR_FAIL_COND_V(!texture.is_valid(), Ref()); + return VS::get_singleton()->texture_get_data(texture, p_layer); +} + +void TextureLayered::set_data_partial(const Ref &p_image, int p_x_ofs, int p_y_ofs, int p_z, int p_mipmap) { + ERR_FAIL_COND(!texture.is_valid()); + VS::get_singleton()->texture_set_data_partial(texture, p_image, 0, 0, p_image->get_width(), p_image->get_height(), p_x_ofs, p_y_ofs, p_mipmap, p_z); +} + +RID TextureLayered::get_rid() const { return texture; } -void Texture3D::set_path(const String &p_path, bool p_take_over) { +void TextureLayered::set_path(const String &p_path, bool p_take_over) { if (texture.is_valid()) { VS::get_singleton()->texture_set_path(texture, p_path); } @@ -2060,7 +1971,35 @@ void Texture3D::set_path(const String &p_path, bool p_take_over) { Resource::set_path(p_path, p_take_over); } -Texture3D::Texture3D() { +void TextureLayered::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextureLayered::set_flags); + ClassDB::bind_method(D_METHOD("get_flags"), &TextureLayered::get_flags); + + ClassDB::bind_method(D_METHOD("get_format"), &TextureLayered::get_format); + + ClassDB::bind_method(D_METHOD("get_width"), &TextureLayered::get_width); + ClassDB::bind_method(D_METHOD("get_height"), &TextureLayered::get_height); + ClassDB::bind_method(D_METHOD("get_depth"), &TextureLayered::get_depth); + + ClassDB::bind_method(D_METHOD("create", "width", "height", "depth", "format", "flags"), &TextureLayered::create, DEFVAL(FLAGS_DEFAULT)); + ClassDB::bind_method(D_METHOD("set_layer_data", "image", "layer"), &TextureLayered::set_layer_data); + ClassDB::bind_method(D_METHOD("get_layer_data", "layer"), &TextureLayered::set_layer_data); + ClassDB::bind_method(D_METHOD("set_data_partial", "image", "x_offset", "y_offset", "layer", "mipmap"), &TextureLayered::set_data_partial, DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("_set_data", "data"), &TextureLayered::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &TextureLayered::_get_data); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter"), "set_flags", "get_flags"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + + BIND_ENUM_CONSTANT(FLAG_MIPMAPS); + BIND_ENUM_CONSTANT(FLAG_REPEAT); + BIND_ENUM_CONSTANT(FLAG_FILTER); + BIND_ENUM_CONSTANT(FLAGS_DEFAULT); +} + +TextureLayered::TextureLayered(bool p_3d) { + is_3d = p_3d; format = Image::FORMAT_MAX; flags = FLAGS_DEFAULT; @@ -2068,17 +2007,180 @@ Texture3D::Texture3D() { height = 0; depth = 0; - split_single_image_enabled = false; - split_single_image_h_split = 0; - split_single_image_v_split = 0; - split_single_image_num_layers = 0; - split_single_image_image = Ref(); - texture = VS::get_singleton()->texture_create(); } -Texture3D::~Texture3D() { +TextureLayered::~TextureLayered() { if (texture.is_valid()) { VS::get_singleton()->free(texture); } } + +RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error) { + + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + + Ref lt; + Ref tex3d; + Ref texarr; + + if (p_path.ends_with("tex3d")) { + tex3d.instance(); + lt = tex3d; + } else if (p_path.ends_with("texarr")) { + texarr.instance(); + lt = texarr; + } else { + ERR_EXPLAIN("Unrecognized layered texture extension"); + ERR_FAIL_V(RES()); + } + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, RES()); + + uint8_t header[5] = { 0, 0, 0, 0, 0 }; + f->get_buffer(header, 4); + + if (header[0] == 'G' && header[1] == 'D' && header[2] == '3' && header[3] == 'T') { + if (tex3d.is_null()) { + memdelete(f); + ERR_FAIL_COND_V(tex3d.is_null(), RES()) + } + } else if (header[0] == 'G' && header[1] == 'D' && header[2] == 'A' && header[3] == 'T') { + if (texarr.is_null()) { + memdelete(f); + ERR_FAIL_COND_V(texarr.is_null(), RES()) + } + } else { + + ERR_EXPLAIN("Unrecognized layered texture file format: " + String((const char *)header)); + ERR_FAIL_V(RES()); + } + + int tw = f->get_32(); + int th = f->get_32(); + int td = f->get_32(); + int flags = f->get_32(); //texture flags! + Image::Format format = Image::Format(f->get_32()); + uint32_t compression = f->get_32(); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed + + lt->create(tw, th, td, format, flags); + + for (int layer = 0; layer < td; layer++) { + + Ref image; + image.instance(); + + if (compression == COMPRESSION_LOSSLESS) { + //look for a PNG file inside + + int mipmaps = f->get_32(); + Vector > mipmap_images; + + for (int i = 0; i < mipmaps; i++) { + uint32_t size = f->get_32(); + + PoolVector pv; + pv.resize(size); + { + PoolVector::Write w = pv.write(); + f->get_buffer(w.ptr(), size); + } + + Ref img = Image::lossless_unpacker(pv); + + if (img.is_null() || img->empty() || format != img->get_format()) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + memdelete(f); + ERR_FAIL_V(RES()); + } + + mipmap_images.push_back(img); + } + + if (mipmap_images.size() == 1) { + + image = mipmap_images[0]; + + } else { + int total_size = Image::get_image_data_size(tw, th, format, true); + PoolVector img_data; + img_data.resize(total_size); + + { + PoolVector::Write w = img_data.write(); + + int ofs = 0; + for (int i = 0; i < mipmap_images.size(); i++) { + + PoolVector id = mipmap_images[i]->get_data(); + int len = id.size(); + PoolVector::Read r = id.read(); + copymem(&w[ofs], r.ptr(), len); + ofs += len; + } + } + + image->create(tw, th, true, format, img_data); + if (image->empty()) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + memdelete(f); + ERR_FAIL_V(RES()); + } + } + + } else { + + //look for regular format + bool mipmaps = (flags & Texture::FLAG_MIPMAPS); + int total_size = Image::get_image_data_size(tw, th, format, mipmaps); + + PoolVector img_data; + img_data.resize(total_size); + + { + PoolVector::Write w = img_data.write(); + int bytes = f->get_buffer(w.ptr(), total_size); + if (bytes != total_size) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + memdelete(f); + } + ERR_FAIL_V(RES()); + } + } + + image->create(tw, th, mipmaps, format, img_data); + } + + lt->set_layer_data(image, layer); + } + + if (r_error) + *r_error = OK; + + return lt; +} + +void ResourceFormatLoaderTextureLayered::get_recognized_extensions(List *p_extensions) const { + + p_extensions->push_back("tex3d"); + p_extensions->push_back("texarr"); +} +bool ResourceFormatLoaderTextureLayered::handles_type(const String &p_type) const { + return p_type == "Texture3D" || p_type == "TextureArray"; +} +String ResourceFormatLoaderTextureLayered::get_resource_type(const String &p_path) const { + + if (p_path.get_extension().to_lower() == "tex3d") + return "Texture3D"; + if (p_path.get_extension().to_lower() == "texarr") + return "TextureArray"; + return ""; +} diff --git a/scene/resources/texture.h b/scene/resources/texture.h index ebb40ab0cb1c..1c18189b2c04 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -401,39 +401,33 @@ VARIANT_ENUM_CAST(CubeMap::Flags) VARIANT_ENUM_CAST(CubeMap::Side) VARIANT_ENUM_CAST(CubeMap::Storage) -class Texture3D : public Resource { +class TextureLayered : public Resource { - GDCLASS(Texture3D, Resource) - RES_BASE_EXTENSION("tex3d") + GDCLASS(TextureLayered, Resource) public: enum Flags { FLAG_MIPMAPS = VisualServer::TEXTURE_FLAG_MIPMAPS, FLAG_REPEAT = VisualServer::TEXTURE_FLAG_REPEAT, FLAG_FILTER = VisualServer::TEXTURE_FLAG_FILTER, - FLAGS_DEFAULT = FLAG_REPEAT | FLAG_FILTER, + FLAG_CONVERT_TO_LINEAR = VisualServer::TEXTURE_FLAG_CONVERT_TO_LINEAR, + FLAGS_DEFAULT = FLAG_FILTER, }; private: + bool is_3d; RID texture; Image::Format format; uint32_t flags; - uint32_t width; - uint32_t height; - uint32_t depth; + int width; + int height; + int depth; - // for setting an image as a property - bool split_single_image_enabled; - - uint32_t split_single_image_h_split; - uint32_t split_single_image_v_split; - uint32_t split_single_image_num_layers; - Ref split_single_image_image; + void _set_data(const Dictionary &p_data); + Dictionary _get_data() const; protected: - void _validate_property(PropertyInfo &property) const; - static void _bind_methods(); public: @@ -446,30 +440,47 @@ public: uint32_t get_depth() const; void create(uint32_t p_width, uint32_t p_height, uint32_t p_depth, Image::Format p_format, uint32_t p_flags = FLAGS_DEFAULT); - void create_from_image(const Ref &p_image, uint32_t p_h_split, uint32_t p_v_split, uint32_t p_num_layer, uint32_t flags = FLAGS_DEFAULT); - - void set_data_partial(const Ref &p_image, int p_x_ofs, int p_y_ofs, int p_layer, int p_mipmap = 0); + void set_layer_data(const Ref &p_image, int p_layer); + Ref get_layer_data(int p_layer) const; + void set_data_partial(const Ref &p_image, int p_x_ofs, int p_y_ofs, int p_z, int p_mipmap = 0); virtual RID get_rid() const; - - bool get_split_single_image_enabled() const; - void set_split_single_image_enabled(bool p_split_enabled); - - uint32_t get_split_single_image_h_split() const; - void set_split_single_image_h_split(uint32_t p_h_split); - uint32_t get_split_single_image_v_split() const; - void set_split_single_image_v_split(uint32_t p_v_split); - - uint32_t get_split_single_image_num_layers() const; - void set_split_single_image_num_layers(uint32_t p_num_layers); - - Ref get_split_single_image_image() const; - void set_split_single_image_image(const Ref &p_image); - virtual void set_path(const String &p_path, bool p_take_over = false); - Texture3D(); - ~Texture3D(); + TextureLayered(bool p_3d = false); + ~TextureLayered(); +}; + +VARIANT_ENUM_CAST(TextureLayered::Flags) + +class Texture3D : public TextureLayered { + + GDCLASS(Texture3D, TextureLayered) +public: + Texture3D() : + TextureLayered(true) {} +}; + +class TextureArray : public TextureLayered { + + GDCLASS(TextureArray, TextureLayered) +public: + TextureArray() : + TextureLayered(false) {} +}; + +class ResourceFormatLoaderTextureLayered : public ResourceFormatLoader { +public: + enum Compression { + COMPRESSION_LOSSLESS, + COMPRESSION_VRAM, + COMPRESSION_UNCOMPRESSED + }; + + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; }; class CurveTexture : public Texture {