godot/scene/resources/compressed_texture.cpp
K. S. Ernest (iFire) Lee 06a7b11a47 Fix Texture3D import not working
Fix Texture3D import not working when texture has fully opaque and (partly or fully) transparent slices.

For better style make cases of detect_alpha check against the exact enums rather than the enum 0.
2025-07-26 10:22:36 -07:00

896 lines
26 KiB
C++

/**************************************************************************/
/* compressed_texture.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "compressed_texture.h"
#include "scene/resources/bit_map.h"
Error CompressedTexture2D::_load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
alpha_cache.unref();
ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != '2') {
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is corrupt (Bad header).");
}
uint32_t version = f->get_32();
if (version > FORMAT_VERSION) {
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
}
r_width = f->get_32();
r_height = f->get_32();
uint32_t df = f->get_32(); //data format
//skip reserved
mipmap_limit = int(f->get_32());
//reserved
f->get_32();
f->get_32();
f->get_32();
#ifdef TOOLS_ENABLED
r_request_3d = request_3d_callback && df & FORMAT_BIT_DETECT_3D;
r_request_roughness = request_roughness_callback && df & FORMAT_BIT_DETECT_ROUGNESS;
r_request_normal = request_normal_callback && df & FORMAT_BIT_DETECT_NORMAL;
#else
r_request_3d = false;
r_request_roughness = false;
r_request_normal = false;
#endif
if (!(df & FORMAT_BIT_STREAM)) {
p_size_limit = 0;
}
image = load_image_from_file(f, p_size_limit);
if (image.is_null() || image->is_empty()) {
return ERR_CANT_OPEN;
}
return OK;
}
void CompressedTexture2D::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 CompressedTexture2D::_requested_3d(void *p_ud) {
CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
Ref<CompressedTexture2D> ctex(ct);
ERR_FAIL_NULL(request_3d_callback);
request_3d_callback(ctex);
}
void CompressedTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) {
CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
Ref<CompressedTexture2D> ctex(ct);
ERR_FAIL_NULL(request_roughness_callback);
request_roughness_callback(ctex, p_normal_path, p_roughness_channel);
}
void CompressedTexture2D::_requested_normal(void *p_ud) {
CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
Ref<CompressedTexture2D> ctex(ct);
ERR_FAIL_NULL(request_normal_callback);
request_normal_callback(ctex);
}
CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_3d_callback = nullptr;
CompressedTexture2D::TextureFormatRoughnessRequestCallback CompressedTexture2D::request_roughness_callback = nullptr;
CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_normal_callback = nullptr;
Image::Format CompressedTexture2D::get_format() const {
return format;
}
Error CompressedTexture2D::load(const String &p_path) {
int lw, lh;
Ref<Image> image;
image.instantiate();
bool request_3d;
bool request_normal;
bool request_roughness;
int mipmap_limit;
Error err = _load_data(p_path, lw, lh, image, request_3d, request_normal, request_roughness, mipmap_limit);
if (err) {
return err;
}
if (texture.is_valid()) {
RID new_texture = RS::get_singleton()->texture_2d_create(image);
RS::get_singleton()->texture_replace(texture, new_texture);
} else {
texture = RS::get_singleton()->texture_2d_create(image);
}
if (lw || lh) {
RS::get_singleton()->texture_set_size_override(texture, lw, lh);
}
w = lw;
h = lh;
path_to_file = p_path;
format = image->get_format();
if (get_path().is_empty()) {
//temporarily set path if no path set for resource, helps find errors
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
#ifdef TOOLS_ENABLED
if (request_3d) {
//print_line("request detect 3D at " + p_path);
RS::get_singleton()->texture_set_detect_3d_callback(texture, _requested_3d, this);
} else {
//print_line("not requesting detect 3D at " + p_path);
RS::get_singleton()->texture_set_detect_3d_callback(texture, nullptr, nullptr);
}
if (request_roughness) {
//print_line("request detect srgb at " + p_path);
RS::get_singleton()->texture_set_detect_roughness_callback(texture, _requested_roughness, this);
} else {
//print_line("not requesting detect srgb at " + p_path);
RS::get_singleton()->texture_set_detect_roughness_callback(texture, nullptr, nullptr);
}
if (request_normal) {
//print_line("request detect srgb at " + p_path);
RS::get_singleton()->texture_set_detect_normal_callback(texture, _requested_normal, this);
} else {
//print_line("not requesting detect normal at " + p_path);
RS::get_singleton()->texture_set_detect_normal_callback(texture, nullptr, nullptr);
}
#endif
notify_property_list_changed();
emit_changed();
return OK;
}
String CompressedTexture2D::get_load_path() const {
return path_to_file;
}
int CompressedTexture2D::get_width() const {
return w;
}
int CompressedTexture2D::get_height() const {
return h;
}
RID CompressedTexture2D::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_2d_placeholder_create();
}
return texture;
}
void CompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
}
void CompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
}
void CompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if ((w | h) == 0) {
return;
}
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
bool CompressedTexture2D::has_alpha() const {
return false;
}
Ref<Image> CompressedTexture2D::get_image() const {
if (texture.is_valid()) {
return RS::get_singleton()->texture_2d_get(texture);
} else {
return Ref<Image>();
}
}
bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
if (alpha_cache.is_null()) {
Ref<Image> img = get_image();
if (img.is_valid()) {
if (img->is_compressed()) { //must decompress, if compressed
Ref<Image> decom = img->duplicate();
decom->decompress();
img = decom;
}
alpha_cache.instantiate();
alpha_cache->create_from_image_alpha(img);
}
}
if (alpha_cache.is_valid()) {
int aw = int(alpha_cache->get_size().width);
int ah = int(alpha_cache->get_size().height);
if (aw == 0 || ah == 0) {
return true;
}
int x = p_x * aw / w;
int y = p_y * ah / h;
x = CLAMP(x, 0, aw - 1);
y = CLAMP(y, 0, ah - 1);
return alpha_cache->get_bit(x, y);
}
return true;
}
void CompressedTexture2D::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);
}
Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_size_limit) {
uint32_t data_format = f->get_32();
uint32_t w = f->get_16();
uint32_t h = f->get_16();
uint32_t mipmaps = f->get_32();
Image::Format format = Image::Format(f->get_32());
if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP) {
//look for a PNG or WebP file inside
int sw = w;
int sh = h;
//mipmaps need to be read independently, they will be later combined
Vector<Ref<Image>> mipmap_images;
uint64_t total_size = 0;
for (uint32_t i = 0; i < mipmaps + 1; i++) {
uint32_t size = f->get_32();
if (p_size_limit > 0 && i < (mipmaps - 1) && (sw > p_size_limit || sh > p_size_limit)) {
//can't load this due to size limit
sw = MAX(sw >> 1, 1);
sh = MAX(sh >> 1, 1);
f->seek(f->get_position() + size);
continue;
}
Vector<uint8_t> pv;
pv.resize(size);
{
uint8_t *wr = pv.ptrw();
f->get_buffer(wr, size);
}
Ref<Image> img;
if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) {
img = Image::png_unpacker(pv);
} else if (data_format == DATA_FORMAT_WEBP && Image::webp_unpacker) {
img = Image::webp_unpacker(pv);
}
if (img.is_null() || img->is_empty()) {
ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());
}
// If the image is compressed and its format doesn't match the desired format, return an empty reference.
// This is done to avoid recompressing the image on load.
ERR_FAIL_COND_V(img->is_compressed() && format != img->get_format(), Ref<Image>());
// The format will actually be the format of the header,
// as it may have changed on compression.
if (format != img->get_format()) {
// Convert the image to the desired format.
// Note: We are not decompressing the image here, just changing its format.
// It's important that all images in the texture array share the same format for correct rendering.
img->convert(format);
}
total_size += img->get_data().size();
mipmap_images.push_back(img);
sw = MAX(sw >> 1, 1);
sh = MAX(sh >> 1, 1);
}
//print_line("mipmap read total: " + itos(mipmap_images.size()));
Ref<Image> image;
image.instantiate();
if (mipmap_images.size() == 1) {
//only one image (which will most likely be the case anyway for this format)
image = mipmap_images[0];
return image;
} else {
//rarer use case, but needs to be supported
Vector<uint8_t> img_data;
img_data.resize(total_size);
{
uint8_t *wr = img_data.ptrw();
int ofs = 0;
for (int i = 0; i < mipmap_images.size(); i++) {
Vector<uint8_t> id = mipmap_images[i]->get_data();
int len = id.size();
const uint8_t *r = id.ptr();
memcpy(&wr[ofs], r, len);
ofs += len;
}
}
image->set_data(w, h, true, mipmap_images[0]->get_format(), img_data);
return image;
}
} else if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) {
int sw = w;
int sh = h;
uint32_t size = f->get_32();
if (p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) {
//can't load this due to size limit
sw = MAX(sw >> 1, 1);
sh = MAX(sh >> 1, 1);
f->seek(f->get_position() + size);
return Ref<Image>();
}
Vector<uint8_t> pv;
pv.resize(size);
{
uint8_t *wr = pv.ptrw();
f->get_buffer(wr, size);
}
Ref<Image> img;
img = Image::basis_universal_unpacker(pv);
if (img.is_null() || img->is_empty()) {
ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());
}
format = img->get_format();
sw = MAX(sw >> 1, 1);
sh = MAX(sh >> 1, 1);
return img;
} else if (data_format == DATA_FORMAT_IMAGE) {
int size = Image::get_image_data_size(w, h, format, mipmaps ? true : false);
for (uint32_t i = 0; i < mipmaps + 1; i++) {
int tw, th;
int ofs = Image::get_image_mipmap_offset_and_dimensions(w, h, format, i, tw, th);
if (p_size_limit > 0 && i < mipmaps && (p_size_limit > tw || p_size_limit > th)) {
if (ofs) {
f->seek(f->get_position() + ofs);
}
continue; //oops, size limit enforced, go to next
}
Vector<uint8_t> data;
data.resize(size - ofs);
{
uint8_t *wr = data.ptrw();
f->get_buffer(wr, data.size());
}
Ref<Image> image = Image::create_from_data(tw, th, mipmaps - i ? true : false, format, data);
return image;
}
}
return Ref<Image>();
}
void CompressedTexture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture2D::load);
ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture2D::get_load_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
}
CompressedTexture2D::~CompressedTexture2D() {
if (texture.is_valid()) {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
Ref<Resource> ResourceFormatLoaderCompressedTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Ref<CompressedTexture2D> st;
st.instantiate();
Error err = st->load(p_path);
if (r_error) {
*r_error = err;
}
if (err != OK) {
return Ref<Resource>();
}
return st;
}
void ResourceFormatLoaderCompressedTexture2D::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("ctex");
}
bool ResourceFormatLoaderCompressedTexture2D::handles_type(const String &p_type) const {
return p_type == "CompressedTexture2D";
}
String ResourceFormatLoaderCompressedTexture2D::get_resource_type(const String &p_path) const {
if (p_path.get_extension().to_lower() == "ctex") {
return "CompressedTexture2D";
}
return "";
}
void CompressedTexture3D::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 CompressedTexture3D::get_format() const {
return format;
}
Error CompressedTexture3D::_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) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
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 compressed textures (used for lossless and lossy compression)
uint32_t version = f->get_32();
if (version > FORMAT_VERSION) {
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed 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 mipmap_count = f->get_32();
f->get_32(); //ignored
f->get_32(); //ignored
r_mipmaps = mipmap_count != 0;
r_data.clear();
for (int i = 0; i < (r_depth + mipmap_count); i++) {
Ref<Image> image = CompressedTexture2D::load_image_from_file(f, 0);
ERR_FAIL_COND_V(image.is_null() || image->is_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 CompressedTexture3D::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().is_empty()) {
//temporarily set path if no path set for resource, helps find errors
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
notify_property_list_changed();
emit_changed();
return OK;
}
String CompressedTexture3D::get_load_path() const {
return path_to_file;
}
int CompressedTexture3D::get_width() const {
return w;
}
int CompressedTexture3D::get_height() const {
return h;
}
int CompressedTexture3D::get_depth() const {
return d;
}
bool CompressedTexture3D::has_mipmaps() const {
return mipmaps;
}
RID CompressedTexture3D::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_3d_placeholder_create();
}
return texture;
}
Vector<Ref<Image>> CompressedTexture3D::get_data() const {
if (texture.is_valid()) {
return RS::get_singleton()->texture_3d_get(texture);
} else {
return Vector<Ref<Image>>();
}
}
void CompressedTexture3D::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 CompressedTexture3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture3D::load);
ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture3D::get_load_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
}
CompressedTexture3D::~CompressedTexture3D() {
if (texture.is_valid()) {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
Ref<Resource> ResourceFormatLoaderCompressedTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Ref<CompressedTexture3D> st;
st.instantiate();
Error err = st->load(p_path);
if (r_error) {
*r_error = err;
}
if (err != OK) {
return Ref<Resource>();
}
return st;
}
void ResourceFormatLoaderCompressedTexture3D::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("ctex3d");
}
bool ResourceFormatLoaderCompressedTexture3D::handles_type(const String &p_type) const {
return p_type == "CompressedTexture3D";
}
String ResourceFormatLoaderCompressedTexture3D::get_resource_type(const String &p_path) const {
if (p_path.get_extension().to_lower() == "ctex3d") {
return "CompressedTexture3D";
}
return "";
}
void CompressedTextureLayered::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 CompressedTextureLayered::get_format() const {
return format;
}
Error CompressedTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) {
ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER);
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L') {
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture layered file is corrupt (Bad header).");
}
uint32_t version = f->get_32();
if (version > FORMAT_VERSION) {
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
}
uint32_t layer_count = f->get_32(); //layer count
uint32_t type = f->get_32(); //layer count
ERR_FAIL_COND_V((int)type != layered_type, ERR_INVALID_DATA);
uint32_t df = f->get_32(); //data format
mipmap_limit = int(f->get_32());
//reserved
f->get_32();
f->get_32();
f->get_32();
if (!(df & FORMAT_BIT_STREAM)) {
p_size_limit = 0;
}
images.resize(layer_count);
for (uint32_t i = 0; i < layer_count; i++) {
Ref<Image> image = CompressedTexture2D::load_image_from_file(f, p_size_limit);
ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
images.write[i] = image;
}
return OK;
}
Error CompressedTextureLayered::load(const String &p_path) {
Vector<Ref<Image>> images;
int mipmap_limit;
Error err = _load_data(p_path, images, mipmap_limit);
if (err) {
return err;
}
if (texture.is_valid()) {
RID new_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
RS::get_singleton()->texture_replace(texture, new_texture);
} else {
texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
}
w = images[0]->get_width();
h = images[0]->get_height();
mipmaps = images[0]->has_mipmaps();
format = images[0]->get_format();
layers = images.size();
path_to_file = p_path;
if (get_path().is_empty()) {
//temporarily set path if no path set for resource, helps find errors
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
notify_property_list_changed();
emit_changed();
return OK;
}
String CompressedTextureLayered::get_load_path() const {
return path_to_file;
}
int CompressedTextureLayered::get_width() const {
return w;
}
int CompressedTextureLayered::get_height() const {
return h;
}
int CompressedTextureLayered::get_layers() const {
return layers;
}
bool CompressedTextureLayered::has_mipmaps() const {
return mipmaps;
}
TextureLayered::LayeredType CompressedTextureLayered::get_layered_type() const {
return layered_type;
}
RID CompressedTextureLayered::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
}
return texture;
}
Ref<Image> CompressedTextureLayered::get_layer_data(int p_layer) const {
if (texture.is_valid()) {
ERR_FAIL_INDEX_V(p_layer, get_layers(), Ref<Image>());
return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
} else {
return Ref<Image>();
}
}
void CompressedTextureLayered::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 CompressedTextureLayered::_bind_methods() {
ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTextureLayered::load);
ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTextureLayered::get_load_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
}
CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) {
layered_type = p_type;
}
CompressedTextureLayered::~CompressedTextureLayered() {
if (texture.is_valid()) {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
/////////////////////////////////////////////////
Ref<Resource> ResourceFormatLoaderCompressedTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Ref<CompressedTextureLayered> ct;
if (p_path.get_extension().to_lower() == "ctexarray") {
Ref<CompressedTexture2DArray> c;
c.instantiate();
ct = c;
} else if (p_path.get_extension().to_lower() == "ccube") {
Ref<CompressedCubemap> c;
c.instantiate();
ct = c;
} else if (p_path.get_extension().to_lower() == "ccubearray") {
Ref<CompressedCubemapArray> c;
c.instantiate();
ct = c;
} else {
if (r_error) {
*r_error = ERR_FILE_UNRECOGNIZED;
}
return Ref<Resource>();
}
Error err = ct->load(p_path);
if (r_error) {
*r_error = err;
}
if (err != OK) {
return Ref<Resource>();
}
return ct;
}
void ResourceFormatLoaderCompressedTextureLayered::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("ctexarray");
p_extensions->push_back("ccube");
p_extensions->push_back("ccubearray");
}
bool ResourceFormatLoaderCompressedTextureLayered::handles_type(const String &p_type) const {
return p_type == "CompressedTexture2DArray" || p_type == "CompressedCubemap" || p_type == "CompressedCubemapArray";
}
String ResourceFormatLoaderCompressedTextureLayered::get_resource_type(const String &p_path) const {
if (p_path.get_extension().to_lower() == "ctexarray") {
return "CompressedTexture2DArray";
}
if (p_path.get_extension().to_lower() == "ccube") {
return "CompressedCubemap";
}
if (p_path.get_extension().to_lower() == "ccubearray") {
return "CompressedCubemapArray";
}
return "";
}