Implement 3D textures as import and resource format.

This commit is contained in:
reduz 2020-09-09 12:40:51 -03:00
parent 7c3f411cb1
commit a674da4eec
16 changed files with 1174 additions and 40 deletions

View File

@ -363,6 +363,82 @@ void Image::get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int
r_size = ofs2 - ofs; 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<Ref<Image>> &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 { int Image::get_width() const {
return width; return width;
} }

View File

@ -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_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 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<Ref<Image>> &p_images);
static String get_3d_image_validation_error_text(Image3DValidateError p_error);
/** /**
* Resize the image, using the preferred interpolation method. * Resize the image, using the preferred interpolation method.
*/ */

View File

@ -157,6 +157,7 @@
#include "editor/plugins/sprite_frames_editor_plugin.h" #include "editor/plugins/sprite_frames_editor_plugin.h"
#include "editor/plugins/style_box_editor_plugin.h" #include "editor/plugins/style_box_editor_plugin.h"
#include "editor/plugins/text_editor.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_editor_plugin.h"
#include "editor/plugins/texture_layered_editor_plugin.h" #include "editor/plugins/texture_layered_editor_plugin.h"
#include "editor/plugins/texture_region_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); import_cubemap_array->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP_ARRAY);
ResourceFormatImporter::get_singleton()->add_importer(import_cubemap_array); ResourceFormatImporter::get_singleton()->add_importer(import_cubemap_array);
/*Ref<ResourceImporterLayeredTexture> import_3d; Ref<ResourceImporterLayeredTexture> import_3d;
import_3d.instance(); import_3d.instance();
import_3d->set_mode(ResourceImporterLayeredTexture::MODE_3D); import_3d->set_mode(ResourceImporterLayeredTexture::MODE_3D);
ResourceFormatImporter::get_singleton()->add_importer(import_3d);*/ ResourceFormatImporter::get_singleton()->add_importer(import_3d);
Ref<ResourceImporterImage> import_image; Ref<ResourceImporterImage> import_image;
import_image.instance(); import_image.instance();
@ -6622,6 +6623,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(CurveEditorPlugin(this))); add_editor_plugin(memnew(CurveEditorPlugin(this)));
add_editor_plugin(memnew(TextureEditorPlugin(this))); add_editor_plugin(memnew(TextureEditorPlugin(this)));
add_editor_plugin(memnew(TextureLayeredEditorPlugin(this))); add_editor_plugin(memnew(TextureLayeredEditorPlugin(this)));
add_editor_plugin(memnew(Texture3DEditorPlugin(this)));
add_editor_plugin(memnew(AudioStreamEditorPlugin(this))); add_editor_plugin(memnew(AudioStreamEditorPlugin(this)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
add_editor_plugin(memnew(Skeleton3DEditorPlugin(this))); add_editor_plugin(memnew(Skeleton3DEditorPlugin(this)));

View File

@ -70,7 +70,7 @@ String ResourceImporterLayeredTexture::get_visible_name() const {
return "CubemapArray"; return "CubemapArray";
} break; } break;
case MODE_3D: { case MODE_3D: {
return "3D"; return "Texture3D";
} break; } break;
} }
@ -156,15 +156,103 @@ void ResourceImporterLayeredTexture::get_import_options(List<ImportOption> *r_op
} }
void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2) { void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2) {
for (int i = 0; i < p_images.size(); i++) { Vector<Ref<Image>> mipmap_images; //for 3D
if (p_force_po2) {
p_images.write[i]->resize_to_po2(); 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) { if (p_mipmaps) {
p_images.write[i]->generate_mipmaps(); Vector<Ref<Image>> parent_images = p_images;
} else { //create 3D mipmaps, this is horrible, though not used very often
p_images.write[i]->clear_mipmaps(); 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<Ref<Image>> 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<Image> 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<Ref<Image>> p_images, cons
f->store_8('L'); f->store_8('L');
f->store_32(StreamTextureLayered::FORMAT_VERSION); 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(mode);
f->store_32(0); //dataformat
f->store_32(0); //mipmap limit
//reserved
f->store_32(0); 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);
f->store_32(0); f->store_32(0);
@ -189,6 +276,10 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons
ResourceImporterTexture::save_to_stex_format(f, p_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy); 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(); f->close();
} }

View File

@ -93,10 +93,6 @@ private:
static const char *compression_formats[]; static const char *compression_formats[];
protected: protected:
static void _texture_reimport_srgb(const Ref<StreamTexture2D> &p_tex);
static void _texture_reimport_3d(const Ref<StreamTexture2D> &p_tex);
static void _texture_reimport_normal(const Ref<StreamTexture2D> &p_tex);
static ResourceImporterLayeredTexture *singleton; static ResourceImporterLayeredTexture *singleton;
public: public:

View File

@ -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<InputEvent> 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<Texture2D> 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<Texture3D> 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<Texture3D>(p_object) != nullptr;
}
void EditorInspectorPlugin3DTexture::parse_begin(Object *p_object) {
Texture3D *texture = Object::cast_to<Texture3D>(p_object);
if (!texture) {
return;
}
Ref<Texture3D> m(texture);
Texture3DEditor *editor = memnew(Texture3DEditor);
editor->edit(m);
add_custom_control(editor);
}
Texture3DEditorPlugin::Texture3DEditorPlugin(EditorNode *p_node) {
Ref<EditorInspectorPlugin3DTexture> plugin;
plugin.instance();
add_inspector_plugin(plugin);
}

View File

@ -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<Texture3D> texture;
Ref<Shader> shader;
Ref<ShaderMaterial> 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<InputEvent> p_event);
void _changed_callback(Object *p_changed, const char *p_prop) override;
static void _bind_methods();
public:
void edit(Ref<Texture3D> 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

View File

@ -230,6 +230,7 @@ static Ref<ResourceFormatLoaderDynamicFont> resource_loader_dynamic_font;
static Ref<ResourceFormatLoaderStreamTexture2D> resource_loader_stream_texture; static Ref<ResourceFormatLoaderStreamTexture2D> resource_loader_stream_texture;
static Ref<ResourceFormatLoaderStreamTextureLayered> resource_loader_texture_layered; static Ref<ResourceFormatLoaderStreamTextureLayered> resource_loader_texture_layered;
static Ref<ResourceFormatLoaderStreamTexture3D> resource_loader_texture_3d;
static Ref<ResourceFormatLoaderBMFont> resource_loader_bmfont; static Ref<ResourceFormatLoaderBMFont> resource_loader_bmfont;
@ -252,6 +253,9 @@ void register_scene_types() {
resource_loader_texture_layered.instance(); resource_loader_texture_layered.instance();
ResourceLoader::add_resource_format_loader(resource_loader_texture_layered); 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(); resource_saver_text.instance();
ResourceSaver::add_resource_format_saver(resource_saver_text, true); ResourceSaver::add_resource_format_saver(resource_saver_text, true);
@ -701,6 +705,9 @@ void register_scene_types() {
ClassDB::register_class<CameraTexture>(); ClassDB::register_class<CameraTexture>();
ClassDB::register_virtual_class<TextureLayered>(); ClassDB::register_virtual_class<TextureLayered>();
ClassDB::register_virtual_class<ImageTextureLayered>(); ClassDB::register_virtual_class<ImageTextureLayered>();
ClassDB::register_virtual_class<Texture3D>();
ClassDB::register_class<ImageTexture3D>();
ClassDB::register_class<StreamTexture3D>();
ClassDB::register_class<Cubemap>(); ClassDB::register_class<Cubemap>();
ClassDB::register_class<CubemapArray>(); ClassDB::register_class<CubemapArray>();
ClassDB::register_class<Texture2DArray>(); ClassDB::register_class<Texture2DArray>();
@ -946,6 +953,9 @@ void unregister_scene_types() {
ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered); ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered);
resource_loader_texture_layered.unref(); 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); ResourceLoader::remove_resource_format_loader(resource_loader_stream_texture);
resource_loader_stream_texture.unref(); resource_loader_stream_texture.unref();

View File

@ -791,8 +791,312 @@ String ResourceFormatLoaderStreamTexture2D::get_resource_type(const String &p_pa
return ""; return "";
} }
////////////////////////////////////
TypedArray<Image> Texture3D::_get_data() const {
Vector<Ref<Image>> data = get_data();
TypedArray<Image> 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<Image> &p_data) {
Vector<Ref<Image>> 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<Image> &p_data) {
Vector<Ref<Image>> 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<Ref<Image>> &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<Ref<Image>> &p_data) {
ERR_FAIL_COND(!texture.is_valid());
RenderingServer::get_singleton()->texture_3d_update(texture, p_data);
}
Vector<Ref<Image>> ImageTexture3D::get_data() const {
ERR_FAIL_COND_V(!texture.is_valid(), Vector<Ref<Image>>());
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<Ref<Image>> &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> 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<Ref<Image>> 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<Ref<Image>> StreamTexture3D::get_data() const {
if (texture.is_valid()) {
return RS::get_singleton()->texture_3d_get(texture);
} else {
return Vector<Ref<Image>>();
}
}
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<StreamTexture3D> 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<String> *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 { int AtlasTexture::get_width() const {
if (region.size.width == 0) { if (region.size.width == 0) {
if (atlas.is_valid()) { if (atlas.is_valid()) {

View File

@ -515,6 +515,122 @@ public:
virtual String get_resource_type(const String &p_path) const; virtual String get_resource_type(const String &p_path) const;
}; };
class Texture3D : public Texture {
GDCLASS(Texture3D, Texture);
protected:
static void _bind_methods();
TypedArray<Image> _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<Ref<Image>> 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<Image> &p_data);
void _update(const TypedArray<Image> &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<Ref<Image>> &p_data);
void update(const Vector<Ref<Image>> &p_data);
virtual Vector<Ref<Image>> 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<Ref<Image>> &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<Ref<Image>> 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<String> *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 { class CurveTexture : public Texture2D {
GDCLASS(CurveTexture, Texture2D); GDCLASS(CurveTexture, Texture2D);
RES_BASE_EXTENSION("curvetex") RES_BASE_EXTENSION("curvetex")

View File

@ -331,12 +331,12 @@ public:
virtual RID texture_2d_create(const Ref<Image> &p_image) = 0; virtual RID texture_2d_create(const Ref<Image> &p_image) = 0;
virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) = 0; virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) = 0;
virtual RID texture_3d_create(const Vector<Ref<Image>> &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<Ref<Image>> &p_data) = 0;
virtual RID texture_proxy_create(RID p_base) = 0; //all slices, then all the mipmaps, must be coherent 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<Image> &p_image, int p_layer = 0) = 0; //mostly used for video and streaming virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; //mostly used for video and streaming
virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0;
virtual void texture_3d_update(RID p_texture, const Ref<Image> &p_image, int p_depth, int p_mipmap) = 0; virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) = 0;
virtual void texture_proxy_update(RID p_proxy, RID p_base) = 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. //these two APIs can be used together or in combination with the others.
@ -346,7 +346,7 @@ public:
virtual Ref<Image> texture_2d_get(RID p_texture) const = 0; virtual Ref<Image> texture_2d_get(RID p_texture) const = 0;
virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const = 0; virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const = 0;
virtual Ref<Image> texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const = 0; virtual Vector<Ref<Image>> texture_3d_get(RID p_texture) const = 0;
virtual void texture_replace(RID p_texture, RID p_by_texture) = 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; virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) = 0;

View File

@ -715,8 +715,120 @@ RID RasterizerStorageRD::texture_2d_layered_create(const Vector<Ref<Image>> &p_l
return texture_owner.make_rid(texture); return texture_owner.make_rid(texture);
} }
RID RasterizerStorageRD::texture_3d_create(const Vector<Ref<Image>> &p_slices) { RID RasterizerStorageRD::texture_3d_create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) {
return RID(); 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<uint8_t> all_data;
uint32_t mipmap_count = 0;
Vector<Texture::BufferSlice3D> slices;
{
Vector<Ref<Image>> 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<Vector<uint8_t>> 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) { RID RasterizerStorageRD::texture_proxy_create(RID p_base) {
@ -772,7 +884,41 @@ void RasterizerStorageRD::texture_2d_update(RID p_texture, const Ref<Image> &p_i
_texture_2d_update(p_texture, p_image, p_layer, false); _texture_2d_update(p_texture, p_image, p_layer, false);
} }
void RasterizerStorageRD::texture_3d_update(RID p_texture, const Ref<Image> &p_image, int p_depth, int p_mipmap) { void RasterizerStorageRD::texture_3d_update(RID p_texture, const Vector<Ref<Image>> &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<uint8_t> all_data;
{
Vector<Ref<Image>> images;
uint32_t all_data_size = 0;
images.resize(p_data.size());
for (int i = 0; i < p_data.size(); i++) {
Ref<Image> 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) { 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() { 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;
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<Ref<Image>> 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<Image> RasterizerStorageRD::texture_2d_get(RID p_texture) const { Ref<Image> RasterizerStorageRD::texture_2d_get(RID p_texture) const {
@ -890,11 +1054,51 @@ Ref<Image> RasterizerStorageRD::texture_2d_get(RID p_texture) const {
} }
Ref<Image> RasterizerStorageRD::texture_2d_layer_get(RID p_texture, int p_layer) const { Ref<Image> RasterizerStorageRD::texture_2d_layer_get(RID p_texture, int p_layer) const {
return Ref<Image>(); Texture *tex = texture_owner.getornull(p_texture);
ERR_FAIL_COND_V(!tex, Ref<Image>());
Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, p_layer);
ERR_FAIL_COND_V(data.size() == 0, Ref<Image>());
Ref<Image> image;
image.instance();
image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data);
ERR_FAIL_COND_V(image->empty(), Ref<Image>());
if (tex->format != tex->validated_format) {
image->convert(tex->format);
}
return image;
} }
Ref<Image> RasterizerStorageRD::texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const { Vector<Ref<Image>> RasterizerStorageRD::texture_3d_get(RID p_texture) const {
return Ref<Image>(); Texture *tex = texture_owner.getornull(p_texture);
ERR_FAIL_COND_V(!tex, Vector<Ref<Image>>());
ERR_FAIL_COND_V(tex->type != Texture::TYPE_3D, Vector<Ref<Image>>());
Vector<uint8_t> 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<Ref<Image>>());
Vector<Ref<Image>> 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<Ref<Image>>());
ERR_FAIL_COND_V(bs.offset + bs.buffer_size > (uint32_t)all_data.size(), Vector<Ref<Image>>());
Vector<uint8_t> sub_region = all_data.subarray(bs.offset, bs.offset + bs.buffer_size - 1);
Ref<Image> img;
img.instance();
img->create(bs.size.width, bs.size.height, false, tex->validated_format, sub_region);
ERR_FAIL_COND_V(img->empty(), Vector<Ref<Image>>());
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) { void RasterizerStorageRD::texture_replace(RID p_texture, RID p_by_texture) {

View File

@ -205,6 +205,14 @@ private:
int height_2d; int height_2d;
int width_2d; int width_2d;
struct BufferSlice3D {
Size2i size;
uint32_t offset = 0;
uint32_t buffer_size = 0;
};
Vector<BufferSlice3D> buffer_slices_3d;
uint32_t buffer_size_3d = 0;
bool is_render_target; bool is_render_target;
bool is_proxy; bool is_proxy;
@ -980,14 +988,14 @@ public:
virtual RID texture_2d_create(const Ref<Image> &p_image); virtual RID texture_2d_create(const Ref<Image> &p_image);
virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type); virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type);
virtual RID texture_3d_create(const Vector<Ref<Image>> &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<Ref<Image>> &p_data); //all slices, then all the mipmaps, must be coherent
virtual RID texture_proxy_create(RID p_base); virtual RID texture_proxy_create(RID p_base);
virtual void _texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer, bool p_immediate); virtual void _texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer, bool p_immediate);
virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); //mostly used for video and streaming virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); //mostly used for video and streaming
virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0);
virtual void texture_3d_update(RID p_texture, const Ref<Image> &p_image, int p_depth, int p_mipmap); virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data);
virtual void texture_proxy_update(RID p_texture, RID p_proxy_to); 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. //these two APIs can be used together or in combination with the others.
@ -997,7 +1005,7 @@ public:
virtual Ref<Image> texture_2d_get(RID p_texture) const; virtual Ref<Image> texture_2d_get(RID p_texture) const;
virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const; virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const;
virtual Ref<Image> texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const; virtual Vector<Ref<Image>> texture_3d_get(RID p_texture) const;
virtual void texture_replace(RID p_texture, RID p_by_texture); 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); virtual void texture_set_size_override(RID p_texture, int p_width, int p_height);

View File

@ -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); } 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) \ #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); } 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) \ #define BIND0(m_name) \
void m_name() { DISPLAY_CHANGED BINDBASE->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 //these go pass-through, as they can be called from any thread
BIND1R(RID, texture_2d_create, const Ref<Image> &) BIND1R(RID, texture_2d_create, const Ref<Image> &)
BIND2R(RID, texture_2d_layered_create, const Vector<Ref<Image>> &, TextureLayeredType) BIND2R(RID, texture_2d_layered_create, const Vector<Ref<Image>> &, TextureLayeredType)
BIND1R(RID, texture_3d_create, const Vector<Ref<Image>> &) BIND6R(RID, texture_3d_create, Image::Format, int, int, int, bool, const Vector<Ref<Image>> &)
BIND1R(RID, texture_proxy_create, RID) BIND1R(RID, texture_proxy_create, RID)
//goes pass-through //goes pass-through
BIND3(texture_2d_update_immediate, RID, const Ref<Image> &, int) BIND3(texture_2d_update_immediate, RID, const Ref<Image> &, int)
//these go through command queue if they are in another thread //these go through command queue if they are in another thread
BIND3(texture_2d_update, RID, const Ref<Image> &, int) BIND3(texture_2d_update, RID, const Ref<Image> &, int)
BIND4(texture_3d_update, RID, const Ref<Image> &, int, int) BIND2(texture_3d_update, RID, const Vector<Ref<Image>> &)
BIND2(texture_proxy_update, RID, RID) BIND2(texture_proxy_update, RID, RID)
//these also go pass-through //these also go pass-through
@ -177,7 +185,7 @@ public:
BIND1RC(Ref<Image>, texture_2d_get, RID) BIND1RC(Ref<Image>, texture_2d_get, RID)
BIND2RC(Ref<Image>, texture_2d_layer_get, RID, int) BIND2RC(Ref<Image>, texture_2d_layer_get, RID, int)
BIND3RC(Ref<Image>, texture_3d_slice_get, RID, int, int) BIND1RC(Vector<Ref<Image>>, texture_3d_get, RID)
BIND2(texture_replace, RID, RID) BIND2(texture_replace, RID, RID)

View File

@ -79,14 +79,14 @@ public:
//these go pass-through, as they can be called from any thread //these go pass-through, as they can be called from any thread
virtual RID texture_2d_create(const Ref<Image> &p_image) { return rendering_server->texture_2d_create(p_image); } virtual RID texture_2d_create(const Ref<Image> &p_image) { return rendering_server->texture_2d_create(p_image); }
virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, TextureLayeredType p_layered_type) { return rendering_server->texture_2d_layered_create(p_layers, p_layered_type); } virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &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<Ref<Image>> &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<Ref<Image>> &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); } virtual RID texture_proxy_create(RID p_base) { return rendering_server->texture_proxy_create(p_base); }
//goes pass-through //goes pass-through
virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) { rendering_server->texture_2d_update_immediate(p_texture, p_image, p_layer); } virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &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 //these go through command queue if they are in another thread
FUNC3(texture_2d_update, RID, const Ref<Image> &, int) FUNC3(texture_2d_update, RID, const Ref<Image> &, int)
FUNC4(texture_3d_update, RID, const Ref<Image> &, int, int) FUNC2(texture_3d_update, RID, const Vector<Ref<Image>> &)
FUNC2(texture_proxy_update, RID, RID) FUNC2(texture_proxy_update, RID, RID)
//these also go pass-through //these also go pass-through
@ -96,7 +96,7 @@ public:
FUNC1RC(Ref<Image>, texture_2d_get, RID) FUNC1RC(Ref<Image>, texture_2d_get, RID)
FUNC2RC(Ref<Image>, texture_2d_layer_get, RID, int) FUNC2RC(Ref<Image>, texture_2d_layer_get, RID, int)
FUNC3RC(Ref<Image>, texture_3d_slice_get, RID, int, int) FUNC1RC(Vector<Ref<Image>>, texture_3d_get, RID)
FUNC2(texture_replace, RID, RID) FUNC2(texture_replace, RID, RID)

View File

@ -97,12 +97,12 @@ public:
virtual RID texture_2d_create(const Ref<Image> &p_image) = 0; virtual RID texture_2d_create(const Ref<Image> &p_image) = 0;
virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, TextureLayeredType p_layered_type) = 0; virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, TextureLayeredType p_layered_type) = 0;
virtual RID texture_3d_create(const Vector<Ref<Image>> &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<Ref<Image>> &p_data) = 0; //all slices, then all the mipmaps, must be coherent
virtual RID texture_proxy_create(RID p_base) = 0; virtual RID texture_proxy_create(RID p_base) = 0;
virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; //mostly used for video and streaming virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; //mostly used for video and streaming
virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0;
virtual void texture_3d_update(RID p_texture, const Ref<Image> &p_image, int p_depth, int p_mipmap) = 0; virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) = 0;
virtual void texture_proxy_update(RID p_texture, RID p_proxy_to) = 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. //these two APIs can be used together or in combination with the others.
@ -112,7 +112,7 @@ public:
virtual Ref<Image> texture_2d_get(RID p_texture) const = 0; virtual Ref<Image> texture_2d_get(RID p_texture) const = 0;
virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const = 0; virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const = 0;
virtual Ref<Image> texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const = 0; virtual Vector<Ref<Image>> texture_3d_get(RID p_texture) const = 0;
virtual void texture_replace(RID p_texture, RID p_by_texture) = 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; virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) = 0;