diff options
Diffstat (limited to 'scene/resources/texture.cpp')
-rw-r--r-- | scene/resources/texture.cpp | 748 |
1 files changed, 690 insertions, 58 deletions
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index c0f6756fd1..811e5c3d2c 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "texture.h" +#include "bit_mask.h" #include "core/method_bind_ext.gen.inc" #include "core/os/os.h" #include "core_string_names.h" @@ -39,6 +40,9 @@ Size2 Texture::get_size() const { return Size2(get_width(), get_height()); } +bool Texture::is_pixel_opaque(int p_x, int p_y) const { + return true; +} void Texture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) const { RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); @@ -76,7 +80,9 @@ void Texture::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "normal_map", "clip_uv"), &Texture::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_data"), &Texture::get_data); + ADD_GROUP("Flags", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"), "set_flags", "get_flags"); + ADD_GROUP("", ""); BIND_ENUM_CONSTANT(FLAGS_DEFAULT); BIND_ENUM_CONSTANT(FLAG_MIPMAPS); @@ -122,7 +128,7 @@ bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) { Size2 s = p_value; w = s.width; h = s.height; - VisualServer::get_singleton()->texture_set_size_override(texture, w, h); + VisualServer::get_singleton()->texture_set_size_override(texture, w, h, 0); } else if (p_name == "_data") { _set_data(p_value); } else @@ -149,13 +155,6 @@ bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const { void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const { - PropertyHint img_hint = PROPERTY_HINT_NONE; - if (storage == STORAGE_COMPRESS_LOSSY) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSY; - } else if (storage == STORAGE_COMPRESS_LOSSLESS) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS; - } - p_list->push_back(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic,sRGB,Mirrored Repeat")); p_list->push_back(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "")); @@ -181,7 +180,7 @@ void ImageTexture::_reload_hook(const RID &p_hook) { void ImageTexture::create(int p_width, int p_height, Image::Format p_format, uint32_t p_flags) { flags = p_flags; - VisualServer::get_singleton()->texture_allocate(texture, p_width, p_height, p_format, p_flags); + VisualServer::get_singleton()->texture_allocate(texture, p_width, p_height, 0, p_format, VS::TEXTURE_TYPE_2D, p_flags); format = p_format; w = p_width; h = p_height; @@ -194,7 +193,7 @@ void ImageTexture::create_from_image(const Ref<Image> &p_image, uint32_t p_flags h = p_image->get_height(); format = p_image->get_format(); - VisualServer::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), p_image->get_format(), p_flags); + VisualServer::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, p_flags); VisualServer::get_singleton()->texture_set_data(texture, p_image); _change_notify(); } @@ -219,20 +218,27 @@ Image::Format ImageTexture::get_format() const { return format; } +#ifndef DISABLE_DEPRECATED +Error ImageTexture::load(const String &p_path) { -void ImageTexture::load(const String &p_path) { - + WARN_DEPRECATED Ref<Image> img; img.instance(); - img->load(p_path); - create_from_image(img); + Error err = img->load(p_path); + if (err == OK) { + create_from_image(img); + } + return err; } - +#endif void ImageTexture::set_data(const Ref<Image> &p_image) { + ERR_FAIL_COND(p_image.is_null()); + VisualServer::get_singleton()->texture_set_data(texture, p_image); _change_notify(); + alpha_cache.unref(); } void ImageTexture::_resource_path_changed() { @@ -287,6 +293,41 @@ void ImageTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, normal_rid, p_clip_uv); } +bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const { + + if (!alpha_cache.is_valid()) { + Ref<Image> img = get_data(); + if (img.is_valid()) { + if (img->is_compressed()) { //must decompress, if compressed + Ref<Image> decom = img->duplicate(); + decom->decompress(); + img = decom; + } + alpha_cache.instance(); + 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); + y = CLAMP(y, 0, aw); + + return alpha_cache->get_bit(Point2(x, y)); + } + + return true; +} + void ImageTexture::set_size_override(const Size2 &p_size) { Size2 s = p_size; @@ -294,7 +335,7 @@ void ImageTexture::set_size_override(const Size2 &p_size) { w = s.x; if (s.y != 0) h = s.y; - VisualServer::get_singleton()->texture_set_size_override(texture, w, h); + VisualServer::get_singleton()->texture_set_size_override(texture, w, h, 0); } void ImageTexture::set_path(const String &p_path, bool p_take_over) { @@ -345,7 +386,9 @@ void ImageTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "width", "height", "format", "flags"), &ImageTexture::create, DEFVAL(FLAGS_DEFAULT)); ClassDB::bind_method(D_METHOD("create_from_image", "image", "flags"), &ImageTexture::create_from_image, DEFVAL(FLAGS_DEFAULT)); ClassDB::bind_method(D_METHOD("get_format"), &ImageTexture::get_format); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("load", "path"), &ImageTexture::load); +#endif ClassDB::bind_method(D_METHOD("set_data", "image"), &ImageTexture::set_data); ClassDB::bind_method(D_METHOD("set_storage", "mode"), &ImageTexture::set_storage); ClassDB::bind_method(D_METHOD("get_storage"), &ImageTexture::get_storage); @@ -418,6 +461,8 @@ Image::Format StreamTexture::get_format() const { Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &flags, Ref<Image> &image, int p_size_limit) { + alpha_cache.unref(); + ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER); FileAccess *f = FileAccess::open(p_path, FileAccess::READ); @@ -435,7 +480,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla flags = f->get_32(); //texture flags! uint32_t df = f->get_32(); //data format -/* + /* print_line("width: " + itos(tw)); print_line("height: " + itos(th)); print_line("flags: " + itos(flags)); @@ -585,7 +630,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla int sh = th; int mipmaps = Image::get_image_required_mipmaps(tw, th, format); - int total_size = Image::get_image_data_size(tw, th, format, mipmaps); + int total_size = Image::get_image_data_size(tw, th, format, true); int idx = 0; int ofs = 0; @@ -618,7 +663,11 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla memdelete(f); - if (bytes != total_size - ofs) { + int expected = total_size - ofs; + if (bytes < expected) { + //this is a compatibility workaround for older format, which saved less mipmaps. It is still recommended the image is reimported. + zeromem(w.ptr() + bytes, (expected - bytes)); + } else if (bytes != expected) { ERR_FAIL_V(ERR_FILE_CORRUPT); } } @@ -641,7 +690,7 @@ Error StreamTexture::load(const String &p_path) { if (err) return err; - VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), image->get_format(), lflags); + VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), 0, image->get_format(), VS::TEXTURE_TYPE_2D, lflags); VS::get_singleton()->texture_set_data(texture, image); w = lw; @@ -702,6 +751,40 @@ Ref<Image> StreamTexture::get_data() const { return VS::get_singleton()->texture_get_data(texture); } +bool StreamTexture::is_pixel_opaque(int p_x, int p_y) const { + + if (!alpha_cache.is_valid()) { + Ref<Image> img = get_data(); + if (img.is_valid()) { + if (img->is_compressed()) { //must decompress, if compressed + Ref<Image> decom = img->duplicate(); + decom->decompress(); + img = decom; + } + alpha_cache.instance(); + 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); + y = CLAMP(y, 0, aw); + + return alpha_cache->get_bit(Point2(x, y)); + } + + return true; +} void StreamTexture::set_flags(uint32_t p_flags) { flags = p_flags; VS::get_singleton()->texture_set_flags(texture, flags); @@ -939,30 +1022,12 @@ void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map, bool p_clip_uv) const { //this might not necessarily work well if using a rect, needs to be fixed properly - Rect2 rc = region; - if (!atlas.is_valid()) return; - Rect2 src = p_src_rect; - src.position += (rc.position - margin.position); - Rect2 src_c = rc.clip(src); - if (src_c.size == Size2()) - return; - Vector2 ofs = (src_c.position - src.position); - - Vector2 scale = p_rect.size / p_src_rect.size; - if (scale.x < 0) { - float mx = (margin.size.width - margin.position.x); - mx -= margin.position.x; - ofs.x = -(ofs.x + mx); - } - if (scale.y < 0) { - float my = margin.size.height - margin.position.y; - my -= margin.position.y; - ofs.y = -(ofs.y + my); - } - Rect2 dr(p_rect.position + ofs * scale, src_c.size * scale); + Rect2 dr; + Rect2 src_c; + get_rect_region(p_rect, p_src_rect, dr, src_c); RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); VS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), src_c, p_modulate, p_transpose, normal_rid, filter_clip); @@ -976,13 +1041,17 @@ bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, return false; Rect2 src = p_src_rect; + if (src.size == Size2()) { + src.size = rc.size; + } + Vector2 scale = p_rect.size / src.size; + src.position += (rc.position - margin.position); Rect2 src_c = rc.clip(src); if (src_c.size == Size2()) return false; Vector2 ofs = (src_c.position - src.position); - Vector2 scale = p_rect.size / p_src_rect.size; if (scale.x < 0) { float mx = (margin.size.width - margin.position.x); mx -= margin.position.x; @@ -1000,6 +1069,15 @@ bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, return true; } +bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const { + + if (atlas.is_valid()) { + return atlas->is_pixel_opaque(p_x + region.position.x + margin.position.x, p_x + region.position.y + margin.position.y); + } + + return true; +} + AtlasTexture::AtlasTexture() { filter_clip = false; } @@ -1032,7 +1110,7 @@ bool LargeTexture::has_alpha() const { void LargeTexture::set_flags(uint32_t p_flags) { for (int i = 0; i < pieces.size(); i++) { - pieces[i].texture->set_flags(p_flags); + pieces.write[i].texture->set_flags(p_flags); } } @@ -1058,13 +1136,13 @@ int LargeTexture::add_piece(const Point2 &p_offset, const Ref<Texture> &p_textur void LargeTexture::set_piece_offset(int p_idx, const Point2 &p_offset) { ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces[p_idx].offset = p_offset; + pieces.write[p_idx].offset = p_offset; }; void LargeTexture::set_piece_texture(int p_idx, const Ref<Texture> &p_texture) { ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces[p_idx].texture = p_texture; + pieces.write[p_idx].texture = p_texture; }; void LargeTexture::set_size(const Size2 &p_size) { @@ -1148,7 +1226,6 @@ void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile Size2 scale = p_rect.size / size; - RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); for (int i = 0; i < pieces.size(); i++) { // TODO @@ -1163,7 +1240,6 @@ void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons Size2 scale = p_rect.size / p_src_rect.size; - RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); for (int i = 0; i < pieces.size(); i++) { // TODO @@ -1179,6 +1255,23 @@ void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons } } +bool LargeTexture::is_pixel_opaque(int p_x, int p_y) const { + + for (int i = 0; i < pieces.size(); i++) { + + // TODO + if (!pieces[i].texture.is_valid()) + continue; + + Rect2 rect(pieces[i].offset, pieces[i].texture->get_size()); + if (rect.has_point(Point2(p_x, p_y))) { + return pieces[i].texture->is_pixel_opaque(p_x - rect.position.x, p_y - rect.position.y); + } + } + + return true; +} + LargeTexture::LargeTexture() { } @@ -1188,7 +1281,7 @@ void CubeMap::set_flags(uint32_t p_flags) { flags = p_flags; if (_is_valid()) - VS::get_singleton()->texture_set_flags(cubemap, flags | VS::TEXTURE_FLAG_CUBEMAP); + VS::get_singleton()->texture_set_flags(cubemap, flags); } uint32_t CubeMap::get_flags() const { @@ -1204,7 +1297,7 @@ void CubeMap::set_side(Side p_side, const Ref<Image> &p_image) { format = p_image->get_format(); w = p_image->get_width(); h = p_image->get_height(); - VS::get_singleton()->texture_allocate(cubemap, w, h, p_image->get_format(), flags | VS::TEXTURE_FLAG_CUBEMAP); + VS::get_singleton()->texture_allocate(cubemap, w, h, 0, p_image->get_format(), VS::TEXTURE_TYPE_CUBEMAP, flags); } VS::get_singleton()->texture_set_data(cubemap, p_image, VS::CubeMapSide(p_side)); @@ -1315,13 +1408,6 @@ bool CubeMap::_get(const StringName &p_name, Variant &r_ret) const { void CubeMap::_get_property_list(List<PropertyInfo> *p_list) const { - PropertyHint img_hint = PROPERTY_HINT_NONE; - if (storage == STORAGE_COMPRESS_LOSSY) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSY; - } else if (storage == STORAGE_COMPRESS_LOSSLESS) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS; - } - p_list->push_back(PropertyInfo(Variant::OBJECT, "side/left", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/right", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/bottom", PROPERTY_HINT_RESOURCE_TYPE, "Image")); @@ -1467,7 +1553,7 @@ void CurveTexture::_update() { Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data)); - VS::get_singleton()->texture_allocate(_texture, _width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER); + VS::get_singleton()->texture_allocate(_texture, _width, 1, 0, Image::FORMAT_RF, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); VS::get_singleton()->texture_set_data(_texture, image); emit_changed(); @@ -1576,7 +1662,7 @@ void GradientTexture::_update() { Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data)); - VS::get_singleton()->texture_allocate(texture, width, 1, Image::FORMAT_RGBA8, VS::TEXTURE_FLAG_FILTER); + VS::get_singleton()->texture_allocate(texture, width, 1, 0, Image::FORMAT_RGBA8, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); VS::get_singleton()->texture_set_data(texture, image); emit_changed(); @@ -1664,3 +1750,549 @@ ProxyTexture::~ProxyTexture() { VS::get_singleton()->free(proxy); } +////////////////////////////////////////////// + +void AnimatedTexture::_update_proxy() { + + RWLockRead r(rw_lock); + + float delta; + if (prev_ticks == 0) { + delta = 0; + prev_ticks = OS::get_singleton()->get_ticks_usec(); + } else { + uint64_t ticks = OS::get_singleton()->get_ticks_usec(); + delta = float(double(ticks - prev_ticks) / 1000000.0); + prev_ticks = ticks; + } + + time += delta; + + float limit; + + if (fps == 0) { + limit = 0; + } else { + limit = 1.0 / fps; + } + + int iter_max = frame_count; + while (iter_max) { + float frame_limit = limit + frames[current_frame].delay_sec; + + if (time > frame_limit) { + current_frame++; + if (current_frame >= frame_count) { + current_frame = 0; + } + time -= frame_limit; + } else { + break; + } + iter_max--; + } + + if (frames[current_frame].texture.is_valid()) { + VisualServer::get_singleton()->texture_set_proxy(proxy, frames[current_frame].texture->get_rid()); + } +} + +void AnimatedTexture::set_frames(int p_frames) { + ERR_FAIL_COND(p_frames < 1 || p_frames > MAX_FRAMES); + + RWLockWrite r(rw_lock); + + frame_count = p_frames; +} +int AnimatedTexture::get_frames() const { + return frame_count; +} + +void AnimatedTexture::set_frame_texture(int p_frame, const Ref<Texture> &p_texture) { + ERR_FAIL_INDEX(p_frame, MAX_FRAMES); + + RWLockWrite w(rw_lock); + + frames[p_frame].texture = p_texture; +} +Ref<Texture> AnimatedTexture::get_frame_texture(int p_frame) const { + ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, Ref<Texture>()); + + RWLockRead r(rw_lock); + + return frames[p_frame].texture; +} + +void AnimatedTexture::set_frame_delay(int p_frame, float p_delay_sec) { + ERR_FAIL_INDEX(p_frame, MAX_FRAMES); + + RWLockRead r(rw_lock); + + frames[p_frame].delay_sec = p_delay_sec; +} +float AnimatedTexture::get_frame_delay(int p_frame) const { + ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, 0); + + RWLockRead r(rw_lock); + + return frames[p_frame].delay_sec; +} + +void AnimatedTexture::set_fps(float p_fps) { + ERR_FAIL_COND(p_fps < 0 || p_fps >= 1000); + + fps = p_fps; +} +float AnimatedTexture::get_fps() const { + return fps; +} + +int AnimatedTexture::get_width() const { + RWLockRead r(rw_lock); + + if (!frames[current_frame].texture.is_valid()) { + return 1; + } + + return frames[current_frame].texture->get_width(); +} +int AnimatedTexture::get_height() const { + RWLockRead r(rw_lock); + + if (!frames[current_frame].texture.is_valid()) { + return 1; + } + + return frames[current_frame].texture->get_height(); +} +RID AnimatedTexture::get_rid() const { + return proxy; +} + +bool AnimatedTexture::has_alpha() const { + + RWLockRead r(rw_lock); + + if (!frames[current_frame].texture.is_valid()) { + return false; + } + + return frames[current_frame].texture->has_alpha(); +} + +Ref<Image> AnimatedTexture::get_data() const { + + RWLockRead r(rw_lock); + + if (!frames[current_frame].texture.is_valid()) { + return Ref<Image>(); + } + + return frames[current_frame].texture->get_data(); +} + +bool AnimatedTexture::is_pixel_opaque(int p_x, int p_y) const { + + RWLockRead r(rw_lock); + + if (frames[current_frame].texture.is_valid()) { + return frames[current_frame].texture->is_pixel_opaque(p_x, p_y); + } + return true; +} + +void AnimatedTexture::set_flags(uint32_t p_flags) { +} +uint32_t AnimatedTexture::get_flags() const { + + RWLockRead r(rw_lock); + + if (!frames[current_frame].texture.is_valid()) { + return 0; + } + + return frames[current_frame].texture->get_flags(); +} + +void AnimatedTexture::_validate_property(PropertyInfo &property) const { + + String prop = property.name; + if (prop.begins_with("frame_")) { + int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int(); + if (frame >= frame_count) { + property.usage = 0; + } + } +} + +void AnimatedTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_frames", "frames"), &AnimatedTexture::set_frames); + ClassDB::bind_method(D_METHOD("get_frames"), &AnimatedTexture::get_frames); + + ClassDB::bind_method(D_METHOD("set_fps", "fps"), &AnimatedTexture::set_fps); + ClassDB::bind_method(D_METHOD("get_fps"), &AnimatedTexture::get_fps); + + ClassDB::bind_method(D_METHOD("set_frame_texture", "frame", "texture"), &AnimatedTexture::set_frame_texture); + ClassDB::bind_method(D_METHOD("get_frame_texture", "frame"), &AnimatedTexture::get_frame_texture); + + ClassDB::bind_method(D_METHOD("set_frame_delay", "frame", "delay"), &AnimatedTexture::set_frame_delay); + ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &AnimatedTexture::get_frame_delay); + + ClassDB::bind_method(D_METHOD("_update_proxy"), &AnimatedTexture::_update_proxy); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps"); + + for (int i = 0; i < MAX_FRAMES; i++) { + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_frame_texture", "get_frame_texture", i); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_frame_delay", "get_frame_delay", i); + } +} + +AnimatedTexture::AnimatedTexture() { + proxy = VS::get_singleton()->texture_create(); + VisualServer::get_singleton()->texture_set_force_redraw_if_visible(proxy, true); + time = 0; + frame_count = 1; + fps = 4; + prev_ticks = 0; + current_frame = 0; + VisualServer::get_singleton()->connect("frame_pre_draw", this, "_update_proxy"); + +#ifndef NO_THREADS + rw_lock = RWLock::create(); +#else + rw_lock = NULL; +#endif +} + +AnimatedTexture::~AnimatedTexture() { + VS::get_singleton()->free(proxy); + if (rw_lock) { + memdelete(rw_lock); + } +} +/////////////////////////////// + +void TextureLayered::set_flags(uint32_t p_flags) { + flags = p_flags; + + if (texture.is_valid()) { + VS::get_singleton()->texture_set_flags(texture, flags); + } +} + +uint32_t TextureLayered::get_flags() const { + return flags; +} + +Image::Format TextureLayered::get_format() const { + return format; +} + +uint32_t TextureLayered::get_width() const { + return width; +} + +uint32_t TextureLayered::get_height() const { + return height; +} + +uint32_t TextureLayered::get_depth() const { + return depth; +} + +void TextureLayered::_set_data(const Dictionary &p_data) { + ERR_FAIL_COND(!p_data.has("width")); + ERR_FAIL_COND(!p_data.has("height")); + ERR_FAIL_COND(!p_data.has("depth")); + ERR_FAIL_COND(!p_data.has("format")); + ERR_FAIL_COND(!p_data.has("flags")); + ERR_FAIL_COND(!p_data.has("layers")); + int w = p_data["width"]; + int h = p_data["height"]; + int d = p_data["depth"]; + Image::Format format = Image::Format(int(p_data["format"])); + int flags = p_data["flags"]; + Array layers = p_data["layers"]; + ERR_FAIL_COND(layers.size() != d); + + create(w, h, d, format, flags); + + for (int i = 0; i < layers.size(); i++) { + Ref<Image> img = layers[i]; + ERR_CONTINUE(!img.is_valid()); + ERR_CONTINUE(img->get_format() != format); + ERR_CONTINUE(img->get_width() != w); + ERR_CONTINUE(img->get_height() != h); + set_layer_data(img, i); + } +} + +Dictionary TextureLayered::_get_data() const { + Dictionary d; + d["width"] = width; + d["height"] = height; + d["depth"] = depth; + d["flags"] = flags; + d["format"] = format; + + Array layers; + for (int i = 0; i < depth; i++) { + layers.push_back(get_layer_data(i)); + } + d["layers"] = layers; + return d; +} + +void TextureLayered::create(uint32_t p_width, uint32_t p_height, uint32_t p_depth, Image::Format p_format, uint32_t p_flags) { + VS::get_singleton()->texture_allocate(texture, p_width, p_height, p_depth, p_format, is_3d ? VS::TEXTURE_TYPE_3D : VS::TEXTURE_TYPE_2D_ARRAY, p_flags); + + width = p_width; + height = p_height; + depth = p_depth; + + flags = p_flags; +} + +void TextureLayered::set_layer_data(const Ref<Image> &p_image, int p_layer) { + ERR_FAIL_COND(!texture.is_valid()); + VS::get_singleton()->texture_set_data(texture, p_image, p_layer); +} + +Ref<Image> TextureLayered::get_layer_data(int p_layer) const { + + ERR_FAIL_COND_V(!texture.is_valid(), Ref<Image>()); + return VS::get_singleton()->texture_get_data(texture, p_layer); +} + +void TextureLayered::set_data_partial(const Ref<Image> &p_image, int p_x_ofs, int p_y_ofs, int p_z, int p_mipmap) { + ERR_FAIL_COND(!texture.is_valid()); + VS::get_singleton()->texture_set_data_partial(texture, p_image, 0, 0, p_image->get_width(), p_image->get_height(), p_x_ofs, p_y_ofs, p_mipmap, p_z); +} + +RID TextureLayered::get_rid() const { + return texture; +} + +void TextureLayered::set_path(const String &p_path, bool p_take_over) { + if (texture.is_valid()) { + VS::get_singleton()->texture_set_path(texture, p_path); + } + + Resource::set_path(p_path, p_take_over); +} + +void TextureLayered::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextureLayered::set_flags); + ClassDB::bind_method(D_METHOD("get_flags"), &TextureLayered::get_flags); + + ClassDB::bind_method(D_METHOD("get_format"), &TextureLayered::get_format); + + ClassDB::bind_method(D_METHOD("get_width"), &TextureLayered::get_width); + ClassDB::bind_method(D_METHOD("get_height"), &TextureLayered::get_height); + ClassDB::bind_method(D_METHOD("get_depth"), &TextureLayered::get_depth); + + ClassDB::bind_method(D_METHOD("create", "width", "height", "depth", "format", "flags"), &TextureLayered::create, DEFVAL(FLAGS_DEFAULT)); + ClassDB::bind_method(D_METHOD("set_layer_data", "image", "layer"), &TextureLayered::set_layer_data); + ClassDB::bind_method(D_METHOD("get_layer_data", "layer"), &TextureLayered::get_layer_data); + ClassDB::bind_method(D_METHOD("set_data_partial", "image", "x_offset", "y_offset", "layer", "mipmap"), &TextureLayered::set_data_partial, DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("_set_data", "data"), &TextureLayered::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &TextureLayered::_get_data); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter"), "set_flags", "get_flags"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + + BIND_ENUM_CONSTANT(FLAG_MIPMAPS); + BIND_ENUM_CONSTANT(FLAG_REPEAT); + BIND_ENUM_CONSTANT(FLAG_FILTER); + BIND_ENUM_CONSTANT(FLAGS_DEFAULT); +} + +TextureLayered::TextureLayered(bool p_3d) { + is_3d = p_3d; + format = Image::FORMAT_MAX; + flags = FLAGS_DEFAULT; + + width = 0; + height = 0; + depth = 0; + + texture = VS::get_singleton()->texture_create(); +} + +TextureLayered::~TextureLayered() { + if (texture.is_valid()) { + VS::get_singleton()->free(texture); + } +} + +RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error) { + + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + + Ref<TextureLayered> lt; + Ref<Texture3D> tex3d; + Ref<TextureArray> texarr; + + if (p_path.ends_with("tex3d")) { + tex3d.instance(); + lt = tex3d; + } else if (p_path.ends_with("texarr")) { + texarr.instance(); + lt = texarr; + } else { + ERR_EXPLAIN("Unrecognized layered texture extension"); + ERR_FAIL_V(RES()); + } + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, RES()); + + uint8_t header[5] = { 0, 0, 0, 0, 0 }; + f->get_buffer(header, 4); + + if (header[0] == 'G' && header[1] == 'D' && header[2] == '3' && header[3] == 'T') { + if (tex3d.is_null()) { + memdelete(f); + ERR_FAIL_COND_V(tex3d.is_null(), RES()) + } + } else if (header[0] == 'G' && header[1] == 'D' && header[2] == 'A' && header[3] == 'T') { + if (texarr.is_null()) { + memdelete(f); + ERR_FAIL_COND_V(texarr.is_null(), RES()) + } + } else { + + ERR_EXPLAIN("Unrecognized layered texture file format: " + String((const char *)header)); + ERR_FAIL_V(RES()); + } + + int tw = f->get_32(); + int th = f->get_32(); + int td = f->get_32(); + int flags = f->get_32(); //texture flags! + Image::Format format = Image::Format(f->get_32()); + uint32_t compression = f->get_32(); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed + + lt->create(tw, th, td, format, flags); + + for (int layer = 0; layer < td; layer++) { + + Ref<Image> image; + image.instance(); + + if (compression == COMPRESSION_LOSSLESS) { + //look for a PNG file inside + + int mipmaps = f->get_32(); + Vector<Ref<Image> > mipmap_images; + + for (int i = 0; i < mipmaps; i++) { + uint32_t size = f->get_32(); + + PoolVector<uint8_t> pv; + pv.resize(size); + { + PoolVector<uint8_t>::Write w = pv.write(); + f->get_buffer(w.ptr(), size); + } + + Ref<Image> img = Image::lossless_unpacker(pv); + + if (img.is_null() || img->empty() || format != img->get_format()) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + memdelete(f); + ERR_FAIL_V(RES()); + } + + mipmap_images.push_back(img); + } + + if (mipmap_images.size() == 1) { + + image = mipmap_images[0]; + + } else { + int total_size = Image::get_image_data_size(tw, th, format, true); + PoolVector<uint8_t> img_data; + img_data.resize(total_size); + + { + PoolVector<uint8_t>::Write w = img_data.write(); + + int ofs = 0; + for (int i = 0; i < mipmap_images.size(); i++) { + + PoolVector<uint8_t> id = mipmap_images[i]->get_data(); + int len = id.size(); + PoolVector<uint8_t>::Read r = id.read(); + copymem(&w[ofs], r.ptr(), len); + ofs += len; + } + } + + image->create(tw, th, true, format, img_data); + if (image->empty()) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + memdelete(f); + ERR_FAIL_V(RES()); + } + } + + } else { + + //look for regular format + bool mipmaps = (flags & Texture::FLAG_MIPMAPS); + int total_size = Image::get_image_data_size(tw, th, format, mipmaps); + + PoolVector<uint8_t> img_data; + img_data.resize(total_size); + + { + PoolVector<uint8_t>::Write w = img_data.write(); + int bytes = f->get_buffer(w.ptr(), total_size); + if (bytes != total_size) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + memdelete(f); + } + ERR_FAIL_V(RES()); + } + } + + image->create(tw, th, mipmaps, format, img_data); + } + + lt->set_layer_data(image, layer); + } + + if (r_error) + *r_error = OK; + + return lt; +} + +void ResourceFormatLoaderTextureLayered::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("tex3d"); + p_extensions->push_back("texarr"); +} +bool ResourceFormatLoaderTextureLayered::handles_type(const String &p_type) const { + return p_type == "Texture3D" || p_type == "TextureArray"; +} +String ResourceFormatLoaderTextureLayered::get_resource_type(const String &p_path) const { + + if (p_path.get_extension().to_lower() == "tex3d") + return "Texture3D"; + if (p_path.get_extension().to_lower() == "texarr") + return "TextureArray"; + return ""; +} |