From a674da4eecd25694ff71d30370e64a6a2d8799d8 Mon Sep 17 00:00:00 2001 From: reduz Date: Wed, 9 Sep 2020 12:40:51 -0300 Subject: [PATCH] Implement 3D textures as import and resource format. --- core/image.cpp | 76 +++++ core/image.h | 13 + editor/editor_node.cpp | 6 +- .../resource_importer_layered_texture.cpp | 115 ++++++- .../resource_importer_layered_texture.h | 4 - editor/plugins/texture_3d_editor_plugin.cpp | 213 ++++++++++++ editor/plugins/texture_3d_editor_plugin.h | 93 ++++++ scene/register_scene_types.cpp | 10 + scene/resources/texture.cpp | 304 ++++++++++++++++++ scene/resources/texture.h | 116 +++++++ servers/rendering/rasterizer.h | 6 +- .../rasterizer_rd/rasterizer_storage_rd.cpp | 218 ++++++++++++- .../rasterizer_rd/rasterizer_storage_rd.h | 14 +- servers/rendering/rendering_server_raster.h | 14 +- servers/rendering/rendering_server_wrap_mt.h | 6 +- servers/rendering_server.h | 6 +- 16 files changed, 1174 insertions(+), 40 deletions(-) create mode 100644 editor/plugins/texture_3d_editor_plugin.cpp create mode 100644 editor/plugins/texture_3d_editor_plugin.h diff --git a/core/image.cpp b/core/image.cpp index e2f353698fe..b8a443eed2d 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -363,6 +363,82 @@ void Image::get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int r_size = ofs2 - ofs; } +Image::Image3DValidateError Image::validate_3d_image(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_images) { + int w = p_width; + int h = p_height; + int d = p_depth; + + int arr_ofs = 0; + + while (true) { + for (int i = 0; i < d; i++) { + int idx = i + arr_ofs; + if (idx >= p_images.size()) { + return VALIDATE_3D_ERR_MISSING_IMAGES; + } + if (p_images[idx].is_null() || p_images[idx]->empty()) { + return VALIDATE_3D_ERR_IMAGE_EMPTY; + } + if (p_images[idx]->get_format() != p_format) { + return VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH; + } + if (p_images[idx]->get_width() != w || p_images[idx]->get_height() != h) { + return VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH; + } + if (p_images[idx]->has_mipmaps()) { + return VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS; + } + } + + arr_ofs += d; + + if (!p_mipmaps) { + break; + } + + if (w == 1 && h == 1 && d == 1) { + break; + } + + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + d = MAX(1, d >> 1); + } + + if (arr_ofs != p_images.size()) { + return VALIDATE_3D_ERR_EXTRA_IMAGES; + } + + return VALIDATE_3D_OK; +} + +String Image::get_3d_image_validation_error_text(Image3DValidateError p_error) { + switch (p_error) { + case VALIDATE_3D_OK: { + return TTR("Ok"); + } break; + case VALIDATE_3D_ERR_IMAGE_EMPTY: { + return TTR("Empty Image found"); + } break; + case VALIDATE_3D_ERR_MISSING_IMAGES: { + return TTR("Missing Images"); + } break; + case VALIDATE_3D_ERR_EXTRA_IMAGES: { + return TTR("Too many Images"); + } break; + case VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH: { + return TTR("Image size mismatch"); + } break; + case VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH: { + return TTR("Image format mismatch"); + } break; + case VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS: { + return TTR("Image has included mipmaps"); + } break; + } + return String(); +} + int Image::get_width() const { return width; } diff --git a/core/image.h b/core/image.h index d2572b072ef..06794c7fed5 100644 --- a/core/image.h +++ b/core/image.h @@ -230,6 +230,19 @@ public: void get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const; //get where the mipmap begins in data void get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int &r_size, int &w, int &h) const; //get where the mipmap begins in data + enum Image3DValidateError { + VALIDATE_3D_OK, + VALIDATE_3D_ERR_IMAGE_EMPTY, + VALIDATE_3D_ERR_MISSING_IMAGES, + VALIDATE_3D_ERR_EXTRA_IMAGES, + VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH, + VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH, + VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS, + }; + + static Image3DValidateError validate_3d_image(Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_images); + static String get_3d_image_validation_error_text(Image3DValidateError p_error); + /** * Resize the image, using the preferred interpolation method. */ diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index e90f30496cf..6c830b8074f 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -157,6 +157,7 @@ #include "editor/plugins/sprite_frames_editor_plugin.h" #include "editor/plugins/style_box_editor_plugin.h" #include "editor/plugins/text_editor.h" +#include "editor/plugins/texture_3d_editor_plugin.h" #include "editor/plugins/texture_editor_plugin.h" #include "editor/plugins/texture_layered_editor_plugin.h" #include "editor/plugins/texture_region_editor_plugin.h" @@ -5620,10 +5621,10 @@ EditorNode::EditorNode() { import_cubemap_array->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP_ARRAY); ResourceFormatImporter::get_singleton()->add_importer(import_cubemap_array); - /*Ref import_3d; + Ref import_3d; import_3d.instance(); import_3d->set_mode(ResourceImporterLayeredTexture::MODE_3D); - ResourceFormatImporter::get_singleton()->add_importer(import_3d);*/ + ResourceFormatImporter::get_singleton()->add_importer(import_3d); Ref import_image; import_image.instance(); @@ -6622,6 +6623,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(CurveEditorPlugin(this))); add_editor_plugin(memnew(TextureEditorPlugin(this))); add_editor_plugin(memnew(TextureLayeredEditorPlugin(this))); + add_editor_plugin(memnew(Texture3DEditorPlugin(this))); add_editor_plugin(memnew(AudioStreamEditorPlugin(this))); add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); add_editor_plugin(memnew(Skeleton3DEditorPlugin(this))); diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index f954931cee0..bbf62596d0b 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -70,7 +70,7 @@ String ResourceImporterLayeredTexture::get_visible_name() const { return "CubemapArray"; } break; case MODE_3D: { - return "3D"; + return "Texture3D"; } break; } @@ -156,15 +156,103 @@ void ResourceImporterLayeredTexture::get_import_options(List *r_op } void ResourceImporterLayeredTexture::_save_tex(Vector> 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) { - for (int i = 0; i < p_images.size(); i++) { - if (p_force_po2) { - p_images.write[i]->resize_to_po2(); + Vector> mipmap_images; //for 3D + + if (mode == MODE_3D) { + //3D saves in its own way + + for (int i = 0; i < p_images.size(); i++) { + if (p_images.write[i]->has_mipmaps()) { + p_images.write[i]->clear_mipmaps(); + } + + if (p_force_po2) { + p_images.write[i]->resize_to_po2(); + } } if (p_mipmaps) { - p_images.write[i]->generate_mipmaps(); - } else { - p_images.write[i]->clear_mipmaps(); + Vector> parent_images = p_images; + //create 3D mipmaps, this is horrible, though not used very often + int w = p_images[0]->get_width(); + int h = p_images[0]->get_height(); + int d = p_images.size(); + + while (w > 1 || h > 1 || d > 1) { + Vector> mipmaps; + int mm_w = MAX(1, w >> 1); + int mm_h = MAX(1, h >> 1); + int mm_d = MAX(1, d >> 1); + + for (int i = 0; i < mm_d; i++) { + Ref mm; + mm.instance(); + mm->create(mm_w, mm_h, false, p_images[0]->get_format()); + Vector3 pos; + pos.z = float(i) * float(d) / float(mm_d) + 0.5; + for (int x = 0; x < mm_w; x++) { + for (int y = 0; y < mm_h; y++) { + pos.x = float(x) * float(w) / float(mm_w) + 0.5; + pos.y = float(y) * float(h) / float(mm_h) + 0.5; + + Vector3i posi = Vector3i(pos); + Vector3 fract = pos - Vector3(posi); + Vector3i posi_n = posi; + if (posi_n.x < w - 1) { + posi_n.x++; + } + if (posi_n.y < h - 1) { + posi_n.y++; + } + if (posi_n.z < d - 1) { + posi_n.z++; + } + + Color c000 = parent_images[posi.z]->get_pixel(posi.x, posi.y); + Color c100 = parent_images[posi.z]->get_pixel(posi_n.x, posi.y); + Color c010 = parent_images[posi.z]->get_pixel(posi.x, posi_n.y); + Color c110 = parent_images[posi.z]->get_pixel(posi_n.x, posi_n.y); + Color c001 = parent_images[posi_n.z]->get_pixel(posi.x, posi.y); + Color c101 = parent_images[posi_n.z]->get_pixel(posi_n.x, posi.y); + Color c011 = parent_images[posi_n.z]->get_pixel(posi.x, posi_n.y); + Color c111 = parent_images[posi_n.z]->get_pixel(posi_n.x, posi_n.y); + + Color cx00 = c000.lerp(c100, fract.x); + Color cx01 = c001.lerp(c101, fract.x); + Color cx10 = c010.lerp(c110, fract.x); + Color cx11 = c011.lerp(c111, fract.x); + + Color cy0 = cx00.lerp(cx10, fract.y); + Color cy1 = cx01.lerp(cx11, fract.y); + + Color cz = cy0.lerp(cy1, fract.z); + + mm->set_pixel(x, y, cz); + } + } + + mipmaps.push_back(mm); + } + + w = mm_w; + h = mm_h; + d = mm_d; + + mipmap_images.append_array(mipmaps); + parent_images = mipmaps; + } + } + } else { + for (int i = 0; i < p_images.size(); i++) { + if (p_force_po2) { + p_images.write[i]->resize_to_po2(); + } + + if (p_mipmaps) { + p_images.write[i]->generate_mipmaps(); + } else { + p_images.write[i]->clear_mipmaps(); + } } } @@ -175,13 +263,12 @@ void ResourceImporterLayeredTexture::_save_tex(Vector> p_images, cons f->store_8('L'); f->store_32(StreamTextureLayered::FORMAT_VERSION); - f->store_32(p_images.size()); + f->store_32(p_images.size()); //2d layers or 3d depth f->store_32(mode); - f->store_32(0); //dataformat - f->store_32(0); //mipmap limit - - //reserved f->store_32(0); + + f->store_32(0); + f->store_32(mipmap_images.size()); // amount of mipmaps f->store_32(0); f->store_32(0); @@ -189,6 +276,10 @@ void ResourceImporterLayeredTexture::_save_tex(Vector> p_images, cons ResourceImporterTexture::save_to_stex_format(f, p_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy); } + for (int i = 0; i < mipmap_images.size(); i++) { + ResourceImporterTexture::save_to_stex_format(f, mipmap_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy); + } + f->close(); } diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h index 2d50889e9ed..b54923be00c 100644 --- a/editor/import/resource_importer_layered_texture.h +++ b/editor/import/resource_importer_layered_texture.h @@ -93,10 +93,6 @@ private: static const char *compression_formats[]; 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: diff --git a/editor/plugins/texture_3d_editor_plugin.cpp b/editor/plugins/texture_3d_editor_plugin.cpp new file mode 100644 index 00000000000..ba2eef84843 --- /dev/null +++ b/editor/plugins/texture_3d_editor_plugin.cpp @@ -0,0 +1,213 @@ +/*************************************************************************/ +/* texture_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "texture_3d_editor_plugin.h" + +#include "core/io/resource_loader.h" +#include "core/project_settings.h" +#include "editor/editor_settings.h" + +void Texture3DEditor::_gui_input(Ref p_event) { +} + +void Texture3DEditor::_texture_rect_draw() { + texture_rect->draw_rect(Rect2(Point2(), texture_rect->get_size()), Color(1, 1, 1, 1)); +} + +void Texture3DEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_READY) { + //get_scene()->connect("node_removed",this,"_node_removed"); + } + if (p_what == NOTIFICATION_RESIZED) { + _texture_rect_update_area(); + } + + if (p_what == NOTIFICATION_DRAW) { + Ref checkerboard = get_theme_icon("Checkerboard", "EditorIcons"); + Size2 size = get_size(); + + draw_texture_rect(checkerboard, Rect2(Point2(), size), true); + } +} + +void Texture3DEditor::_changed_callback(Object *p_changed, const char *p_prop) { + if (!is_visible()) { + return; + } + update(); +} + +void Texture3DEditor::_update_material() { + material->set_shader_param("layer", (layer->get_value() + 0.5) / texture->get_depth()); + material->set_shader_param("tex", texture->get_rid()); + + String format = Image::get_format_name(texture->get_format()); + + String text; + text = itos(texture->get_width()) + "x" + itos(texture->get_height()) + "x" + itos(texture->get_depth()) + " " + format; + + info->set_text(text); +} + +void Texture3DEditor::_make_shaders() { + String shader_3d = "" + "shader_type canvas_item;\n" + "uniform sampler3D tex;\n" + "uniform float layer;\n" + "void fragment() {\n" + " COLOR = textureLod(tex,vec3(UV,layer),0.0);\n" + "}"; + + shader.instance(); + shader->set_code(shader_3d); + material.instance(); + material->set_shader(shader); +} + +void Texture3DEditor::_texture_rect_update_area() { + Size2 size = get_size(); + int tex_width = texture->get_width() * size.height / texture->get_height(); + int tex_height = size.height; + + if (tex_width > size.width) { + tex_width = size.width; + tex_height = texture->get_height() * tex_width / texture->get_width(); + } + + // Prevent the texture from being unpreviewable after the rescale, so that we can still see something + if (tex_height <= 0) { + tex_height = 1; + } + if (tex_width <= 0) { + tex_width = 1; + } + + int ofs_x = (size.width - tex_width) / 2; + int ofs_y = (size.height - tex_height) / 2; + + texture_rect->set_position(Vector2(ofs_x, ofs_y)); + texture_rect->set_size(Vector2(tex_width, tex_height)); +} + +void Texture3DEditor::edit(Ref p_texture) { + if (!texture.is_null()) { + texture->remove_change_receptor(this); + } + + texture = p_texture; + + if (!texture.is_null()) { + if (shader.is_null()) { + _make_shaders(); + } + + texture->add_change_receptor(this); + update(); + texture_rect->set_material(material); + setting = true; + layer->set_max(texture->get_depth() - 1); + layer->set_value(0); + layer->show(); + _update_material(); + setting = false; + _texture_rect_update_area(); + } else { + hide(); + } +} + +void Texture3DEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("_gui_input"), &Texture3DEditor::_gui_input); + ClassDB::bind_method(D_METHOD("_layer_changed"), &Texture3DEditor::_layer_changed); +} + +Texture3DEditor::Texture3DEditor() { + set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); + set_custom_minimum_size(Size2(1, 150)); + texture_rect = memnew(Control); + texture_rect->connect("draw", callable_mp(this, &Texture3DEditor::_texture_rect_draw)); + texture_rect->set_mouse_filter(MOUSE_FILTER_IGNORE); + add_child(texture_rect); + + layer = memnew(SpinBox); + layer->set_step(1); + layer->set_max(100); + add_child(layer); + layer->set_anchor(MARGIN_RIGHT, 1); + layer->set_anchor(MARGIN_LEFT, 1); + layer->set_h_grow_direction(GROW_DIRECTION_BEGIN); + layer->set_modulate(Color(1, 1, 1, 0.8)); + info = memnew(Label); + add_child(info); + info->set_anchor(MARGIN_RIGHT, 1); + info->set_anchor(MARGIN_LEFT, 1); + info->set_anchor(MARGIN_BOTTOM, 1); + info->set_anchor(MARGIN_TOP, 1); + info->set_h_grow_direction(GROW_DIRECTION_BEGIN); + info->set_v_grow_direction(GROW_DIRECTION_BEGIN); + info->add_theme_color_override("font_color", Color(1, 1, 1, 1)); + info->add_theme_color_override("font_color_shadow", Color(0, 0, 0, 0.5)); + info->add_theme_color_override("font_color_shadow", Color(0, 0, 0, 0.5)); + info->add_theme_constant_override("shadow_as_outline", 1); + info->add_theme_constant_override("shadow_offset_x", 2); + info->add_theme_constant_override("shadow_offset_y", 2); + + setting = false; + layer->connect("value_changed", Callable(this, "_layer_changed")); +} + +Texture3DEditor::~Texture3DEditor() { + if (!texture.is_null()) { + texture->remove_change_receptor(this); + } +} + +// +bool EditorInspectorPlugin3DTexture::can_handle(Object *p_object) { + return Object::cast_to(p_object) != nullptr; +} + +void EditorInspectorPlugin3DTexture::parse_begin(Object *p_object) { + Texture3D *texture = Object::cast_to(p_object); + if (!texture) { + return; + } + Ref m(texture); + + Texture3DEditor *editor = memnew(Texture3DEditor); + editor->edit(m); + add_custom_control(editor); +} + +Texture3DEditorPlugin::Texture3DEditorPlugin(EditorNode *p_node) { + Ref plugin; + plugin.instance(); + add_inspector_plugin(plugin); +} diff --git a/editor/plugins/texture_3d_editor_plugin.h b/editor/plugins/texture_3d_editor_plugin.h new file mode 100644 index 00000000000..4fbf47ecfef --- /dev/null +++ b/editor/plugins/texture_3d_editor_plugin.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/* texture_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEXTURE_3D_EDITOR_PLUGIN_H +#define TEXTURE_3D_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/resources/shader.h" +#include "scene/resources/texture.h" + +class Texture3DEditor : public Control { + GDCLASS(Texture3DEditor, Control); + + SpinBox *layer; + Label *info; + Ref texture; + + Ref shader; + Ref material; + + Control *texture_rect; + + void _make_shaders(); + + void _update_material(); + bool setting; + void _layer_changed(double) { + if (!setting) { + _update_material(); + } + } + + void _texture_rect_update_area(); + void _texture_rect_draw(); + +protected: + void _notification(int p_what); + void _gui_input(Ref p_event); + void _changed_callback(Object *p_changed, const char *p_prop) override; + static void _bind_methods(); + +public: + void edit(Ref p_texture); + Texture3DEditor(); + ~Texture3DEditor(); +}; + +class EditorInspectorPlugin3DTexture : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPlugin3DTexture, EditorInspectorPlugin); + +public: + virtual bool can_handle(Object *p_object) override; + virtual void parse_begin(Object *p_object) override; +}; + +class Texture3DEditorPlugin : public EditorPlugin { + GDCLASS(Texture3DEditorPlugin, EditorPlugin); + +public: + virtual String get_name() const override { return "Texture3D"; } + + Texture3DEditorPlugin(EditorNode *p_node); +}; + +#endif // TEXTURE_EDITOR_PLUGIN_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 0f6475cf0d2..af900a22dbb 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -230,6 +230,7 @@ static Ref resource_loader_dynamic_font; static Ref resource_loader_stream_texture; static Ref resource_loader_texture_layered; +static Ref resource_loader_texture_3d; static Ref resource_loader_bmfont; @@ -252,6 +253,9 @@ void register_scene_types() { resource_loader_texture_layered.instance(); ResourceLoader::add_resource_format_loader(resource_loader_texture_layered); + resource_loader_texture_3d.instance(); + ResourceLoader::add_resource_format_loader(resource_loader_texture_3d); + resource_saver_text.instance(); ResourceSaver::add_resource_format_saver(resource_saver_text, true); @@ -701,6 +705,9 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_virtual_class(); ClassDB::register_virtual_class(); + ClassDB::register_virtual_class(); + ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -946,6 +953,9 @@ void unregister_scene_types() { ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered); resource_loader_texture_layered.unref(); + ResourceLoader::remove_resource_format_loader(resource_loader_texture_3d); + resource_loader_texture_3d.unref(); + ResourceLoader::remove_resource_format_loader(resource_loader_stream_texture); resource_loader_stream_texture.unref(); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 5681613c04c..39237e1a33d 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -791,8 +791,312 @@ String ResourceFormatLoaderStreamTexture2D::get_resource_type(const String &p_pa return ""; } +//////////////////////////////////// + +TypedArray Texture3D::_get_data() const { + Vector> data = get_data(); + + TypedArray ret; + ret.resize(data.size()); + for (int i = 0; i < data.size(); i++) { + ret[i] = data[i]; + } + return ret; +} + +void Texture3D::_bind_methods() { + 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("has_mipmaps"), &Texture3D::has_mipmaps); + ClassDB::bind_method(D_METHOD("get_data"), &Texture3D::_get_data); +} ////////////////////////////////////////// +Image::Format ImageTexture3D::get_format() const { + return format; +} +int ImageTexture3D::get_width() const { + return width; +} +int ImageTexture3D::get_height() const { + return height; +} +int ImageTexture3D::get_depth() const { + return depth; +} +bool ImageTexture3D::has_mipmaps() const { + return mipmaps; +} + +Error ImageTexture3D::_create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const TypedArray &p_data) { + Vector> images; + images.resize(p_data.size()); + for (int i = 0; i < images.size(); i++) { + images.write[i] = p_data[i]; + } + return create(p_format, p_width, p_height, p_depth, p_mipmaps, images); +} + +void ImageTexture3D::_update(const TypedArray &p_data) { + Vector> images; + images.resize(p_data.size()); + for (int i = 0; i < images.size(); i++) { + images.write[i] = p_data[i]; + } + return update(images); +} + +Error ImageTexture3D::create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_data) { + RID tex = RenderingServer::get_singleton()->texture_3d_create(p_format, p_width, p_height, p_depth, p_mipmaps, p_data); + ERR_FAIL_COND_V(tex.is_null(), ERR_CANT_CREATE); + + if (texture.is_valid()) { + RenderingServer::get_singleton()->texture_replace(texture, tex); + } + + return OK; +} + +void ImageTexture3D::update(const Vector> &p_data) { + ERR_FAIL_COND(!texture.is_valid()); + RenderingServer::get_singleton()->texture_3d_update(texture, p_data); +} + +Vector> ImageTexture3D::get_data() const { + ERR_FAIL_COND_V(!texture.is_valid(), Vector>()); + return RS::get_singleton()->texture_3d_get(texture); +} + +RID ImageTexture3D::get_rid() const { + if (!texture.is_valid()) { + texture = RS::get_singleton()->texture_3d_placeholder_create(); + } + return texture; +} +void ImageTexture3D::set_path(const String &p_path, bool p_take_over) { + if (texture.is_valid()) { + RenderingServer::get_singleton()->texture_set_path(texture, p_path); + } + + Resource::set_path(p_path, p_take_over); +} + +void ImageTexture3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("create", "format", "width", "height", "depth", "use_mipmaps", "data"), &ImageTexture3D::_create); + ClassDB::bind_method(D_METHOD("update", "data"), &ImageTexture3D::_update); +} + +ImageTexture3D::ImageTexture3D() { +} + +ImageTexture3D::~ImageTexture3D() { + if (texture.is_valid()) { + RS::get_singleton()->free(texture); + } +} + +//////////////////////////////////////////// + +void StreamTexture3D::set_path(const String &p_path, bool p_take_over) { + if (texture.is_valid()) { + RenderingServer::get_singleton()->texture_set_path(texture, p_path); + } + + Resource::set_path(p_path, p_take_over); +} + +Image::Format StreamTexture3D::get_format() const { + return format; +} + +Error StreamTexture3D::_load_data(const String &p_path, Vector> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) { + FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + + uint8_t header[4]; + f->get_buffer(header, 4); + ERR_FAIL_COND_V(header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L', ERR_FILE_UNRECOGNIZED); + + //stored as stream textures (used for lossless and lossy compression) + uint32_t version = f->get_32(); + + if (version > FORMAT_VERSION) { + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new."); + } + + r_depth = f->get_32(); //depth + f->get_32(); //ignored (mode) + f->get_32(); // ignored (data format) + + f->get_32(); //ignored + int mipmaps = f->get_32(); + f->get_32(); //ignored + f->get_32(); //ignored + + r_mipmaps = mipmaps != 0; + + r_data.clear(); + + for (int i = 0; i < (r_depth + mipmaps); i++) { + Ref image = StreamTexture2D::load_image_from_file(f, 0); + ERR_FAIL_COND_V(image.is_null() || image->empty(), ERR_CANT_OPEN); + if (i == 0) { + r_format = image->get_format(); + r_width = image->get_width(); + r_height = image->get_height(); + } + r_data.push_back(image); + } + + return OK; +} + +Error StreamTexture3D::load(const String &p_path) { + Vector> data; + + int tw, th, td; + Image::Format tfmt; + bool tmm; + + Error err = _load_data(p_path, data, tfmt, tw, th, td, tmm); + if (err) { + return err; + } + + if (texture.is_valid()) { + RID new_texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data); + RS::get_singleton()->texture_replace(texture, new_texture); + } else { + texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data); + } + + w = tw; + h = th; + d = td; + mipmaps = tmm; + format = tfmt; + + path_to_file = p_path; + + if (get_path() == String()) { + //temporarily set path if no path set for resource, helps find errors + RenderingServer::get_singleton()->texture_set_path(texture, p_path); + } + + _change_notify(); + emit_changed(); + return OK; +} + +String StreamTexture3D::get_load_path() const { + return path_to_file; +} + +int StreamTexture3D::get_width() const { + return w; +} + +int StreamTexture3D::get_height() const { + return h; +} + +int StreamTexture3D::get_depth() const { + return d; +} + +bool StreamTexture3D::has_mipmaps() const { + return mipmaps; +} + +RID StreamTexture3D::get_rid() const { + if (!texture.is_valid()) { + texture = RS::get_singleton()->texture_3d_placeholder_create(); + } + return texture; +} + +Vector> StreamTexture3D::get_data() const { + if (texture.is_valid()) { + return RS::get_singleton()->texture_3d_get(texture); + } else { + return Vector>(); + } +} + +void StreamTexture3D::reload_from_file() { + String path = get_path(); + if (!path.is_resource_file()) { + return; + } + + path = ResourceLoader::path_remap(path); //remap for translation + path = ResourceLoader::import_remap(path); //remap for import + if (!path.is_resource_file()) { + return; + } + + load(path); +} + +void StreamTexture3D::_validate_property(PropertyInfo &property) const { +} + +void StreamTexture3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("load", "path"), &StreamTexture3D::load); + ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTexture3D::get_load_path); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path"); +} + +StreamTexture3D::StreamTexture3D() { + format = Image::FORMAT_MAX; + w = 0; + h = 0; + d = 0; + mipmaps = false; +} + +StreamTexture3D::~StreamTexture3D() { + if (texture.is_valid()) { + RS::get_singleton()->free(texture); + } +} + +///////////////////////////// + +RES ResourceFormatLoaderStreamTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { + Ref st; + st.instance(); + Error err = st->load(p_path); + if (r_error) { + *r_error = err; + } + if (err != OK) { + return RES(); + } + + return st; +} + +void ResourceFormatLoaderStreamTexture3D::get_recognized_extensions(List *p_extensions) const { + p_extensions->push_back("stex3d"); +} + +bool ResourceFormatLoaderStreamTexture3D::handles_type(const String &p_type) const { + return p_type == "StreamTexture3D"; +} + +String ResourceFormatLoaderStreamTexture3D::get_resource_type(const String &p_path) const { + if (p_path.get_extension().to_lower() == "stex3d") { + return "StreamTexture3D"; + } + return ""; +} + +//////////////////////////////////////////// + int AtlasTexture::get_width() const { if (region.size.width == 0) { if (atlas.is_valid()) { diff --git a/scene/resources/texture.h b/scene/resources/texture.h index fd213859b73..eebbf4f2330 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -515,6 +515,122 @@ public: virtual String get_resource_type(const String &p_path) const; }; +class Texture3D : public Texture { + GDCLASS(Texture3D, Texture); + +protected: + static void _bind_methods(); + + TypedArray _get_data() const; + +public: + virtual Image::Format get_format() const = 0; + virtual int get_width() const = 0; + virtual int get_height() const = 0; + virtual int get_depth() const = 0; + virtual bool has_mipmaps() const = 0; + virtual Vector> get_data() const = 0; +}; + +class ImageTexture3D : public Texture3D { + GDCLASS(ImageTexture3D, Texture3D); + + mutable RID texture; + + Image::Format format = Image::FORMAT_MAX; + int width = 1; + int height = 1; + int depth = 1; + bool mipmaps = false; + +protected: + static void _bind_methods(); + + Error _create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const TypedArray &p_data); + void _update(const TypedArray &p_data); + +public: + virtual Image::Format get_format() const override; + virtual int get_width() const override; + virtual int get_height() const override; + virtual int get_depth() const override; + virtual bool has_mipmaps() const override; + + Error create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_data); + void update(const Vector> &p_data); + virtual Vector> get_data() const override; + + virtual RID get_rid() const override; + virtual void set_path(const String &p_path, bool p_take_over = false) override; + + ImageTexture3D(); + ~ImageTexture3D(); +}; + +class StreamTexture3D : public Texture3D { + GDCLASS(StreamTexture3D, Texture3D); + +public: + enum DataFormat { + DATA_FORMAT_IMAGE, + DATA_FORMAT_LOSSLESS, + DATA_FORMAT_LOSSY, + DATA_FORMAT_BASIS_UNIVERSAL, + }; + + enum { + FORMAT_VERSION = 1 + }; + + enum FormatBits { + FORMAT_MASK_IMAGE_FORMAT = (1 << 20) - 1, + FORMAT_BIT_LOSSLESS = 1 << 20, + FORMAT_BIT_LOSSY = 1 << 21, + FORMAT_BIT_STREAM = 1 << 22, + FORMAT_BIT_HAS_MIPMAPS = 1 << 23, + }; + +private: + Error _load_data(const String &p_path, Vector> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps); + String path_to_file; + mutable RID texture; + Image::Format format; + int w, h, d; + bool mipmaps; + + virtual void reload_from_file() override; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; + +public: + Image::Format get_format() const override; + Error load(const String &p_path); + String get_load_path() const; + + int get_width() const override; + int get_height() const override; + int get_depth() const override; + virtual bool has_mipmaps() const override; + virtual RID get_rid() const override; + + virtual void set_path(const String &p_path, bool p_take_over) override; + + virtual Vector> get_data() const override; + + StreamTexture3D(); + ~StreamTexture3D(); +}; + +class ResourceFormatLoaderStreamTexture3D : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false); + 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 Texture2D { GDCLASS(CurveTexture, Texture2D); RES_BASE_EXTENSION("curvetex") diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index 9cb611f3b4c..a24189bdd71 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -331,12 +331,12 @@ public: virtual RID texture_2d_create(const Ref &p_image) = 0; virtual RID texture_2d_layered_create(const Vector> &p_layers, RS::TextureLayeredType p_layered_type) = 0; - virtual RID texture_3d_create(const Vector> &p_slices) = 0; //all slices, then all the mipmaps, must be coherent + virtual RID texture_3d_create(Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_data) = 0; virtual RID texture_proxy_create(RID p_base) = 0; //all slices, then all the mipmaps, must be coherent virtual void texture_2d_update_immediate(RID p_texture, const Ref &p_image, int p_layer = 0) = 0; //mostly used for video and streaming virtual void texture_2d_update(RID p_texture, const Ref &p_image, int p_layer = 0) = 0; - virtual void texture_3d_update(RID p_texture, const Ref &p_image, int p_depth, int p_mipmap) = 0; + virtual void texture_3d_update(RID p_texture, const Vector> &p_data) = 0; virtual void texture_proxy_update(RID p_proxy, RID p_base) = 0; //these two APIs can be used together or in combination with the others. @@ -346,7 +346,7 @@ public: virtual Ref texture_2d_get(RID p_texture) const = 0; virtual Ref texture_2d_layer_get(RID p_texture, int p_layer) const = 0; - virtual Ref texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const = 0; + virtual Vector> texture_3d_get(RID p_texture) const = 0; virtual void texture_replace(RID p_texture, RID p_by_texture) = 0; virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) = 0; diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index d751f474cd5..dca7f59c8c7 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -715,8 +715,120 @@ RID RasterizerStorageRD::texture_2d_layered_create(const Vector> &p_l return texture_owner.make_rid(texture); } -RID RasterizerStorageRD::texture_3d_create(const Vector> &p_slices) { - return RID(); +RID RasterizerStorageRD::texture_3d_create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_data) { + ERR_FAIL_COND_V(p_data.size() == 0, RID()); + Image::Image3DValidateError verr = Image::validate_3d_image(p_format, p_width, p_height, p_depth, p_mipmaps, p_data); + if (verr != Image::VALIDATE_3D_OK) { + ERR_FAIL_V_MSG(RID(), Image::get_3d_image_validation_error_text(verr)); + } + + TextureToRDFormat ret_format; + Image::Format validated_format = Image::FORMAT_MAX; + Vector all_data; + uint32_t mipmap_count = 0; + Vector slices; + { + Vector> images; + uint32_t all_data_size = 0; + images.resize(p_data.size()); + for (int i = 0; i < p_data.size(); i++) { + TextureToRDFormat f; + images.write[i] = _validate_texture_format(p_data[i], f); + if (i == 0) { + ret_format = f; + validated_format = images[0]->get_format(); + } + + all_data_size += images[i]->get_data().size(); + } + + all_data.resize(all_data_size); //consolidate all data here + uint32_t offset = 0; + Size2i prev_size; + for (int i = 0; i < p_data.size(); i++) { + uint32_t s = images[i]->get_data().size(); + + copymem(&all_data.write[offset], images[i]->get_data().ptr(), s); + { + Texture::BufferSlice3D slice; + slice.size.width = images[i]->get_width(); + slice.size.height = images[i]->get_height(); + slice.offset = offset; + slice.buffer_size = s; + slices.push_back(slice); + } + offset += s; + + Size2i img_size(images[i]->get_width(), images[i]->get_height()); + if (img_size != prev_size) { + mipmap_count++; + } + prev_size = img_size; + } + } + + Texture texture; + + texture.type = Texture::TYPE_3D; + texture.width = p_width; + texture.height = p_height; + texture.depth = p_depth; + texture.mipmaps = mipmap_count; + texture.format = p_data[0]->get_format(); + texture.validated_format = validated_format; + + texture.buffer_size_3d = all_data.size(); + texture.buffer_slices_3d = slices; + + texture.rd_type = RD::TEXTURE_TYPE_3D; + texture.rd_format = ret_format.format; + texture.rd_format_srgb = ret_format.format_srgb; + + RD::TextureFormat rd_format; + RD::TextureView rd_view; + { //attempt register + rd_format.format = texture.rd_format; + rd_format.width = texture.width; + rd_format.height = texture.height; + rd_format.depth = texture.depth; + rd_format.array_layers = 1; + rd_format.mipmaps = texture.mipmaps; + rd_format.type = texture.rd_type; + rd_format.samples = RD::TEXTURE_SAMPLES_1; + rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { + rd_format.shareable_formats.push_back(texture.rd_format); + rd_format.shareable_formats.push_back(texture.rd_format_srgb); + } + } + { + rd_view.swizzle_r = ret_format.swizzle_r; + rd_view.swizzle_g = ret_format.swizzle_g; + rd_view.swizzle_b = ret_format.swizzle_b; + rd_view.swizzle_a = ret_format.swizzle_a; + } + Vector> data_slices; + data_slices.push_back(all_data); //one slice + + texture.rd_texture = RD::get_singleton()->texture_create(rd_format, rd_view, data_slices); + ERR_FAIL_COND_V(texture.rd_texture.is_null(), RID()); + if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { + rd_view.format_override = texture.rd_format_srgb; + texture.rd_texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, texture.rd_texture); + if (texture.rd_texture_srgb.is_null()) { + RD::get_singleton()->free(texture.rd_texture); + ERR_FAIL_COND_V(texture.rd_texture_srgb.is_null(), RID()); + } + } + + //used for 2D, overridable + texture.width_2d = texture.width; + texture.height_2d = texture.height; + texture.is_render_target = false; + texture.rd_view = rd_view; + texture.is_proxy = false; + + return texture_owner.make_rid(texture); } RID RasterizerStorageRD::texture_proxy_create(RID p_base) { @@ -772,7 +884,41 @@ void RasterizerStorageRD::texture_2d_update(RID p_texture, const Ref &p_i _texture_2d_update(p_texture, p_image, p_layer, false); } -void RasterizerStorageRD::texture_3d_update(RID p_texture, const Ref &p_image, int p_depth, int p_mipmap) { +void RasterizerStorageRD::texture_3d_update(RID p_texture, const Vector> &p_data) { + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!tex); + ERR_FAIL_COND(tex->type != Texture::TYPE_3D); + Image::Image3DValidateError verr = Image::validate_3d_image(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps > 1, p_data); + if (verr != Image::VALIDATE_3D_OK) { + ERR_FAIL_MSG(Image::get_3d_image_validation_error_text(verr)); + } + + Vector all_data; + { + Vector> images; + uint32_t all_data_size = 0; + images.resize(p_data.size()); + for (int i = 0; i < p_data.size(); i++) { + Ref image = p_data[i]; + if (image->get_format() != tex->validated_format) { + image = image->duplicate(); + image->convert(tex->validated_format); + } + all_data_size += images[i]->get_data().size(); + images.push_back(image); + } + + all_data.resize(all_data_size); //consolidate all data here + uint32_t offset = 0; + + for (int i = 0; i < p_data.size(); i++) { + uint32_t s = images[i]->get_data().size(); + copymem(&all_data.write[offset], images[i]->get_data().ptr(), s); + offset += s; + } + } + + RD::get_singleton()->texture_update(tex->rd_texture, 0, all_data, true); } void RasterizerStorageRD::texture_proxy_update(RID p_texture, RID p_proxy_to) { @@ -858,7 +1004,25 @@ RID RasterizerStorageRD::texture_2d_layered_placeholder_create(RS::TextureLayere } RID RasterizerStorageRD::texture_3d_placeholder_create() { - return RID(); + //this could be better optimized to reuse an existing image , done this way + //for now to get it working + Ref image; + image.instance(); + image->create(4, 4, false, Image::FORMAT_RGBA8); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + image->set_pixel(i, j, Color(1, 0, 1, 1)); + } + } + + Vector> images; + //cube + for (int i = 0; i < 4; i++) { + images.push_back(image); + } + + return texture_3d_create(Image::FORMAT_RGBA8, 4, 4, 4, false, images); } Ref RasterizerStorageRD::texture_2d_get(RID p_texture) const { @@ -890,11 +1054,51 @@ Ref RasterizerStorageRD::texture_2d_get(RID p_texture) const { } Ref RasterizerStorageRD::texture_2d_layer_get(RID p_texture, int p_layer) const { - return Ref(); + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND_V(!tex, Ref()); + + Vector data = RD::get_singleton()->texture_get_data(tex->rd_texture, p_layer); + ERR_FAIL_COND_V(data.size() == 0, Ref()); + Ref image; + image.instance(); + image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); + ERR_FAIL_COND_V(image->empty(), Ref()); + if (tex->format != tex->validated_format) { + image->convert(tex->format); + } + + return image; } -Ref RasterizerStorageRD::texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const { - return Ref(); +Vector> RasterizerStorageRD::texture_3d_get(RID p_texture) const { + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND_V(!tex, Vector>()); + ERR_FAIL_COND_V(tex->type != Texture::TYPE_3D, Vector>()); + + Vector all_data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0); + + ERR_FAIL_COND_V(all_data.size() != (int)tex->buffer_size_3d, Vector>()); + + Vector> ret; + + for (int i = 0; i < tex->buffer_slices_3d.size(); i++) { + const Texture::BufferSlice3D &bs = tex->buffer_slices_3d[i]; + ERR_FAIL_COND_V(bs.offset >= (uint32_t)all_data.size(), Vector>()); + ERR_FAIL_COND_V(bs.offset + bs.buffer_size > (uint32_t)all_data.size(), Vector>()); + Vector sub_region = all_data.subarray(bs.offset, bs.offset + bs.buffer_size - 1); + + Ref img; + img.instance(); + img->create(bs.size.width, bs.size.height, false, tex->validated_format, sub_region); + ERR_FAIL_COND_V(img->empty(), Vector>()); + if (tex->format != tex->validated_format) { + img->convert(tex->format); + } + + ret.push_back(img); + } + + return ret; } void RasterizerStorageRD::texture_replace(RID p_texture, RID p_by_texture) { diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h index cecae6bbb2a..e14b9528cf8 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h @@ -205,6 +205,14 @@ private: int height_2d; int width_2d; + struct BufferSlice3D { + Size2i size; + uint32_t offset = 0; + uint32_t buffer_size = 0; + }; + Vector buffer_slices_3d; + uint32_t buffer_size_3d = 0; + bool is_render_target; bool is_proxy; @@ -980,14 +988,14 @@ public: virtual RID texture_2d_create(const Ref &p_image); virtual RID texture_2d_layered_create(const Vector> &p_layers, RS::TextureLayeredType p_layered_type); - virtual RID texture_3d_create(const Vector> &p_slices); //all slices, then all the mipmaps, must be coherent + virtual RID texture_3d_create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_data); //all slices, then all the mipmaps, must be coherent virtual RID texture_proxy_create(RID p_base); virtual void _texture_2d_update(RID p_texture, const Ref &p_image, int p_layer, bool p_immediate); virtual void texture_2d_update_immediate(RID p_texture, const Ref &p_image, int p_layer = 0); //mostly used for video and streaming virtual void texture_2d_update(RID p_texture, const Ref &p_image, int p_layer = 0); - virtual void texture_3d_update(RID p_texture, const Ref &p_image, int p_depth, int p_mipmap); + virtual void texture_3d_update(RID p_texture, const Vector> &p_data); virtual void texture_proxy_update(RID p_texture, RID p_proxy_to); //these two APIs can be used together or in combination with the others. @@ -997,7 +1005,7 @@ public: virtual Ref texture_2d_get(RID p_texture) const; virtual Ref texture_2d_layer_get(RID p_texture, int p_layer) const; - virtual Ref texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const; + virtual Vector> texture_3d_get(RID p_texture) const; virtual void texture_replace(RID p_texture, RID p_by_texture); virtual void texture_set_size_override(RID p_texture, int p_width, int p_height); diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index ceefcfa1fc7..b554425bef2 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -114,6 +114,14 @@ public: m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) { return BINDBASE->m_name(arg1, arg2, arg3, arg4); } #define BIND4RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4) \ m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) const { return BINDBASE->m_name(arg1, arg2, arg3, arg4); } +#define BIND5R(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) { return BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5); } +#define BIND5RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) const { return BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5); } +#define BIND6R(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) { return BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); } +#define BIND6RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) const { return BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); } #define BIND0(m_name) \ void m_name() { DISPLAY_CHANGED BINDBASE->m_name(); } @@ -160,14 +168,14 @@ public: //these go pass-through, as they can be called from any thread BIND1R(RID, texture_2d_create, const Ref &) BIND2R(RID, texture_2d_layered_create, const Vector> &, TextureLayeredType) - BIND1R(RID, texture_3d_create, const Vector> &) + BIND6R(RID, texture_3d_create, Image::Format, int, int, int, bool, const Vector> &) BIND1R(RID, texture_proxy_create, RID) //goes pass-through BIND3(texture_2d_update_immediate, RID, const Ref &, int) //these go through command queue if they are in another thread BIND3(texture_2d_update, RID, const Ref &, int) - BIND4(texture_3d_update, RID, const Ref &, int, int) + BIND2(texture_3d_update, RID, const Vector> &) BIND2(texture_proxy_update, RID, RID) //these also go pass-through @@ -177,7 +185,7 @@ public: BIND1RC(Ref, texture_2d_get, RID) BIND2RC(Ref, texture_2d_layer_get, RID, int) - BIND3RC(Ref, texture_3d_slice_get, RID, int, int) + BIND1RC(Vector>, texture_3d_get, RID) BIND2(texture_replace, RID, RID) diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index a8a56e7d56d..372a7269dc1 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -79,14 +79,14 @@ public: //these go pass-through, as they can be called from any thread virtual RID texture_2d_create(const Ref &p_image) { return rendering_server->texture_2d_create(p_image); } virtual RID texture_2d_layered_create(const Vector> &p_layers, TextureLayeredType p_layered_type) { return rendering_server->texture_2d_layered_create(p_layers, p_layered_type); } - virtual RID texture_3d_create(const Vector> &p_slices) { return rendering_server->texture_3d_create(p_slices); } + virtual RID texture_3d_create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_data) { return rendering_server->texture_3d_create(p_format, p_width, p_height, p_depth, p_mipmaps, p_data); } virtual RID texture_proxy_create(RID p_base) { return rendering_server->texture_proxy_create(p_base); } //goes pass-through virtual void texture_2d_update_immediate(RID p_texture, const Ref &p_image, int p_layer = 0) { rendering_server->texture_2d_update_immediate(p_texture, p_image, p_layer); } //these go through command queue if they are in another thread FUNC3(texture_2d_update, RID, const Ref &, int) - FUNC4(texture_3d_update, RID, const Ref &, int, int) + FUNC2(texture_3d_update, RID, const Vector> &) FUNC2(texture_proxy_update, RID, RID) //these also go pass-through @@ -96,7 +96,7 @@ public: FUNC1RC(Ref, texture_2d_get, RID) FUNC2RC(Ref, texture_2d_layer_get, RID, int) - FUNC3RC(Ref, texture_3d_slice_get, RID, int, int) + FUNC1RC(Vector>, texture_3d_get, RID) FUNC2(texture_replace, RID, RID) diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 64fa06ae72a..49f840948f8 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -97,12 +97,12 @@ public: virtual RID texture_2d_create(const Ref &p_image) = 0; virtual RID texture_2d_layered_create(const Vector> &p_layers, TextureLayeredType p_layered_type) = 0; - virtual RID texture_3d_create(const Vector> &p_slices) = 0; //all slices, then all the mipmaps, must be coherent + virtual RID texture_3d_create(Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_data) = 0; //all slices, then all the mipmaps, must be coherent virtual RID texture_proxy_create(RID p_base) = 0; virtual void texture_2d_update_immediate(RID p_texture, const Ref &p_image, int p_layer = 0) = 0; //mostly used for video and streaming virtual void texture_2d_update(RID p_texture, const Ref &p_image, int p_layer = 0) = 0; - virtual void texture_3d_update(RID p_texture, const Ref &p_image, int p_depth, int p_mipmap) = 0; + virtual void texture_3d_update(RID p_texture, const Vector> &p_data) = 0; virtual void texture_proxy_update(RID p_texture, RID p_proxy_to) = 0; //these two APIs can be used together or in combination with the others. @@ -112,7 +112,7 @@ public: virtual Ref texture_2d_get(RID p_texture) const = 0; virtual Ref texture_2d_layer_get(RID p_texture, int p_layer) const = 0; - virtual Ref texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const = 0; + virtual Vector> texture_3d_get(RID p_texture) const = 0; virtual void texture_replace(RID p_texture, RID p_by_texture) = 0; virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) = 0;