Implement 3D textures as import and resource format.
This commit is contained in:
parent
7c3f411cb1
commit
a674da4eec
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
13
core/image.h
13
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_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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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)));
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue