Path: blob/master/scene/resources/compressed_texture.cpp
9903 views
/**************************************************************************/1/* compressed_texture.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "compressed_texture.h"3132#include "scene/resources/bit_map.h"3334Error 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) {35alpha_cache.unref();3637ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);3839Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);40ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));4142uint8_t header[4];43f->get_buffer(header, 4);44if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != '2') {45ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is corrupt (Bad header).");46}4748uint32_t version = f->get_32();4950if (version > FORMAT_VERSION) {51ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");52}53r_width = f->get_32();54r_height = f->get_32();55uint32_t df = f->get_32(); //data format5657//skip reserved58mipmap_limit = int(f->get_32());59//reserved60f->get_32();61f->get_32();62f->get_32();6364#ifdef TOOLS_ENABLED6566r_request_3d = request_3d_callback && df & FORMAT_BIT_DETECT_3D;67r_request_roughness = request_roughness_callback && df & FORMAT_BIT_DETECT_ROUGNESS;68r_request_normal = request_normal_callback && df & FORMAT_BIT_DETECT_NORMAL;6970#else7172r_request_3d = false;73r_request_roughness = false;74r_request_normal = false;7576#endif77if (!(df & FORMAT_BIT_STREAM)) {78p_size_limit = 0;79}8081image = load_image_from_file(f, p_size_limit);8283if (image.is_null() || image->is_empty()) {84return ERR_CANT_OPEN;85}8687return OK;88}8990void CompressedTexture2D::set_path(const String &p_path, bool p_take_over) {91if (texture.is_valid()) {92RenderingServer::get_singleton()->texture_set_path(texture, p_path);93}9495Resource::set_path(p_path, p_take_over);96}9798void CompressedTexture2D::_requested_3d(void *p_ud) {99CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;100Ref<CompressedTexture2D> ctex(ct);101ERR_FAIL_NULL(request_3d_callback);102request_3d_callback(ctex);103}104105void CompressedTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) {106CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;107Ref<CompressedTexture2D> ctex(ct);108ERR_FAIL_NULL(request_roughness_callback);109request_roughness_callback(ctex, p_normal_path, p_roughness_channel);110}111112void CompressedTexture2D::_requested_normal(void *p_ud) {113CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;114Ref<CompressedTexture2D> ctex(ct);115ERR_FAIL_NULL(request_normal_callback);116request_normal_callback(ctex);117}118119CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_3d_callback = nullptr;120CompressedTexture2D::TextureFormatRoughnessRequestCallback CompressedTexture2D::request_roughness_callback = nullptr;121CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_normal_callback = nullptr;122123Image::Format CompressedTexture2D::get_format() const {124return format;125}126127Error CompressedTexture2D::load(const String &p_path) {128int lw, lh;129Ref<Image> image;130image.instantiate();131132bool request_3d;133bool request_normal;134bool request_roughness;135int mipmap_limit;136137Error err = _load_data(p_path, lw, lh, image, request_3d, request_normal, request_roughness, mipmap_limit);138if (err) {139return err;140}141142if (texture.is_valid()) {143RID new_texture = RS::get_singleton()->texture_2d_create(image);144RS::get_singleton()->texture_replace(texture, new_texture);145} else {146texture = RS::get_singleton()->texture_2d_create(image);147}148if (lw || lh) {149RS::get_singleton()->texture_set_size_override(texture, lw, lh);150}151152w = lw;153h = lh;154path_to_file = p_path;155format = image->get_format();156157if (get_path().is_empty()) {158//temporarily set path if no path set for resource, helps find errors159RenderingServer::get_singleton()->texture_set_path(texture, p_path);160}161162#ifdef TOOLS_ENABLED163164if (request_3d) {165//print_line("request detect 3D at " + p_path);166RS::get_singleton()->texture_set_detect_3d_callback(texture, _requested_3d, this);167} else {168//print_line("not requesting detect 3D at " + p_path);169RS::get_singleton()->texture_set_detect_3d_callback(texture, nullptr, nullptr);170}171172if (request_roughness) {173//print_line("request detect srgb at " + p_path);174RS::get_singleton()->texture_set_detect_roughness_callback(texture, _requested_roughness, this);175} else {176//print_line("not requesting detect srgb at " + p_path);177RS::get_singleton()->texture_set_detect_roughness_callback(texture, nullptr, nullptr);178}179180if (request_normal) {181//print_line("request detect srgb at " + p_path);182RS::get_singleton()->texture_set_detect_normal_callback(texture, _requested_normal, this);183} else {184//print_line("not requesting detect normal at " + p_path);185RS::get_singleton()->texture_set_detect_normal_callback(texture, nullptr, nullptr);186}187188#endif189notify_property_list_changed();190emit_changed();191return OK;192}193194String CompressedTexture2D::get_load_path() const {195return path_to_file;196}197198int CompressedTexture2D::get_width() const {199return w;200}201202int CompressedTexture2D::get_height() const {203return h;204}205206RID CompressedTexture2D::get_rid() const {207if (!texture.is_valid()) {208texture = RS::get_singleton()->texture_2d_placeholder_create();209}210return texture;211}212213void CompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {214if ((w | h) == 0) {215return;216}217RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);218}219220void CompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {221if ((w | h) == 0) {222return;223}224RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);225}226227void 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 {228if ((w | h) == 0) {229return;230}231RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);232}233234bool CompressedTexture2D::has_alpha() const {235return false;236}237238Ref<Image> CompressedTexture2D::get_image() const {239if (texture.is_valid()) {240return RS::get_singleton()->texture_2d_get(texture);241} else {242return Ref<Image>();243}244}245246bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {247if (alpha_cache.is_null()) {248Ref<Image> img = get_image();249if (img.is_valid()) {250if (img->is_compressed()) { //must decompress, if compressed251Ref<Image> decom = img->duplicate();252decom->decompress();253img = decom;254}255256alpha_cache.instantiate();257alpha_cache->create_from_image_alpha(img);258}259}260261if (alpha_cache.is_valid()) {262int aw = int(alpha_cache->get_size().width);263int ah = int(alpha_cache->get_size().height);264if (aw == 0 || ah == 0) {265return true;266}267268int x = p_x * aw / w;269int y = p_y * ah / h;270271x = CLAMP(x, 0, aw - 1);272y = CLAMP(y, 0, ah - 1);273274return alpha_cache->get_bit(x, y);275}276277return true;278}279280void CompressedTexture2D::reload_from_file() {281String path = get_path();282if (!path.is_resource_file()) {283return;284}285286path = ResourceLoader::path_remap(path); //remap for translation287path = ResourceLoader::import_remap(path); //remap for import288if (!path.is_resource_file()) {289return;290}291292load(path);293}294295Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_size_limit) {296uint32_t data_format = f->get_32();297uint32_t w = f->get_16();298uint32_t h = f->get_16();299uint32_t mipmaps = f->get_32();300Image::Format format = Image::Format(f->get_32());301302if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP) {303//look for a PNG or WebP file inside304305int sw = w;306int sh = h;307308//mipmaps need to be read independently, they will be later combined309Vector<Ref<Image>> mipmap_images;310uint64_t total_size = 0;311312for (uint32_t i = 0; i < mipmaps + 1; i++) {313uint32_t size = f->get_32();314315if (p_size_limit > 0 && i < (mipmaps - 1) && (sw > p_size_limit || sh > p_size_limit)) {316//can't load this due to size limit317sw = MAX(sw >> 1, 1);318sh = MAX(sh >> 1, 1);319f->seek(f->get_position() + size);320continue;321}322323Vector<uint8_t> pv;324pv.resize(size);325{326uint8_t *wr = pv.ptrw();327f->get_buffer(wr, size);328}329330Ref<Image> img;331if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) {332img = Image::png_unpacker(pv);333} else if (data_format == DATA_FORMAT_WEBP && Image::webp_unpacker) {334img = Image::webp_unpacker(pv);335}336337if (img.is_null() || img->is_empty()) {338ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());339}340// If the image is compressed and its format doesn't match the desired format, return an empty reference.341// This is done to avoid recompressing the image on load.342ERR_FAIL_COND_V(img->is_compressed() && format != img->get_format(), Ref<Image>());343344// The format will actually be the format of the header,345// as it may have changed on compression.346if (format != img->get_format()) {347// Convert the image to the desired format.348// Note: We are not decompressing the image here, just changing its format.349// It's important that all images in the texture array share the same format for correct rendering.350img->convert(format);351}352353total_size += img->get_data().size();354355mipmap_images.push_back(img);356357sw = MAX(sw >> 1, 1);358sh = MAX(sh >> 1, 1);359}360361//print_line("mipmap read total: " + itos(mipmap_images.size()));362363Ref<Image> image;364image.instantiate();365366if (mipmap_images.size() == 1) {367//only one image (which will most likely be the case anyway for this format)368image = mipmap_images[0];369return image;370371} else {372//rarer use case, but needs to be supported373Vector<uint8_t> img_data;374img_data.resize(total_size);375376{377uint8_t *wr = img_data.ptrw();378379int ofs = 0;380for (int i = 0; i < mipmap_images.size(); i++) {381Vector<uint8_t> id = mipmap_images[i]->get_data();382int len = id.size();383const uint8_t *r = id.ptr();384memcpy(&wr[ofs], r, len);385ofs += len;386}387}388389image->set_data(w, h, true, mipmap_images[0]->get_format(), img_data);390return image;391}392393} else if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) {394int sw = w;395int sh = h;396uint32_t size = f->get_32();397if (p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) {398//can't load this due to size limit399sw = MAX(sw >> 1, 1);400sh = MAX(sh >> 1, 1);401f->seek(f->get_position() + size);402return Ref<Image>();403}404Vector<uint8_t> pv;405pv.resize(size);406{407uint8_t *wr = pv.ptrw();408f->get_buffer(wr, size);409}410Ref<Image> img;411img = Image::basis_universal_unpacker(pv);412if (img.is_null() || img->is_empty()) {413ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());414}415format = img->get_format();416sw = MAX(sw >> 1, 1);417sh = MAX(sh >> 1, 1);418return img;419} else if (data_format == DATA_FORMAT_IMAGE) {420int size = Image::get_image_data_size(w, h, format, mipmaps ? true : false);421422for (uint32_t i = 0; i < mipmaps + 1; i++) {423int tw, th;424int ofs = Image::get_image_mipmap_offset_and_dimensions(w, h, format, i, tw, th);425426if (p_size_limit > 0 && i < mipmaps && (p_size_limit > tw || p_size_limit > th)) {427if (ofs) {428f->seek(f->get_position() + ofs);429}430continue; //oops, size limit enforced, go to next431}432433Vector<uint8_t> data;434data.resize(size - ofs);435436{437uint8_t *wr = data.ptrw();438f->get_buffer(wr, data.size());439}440441Ref<Image> image = Image::create_from_data(tw, th, mipmaps - i ? true : false, format, data);442443return image;444}445}446447return Ref<Image>();448}449450void CompressedTexture2D::_bind_methods() {451ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture2D::load);452ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture2D::get_load_path);453454ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");455}456457CompressedTexture2D::~CompressedTexture2D() {458if (texture.is_valid()) {459ERR_FAIL_NULL(RenderingServer::get_singleton());460RS::get_singleton()->free(texture);461}462}463464Ref<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) {465Ref<CompressedTexture2D> st;466st.instantiate();467Error err = st->load(p_path);468if (r_error) {469*r_error = err;470}471if (err != OK) {472return Ref<Resource>();473}474475return st;476}477478void ResourceFormatLoaderCompressedTexture2D::get_recognized_extensions(List<String> *p_extensions) const {479p_extensions->push_back("ctex");480}481482bool ResourceFormatLoaderCompressedTexture2D::handles_type(const String &p_type) const {483return p_type == "CompressedTexture2D";484}485486String ResourceFormatLoaderCompressedTexture2D::get_resource_type(const String &p_path) const {487if (p_path.get_extension().to_lower() == "ctex") {488return "CompressedTexture2D";489}490return "";491}492493void CompressedTexture3D::set_path(const String &p_path, bool p_take_over) {494if (texture.is_valid()) {495RenderingServer::get_singleton()->texture_set_path(texture, p_path);496}497498Resource::set_path(p_path, p_take_over);499}500501Image::Format CompressedTexture3D::get_format() const {502return format;503}504505Error 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) {506Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);507ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));508509uint8_t header[4];510f->get_buffer(header, 4);511ERR_FAIL_COND_V(header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L', ERR_FILE_UNRECOGNIZED);512513//stored as compressed textures (used for lossless and lossy compression)514uint32_t version = f->get_32();515516if (version > FORMAT_VERSION) {517ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");518}519520r_depth = f->get_32(); //depth521f->get_32(); //ignored (mode)522f->get_32(); // ignored (data format)523524f->get_32(); //ignored525int mipmap_count = f->get_32();526f->get_32(); //ignored527f->get_32(); //ignored528529r_mipmaps = mipmap_count != 0;530531r_data.clear();532533for (int i = 0; i < (r_depth + mipmap_count); i++) {534Ref<Image> image = CompressedTexture2D::load_image_from_file(f, 0);535ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);536if (i == 0) {537r_format = image->get_format();538r_width = image->get_width();539r_height = image->get_height();540}541r_data.push_back(image);542}543544return OK;545}546547Error CompressedTexture3D::load(const String &p_path) {548Vector<Ref<Image>> data;549550int tw, th, td;551Image::Format tfmt;552bool tmm;553554Error err = _load_data(p_path, data, tfmt, tw, th, td, tmm);555if (err) {556return err;557}558559if (texture.is_valid()) {560RID new_texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);561RS::get_singleton()->texture_replace(texture, new_texture);562} else {563texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);564}565566w = tw;567h = th;568d = td;569mipmaps = tmm;570format = tfmt;571572path_to_file = p_path;573574if (get_path().is_empty()) {575//temporarily set path if no path set for resource, helps find errors576RenderingServer::get_singleton()->texture_set_path(texture, p_path);577}578579notify_property_list_changed();580emit_changed();581return OK;582}583584String CompressedTexture3D::get_load_path() const {585return path_to_file;586}587588int CompressedTexture3D::get_width() const {589return w;590}591592int CompressedTexture3D::get_height() const {593return h;594}595596int CompressedTexture3D::get_depth() const {597return d;598}599600bool CompressedTexture3D::has_mipmaps() const {601return mipmaps;602}603604RID CompressedTexture3D::get_rid() const {605if (!texture.is_valid()) {606texture = RS::get_singleton()->texture_3d_placeholder_create();607}608return texture;609}610611Vector<Ref<Image>> CompressedTexture3D::get_data() const {612if (texture.is_valid()) {613return RS::get_singleton()->texture_3d_get(texture);614} else {615return Vector<Ref<Image>>();616}617}618619void CompressedTexture3D::reload_from_file() {620String path = get_path();621if (!path.is_resource_file()) {622return;623}624625path = ResourceLoader::path_remap(path); //remap for translation626path = ResourceLoader::import_remap(path); //remap for import627if (!path.is_resource_file()) {628return;629}630631load(path);632}633634void CompressedTexture3D::_bind_methods() {635ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture3D::load);636ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture3D::get_load_path);637638ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");639}640641CompressedTexture3D::~CompressedTexture3D() {642if (texture.is_valid()) {643ERR_FAIL_NULL(RenderingServer::get_singleton());644RS::get_singleton()->free(texture);645}646}647648Ref<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) {649Ref<CompressedTexture3D> st;650st.instantiate();651Error err = st->load(p_path);652if (r_error) {653*r_error = err;654}655if (err != OK) {656return Ref<Resource>();657}658659return st;660}661662void ResourceFormatLoaderCompressedTexture3D::get_recognized_extensions(List<String> *p_extensions) const {663p_extensions->push_back("ctex3d");664}665666bool ResourceFormatLoaderCompressedTexture3D::handles_type(const String &p_type) const {667return p_type == "CompressedTexture3D";668}669670String ResourceFormatLoaderCompressedTexture3D::get_resource_type(const String &p_path) const {671if (p_path.get_extension().to_lower() == "ctex3d") {672return "CompressedTexture3D";673}674return "";675}676677void CompressedTextureLayered::set_path(const String &p_path, bool p_take_over) {678if (texture.is_valid()) {679RenderingServer::get_singleton()->texture_set_path(texture, p_path);680}681682Resource::set_path(p_path, p_take_over);683}684685Image::Format CompressedTextureLayered::get_format() const {686return format;687}688689Error CompressedTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) {690ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER);691692Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);693ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));694695uint8_t header[4];696f->get_buffer(header, 4);697if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L') {698ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture layered file is corrupt (Bad header).");699}700701uint32_t version = f->get_32();702703if (version > FORMAT_VERSION) {704ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");705}706707uint32_t layer_count = f->get_32(); //layer count708uint32_t type = f->get_32(); //layer count709ERR_FAIL_COND_V((int)type != layered_type, ERR_INVALID_DATA);710711uint32_t df = f->get_32(); //data format712mipmap_limit = int(f->get_32());713//reserved714f->get_32();715f->get_32();716f->get_32();717718if (!(df & FORMAT_BIT_STREAM)) {719p_size_limit = 0;720}721722images.resize(layer_count);723724for (uint32_t i = 0; i < layer_count; i++) {725Ref<Image> image = CompressedTexture2D::load_image_from_file(f, p_size_limit);726ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);727images.write[i] = image;728}729730return OK;731}732733Error CompressedTextureLayered::load(const String &p_path) {734Vector<Ref<Image>> images;735736int mipmap_limit;737738Error err = _load_data(p_path, images, mipmap_limit);739if (err) {740return err;741}742743if (texture.is_valid()) {744RID new_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));745RS::get_singleton()->texture_replace(texture, new_texture);746} else {747texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));748}749750w = images[0]->get_width();751h = images[0]->get_height();752mipmaps = images[0]->has_mipmaps();753format = images[0]->get_format();754layers = images.size();755756path_to_file = p_path;757758if (get_path().is_empty()) {759//temporarily set path if no path set for resource, helps find errors760RenderingServer::get_singleton()->texture_set_path(texture, p_path);761}762763notify_property_list_changed();764emit_changed();765return OK;766}767768String CompressedTextureLayered::get_load_path() const {769return path_to_file;770}771772int CompressedTextureLayered::get_width() const {773return w;774}775776int CompressedTextureLayered::get_height() const {777return h;778}779780int CompressedTextureLayered::get_layers() const {781return layers;782}783784bool CompressedTextureLayered::has_mipmaps() const {785return mipmaps;786}787788TextureLayered::LayeredType CompressedTextureLayered::get_layered_type() const {789return layered_type;790}791792RID CompressedTextureLayered::get_rid() const {793if (!texture.is_valid()) {794texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));795}796return texture;797}798799Ref<Image> CompressedTextureLayered::get_layer_data(int p_layer) const {800if (texture.is_valid()) {801ERR_FAIL_INDEX_V(p_layer, get_layers(), Ref<Image>());802return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);803} else {804return Ref<Image>();805}806}807808void CompressedTextureLayered::reload_from_file() {809String path = get_path();810if (!path.is_resource_file()) {811return;812}813814path = ResourceLoader::path_remap(path); //remap for translation815path = ResourceLoader::import_remap(path); //remap for import816if (!path.is_resource_file()) {817return;818}819820load(path);821}822823void CompressedTextureLayered::_bind_methods() {824ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTextureLayered::load);825ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTextureLayered::get_load_path);826827ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");828}829830CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) {831layered_type = p_type;832}833834CompressedTextureLayered::~CompressedTextureLayered() {835if (texture.is_valid()) {836ERR_FAIL_NULL(RenderingServer::get_singleton());837RS::get_singleton()->free(texture);838}839}840841/////////////////////////////////////////////////842843Ref<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) {844Ref<CompressedTextureLayered> ct;845if (p_path.get_extension().to_lower() == "ctexarray") {846Ref<CompressedTexture2DArray> c;847c.instantiate();848ct = c;849} else if (p_path.get_extension().to_lower() == "ccube") {850Ref<CompressedCubemap> c;851c.instantiate();852ct = c;853} else if (p_path.get_extension().to_lower() == "ccubearray") {854Ref<CompressedCubemapArray> c;855c.instantiate();856ct = c;857} else {858if (r_error) {859*r_error = ERR_FILE_UNRECOGNIZED;860}861return Ref<Resource>();862}863Error err = ct->load(p_path);864if (r_error) {865*r_error = err;866}867if (err != OK) {868return Ref<Resource>();869}870871return ct;872}873874void ResourceFormatLoaderCompressedTextureLayered::get_recognized_extensions(List<String> *p_extensions) const {875p_extensions->push_back("ctexarray");876p_extensions->push_back("ccube");877p_extensions->push_back("ccubearray");878}879880bool ResourceFormatLoaderCompressedTextureLayered::handles_type(const String &p_type) const {881return p_type == "CompressedTexture2DArray" || p_type == "CompressedCubemap" || p_type == "CompressedCubemapArray";882}883884String ResourceFormatLoaderCompressedTextureLayered::get_resource_type(const String &p_path) const {885if (p_path.get_extension().to_lower() == "ctexarray") {886return "CompressedTexture2DArray";887}888if (p_path.get_extension().to_lower() == "ccube") {889return "CompressedCubemap";890}891if (p_path.get_extension().to_lower() == "ccubearray") {892return "CompressedCubemapArray";893}894return "";895}896897898